Magento PWA – Quantity Increment Decrement Component

Current Magento pwa-studio provides you a dropdown field for quantity selection on product detail page which I found very amusing as it is literally useless for real world applications.
Quantity field on product details page only contains 1 to 4 quantity selection from dropdown and there is no option to the user to add more if he needs.

With this post, I will show you how we can replace this with an increment decrement field which makes more sense for a quantity field. I have created a separate component for this. Below are the things I did to achieve it.
First of all create a new component with name “ProductQuantityIncDec

Create a new file pwa-studio/packages/venia-concept/src/components/ProductQuantityIncDec/index.js
This will be a simple index file.

export { default } from './quantity';

Create another file with name pwa-studio/packages/venia-concept/src/components/ProductQuantityIncDec/quantity.js
This file contains all the elements like quantity textbox, increment and decrement buttons and quantity update functionality.

import React, { Component } from 'react';
import { arrayOf, number, shape, string } from 'prop-types';

import classify from 'src/classify';
import defaultClasses from './quantity.css';
import { Text } from 'informed';

class Quantity extends Component {
    static propTypes = {
        classes: shape({
            root: string,
            increment_button: string,
            decrement_button: string,
            quaText: string,
            quaBtn: string
        })
    };

    state = {
        quantity: 1,
        show: true,
        max: 9999,
        min: 1
    };

    constructor(props) {
        super(props);
        this.ref = React.createRef();
    }

    incrementQty = () => {
        this.setState(prevState => {
            if(prevState.quantity < this.state.max) {
                this.ref.current.value = this.state.quantity + 1;
                this.props.clickHandler(this.state.quantity + 1);
                return {
                    quantity: parseInt(prevState.quantity) + 1
                }
            } else {
                return null;
            }
        });
    };
    decrementQty = () => {
        this.setState(prevState => {
            if(prevState.quantity > this.state.min) {
                this.ref.current.value = this.state.quantity - 1;
                this.props.clickHandler(this.state.quantity - 1);
                return {
                    quantity: parseInt(prevState.quantity) - 1
                }
            } else {
                return null;
            }
        });
    };
    
    handleChange =(event)=>{
        let elementValue = parseInt(event.target.value) || 0 ;
        if(elementValue != '' && elementValue != '0') {
            this.setState({quantity: elementValue});
        }
        else {
            this.setState({quantity: ''});
        }
        var event = new Event('input', { bubbles: true });
        this.ref.current.dispatchEvent(event);
    }

    render() {
        const { classes, ...restProps } = this.props;
        return (
            <div className={classes.root}>
	            <div className={classes.quaBtn}>
	            	<span onClick={this.decrementQty} className={classes.decrement_button}>-</span>
	           	</div>
	            <div className={classes.quaText} >
	            	<Text
	                className={classes.quantity_input}
	                initialValue={`${this.state.min}`}
	                field="quantity"
	                label=""
	                forwardedRef={this.ref}
	                onChange = {this.handleChange}
	                />
	            </div>
	            <div className={classes.quaBtn}>
	            	<span onClick={this.incrementQty} className={classes.increment_button}>+</span>
	            </div>
            </div>
    );
    }

}
export default classify(defaultClasses)(Quantity);

I have also added a css file to add styling for the elements.

.root {
    display: flex;
    width: 100%;
}
.quantityTitle {
    font-size: 14px;
    font-weight: normal;
    margin: 0 0 10px 0;
}
.quantityBox {
    display: table;
    width: 100%;
    height: 36px;
    width: 100%;
}
.quaBtn {
    display: inline-block;
    width: 40px;
    vertical-align: top;
    height: 40px;
}
.quaBtn span {
    border-radius: 4px;
    background: #ebebeb;
    height: 36px;
    font-size: 28px;
    line-height: 30px;
    cursor: pointer;
    width: 40px;
    display: block;
    text-align: center;
}
.quaText {
    display: inline-block;
    vertical-align: top;
    width: 56px;
    height: 36px;
    padding: 0 5px;
}
.quaText input {
    width: 100%;
    height: 36px;
    text-align: center;
    border: #eeeeee solid 1px;
    color: #333;
    font-size: 15px;
    font-weight: 700;
    margin: 0;
    border-radius: 4px;
}
.errormsg {
    display: block;
    font-size: 13px;
    color:red;
}

That’s it. This is the component for quantity increment-decrement field. Now you can use it in your product details page to replace existing dropdown. To add the component on Product details page, edit your pwa-studio/packages/venia-concept/src/components/ProductFullDetail/ProductFullDetail.js file and import our newly created component.

import QuantityIncDecField from 'src/components/ProductQuantityIncDec';

Now replace the quantity dropdown code with below code to add our component.

<QuantityIncDecField clickHandler={this.setQuantity}/>

The component will display like below.

Magento PWA – Quantity Increment Decrement Component