Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
add min and step support
Browse files Browse the repository at this point in the history
  • Loading branch information
senadir committed Oct 28, 2021
1 parent 5c7ab9a commit 41f16e7
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 32 deletions.
77 changes: 59 additions & 18 deletions assets/js/base/components/quantity-selector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { speak } from '@wordpress/a11y';
import classNames from 'classnames';
import { useCallback } from '@wordpress/element';
import { DOWN, UP } from '@wordpress/keycodes';
import { isNumber } from '@woocommerce/types';
import useDebouncedCallback from 'use-debounce/lib/useDebouncedCallback';

/**
* Internal dependencies
Expand All @@ -18,6 +18,7 @@ interface QuantitySelectorProps {
quantity?: number;
minimum?: number;
maximum: number;
step?: number;
onChange: ( newQuantity: number ) => void;
itemName?: string;
disabled: boolean;
Expand All @@ -28,6 +29,7 @@ const QuantitySelector = ( {
quantity = 1,
minimum = 1,
maximum,
step = 1,
onChange = () => {
/* Do nothing. */
},
Expand All @@ -40,8 +42,46 @@ const QuantitySelector = ( {
);

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;

/**
* The goal of this function is to normalize what was inserted,
* but after the customer has stopped typing.
*
* It's important to wait before normalizing or we end up with
* a frustrating expiernce, for example, if the minimun is 2 and
* the customer is trying to type "10", premature normalizing would
* always kick in at "1" and turn that into 2.
*/
const [ normalizeQuantity ] = useDebouncedCallback(
( initialValue: number ) => {
// We copy the starting value.
let value = initialValue;

// We check if we have a maximum value, and select the lowest between what was inserted, and the maximum.
if ( hasMaximum ) {
value = Math.min(
value,
// the maximum possible value in step increments.
Math.floor( maximum / step ) * step
);
}

// Select the biggest between what's inserted, the the minimum value in steps.
value = Math.max( value, Math.ceil( minimum / step ) * step );

// We round off the value to our steps.
value = Math.floor( value / step ) * step;

// Only commit if the value has changed
if ( value !== initialValue ) {
onChange( value );
}
},
// This value is delibrtly smaller than what's in useStoreCartItemQuantity so we don't end up with two requests.
300
);

/**
* Handles keyboard up and down keys to change quantity value.
Expand All @@ -61,15 +101,15 @@ 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 ]
[ quantity, onChange, canIncrease, canDecrease, step ]
);

return (
Expand All @@ -78,21 +118,22 @@ 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 );
if ( hasMaximum ) {
value = Math.min( value, maximum );
}
value = Math.max( value, minimum );
// Inputs values are strings, we parse them here.
let value = parseInt( event.target.value, 10 );
// parseInt would throw NaN for anything not a number,
// so we revert value to the quantity value.
value = isNaN( value ) ? quantity : value;

if ( value !== quantity ) {
// we commit this value immideatly.
onChange( value );
// but once the customer has stopped typing, we make sure his value is respecting the bounds (maximum value, minimum value, step value), and commit the normlized value.
normalizeQuantity( value );
}
} }
aria-label={ sprintf(
Expand All @@ -112,7 +153,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(
Expand All @@ -136,7 +177,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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ const CartLineItemRow = forwardRef< HTMLTableRowElement, CartLineItemRowProps >(
low_stock_remaining: lowStockRemaining = null,
show_backorder_badge: showBackorderBadge = false,
quantity_limit: quantityLimit = 99,
quantity_min: quantityMin = 1,
quantity_step: quantityStep = 1,
permalink = '',
images = [],
variation = [],
Expand Down Expand Up @@ -266,6 +268,8 @@ const CartLineItemRow = forwardRef< HTMLTableRowElement, CartLineItemRowProps >(
disabled={ isPendingDelete }
quantity={ quantity }
maximum={ quantityLimit }
minimum={ quantityMin }
step={ quantityStep }
onChange={ ( newQuantity ) => {
setItemQuantity( newQuantity );
dispatchStoreEvent( 'cart-set-item-quantity', {
Expand Down
2 changes: 2 additions & 0 deletions assets/js/types/type-defs/cart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ export interface CartItem {
quantity: number;
catalog_visibility: CatalogVisibility;
quantity_limit: number;
quantity_min: number;
quantity_step: number;
name: string;
summary: string;
short_description: string;
Expand Down
8 changes: 4 additions & 4 deletions bin/hook-docs/data/filters.json
Original file line number Diff line number Diff line change
Expand Up @@ -867,11 +867,11 @@
}
},
{
"name": "woocommerce_store_api_item_quantity_increment",
"name": "woocommerce_store_api_item_quantity_minimum",
"file": "StoreApi/Schemas/CartItemSchema.php",
"type": "filter",
"doc": {
"description": "Filters the quantity increment for a cart item in Store API.",
"description": "Filters the quantity minimum for a cart item in Store API.",
"long_description": "",
"tags": [
{
Expand All @@ -894,11 +894,11 @@
}
},
{
"name": "woocommerce_store_api_item_quantity_minimum",
"name": "woocommerce_store_api_item_quantity_step",
"file": "StoreApi/Schemas/CartItemSchema.php",
"type": "filter",
"doc": {
"description": "Filters the quantity minimum for a cart item in Store API.",
"description": "Filters the quantity increment for a cart item in Store API.",
"long_description": "",
"tags": [
{
Expand Down
14 changes: 7 additions & 7 deletions docs/extensibility/filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
- [woocommerce_shared_settings](#-woocommerce_shared_settings)
- [woocommerce_shipping_package_name](#woocommerce_shipping_package_name)
- [woocommerce_store_api_disable_nonce_check](#woocommerce_store_api_disable_nonce_check)
- [woocommerce_store_api_item_quantity_increment](#woocommerce_store_api_item_quantity_increment)
- [woocommerce_store_api_item_quantity_minimum](#woocommerce_store_api_item_quantity_minimum)
- [woocommerce_store_api_item_quantity_step](#woocommerce_store_api_item_quantity_step)
- [woocommerce_store_api_product_quantity_limit](#woocommerce_store_api_product_quantity_limit)
- [woocommerce_variation_option_name](#woocommerce_variation_option_name)

Expand Down Expand Up @@ -766,13 +766,13 @@ File: [StoreApi/Routes/AbstractCartRoute.php](../src/StoreApi/Routes/AbstractCar

---

## woocommerce_store_api_item_quantity_increment
## woocommerce_store_api_item_quantity_minimum


Filters the quantity increment for a cart item in Store API.
Filters the quantity minimum for a cart item in Store API.

```php
apply_filters( 'woocommerce_store_api_item_quantity_increment', array $cart_item )
apply_filters( 'woocommerce_store_api_item_quantity_minimum', array $cart_item )
```

### Parameters
Expand All @@ -793,13 +793,13 @@ File: [StoreApi/Schemas/CartItemSchema.php](../src/StoreApi/Schemas/CartItemSche

---

## woocommerce_store_api_item_quantity_minimum
## woocommerce_store_api_item_quantity_step


Filters the quantity minimum for a cart item in Store API.
Filters the quantity increment for a cart item in Store API.

```php
apply_filters( 'woocommerce_store_api_item_quantity_minimum', array $cart_item )
apply_filters( 'woocommerce_store_api_item_quantity_step', array $cart_item )
```

### Parameters
Expand Down
6 changes: 3 additions & 3 deletions src/StoreApi/Schemas/CartItemSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ public function get_item_response( $cart_item ) {
'quantity' => wc_stock_amount( $cart_item['quantity'] ),
'quantity_limit' => $this->get_product_quantity_limit( $product ),
'quantity_min' => $this->get_item_quantity_min( $cart_item ),
'quantity_increment' => $this->get_item_quantity_increment( $cart_item ),
'quantity_step' => $this->get_item_quantity_step( $cart_item ),
'name' => $this->prepare_html_response( $product->get_title() ),
'short_description' => $this->prepare_html_response( wc_format_content( wp_kses_post( $product->get_short_description() ) ) ),
'description' => $this->prepare_html_response( wc_format_content( wp_kses_post( $product->get_description() ) ) ),
Expand Down Expand Up @@ -486,14 +486,14 @@ protected function get_item_quantity_min( $cart_item ) {
* @param array $cart_item Cart item array.
* @return number
*/
protected function get_item_quantity_increment( $cart_item ) {
protected function get_item_quantity_step( $cart_item ) {
/**
* Filters the quantity increment for a cart item in Store API.
*
* @param array $cart_item Cart item array.
* @return number
*/
return apply_filters( 'woocommerce_store_api_item_quantity_increment', 1, $cart_item );
return apply_filters( 'woocommerce_store_api_item_quantity_step', 1, $cart_item );
}

/**
Expand Down

0 comments on commit 41f16e7

Please sign in to comment.