diff --git a/plugins/woocommerce-blocks/assets/js/base/components/noninteractive/index.tsx b/plugins/woocommerce-blocks/assets/js/base/components/noninteractive/index.tsx new file mode 100644 index 00000000000..e9d8b69356f --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/base/components/noninteractive/index.tsx @@ -0,0 +1,96 @@ +/** + * External dependencies + */ +import { useRef, useLayoutEffect } from '@wordpress/element'; +import { focus } from '@wordpress/dom'; +import { useDebouncedCallback } from 'use-debounce'; + +/** + * Names of control nodes which need to be disabled. + */ +const FOCUSABLE_NODE_NAMES = [ + 'BUTTON', + 'FIELDSET', + 'INPUT', + 'OPTGROUP', + 'OPTION', + 'SELECT', + 'TEXTAREA', + 'A', +]; + +/** + * Noninteractive component + * + * Makes children elements Noninteractive, preventing both mouse and keyboard events without affecting how the elements + * appear visually. Used for previews. + * + * Based on the component in WordPress. + * + * @see https://github.com/WordPress/gutenberg/blob/trunk/packages/components/src/disabled/index.js + */ +const Noninteractive = ( { + children, + style = {}, + ...props +}: { + children: React.ReactChildren; + style?: Record< string, string >; +} ): JSX.Element => { + const node = useRef< HTMLDivElement >( null ); + + const disableFocus = () => { + if ( node.current ) { + focus.focusable.find( node.current ).forEach( ( focusable ) => { + if ( FOCUSABLE_NODE_NAMES.includes( focusable.nodeName ) ) { + focusable.setAttribute( 'tabindex', '-1' ); + } + if ( focusable.hasAttribute( 'contenteditable' ) ) { + focusable.setAttribute( 'contenteditable', 'false' ); + } + } ); + } + }; + + // Debounce re-disable since disabling process itself will incur additional mutations which should be ignored. + const debounced = useDebouncedCallback( disableFocus, 0, { + leading: true, + } ); + + useLayoutEffect( () => { + let observer: MutationObserver | undefined; + disableFocus(); + if ( node.current ) { + observer = new window.MutationObserver( debounced ); + observer.observe( node.current, { + childList: true, + attributes: true, + subtree: true, + } ); + } + return () => { + if ( observer ) { + observer.disconnect(); + } + debounced.cancel(); + }; + }, [ debounced ] ); + + return ( +
+ { children } +
+ ); +}; + +export default Noninteractive; diff --git a/plugins/woocommerce-blocks/assets/js/base/context/providers/cart-checkout/payment-methods/use-payment-method-registration.ts b/plugins/woocommerce-blocks/assets/js/base/context/providers/cart-checkout/payment-methods/use-payment-method-registration.ts index e1985e42f77..6714cb96130 100644 --- a/plugins/woocommerce-blocks/assets/js/base/context/providers/cart-checkout/payment-methods/use-payment-method-registration.ts +++ b/plugins/woocommerce-blocks/assets/js/base/context/providers/cart-checkout/payment-methods/use-payment-method-registration.ts @@ -161,7 +161,7 @@ const usePaymentMethodRegistration = ( registeredPaymentMethods, ] ); - const [ debouncedRefreshCanMakePayments ] = useDebouncedCallback( + const debouncedRefreshCanMakePayments = useDebouncedCallback( refreshCanMakePayments, 500 ); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart/inner-blocks/cart-line-items-block/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart/inner-blocks/cart-line-items-block/edit.tsx index 60f943aece8..001e8fbdb2b 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart/inner-blocks/cart-line-items-block/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart/inner-blocks/cart-line-items-block/edit.tsx @@ -2,7 +2,7 @@ * External dependencies */ import { useBlockProps } from '@wordpress/block-editor'; -import { Disabled } from '@wordpress/components'; +import Noninteractive from '@woocommerce/base-components/noninteractive'; /** * Internal dependencies @@ -19,9 +19,9 @@ export const Edit = ( { return (
- + - +
); }; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart/inner-blocks/cart-order-summary-block/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart/inner-blocks/cart-order-summary-block/edit.tsx index bf369735326..1f4b764d7cc 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart/inner-blocks/cart-order-summary-block/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart/inner-blocks/cart-order-summary-block/edit.tsx @@ -3,8 +3,9 @@ */ import { __ } from '@wordpress/i18n'; import { useBlockProps, InspectorControls } from '@wordpress/block-editor'; -import { Disabled, PanelBody, ToggleControl } from '@wordpress/components'; +import { PanelBody, ToggleControl } from '@wordpress/components'; import { getSetting } from '@woocommerce/settings'; +import Noninteractive from '@woocommerce/base-components/noninteractive'; /** * Internal dependencies @@ -97,13 +98,13 @@ export const Edit = ( { ) } - + - + ); }; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart/inner-blocks/proceed-to-checkout-block/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart/inner-blocks/proceed-to-checkout-block/edit.tsx index 5d3e2a80b57..8e247fbbb49 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart/inner-blocks/proceed-to-checkout-block/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart/inner-blocks/proceed-to-checkout-block/edit.tsx @@ -6,8 +6,8 @@ import { useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { InspectorControls, useBlockProps } from '@wordpress/block-editor'; import PageSelector from '@woocommerce/editor-components/page-selector'; -import { Disabled } from '@wordpress/components'; import { CART_PAGE_ID } from '@woocommerce/block-settings'; +import Noninteractive from '@woocommerce/base-components/noninteractive'; /** * Internal dependencies */ @@ -60,12 +60,12 @@ export const Edit = ( { /> ) } - + - + ); }; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-actions-block/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-actions-block/edit.tsx index d5acb95c02d..06b70dad54a 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-actions-block/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-actions-block/edit.tsx @@ -6,8 +6,9 @@ import { useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { InspectorControls, useBlockProps } from '@wordpress/block-editor'; import PageSelector from '@woocommerce/editor-components/page-selector'; -import { PanelBody, ToggleControl, Disabled } from '@wordpress/components'; +import { PanelBody, ToggleControl } from '@wordpress/components'; import { CHECKOUT_PAGE_ID } from '@woocommerce/block-settings'; +import Noninteractive from '@woocommerce/base-components/noninteractive'; /** * Internal dependencies */ @@ -82,12 +83,12 @@ export const Edit = ( { /> ) } - + - + ); }; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-billing-address-block/block.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-billing-address-block/block.tsx index 67d5239be0d..c92ed5dfdef 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-billing-address-block/block.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-billing-address-block/block.tsx @@ -2,13 +2,13 @@ * External dependencies */ import { useMemo, useEffect, Fragment } from '@wordpress/element'; -import { Disabled } from 'wordpress-components'; import { useCheckoutAddress, useStoreEvents, useEditorContext, } from '@woocommerce/base-context'; import { AddressForm } from '@woocommerce/base-components/cart-checkout'; +import Noninteractive from '@woocommerce/base-components/noninteractive'; /** * Internal dependencies @@ -56,7 +56,7 @@ const Block = ( { }; }, [ showCompanyField, requireCompanyField, showApartmentField ] ); - const AddressFormWrapperComponent = isEditor ? Disabled : Fragment; + const AddressFormWrapperComponent = isEditor ? Noninteractive : Fragment; return ( diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-contact-information-block/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-contact-information-block/edit.tsx index 08884bdb596..3e6be908d31 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-contact-information-block/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-contact-information-block/edit.tsx @@ -3,8 +3,8 @@ */ import classnames from 'classnames'; import { useBlockProps } from '@wordpress/block-editor'; -import { Disabled } from '@wordpress/components'; import { innerBlockAreas } from '@woocommerce/blocks-checkout'; +import Noninteractive from '@woocommerce/base-components/noninteractive'; /** * Internal dependencies @@ -44,9 +44,9 @@ export const Edit = ( { ) } > - + - + ); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-order-note-block/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-order-note-block/edit.tsx index 2767fdf2926..7f5b022a02d 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-order-note-block/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-order-note-block/edit.tsx @@ -2,7 +2,7 @@ * External dependencies */ import { useBlockProps } from '@wordpress/block-editor'; -import { Disabled } from '@wordpress/components'; +import Noninteractive from '@woocommerce/base-components/noninteractive'; /** * Internal dependencies @@ -14,9 +14,9 @@ export const Edit = (): JSX.Element => { const blockProps = useBlockProps(); return (
- + - +
); }; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-order-summary-block/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-order-summary-block/edit.tsx index bac0ef57d5e..5ab0722ff2f 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-order-summary-block/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-order-summary-block/edit.tsx @@ -3,8 +3,9 @@ */ import { __ } from '@wordpress/i18n'; import { useBlockProps, InspectorControls } from '@wordpress/block-editor'; -import { Disabled, PanelBody, ToggleControl } from '@wordpress/components'; +import { PanelBody, ToggleControl } from '@wordpress/components'; import { getSetting } from '@woocommerce/settings'; +import Noninteractive from '@woocommerce/base-components/noninteractive'; /** * Internal dependencies @@ -65,11 +66,11 @@ export const Edit = ( { ) } - + - + ); }; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-payment-block/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-payment-block/edit.tsx index fe72ee7fd28..a182395c89d 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-payment-block/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-payment-block/edit.tsx @@ -4,10 +4,11 @@ import classnames from 'classnames'; import { __ } from '@wordpress/i18n'; import { InspectorControls, useBlockProps } from '@wordpress/block-editor'; -import { PanelBody, Disabled, ExternalLink } from '@wordpress/components'; +import { PanelBody, ExternalLink } from '@wordpress/components'; import { ADMIN_URL, getSetting } from '@woocommerce/settings'; import ExternalLinkCard from '@woocommerce/editor-components/external-link-card'; import { innerBlockAreas } from '@woocommerce/blocks-checkout'; +import Noninteractive from '@woocommerce/base-components/noninteractive'; /** * Internal dependencies @@ -85,9 +86,9 @@ export const Edit = ( { ) } - + - + ); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-shipping-address-block/block.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-shipping-address-block/block.tsx index 591dba05f0a..80cfb03bc79 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-shipping-address-block/block.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-shipping-address-block/block.tsx @@ -3,7 +3,6 @@ */ import { __ } from '@wordpress/i18n'; import { useMemo, useEffect, Fragment } from '@wordpress/element'; -import { Disabled } from 'wordpress-components'; import { AddressForm } from '@woocommerce/base-components/cart-checkout'; import { useCheckoutAddress, @@ -11,6 +10,7 @@ import { useEditorContext, } from '@woocommerce/base-context'; import { CheckboxControl } from '@woocommerce/blocks-checkout'; +import Noninteractive from '@woocommerce/base-components/noninteractive'; /** * Internal dependencies @@ -60,7 +60,7 @@ const Block = ( { }; }, [ showCompanyField, requireCompanyField, showApartmentField ] ); - const AddressFormWrapperComponent = isEditor ? Disabled : Fragment; + const AddressFormWrapperComponent = isEditor ? Noninteractive : Fragment; return ( <> diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-shipping-methods-block/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-shipping-methods-block/edit.tsx index d80138640d4..2c003a4c19d 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-shipping-methods-block/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/checkout/inner-blocks/checkout-shipping-methods-block/edit.tsx @@ -4,10 +4,11 @@ import classnames from 'classnames'; import { __ } from '@wordpress/i18n'; import { InspectorControls, useBlockProps } from '@wordpress/block-editor'; -import { PanelBody, Disabled, ExternalLink } from '@wordpress/components'; +import { PanelBody, ExternalLink } from '@wordpress/components'; import { ADMIN_URL, getSetting } from '@woocommerce/settings'; import ExternalLinkCard from '@woocommerce/editor-components/external-link-card'; import { innerBlockAreas } from '@woocommerce/blocks-checkout'; +import Noninteractive from '@woocommerce/base-components/noninteractive'; /** * Internal dependencies @@ -119,9 +120,9 @@ export const Edit = ( { ) } - + - + ); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/price-filter/block.js b/plugins/woocommerce-blocks/assets/js/blocks/price-filter/block.js index 2d8f67df180..36e8dc9204c 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/price-filter/block.js +++ b/plugins/woocommerce-blocks/assets/js/blocks/price-filter/block.js @@ -69,7 +69,7 @@ const PriceFilterBlock = ( { attributes, isEditor = false } ) => { ); // Updates the query after a short delay. - const [ debouncedUpdateQuery ] = useDebouncedCallback( onSubmit, 500 ); + const debouncedUpdateQuery = useDebouncedCallback( onSubmit, 500 ); // Callback when slider or input fields are changed. const onChange = useCallback( diff --git a/plugins/woocommerce-blocks/assets/js/hocs/test/with-searched-products.js b/plugins/woocommerce-blocks/assets/js/hocs/test/with-searched-products.js index 8f034816ffe..0eabdcbfdb2 100644 --- a/plugins/woocommerce-blocks/assets/js/hocs/test/with-searched-products.js +++ b/plugins/woocommerce-blocks/assets/js/hocs/test/with-searched-products.js @@ -28,9 +28,7 @@ mockUtils.getProducts = jest.fn().mockImplementation( () => // Add a mock implementation of debounce for testing so we can spy on the onSearch call. mockUseDebounce.useDebouncedCallback = jest .fn() - .mockImplementation( ( search ) => [ - () => mockUtils.getProducts( search ), - ] ); + .mockImplementation( ( search ) => () => mockUtils.getProducts( search ) ); describe( 'withSearchedProducts Component', () => { const { getProducts } = mockUtils; diff --git a/plugins/woocommerce-blocks/assets/js/hocs/with-searched-products.tsx b/plugins/woocommerce-blocks/assets/js/hocs/with-searched-products.tsx index 49a71eb40b3..85bd3490585 100644 --- a/plugins/woocommerce-blocks/assets/js/hocs/with-searched-products.tsx +++ b/plugins/woocommerce-blocks/assets/js/hocs/with-searched-products.tsx @@ -50,17 +50,14 @@ const withSearchedProducts = ( .catch( setErrorState ); }, [ selected ] ); - const [ debouncedSearch ] = useDebouncedCallback( - ( search: string ) => { - getProducts( { selected, search } ) - .then( ( results ) => { - setProductsList( results as ProductResponseItem[] ); - setIsLoading( false ); - } ) - .catch( setErrorState ); - }, - 400 - ); + const debouncedSearch = useDebouncedCallback( ( search: string ) => { + getProducts( { selected, search } ) + .then( ( results ) => { + setProductsList( results as ProductResponseItem[] ); + setIsLoading( false ); + } ) + .catch( setErrorState ); + }, 400 ); const onSearch = useCallback( ( search: string ) => { diff --git a/plugins/woocommerce-blocks/package-lock.json b/plugins/woocommerce-blocks/package-lock.json index 88b45cbd0b9..20a5628a1f2 100644 --- a/plugins/woocommerce-blocks/package-lock.json +++ b/plugins/woocommerce-blocks/package-lock.json @@ -15795,6 +15795,16 @@ "dev": true, "optional": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -21430,6 +21440,13 @@ } } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, "filelist": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz", @@ -29326,6 +29343,13 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "dev": true, + "optional": true + }, "nanoid": { "version": "3.1.23", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", @@ -38756,9 +38780,9 @@ } }, "use-debounce": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-3.4.3.tgz", - "integrity": "sha512-nxy+opOxDccWfhMl36J5BSCTpvcj89iaQk2OZWLAtBJQj7ISCtx1gh+rFbdjGfMl6vtCZf6gke/kYvrkVfHMoA==" + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-7.0.1.tgz", + "integrity": "sha512-fOrzIw2wstbAJuv8PC9Vg4XgwyTLEOdq4y/Z3IhVl8DAE4svRcgyEUvrEXu+BMNgMoc3YND6qLT61kkgEKXh7Q==" }, "use-enhanced-state": { "version": "0.0.13", @@ -39105,7 +39129,11 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "glob-parent": { "version": "3.1.0", diff --git a/plugins/woocommerce-blocks/package.json b/plugins/woocommerce-blocks/package.json index fb38e071f75..88a6a2a642b 100644 --- a/plugins/woocommerce-blocks/package.json +++ b/plugins/woocommerce-blocks/package.json @@ -207,7 +207,7 @@ "react-number-format": "4.4.3", "reakit": "1.3.11", "trim-html": "0.1.9", - "use-debounce": "3.4.3", + "use-debounce": "7.0.1", "wordpress-components": "npm:@wordpress/components@11.1.6" }, "husky": {