diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/attributes.js b/assets/js/atomic/blocks/product-elements/add-to-cart/attributes.ts similarity index 100% rename from assets/js/atomic/blocks/product-elements/add-to-cart/attributes.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/attributes.ts diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/block.js b/assets/js/atomic/blocks/product-elements/add-to-cart/block.tsx similarity index 85% rename from assets/js/atomic/blocks/product-elements/add-to-cart/block.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/block.tsx index 7baba132098..50190e77eb9 100644 --- a/assets/js/atomic/blocks/product-elements/add-to-cart/block.js +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/block.tsx @@ -23,36 +23,16 @@ import { GroupedProductForm, } from './product-types'; -/** - * Product Add to Form Block Component. - * - * @param {Object} props Incoming props. - * @param {string} [props.className] CSS Class name for the component. - * @param {boolean} [props.showFormElements] Should form elements be shown? - * @return {*} The component. - */ -const Block = ( { className, showFormElements } ) => { - const { product } = useProductDataContext(); - const componentClass = classnames( - className, - 'wc-block-components-product-add-to-cart', - { - 'wc-block-components-product-add-to-cart--placeholder': - isEmpty( product ), - } - ); - - return ( - -
- -
-
- ); -}; +interface Props { + /** + * CSS Class name for the component. + */ + className?: string; + /** + * Whether or not to show form elements. + */ + showFormElements?: boolean; +} /** * Renders the add to cart form using useAddToCartFormContext. @@ -79,6 +59,32 @@ const AddToCartForm = () => { return ; }; +/** + * Product Add to Form Block Component. + */ +const Block = ( { className, showFormElements }: Props ) => { + const { product } = useProductDataContext(); + const componentClass = classnames( + className, + 'wc-block-components-product-add-to-cart', + { + 'wc-block-components-product-add-to-cart--placeholder': + isEmpty( product ), + } + ); + + return ( + +
+ +
+
+ ); +}; + Block.propTypes = { className: PropTypes.string, }; diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/constants.js b/assets/js/atomic/blocks/product-elements/add-to-cart/constants.tsx similarity index 100% rename from assets/js/atomic/blocks/product-elements/add-to-cart/constants.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/constants.tsx diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/frontend.js b/assets/js/atomic/blocks/product-elements/add-to-cart/frontend.ts similarity index 100% rename from assets/js/atomic/blocks/product-elements/add-to-cart/frontend.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/frontend.ts diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/index.ts similarity index 100% rename from assets/js/atomic/blocks/product-elements/add-to-cart/index.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/index.ts diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/external.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/external.tsx similarity index 100% rename from assets/js/atomic/blocks/product-elements/add-to-cart/product-types/external.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/external.tsx diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/grouped/group-list/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/grouped/group-list/index.tsx similarity index 100% rename from assets/js/atomic/blocks/product-elements/add-to-cart/product-types/grouped/group-list/index.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/grouped/group-list/index.tsx diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/grouped/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/grouped/index.tsx similarity index 100% rename from assets/js/atomic/blocks/product-elements/add-to-cart/product-types/grouped/index.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/grouped/index.tsx diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/index.ts similarity index 100% rename from assets/js/atomic/blocks/product-elements/add-to-cart/product-types/index.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/index.ts diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/simple.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/simple.tsx similarity index 95% rename from assets/js/atomic/blocks/product-elements/add-to-cart/product-types/simple.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/simple.tsx index ce3670bcd36..8dc707ef27d 100644 --- a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/simple.js +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/simple.tsx @@ -13,6 +13,7 @@ import { AddToCartButton, QuantityInput, ProductUnavailable } from '../shared'; * Simple Product Add To Cart Form */ const Simple = () => { + // @todo Add types for `useAddToCartFormContext` const { product, quantity, diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/index.tsx similarity index 96% rename from assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/index.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/index.tsx index 4cc5e9e16fd..8aeb00f67ac 100644 --- a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/index.js +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/index.tsx @@ -18,6 +18,7 @@ import VariationAttributes from './variation-attributes'; * Variable Product Add To Cart Form */ const Variable = () => { + // @todo Add types for `useAddToCartFormContext` const { product, quantity, diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/types.ts b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/types.ts new file mode 100644 index 00000000000..0704b3ca06d --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/types.ts @@ -0,0 +1,17 @@ +/** + * External dependencies + */ +import { Dictionary } from '@woocommerce/types'; + +export type AttributesMap = Record< + string, + { id: number; attributes: Dictionary } +>; + +export interface VariationParam { + id: number; + variation: { + attribute: string; + value: string; + }[]; +} diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-picker.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-picker.tsx similarity index 83% rename from assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-picker.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-picker.tsx index 6482dde7e43..0a0a48a629e 100644 --- a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-picker.js +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-picker.tsx @@ -3,6 +3,8 @@ */ import { useState, useEffect, useMemo } from '@wordpress/element'; import { useShallowEqual } from '@woocommerce/base-hooks'; +import type { SelectControl } from '@wordpress/components'; +import { Dictionary, ProductResponseAttributeItem } from '@woocommerce/types'; /** * Internal dependencies @@ -13,21 +15,27 @@ import { getActiveSelectControlOptions, getDefaultAttributes, } from './utils'; +import { AttributesMap, VariationParam } from '../types'; + +interface Props { + attributes: Record< string, ProductResponseAttributeItem >; + setRequestParams: ( param: VariationParam ) => void; + variationAttributes: AttributesMap; +} /** * AttributePicker component. - * - * @param {*} props Component props. */ const AttributePicker = ( { attributes, variationAttributes, setRequestParams, -} ) => { +}: Props ) => { const currentAttributes = useShallowEqual( attributes ); const currentVariationAttributes = useShallowEqual( variationAttributes ); const [ variationId, setVariationId ] = useState( 0 ); - const [ selectedAttributes, setSelectedAttributes ] = useState( {} ); + const [ selectedAttributes, setSelectedAttributes ] = + useState< Dictionary >( {} ); const [ hasSetDefaults, setHasSetDefaults ] = useState( false ); // Get options for each attribute picker. @@ -99,7 +107,11 @@ const AttributePicker = ( { { setSelectedAttributes( { diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-select-control.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-select-control.tsx similarity index 90% rename from assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-select-control.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-select-control.tsx index 5bbf0fb89e7..52d419bae36 100644 --- a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-select-control.js +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-select-control.tsx @@ -4,12 +4,18 @@ import { __ } from '@wordpress/i18n'; import { decodeEntities } from '@wordpress/html-entities'; import { SelectControl } from 'wordpress-components'; +import type { SelectControl as SelectControlType } from '@wordpress/components'; import { useEffect } from 'react'; import classnames from 'classnames'; import { ValidationInputError } from '@woocommerce/blocks-checkout'; import { VALIDATION_STORE_KEY } from '@woocommerce/block-data'; import { useDispatch, useSelect } from '@wordpress/data'; +interface Props extends SelectControlType.Props< string > { + attributeName: string; + errorMessage?: string; +} + // Default option for select boxes. const selectAnOption = { value: '', @@ -18,19 +24,17 @@ const selectAnOption = { /** * VariationAttributeSelect component. - * - * @param {*} props Component props. */ const AttributeSelectControl = ( { attributeName, options = [], value = '', - onChange = () => {}, + onChange = () => void 0, errorMessage = __( 'Please select a value.', 'woo-gutenberg-products-block' ), -} ) => { +}: Props ) => { const errorId = attributeName; const { setValidationErrors, clearValidationError } = diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/index.tsx similarity index 66% rename from assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/index.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/index.tsx index 18e0b5eb4df..a83497dacc2 100644 --- a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/index.js +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/index.tsx @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { ProductResponseItem } from '@woocommerce/types'; + /** * Internal dependencies */ @@ -5,19 +10,20 @@ import './style.scss'; import AttributePicker from './attribute-picker'; import { getAttributes, getVariationAttributes } from './utils'; +interface Props { + dispatchers: { setRequestParams: () => void }; + product: ProductResponseItem; +} + /** * VariationAttributes component. - * - * @param {Object} props Incoming props - * @param {Object} props.product Product - * @param {Object} props.dispatchers An object where values are dispatching functions. */ -const VariationAttributes = ( { product, dispatchers } ) => { +const VariationAttributes = ( { dispatchers, product }: Props ) => { const attributes = getAttributes( product.attributes ); const variationAttributes = getVariationAttributes( product.variations ); if ( Object.keys( attributes ).length === 0 || - variationAttributes.length === 0 + Object.keys( variationAttributes ).length === 0 ) { return null; } diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/test/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/test/index.ts similarity index 95% rename from assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/test/index.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/test/index.ts index cfc4490dbc8..d859338da97 100644 --- a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/test/index.js +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/test/index.ts @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { ProductResponseAttributeItem } from '@woocommerce/types'; + /** * Internal dependencies */ @@ -10,7 +15,7 @@ import { getDefaultAttributes, } from '../utils'; -const rawAttributeData = [ +const rawAttributeData: ProductResponseAttributeItem[] = [ { id: 1, name: 'Color', @@ -40,7 +45,7 @@ const rawAttributeData = [ { id: 0, name: 'Logo', - taxonomy: null, + taxonomy: 'pa_logo', has_variations: true, terms: [ { @@ -60,7 +65,7 @@ const rawAttributeData = [ { id: 0, name: 'Non-variable attribute', - taxonomy: null, + taxonomy: 'pa_non-variable-attribute', has_variations: false, terms: [ { @@ -227,7 +232,7 @@ describe( 'Testing utils', () => { Logo: { id: 0, name: 'Logo', - taxonomy: null, + taxonomy: 'pa_logo', has_variations: true, terms: [ { @@ -471,8 +476,11 @@ describe( 'Testing utils', () => { } ); it( 'should return an empty object if given unexpected values', () => { + // @ts-expect-error Expected TS Error as we are checking how the function does with *unexpected values*. expect( getDefaultAttributes( [] ) ).toStrictEqual( {} ); + // @ts-expect-error Ditto above. expect( getDefaultAttributes( null ) ).toStrictEqual( {} ); + // @ts-expect-error Ditto above. expect( getDefaultAttributes( undefined ) ).toStrictEqual( {} ); } ); } ); diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/utils.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/utils.ts similarity index 62% rename from assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/utils.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/utils.ts index 6914d664612..15b452ff594 100644 --- a/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/utils.js +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/utils.ts @@ -3,14 +3,25 @@ */ import { keyBy } from 'lodash'; import { decodeEntities } from '@wordpress/html-entities'; -import { isObject } from '@woocommerce/types'; +import { + Dictionary, + isObject, + ProductResponseAttributeItem, + ProductResponseTermItem, + ProductResponseVariationsItem, +} from '@woocommerce/types'; + +/** + * Internal dependencies + */ +import { AttributesMap } from '../types'; /** * Key an array of attributes by name, - * - * @param {Object} attributes Attributes array. */ -export const getAttributes = ( attributes ) => { +export const getAttributes = ( + attributes?: ProductResponseAttributeItem[] | null +) => { return attributes ? keyBy( Object.values( attributes ).filter( @@ -26,15 +37,18 @@ export const getAttributes = ( attributes ) => { * * Note, each item is keyed by the variation ID with an id: prefix. This is to prevent the object * being reordered when iterated. - * - * @param {Object} variations List of Variation objects and attributes keyed by variation ID. */ -export const getVariationAttributes = ( variations ) => { +export const getVariationAttributes = ( + /** + * List of Variation objects and attributes keyed by variation ID. + */ + variations?: ProductResponseVariationsItem[] | null +) => { if ( ! variations ) { return {}; } - const attributesMap = {}; + const attributesMap: AttributesMap = {}; variations.forEach( ( { id, attributes } ) => { attributesMap[ `id:${ id }` ] = { @@ -42,7 +56,7 @@ export const getVariationAttributes = ( variations ) => { attributes: attributes.reduce( ( acc, { name, value } ) => { acc[ name ] = value; return acc; - }, {} ), + }, {} as Dictionary ), }; } ); @@ -55,20 +69,28 @@ export const getVariationAttributes = ( variations ) => { * Allows an attribute to be excluded by name. This is used to filter displayed options for * individual attribute selects. * - * @param {Object} attributes List of attribute names and terms. - * @param {Object} variationAttributes Attributes for each variation keyed by variation ID. - * @param {Object} selectedAttributes Attribute Name Value pairs of current selections by the user. - * @return {Array} List of matching variation IDs. + * @return List of matching variation IDs. */ export const getVariationsMatchingSelectedAttributes = ( - attributes, - variationAttributes, - selectedAttributes + /** + * List of attribute names and terms. + * + * As returned from {@link getAttributes()}. + */ + attributes: Record< string, ProductResponseAttributeItem >, + /** + * Attributes for each variation keyed by variation ID. + * + * As returned from {@link getVariationAttributes()}. + */ + variationAttributes: AttributesMap, + /** + * Attribute Name Value pairs of current selections by the user. + */ + selectedAttributes: Record< string, string | null > ) => { const variationIds = Object.values( variationAttributes ).map( - ( { id: variationId } ) => { - return variationId; - } + ( { id } ) => id ); // If nothing is selected yet, just return all variations. @@ -105,15 +127,25 @@ export const getVariationsMatchingSelectedAttributes = ( /** * Given a list of variations and a list of attribute values, returns the first matched variation ID. * - * @param {Object} attributes List of attribute names and terms. - * @param {Object} variationAttributes Attributes for each variation keyed by variation ID. - * @param {Object} selectedAttributes Attribute Name Value pairs of current selections by the user. - * @return {number} Variation ID. + * @return Variation ID. */ export const getVariationMatchingSelectedAttributes = ( - attributes, - variationAttributes, - selectedAttributes + /** + * List of attribute names and terms. + * + * As returned from {@link getAttributes()}. + */ + attributes: Record< string, ProductResponseAttributeItem >, + /** + * Attributes for each variation keyed by variation ID. + * + * As returned from {@link getVariationAttributes()}. + */ + variationAttributes: AttributesMap, + /** + * Attribute Name Value pairs of current selections by the user. + */ + selectedAttributes: Dictionary ) => { const matchingVariationIds = getVariationsMatchingSelectedAttributes( attributes, @@ -127,13 +159,18 @@ export const getVariationMatchingSelectedAttributes = ( * Given a list of terms, filter them and return valid options for the select boxes. * * @see getActiveSelectControlOptions - * @param {Object} attributeTerms List of attribute term objects. - * @param {?Array} validAttributeTerms Valid values if selections have been made already. - * @return {Array} Value/Label pairs of select box options. + * + * @return Value/Label pairs of select box options. */ const getValidSelectControlOptions = ( - attributeTerms, - validAttributeTerms = null + /** + * List of attribute term objects. + */ + attributeTerms: ProductResponseTermItem[], + /** + * Valid values if selections have been made already. + */ + validAttributeTerms: Array< string | null > | null = null ) => { return Object.values( attributeTerms ) .map( ( { name, slug } ) => { @@ -156,17 +193,30 @@ const getValidSelectControlOptions = ( * Given a list of terms, filter them and return active options for the select boxes. This factors in * which options should be hidden due to current selections. * - * @param {Object} attributes List of attribute names and terms. - * @param {Object} variationAttributes Attributes for each variation keyed by variation ID. - * @param {Object} selectedAttributes Attribute Name Value pairs of current selections by the user. - * @return {Object} Select box options. + * @return Select box options. */ export const getActiveSelectControlOptions = ( - attributes, - variationAttributes, - selectedAttributes + /** + * List of attribute names and terms. + * + * As returned from {@link getAttributes()}. + */ + attributes: Record< string, ProductResponseAttributeItem >, + /** + * Attributes for each variation keyed by variation ID. + * + * As returned from {@link getVariationAttributes()}. + */ + variationAttributes: AttributesMap, + /** + * Attribute Name Value pairs of current selections by the user. + */ + selectedAttributes: Dictionary ) => { - const options = {}; + const options: Record< + string, + Array< { label: string; value: string } | null > + > = {}; const attributeNames = Object.keys( attributes ); const hasSelectedAttributes = Object.values( selectedAttributes ).filter( Boolean ).length > 0; @@ -211,30 +261,35 @@ export const getActiveSelectControlOptions = ( /** * Return the default values of the given attributes in a format ready to be set in state. * - * @param {Object} attributes List of attribute names and terms. - * @return {Object} Default attributes. + * @return Default attributes. */ -export const getDefaultAttributes = ( attributes = {} ) => { +export const getDefaultAttributes = ( + /** + * List of attribute names and terms. + * + * As returned from {@link getAttributes()}. + */ + attributes: Record< string, ProductResponseAttributeItem > +) => { if ( ! isObject( attributes ) ) { return {}; } const attributeNames = Object.keys( attributes ); - const defaultsToSet = {}; if ( attributeNames.length === 0 ) { - return defaultsToSet; + return {}; } - attributeNames.forEach( ( attributeName ) => { - const currentAttribute = attributes[ attributeName ]; - const defaultValue = currentAttribute.terms.filter( - ( term ) => term.default - ); - if ( defaultValue.length > 0 ) { - defaultsToSet[ currentAttribute.name ] = defaultValue[ 0 ]?.slug; + const attributesEntries = Object.values( attributes ); + + return attributesEntries.reduce( ( acc, curr ) => { + const defaultValues = curr.terms.filter( ( term ) => term.default ); + + if ( defaultValues.length > 0 ) { + acc[ curr.name ] = defaultValues[ 0 ]?.slug; } - } ); - return defaultsToSet; + return acc; + }, {} as Dictionary ); }; diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/shared/add-to-cart-button.js b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/add-to-cart-button.tsx similarity index 77% rename from assets/js/atomic/blocks/product-elements/add-to-cart/shared/add-to-cart-button.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/shared/add-to-cart-button.tsx index 27c4aa24aea..d36e7da5f2d 100644 --- a/assets/js/atomic/blocks/product-elements/add-to-cart/shared/add-to-cart-button.js +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/add-to-cart-button.tsx @@ -2,7 +2,7 @@ * External dependencies */ import { __, _n, sprintf } from '@wordpress/i18n'; -import Button from '@woocommerce/base-components/button'; +import Button, { ButtonProps } from '@woocommerce/base-components/button'; import { Icon, check } from '@wordpress/icons'; import { useState, useEffect } from '@wordpress/element'; import { useAddToCartFormContext } from '@woocommerce/base-context'; @@ -12,10 +12,84 @@ import { } from '@woocommerce/base-context/hooks'; import { useInnerBlockLayoutContext } from '@woocommerce/shared-context'; +type LinkProps = Pick< ButtonProps, 'className' | 'href' | 'onClick' | 'text' >; + +interface ButtonComponentProps + extends Pick< ButtonProps, 'className' | 'onClick' > { + /** + * Whether the button is disabled or not. + */ + isDisabled: boolean; + /** + * Whether processing is done. + */ + isDone: boolean; + /** + * Whether processing action is occurring. + */ + isProcessing: ButtonProps[ 'showSpinner' ]; + /** + * Quantity of said item currently in the cart. + */ + quantityInCart: number; +} + +/** + * Button component for non-purchasable products. + */ +const LinkComponent = ( { className, href, text, onClick }: LinkProps ) => { + return ( + + ); +}; + +/** + * Button for purchasable products. + */ +const ButtonComponent = ( { + className, + quantityInCart, + isProcessing, + isDisabled, + isDone, + onClick, +}: ButtonComponentProps ) => { + return ( + + ); +}; + /** * Add to Cart Form Button Component. */ const AddToCartButton = () => { + // @todo Add types for `useAddToCartFormContext` const { showFormElements, productIsPurchasable, @@ -104,69 +178,4 @@ const AddToCartButton = () => { ); }; -/** - * Button component for non-purchasable products. - * - * @param {Object} props Incoming props. - * @param {string} props.className Css classnames. - * @param {string} props.href Link for button. - * @param {string} props.text Text content for button. - * @param {function():any} props.onClick Callback to execute when button is clicked. - */ -const LinkComponent = ( { className, href, text, onClick } ) => { - return ( - - ); -}; - -/** - * Button for purchasable products. - * - * @param {Object} props Incoming props for component - * @param {string} props.className Incoming css class name. - * @param {number} props.quantityInCart Quantity of item in cart. - * @param {boolean} props.isProcessing Whether processing action is occurring. - * @param {boolean} props.isDisabled Whether the button is disabled or not. - * @param {boolean} props.isDone Whether processing is done. - * @param {function():any} props.onClick Callback to execute when button is clicked. - */ -const ButtonComponent = ( { - className, - quantityInCart, - isProcessing, - isDisabled, - isDone, - onClick, -} ) => { - return ( - - ); -}; - export default AddToCartButton; diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/shared/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/index.ts similarity index 100% rename from assets/js/atomic/blocks/product-elements/add-to-cart/shared/index.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/shared/index.ts diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/shared/product-unavailable.js b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/product-unavailable.tsx similarity index 100% rename from assets/js/atomic/blocks/product-elements/add-to-cart/shared/product-unavailable.js rename to assets/js/atomic/blocks/product-elements/add-to-cart/shared/product-unavailable.tsx diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/shared/quantity-input.js b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/quantity-input.js deleted file mode 100644 index c846567ea54..00000000000 --- a/assets/js/atomic/blocks/product-elements/add-to-cart/shared/quantity-input.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * External dependencies - */ -import { useDebouncedCallback } from 'use-debounce'; - -/** - * Quantity Input Component. - * - * @param {Object} props Incoming props for component - * @param {boolean} props.disabled Whether input is disabled or not. - * @param {number} props.min Minimum value for input. - * @param {number} props.max Maximum value for input. - * @param {number} props.step Step attribute for input. - * @param {number} props.value Value for input. - * @param {function():any} props.onChange Function to call on input change event. - */ -const QuantityInput = ( { disabled, min, max, step = 1, value, onChange } ) => { - const hasMaximum = typeof max !== 'undefined'; - - /** - * 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 experience, for example, if the minimum is 2 and - * the customer is trying to type "10", premature normalizing would - * always kick in at "1" and turn that into 2. - * - * Copied from - */ - const normalizeQuantity = useDebouncedCallback( ( initialValue ) => { - // We copy the starting value. - let newValue = initialValue; - - // We check if we have a maximum value, and select the lowest between what was inserted and the maximum. - if ( hasMaximum ) { - newValue = Math.min( - newValue, - // the maximum possible value in step increments. - Math.floor( max / step ) * step - ); - } - - // Select the biggest between what's inserted, the the minimum value in steps. - newValue = Math.max( newValue, Math.ceil( min / step ) * step ); - - // We round off the value to our steps. - newValue = Math.floor( newValue / step ) * step; - - // Only commit if the value has changed - if ( newValue !== initialValue ) { - onChange( newValue ); - } - }, 300 ); - - return ( - { - onChange( e.target.value ); - normalizeQuantity( e.target.value ); - } } - /> - ); -}; - -export default QuantityInput; diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/shared/quantity-input.tsx b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/quantity-input.tsx new file mode 100644 index 00000000000..a9c1e257ec4 --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/quantity-input.tsx @@ -0,0 +1,85 @@ +/** + * External dependencies + */ +import { useDebouncedCallback } from 'use-debounce'; + +type JSXInputProps = JSX.IntrinsicElements[ 'input' ]; + +interface QuantityInputProps extends Omit< JSXInputProps, 'onChange' > { + max: number; + min: number; + onChange: ( val: number | string ) => void; + step: number; +} + +/** + * Quantity Input Component. + */ +const QuantityInput = ( { + disabled, + min, + max, + step = 1, + value, + onChange, +}: QuantityInputProps ) => { + const hasMaximum = typeof max !== 'undefined'; + + /** + * 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 experience, for example, if the minimum is 2 and + * the customer is trying to type "10", premature normalizing would + * always kick in at "1" and turn that into 2. + * + * Copied from + */ + const normalizeQuantity = useDebouncedCallback< ( val: number ) => void >( + ( initialValue ) => { + // We copy the starting value. + let newValue = initialValue; + + // We check if we have a maximum value, and select the lowest between what was inserted and the maximum. + if ( hasMaximum ) { + newValue = Math.min( + newValue, + // the maximum possible value in step increments. + Math.floor( max / step ) * step + ); + } + + // Select the biggest between what's inserted, the the minimum value in steps. + newValue = Math.max( newValue, Math.ceil( min / step ) * step ); + + // We round off the value to our steps. + newValue = Math.floor( newValue / step ) * step; + + // Only commit if the value has changed + if ( newValue !== initialValue ) { + onChange?.( newValue ); + } + }, + 300 + ); + + return ( + { + onChange?.( e.target.value ); + normalizeQuantity( Number( e.target.value ) ); + } } + /> + ); +}; + +export default QuantityInput; diff --git a/assets/js/base/components/button/index.tsx b/assets/js/base/components/button/index.tsx index 223f44ae363..4caf1498e58 100644 --- a/assets/js/base/components/button/index.tsx +++ b/assets/js/base/components/button/index.tsx @@ -2,7 +2,7 @@ * External dependencies */ import { Button as WPButton } from 'wordpress-components'; -import type { ReactNode } from 'react'; +import type { Button as WPButtonType } from '@wordpress/components'; import classNames from 'classnames'; import Spinner from '@woocommerce/base-components/spinner'; @@ -11,39 +11,21 @@ import Spinner from '@woocommerce/base-components/spinner'; */ import './style.scss'; -export interface ButtonProps extends WPButton.ButtonProps { - /** - * Component wrapper classname - * - * @default 'wc-block-components-button' - */ - className?: string; +export interface ButtonProps + extends Omit< WPButtonType.ButtonProps, 'variant' > { /** * Show spinner * * @default false */ - showSpinner?: boolean; - /** - * Button content - */ - children?: ReactNode; - /** - * Button state - */ - disabled?: boolean; - /** - * Event handler triggered when the button is clicked - */ - onClick?: ( e: React.MouseEvent< HTMLButtonElement, MouseEvent > ) => void; - /** - * Button type - */ - type?: 'button' | 'input' | 'submit'; + showSpinner?: boolean | undefined; /** * Button variant */ variant?: 'text' | 'contained' | 'outlined'; +} + +export interface AnchorProps extends Omit< ButtonProps, 'href' > { /** * Button href */ diff --git a/assets/js/types/type-defs/product-response.ts b/assets/js/types/type-defs/product-response.ts index f875cf42eee..f19d0e1df7b 100644 --- a/assets/js/types/type-defs/product-response.ts +++ b/assets/js/types/type-defs/product-response.ts @@ -31,6 +31,7 @@ export interface ProductResponseImageItem { } export interface ProductResponseTermItem { + default?: boolean; id: number; name: string; slug: string; diff --git a/assets/js/types/type-defs/utils.ts b/assets/js/types/type-defs/utils.ts index f23dd1e23c2..cc698db9fcf 100644 --- a/assets/js/types/type-defs/utils.ts +++ b/assets/js/types/type-defs/utils.ts @@ -1,3 +1,5 @@ +export type Dictionary = Record< string, string >; + /** * Allow for the entire object to be passed, with only some properties * required. diff --git a/package-lock.json b/package-lock.json index 85743d3f825..3d098dba675 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,6 +71,7 @@ "@types/react-dom": "18.0.9", "@types/wordpress__block-editor": "6.0.6", "@types/wordpress__blocks": "11.0.7", + "@types/wordpress__components": "^23.0.0", "@types/wordpress__core-data": "^2.4.5", "@types/wordpress__data": "^6.0.1", "@types/wordpress__data-controls": "2.2.0", @@ -11329,20 +11330,175 @@ } }, "node_modules/@types/wordpress__components": { - "version": "19.3.2", + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/@types/wordpress__components/-/wordpress__components-23.0.0.tgz", + "integrity": "sha512-60S432ZMp9mIv74n+J91fZPFphnSZa6r8r1/QuoNeUHXIFPw3c28hL0kDl3i0GYvYTGlTkbvhGgUJvJA0GMW6g==", "dev": true, - "license": "MIT", "dependencies": { "@types/react": "*", "@types/tinycolor2": "*", "@types/wordpress__components": "*", "@types/wordpress__notices": "*", "@types/wordpress__rich-text": "*", - "@wordpress/element": "^4.1.0", + "@wordpress/element": "^5.0.0", "downshift": "^6.0.15", "re-resizable": "^6.4.0" } }, + "node_modules/@types/wordpress__components/node_modules/@types/react": { + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/wordpress__components/node_modules/@wordpress/element": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-5.0.0.tgz", + "integrity": "sha512-u1DhVIdb6VEe8wzxKCrLfRWbeFLrlhYTFSOY6yrtl2z9vr9bCdlLp9aAppTxLNUs8cFcdoCEMpakVRYFyfZwTQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.16.0", + "@types/react": "^18.0.21", + "@types/react-dom": "^18.0.6", + "@wordpress/escape-html": "^2.23.0", + "change-case": "^4.1.2", + "is-plain-object": "^5.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/wordpress__components/node_modules/change-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", + "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.2", + "capital-case": "^1.0.4", + "constant-case": "^3.0.4", + "dot-case": "^3.0.4", + "header-case": "^2.0.4", + "no-case": "^3.0.4", + "param-case": "^3.0.4", + "pascal-case": "^3.1.2", + "path-case": "^3.0.4", + "sentence-case": "^3.0.4", + "snake-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/@types/wordpress__components/node_modules/constant-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case": "^2.0.2" + } + }, + "node_modules/@types/wordpress__components/node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/@types/wordpress__components/node_modules/path-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", + "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/@types/wordpress__components/node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@types/wordpress__components/node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/@types/wordpress__components/node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/@types/wordpress__components/node_modules/sentence-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", + "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/@types/wordpress__components/node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/@types/wordpress__components/node_modules/upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", + "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/@types/wordpress__components/node_modules/upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/@types/wordpress__core-data": { "version": "2.4.5", "dev": true, @@ -15470,9 +15626,9 @@ } }, "node_modules/@wordpress/escape-html": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-2.22.0.tgz", - "integrity": "sha512-GUo6VLugIZxen1rdYuotvz6Vqa+5fNtVelNjXLwDqRu0iY2RXeoTux9V5bZWXPnGb54ryqfYmR4gH6F8xZhWzQ==", + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-2.23.0.tgz", + "integrity": "sha512-QmMGJVEoVu3+s46Ya7saYZI8D1jPOKN18eFJX21y59/99tAVvmcWWz0k0uTO5bciDQ7R6ACm9AJS6RiZycODkg==", "dependencies": { "@babel/runtime": "^7.16.0" }, @@ -57842,7 +57998,9 @@ } }, "@types/wordpress__components": { - "version": "19.3.2", + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/@types/wordpress__components/-/wordpress__components-23.0.0.tgz", + "integrity": "sha512-60S432ZMp9mIv74n+J91fZPFphnSZa6r8r1/QuoNeUHXIFPw3c28hL0kDl3i0GYvYTGlTkbvhGgUJvJA0GMW6g==", "dev": true, "requires": { "@types/react": "*", @@ -57850,9 +58008,156 @@ "@types/wordpress__components": "*", "@types/wordpress__notices": "*", "@types/wordpress__rich-text": "*", - "@wordpress/element": "^4.1.0", + "@wordpress/element": "^5.0.0", "downshift": "^6.0.15", "re-resizable": "^6.4.0" + }, + "dependencies": { + "@types/react": { + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@wordpress/element": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-5.0.0.tgz", + "integrity": "sha512-u1DhVIdb6VEe8wzxKCrLfRWbeFLrlhYTFSOY6yrtl2z9vr9bCdlLp9aAppTxLNUs8cFcdoCEMpakVRYFyfZwTQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.16.0", + "@types/react": "^18.0.21", + "@types/react-dom": "^18.0.6", + "@wordpress/escape-html": "^2.23.0", + "change-case": "^4.1.2", + "is-plain-object": "^5.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "change-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", + "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "dev": true, + "requires": { + "camel-case": "^4.1.2", + "capital-case": "^1.0.4", + "constant-case": "^3.0.4", + "dot-case": "^3.0.4", + "header-case": "^2.0.4", + "no-case": "^3.0.4", + "param-case": "^3.0.4", + "pascal-case": "^3.1.2", + "path-case": "^3.0.4", + "sentence-case": "^3.0.4", + "snake-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "constant-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case": "^2.0.2" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "path-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", + "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0" + } + }, + "sentence-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", + "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", + "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + } } }, "@types/wordpress__core-data": { @@ -60777,9 +61082,9 @@ } }, "@wordpress/escape-html": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-2.22.0.tgz", - "integrity": "sha512-GUo6VLugIZxen1rdYuotvz6Vqa+5fNtVelNjXLwDqRu0iY2RXeoTux9V5bZWXPnGb54ryqfYmR4gH6F8xZhWzQ==", + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-2.23.0.tgz", + "integrity": "sha512-QmMGJVEoVu3+s46Ya7saYZI8D1jPOKN18eFJX21y59/99tAVvmcWWz0k0uTO5bciDQ7R6ACm9AJS6RiZycODkg==", "requires": { "@babel/runtime": "^7.16.0" } diff --git a/package.json b/package.json index b469a1ac539..5c268ed0c69 100644 --- a/package.json +++ b/package.json @@ -123,6 +123,7 @@ "@types/react-dom": "18.0.9", "@types/wordpress__block-editor": "6.0.6", "@types/wordpress__blocks": "11.0.7", + "@types/wordpress__components": "^23.0.0", "@types/wordpress__core-data": "^2.4.5", "@types/wordpress__data": "^6.0.1", "@types/wordpress__data-controls": "2.2.0",