From 66b053424c11ca310069ec3a0883884a3e541c82 Mon Sep 17 00:00:00 2001 From: Manos Psychogyiopoulos Date: Wed, 19 May 2021 17:22:33 +0300 Subject: [PATCH] Add min quantity and step support in store api + quantity selector component --- .../components/quantity-selector/index.tsx | 35 ++++++++++++------- .../cart/full-cart/cart-line-item-row.tsx | 4 +++ src/StoreApi/Schemas/CartItemSchema.php | 16 ++++++++- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/assets/js/base/components/quantity-selector/index.tsx b/assets/js/base/components/quantity-selector/index.tsx index f5cfa9e9315..d7e2fb6102d 100644 --- a/assets/js/base/components/quantity-selector/index.tsx +++ b/assets/js/base/components/quantity-selector/index.tsx @@ -16,6 +16,7 @@ import { isNumber } from '../../utils/type-guards'; interface QuantitySelectorProps { className?: string; quantity?: number; + step?: number; minimum?: number; maximum: number; onChange: ( newQuantity: number ) => void; @@ -26,6 +27,7 @@ interface QuantitySelectorProps { const QuantitySelector = ( { className, quantity = 1, + step = 1, minimum = 1, maximum, onChange = () => { @@ -39,9 +41,10 @@ const QuantitySelector = ( { className ); + const hasStep = step > 1; const hasMaximum = typeof maximum !== 'undefined'; - const canDecrease = quantity > minimum; - const canIncrease = ! hasMaximum || quantity < maximum; + const canDecrease = quantity - step >= minimum; + const canIncrease = ! hasMaximum || quantity + step <= maximum; /** * Handles keyboard up and down keys to change quantity value. @@ -61,12 +64,12 @@ const QuantitySelector = ( { if ( isArrowDown && canDecrease ) { event.preventDefault(); - onChange( quantity - 1 ); + onChange( quantity - step ); } if ( isArrowUp && canIncrease ) { event.preventDefault(); - onChange( quantity + 1 ); + onChange( quantity + step ); } }, [ quantity, onChange, canIncrease, canDecrease ] @@ -78,19 +81,25 @@ const QuantitySelector = ( { className="wc-block-components-quantity-selector__input" disabled={ disabled } type="number" - step="1" - min="0" + step={ step } + min={ minimum } value={ quantity } onKeyDown={ quantityInputOnKeyDown } onChange={ ( event ) => { - let value = - ! isNumber( event.target.value ) || ! event.target.value - ? 0 - : parseInt( event.target.value, 10 ); + + let valueIsNumber = event.target.value && ( isNumber( event.target.value ) || parseInt( event.target.value, 10 ).toString() === event.target.value ); + let value = ! valueIsNumber ? quantity : parseInt( event.target.value, 10 ); + if ( hasMaximum ) { - value = Math.min( value, maximum ); + value = Math.min( value, Math.floor( maximum / step ) * step ); + } + + if ( hasStep ) { + value = value % step ? quantity : value; } + value = Math.max( value, minimum ); + if ( value !== quantity ) { onChange( value ); } @@ -112,7 +121,7 @@ const QuantitySelector = ( { className="wc-block-components-quantity-selector__button wc-block-components-quantity-selector__button--minus" disabled={ disabled || ! canDecrease } onClick={ () => { - const newQuantity = quantity - 1; + const newQuantity = quantity - step; onChange( newQuantity ); speak( sprintf( @@ -136,7 +145,7 @@ const QuantitySelector = ( { disabled={ disabled || ! canIncrease } className="wc-block-components-quantity-selector__button wc-block-components-quantity-selector__button--plus" onClick={ () => { - const newQuantity = quantity + 1; + const newQuantity = quantity + step; onChange( newQuantity ); speak( sprintf( diff --git a/assets/js/blocks/cart-checkout/cart/full-cart/cart-line-item-row.tsx b/assets/js/blocks/cart-checkout/cart/full-cart/cart-line-item-row.tsx index 59b410e6aa8..03c61f4f879 100644 --- a/assets/js/blocks/cart-checkout/cart/full-cart/cart-line-item-row.tsx +++ b/assets/js/blocks/cart-checkout/cart/full-cart/cart-line-item-row.tsx @@ -65,7 +65,9 @@ const CartLineItemRow = ( { description: fullDescription = '', low_stock_remaining: lowStockRemaining = null, show_backorder_badge: showBackorderBadge = false, + quantity_min: quantityMin = 1, quantity_limit: quantityLimit = 99, + quantity_step: quantityStep = 1, permalink = '', images = [], variation = [], @@ -293,6 +295,8 @@ const CartLineItemRow = ( { { setItemQuantity( newQuantity ); diff --git a/src/StoreApi/Schemas/CartItemSchema.php b/src/StoreApi/Schemas/CartItemSchema.php index c715d0de79d..bc23f42b491 100644 --- a/src/StoreApi/Schemas/CartItemSchema.php +++ b/src/StoreApi/Schemas/CartItemSchema.php @@ -50,8 +50,20 @@ public function get_properties() { 'context' => [ 'view', 'edit' ], 'readonly' => true, ], + 'quantity_min' => [ + 'description' => __( 'The minimum quantity of this item in the cart.', 'woo-gutenberg-products-block' ), + 'type' => 'integer', + 'context' => [ 'view', 'edit' ], + 'readonly' => true, + ], 'quantity_limit' => [ - 'description' => __( 'The maximum quantity than can be added to the cart at once.', 'woo-gutenberg-products-block' ), + 'description' => __( 'The maximum quantity that can be added to the cart at once.', 'woo-gutenberg-products-block' ), + 'type' => 'integer', + 'context' => [ 'view', 'edit' ], + 'readonly' => true, + ], + 'quantity_step' => [ + 'description' => __( 'The quantity step of this item in the cart.', 'woo-gutenberg-products-block' ), 'type' => 'integer', 'context' => [ 'view', 'edit' ], 'readonly' => true, @@ -312,6 +324,8 @@ public function get_item_response( $cart_item ) { 'key' => $cart_item['key'], 'id' => $product->get_id(), 'quantity' => wc_stock_amount( $cart_item['quantity'] ), + 'quantity_min' => 1, + 'quantity_step' => 1, 'quantity_limit' => $this->get_product_quantity_limit( $product ), 'name' => $this->prepare_html_response( $product->get_title() ), 'short_description' => $this->prepare_html_response( wc_format_content( wp_kses_post( $product->get_short_description() ) ) ),