From d194c04f51c76fb4cfffca984345f690c5fa9aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Mon, 1 Feb 2021 18:29:30 +0100 Subject: [PATCH 01/16] Replace filters with an extendibility API to hook into Cart and Checkout blocks --- .../cart-checkout/totals/footer-item/index.js | 14 ++++++---- packages/checkout/index.js | 4 +-- packages/checkout/registry/index.js | 28 +++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 packages/checkout/registry/index.js diff --git a/assets/js/base/components/cart-checkout/totals/footer-item/index.js b/assets/js/base/components/cart-checkout/totals/footer-item/index.js index deab888ca6d..c777af0d47d 100644 --- a/assets/js/base/components/cart-checkout/totals/footer-item/index.js +++ b/assets/js/base/components/cart-checkout/totals/footer-item/index.js @@ -10,10 +10,10 @@ import { createInterpolateElement } from 'wordpress-element'; import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount'; import PropTypes from 'prop-types'; import { - __EXPERIMENTAL_TOTAL_LABEL_FILTER, + __experimentalApplyCheckoutFilter, TotalsItem, } from '@woocommerce/blocks-checkout'; -import { applyFilters } from '@wordpress/hooks'; +import { useStoreCart } from '@woocommerce/base-hooks'; /** * Internal dependencies @@ -24,9 +24,13 @@ const SHOW_TAXES = TAXES_ENABLED && DISPLAY_CART_PRICES_INCLUDING_TAX; const TotalsFooterItem = ( { currency, values } ) => { const { total_price: totalPrice, total_tax: totalTax } = values; - const label = applyFilters( - __EXPERIMENTAL_TOTAL_LABEL_FILTER, - __( 'Total', 'woo-gutenberg-products-block' ) + const { extensions } = useStoreCart(); + const label = __experimentalApplyCheckoutFilter( + 'totalLabel', + __( 'Total', 'woo-gutenberg-products-block' ), + { + extensions, + } ); return ( diff --git a/packages/checkout/index.js b/packages/checkout/index.js index b1828a0e9bb..6025a694b8a 100644 --- a/packages/checkout/index.js +++ b/packages/checkout/index.js @@ -1,10 +1,8 @@ export * from './totals'; export * from './shipping'; export * from './slot'; +export * from './registry'; export { default as ExperimentalOrderMeta } from './order-meta'; export { default as ExperimentalOrderShippingPackages } from './order-shipping-packages'; export { default as Panel } from './panel'; export { SlotFillProvider } from 'wordpress-components'; - -export const __EXPERIMENTAL_TOTAL_LABEL_FILTER = - 'wcBlocks.__experimental_total_label_filter'; diff --git a/packages/checkout/registry/index.js b/packages/checkout/registry/index.js new file mode 100644 index 00000000000..1460f834caf --- /dev/null +++ b/packages/checkout/registry/index.js @@ -0,0 +1,28 @@ +let checkoutFilters = {}; +export const __experimentalRegisterCheckoutFilters = ( namespace, filters ) => { + checkoutFilters = { + ...checkoutFilters, + [ namespace ]: filters, + }; +}; + +const getCheckoutFilters = ( filterName ) => { + const namespaces = Object.keys( checkoutFilters ); + const filters = namespaces + .map( ( namespace ) => checkoutFilters[ namespace ][ filterName ] ) + .filter( Boolean ); + return filters; +}; + +export const __experimentalApplyCheckoutFilter = ( + filterName, + defaultValue, + args +) => { + const filters = getCheckoutFilters( filterName ); + let value = defaultValue; + filters.forEach( ( filter ) => { + value = filter( value, args ); + } ); + return value; +}; From a8259cacd780db0f52937941b58790144c334a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Mon, 1 Feb 2021 18:31:11 +0100 Subject: [PATCH 02/16] Update docs --- docs/blocks/feature-flags-and-experimental-interfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/blocks/feature-flags-and-experimental-interfaces.md b/docs/blocks/feature-flags-and-experimental-interfaces.md index 213b17d079d..b9f6152492d 100644 --- a/docs/blocks/feature-flags-and-experimental-interfaces.md +++ b/docs/blocks/feature-flags-and-experimental-interfaces.md @@ -57,5 +57,5 @@ We also have individual features or code blocks behind a feature flag, this is a - `__experimental_woocommerce_blocks_checkout_order_processed` hook when order has completed processing and is ready for payment ([experimental hook](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/accd1bbf402e043b9fc322f118ab614ba7437c92/src/StoreApi/Routes/Checkout.php#L237)). - `__experimentalDeRegisterPaymentMethod` function used to deregister a payment method, only used in tests ([experimental function](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/b07883b8b76feeb439d655b255507b24fc59e091/assets/js/blocks-registry/payment-methods/registry.js#L70)). - `__experimentalDeRegisterExpressPaymentMethod` function used to deregister an express payment method, only used in tests ([experimental function](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/b07883b8b76feeb439d655b255507b24fc59e091/assets/js/blocks-registry/payment-methods/registry.js#L74)). -- `__EXPERIMENTAL_TOTAL_LABEL_FILTER` constant which maps to the `wcBlocks.__experimental_total_label_filter` hook name. Used to filter the _Total_ label in the Cart and Checkout blocks ([experimental constant](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/9c4288b0ee46960bdc2bf8ef351d05ac23073b0c/packages/checkout/index.js#L8-L9)). +- `__experimentalRegisterCheckoutFilters` and `__experimentalApplyCheckoutFilter` methods included with `@woocommerce/blocks-checkout` package. They allow registering and applying a filter to certain parts of the Cart and Checkout blocks ([experimental method 1](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/3e59ec9842464f783f6e087947e717fa0b0a7b1b/packages/checkout/registry/index.js#L2) | [experimental method 2](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/3e59ec9842464f783f6e087947e717fa0b0a7b1b/packages/checkout/registry/index.js#L17)). - `__experimental_woocommerce_blocks_hidden` property in a Cart item data array that allows overwriting the `hidden` property. This is useful to make some cart item data visible/hidden depending if it needs to be displayed in Blocks or shortcode ([experimental property](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/9c4288b0ee46960bdc2bf8ef351d05ac23073b0c/src/StoreApi/Schemas/CartItemSchema.php#L439-L441)). From 8043025fc5861839de4cd89d64b3a7512b7187d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Mon, 1 Feb 2021 19:06:09 +0100 Subject: [PATCH 03/16] Add a validate argument --- packages/checkout/registry/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/checkout/registry/index.js b/packages/checkout/registry/index.js index 1460f834caf..5e19b49cf13 100644 --- a/packages/checkout/registry/index.js +++ b/packages/checkout/registry/index.js @@ -17,12 +17,14 @@ const getCheckoutFilters = ( filterName ) => { export const __experimentalApplyCheckoutFilter = ( filterName, defaultValue, - args + args = {}, + validate = () => true ) => { const filters = getCheckoutFilters( filterName ); let value = defaultValue; filters.forEach( ( filter ) => { - value = filter( value, args ); + const newValue = filter( value, args ); + value = validate( newValue ) ? newValue : value; } ); return value; }; From 95103f8cc48e9255d4df1ba371fc87a2c713ff68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Mon, 1 Feb 2021 19:06:54 +0100 Subject: [PATCH 04/16] Add docs comments --- packages/checkout/registry/index.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/checkout/registry/index.js b/packages/checkout/registry/index.js index 5e19b49cf13..4469b0cfef2 100644 --- a/packages/checkout/registry/index.js +++ b/packages/checkout/registry/index.js @@ -1,4 +1,12 @@ let checkoutFilters = {}; + +/** + * Register filters for a specific extension. + * + * @param {string} namespace Name of the extension namespace. + * @param {Object} filters Object of filters for that namespace. Each key of + * the object is the name of a filter. + */ export const __experimentalRegisterCheckoutFilters = ( namespace, filters ) => { checkoutFilters = { ...checkoutFilters, @@ -6,6 +14,11 @@ export const __experimentalRegisterCheckoutFilters = ( namespace, filters ) => { }; }; +/** + * Get all filters with a specific name. + * + * @param {string} filterName Name of the filter to search for. + */ const getCheckoutFilters = ( filterName ) => { const namespaces = Object.keys( checkoutFilters ); const filters = namespaces @@ -14,6 +27,16 @@ const getCheckoutFilters = ( filterName ) => { return filters; }; +/** + * Register a regular payment method. + * + * @param {string} filterName Name of the filter to apply. + * @param {any} defaultValue Default value to filter. + * @param {any} args Arguments to pass to registered functions. + * @param {any} [validate] Function that needs to return true when the + * filtered value is passed in order for the filter + * to be applied. + */ export const __experimentalApplyCheckoutFilter = ( filterName, defaultValue, From 91bee6f3b1094ee67912bfb37f1f0df272fdd379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Mon, 1 Feb 2021 19:07:09 +0100 Subject: [PATCH 05/16] Add tests --- packages/checkout/registry/test/index.js | 46 ++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 packages/checkout/registry/test/index.js diff --git a/packages/checkout/registry/test/index.js b/packages/checkout/registry/test/index.js new file mode 100644 index 00000000000..afbbc0cfd98 --- /dev/null +++ b/packages/checkout/registry/test/index.js @@ -0,0 +1,46 @@ +/** + * Internal dependencies + */ +import { + __experimentalRegisterCheckoutFilters, + __experimentalApplyCheckoutFilter, +} from '../'; + +describe( 'Checkout registry', () => { + const filterName = 'loremIpsum'; + + test( 'should return default value if there are no filters', () => { + const value = 'Hello World'; + const newValue = __experimentalApplyCheckoutFilter( filterName, value ); + + expect( newValue ).toBe( value ); + } ); + + test( 'should return filtered value when a filter is registered', () => { + const value = 'Hello World'; + __experimentalRegisterCheckoutFilters( filterName, { + [ filterName ]: ( val, args ) => + val.toUpperCase() + args.punctuationSign, + } ); + const newValue = __experimentalApplyCheckoutFilter( filterName, value, { + punctuationSign: '!', + } ); + + expect( newValue ).toBe( 'HELLO WORLD!' ); + } ); + + test( 'should not return filtered value if validation failed', () => { + const value = 'Hello World'; + __experimentalRegisterCheckoutFilters( filterName, { + [ filterName ]: ( val ) => val.toUpperCase(), + } ); + const newValue = __experimentalApplyCheckoutFilter( + filterName, + value, + {}, + ( val ) => ! val.includes( 'HELLO' ) + ); + + expect( newValue ).toBe( value ); + } ); +} ); From e11393067c7bb367eb21e2345beacfa5efc05fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 2 Feb 2021 14:14:53 +0100 Subject: [PATCH 06/16] Add validation function --- .../cart-checkout/totals/footer-item/index.js | 4 ++- packages/checkout/index.js | 1 + packages/checkout/registry/test/index.js | 31 +++++++++++++++++++ packages/checkout/registry/validations.js | 10 ++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 packages/checkout/registry/validations.js diff --git a/assets/js/base/components/cart-checkout/totals/footer-item/index.js b/assets/js/base/components/cart-checkout/totals/footer-item/index.js index c777af0d47d..a24059f0633 100644 --- a/assets/js/base/components/cart-checkout/totals/footer-item/index.js +++ b/assets/js/base/components/cart-checkout/totals/footer-item/index.js @@ -11,6 +11,7 @@ import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-mone import PropTypes from 'prop-types'; import { __experimentalApplyCheckoutFilter, + validatedElementOrString, TotalsItem, } from '@woocommerce/blocks-checkout'; import { useStoreCart } from '@woocommerce/base-hooks'; @@ -30,7 +31,8 @@ const TotalsFooterItem = ( { currency, values } ) => { __( 'Total', 'woo-gutenberg-products-block' ), { extensions, - } + }, + validatedElementOrString ); return ( diff --git a/packages/checkout/index.js b/packages/checkout/index.js index 6025a694b8a..f393a5bb6ba 100644 --- a/packages/checkout/index.js +++ b/packages/checkout/index.js @@ -2,6 +2,7 @@ export * from './totals'; export * from './shipping'; export * from './slot'; export * from './registry'; +export * from './registry/validations.js'; export { default as ExperimentalOrderMeta } from './order-meta'; export { default as ExperimentalOrderShippingPackages } from './order-shipping-packages'; export { default as Panel } from './panel'; diff --git a/packages/checkout/registry/test/index.js b/packages/checkout/registry/test/index.js index afbbc0cfd98..57db58cc2ff 100644 --- a/packages/checkout/registry/test/index.js +++ b/packages/checkout/registry/test/index.js @@ -5,6 +5,7 @@ import { __experimentalRegisterCheckoutFilters, __experimentalApplyCheckoutFilter, } from '../'; +import { validateElementOrString } from '../validations'; describe( 'Checkout registry', () => { const filterName = 'loremIpsum'; @@ -43,4 +44,34 @@ describe( 'Checkout registry', () => { expect( newValue ).toBe( value ); } ); + + describe( 'validations', () => { + test( 'validateElementOrString should invalidate if filtered value is not an element or string', () => { + __experimentalRegisterCheckoutFilters( filterName, { + [ filterName ]: ( val ) => { + if ( val === 'Hello World' ) { + return 'Valid value'; + } + return [ 'invalid-value' ]; + }, + } ); + const validValue = __experimentalApplyCheckoutFilter( + filterName, + 'Hello World', + {}, + validateElementOrString + ); + + expect( validValue ).toBe( 'Valid value' ); + + const invalidValue = __experimentalApplyCheckoutFilter( + filterName, + 'Hello Earth', + {}, + validateElementOrString + ); + + expect( invalidValue ).toBe( 'Hello Earth' ); + } ); + } ); } ); diff --git a/packages/checkout/registry/validations.js b/packages/checkout/registry/validations.js new file mode 100644 index 00000000000..16be39286ac --- /dev/null +++ b/packages/checkout/registry/validations.js @@ -0,0 +1,10 @@ +/** + * External dependencies + */ +import { isValidElement } from '@wordpress/element'; + +export const validateElementOrString = ( value ) => { + return ( + value === null || isValidElement( value ) || typeof value === 'string' + ); +}; From 24fceeb7fa60a49e11cf5418ed86c579e16f1f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 2 Feb 2021 15:29:13 +0100 Subject: [PATCH 07/16] Prefix validateElementOrString with __experimental --- .../components/cart-checkout/totals/footer-item/index.js | 4 ++-- packages/checkout/registry/test/index.js | 8 ++++---- packages/checkout/registry/validations.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/assets/js/base/components/cart-checkout/totals/footer-item/index.js b/assets/js/base/components/cart-checkout/totals/footer-item/index.js index a24059f0633..d87fac874b2 100644 --- a/assets/js/base/components/cart-checkout/totals/footer-item/index.js +++ b/assets/js/base/components/cart-checkout/totals/footer-item/index.js @@ -11,7 +11,7 @@ import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-mone import PropTypes from 'prop-types'; import { __experimentalApplyCheckoutFilter, - validatedElementOrString, + __experimentalValidatedElementOrString, TotalsItem, } from '@woocommerce/blocks-checkout'; import { useStoreCart } from '@woocommerce/base-hooks'; @@ -32,7 +32,7 @@ const TotalsFooterItem = ( { currency, values } ) => { { extensions, }, - validatedElementOrString + __experimentalValidatedElementOrString ); return ( diff --git a/packages/checkout/registry/test/index.js b/packages/checkout/registry/test/index.js index 57db58cc2ff..437c26a7b1d 100644 --- a/packages/checkout/registry/test/index.js +++ b/packages/checkout/registry/test/index.js @@ -5,7 +5,7 @@ import { __experimentalRegisterCheckoutFilters, __experimentalApplyCheckoutFilter, } from '../'; -import { validateElementOrString } from '../validations'; +import { __experimentalValidateElementOrString } from '../validations'; describe( 'Checkout registry', () => { const filterName = 'loremIpsum'; @@ -46,7 +46,7 @@ describe( 'Checkout registry', () => { } ); describe( 'validations', () => { - test( 'validateElementOrString should invalidate if filtered value is not an element or string', () => { + test( '__experimentalValidateElementOrString should invalidate if filtered value is not an element or string', () => { __experimentalRegisterCheckoutFilters( filterName, { [ filterName ]: ( val ) => { if ( val === 'Hello World' ) { @@ -59,7 +59,7 @@ describe( 'Checkout registry', () => { filterName, 'Hello World', {}, - validateElementOrString + __experimentalValidateElementOrString ); expect( validValue ).toBe( 'Valid value' ); @@ -68,7 +68,7 @@ describe( 'Checkout registry', () => { filterName, 'Hello Earth', {}, - validateElementOrString + __experimentalValidateElementOrString ); expect( invalidValue ).toBe( 'Hello Earth' ); diff --git a/packages/checkout/registry/validations.js b/packages/checkout/registry/validations.js index 16be39286ac..00691ec39c0 100644 --- a/packages/checkout/registry/validations.js +++ b/packages/checkout/registry/validations.js @@ -3,7 +3,7 @@ */ import { isValidElement } from '@wordpress/element'; -export const validateElementOrString = ( value ) => { +export const __experimentalValidateElementOrString = ( value ) => { return ( value === null || isValidElement( value ) || typeof value === 'string' ); From 2aeba81f46ab4ea49d6d0ea9ab4990f8b7caee61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 2 Feb 2021 15:30:31 +0100 Subject: [PATCH 08/16] Update experimental docs --- docs/blocks/feature-flags-and-experimental-interfaces.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/blocks/feature-flags-and-experimental-interfaces.md b/docs/blocks/feature-flags-and-experimental-interfaces.md index b9f6152492d..85e6d346bbf 100644 --- a/docs/blocks/feature-flags-and-experimental-interfaces.md +++ b/docs/blocks/feature-flags-and-experimental-interfaces.md @@ -58,4 +58,5 @@ We also have individual features or code blocks behind a feature flag, this is a - `__experimentalDeRegisterPaymentMethod` function used to deregister a payment method, only used in tests ([experimental function](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/b07883b8b76feeb439d655b255507b24fc59e091/assets/js/blocks-registry/payment-methods/registry.js#L70)). - `__experimentalDeRegisterExpressPaymentMethod` function used to deregister an express payment method, only used in tests ([experimental function](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/b07883b8b76feeb439d655b255507b24fc59e091/assets/js/blocks-registry/payment-methods/registry.js#L74)). - `__experimentalRegisterCheckoutFilters` and `__experimentalApplyCheckoutFilter` methods included with `@woocommerce/blocks-checkout` package. They allow registering and applying a filter to certain parts of the Cart and Checkout blocks ([experimental method 1](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/3e59ec9842464f783f6e087947e717fa0b0a7b1b/packages/checkout/registry/index.js#L2) | [experimental method 2](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/3e59ec9842464f783f6e087947e717fa0b0a7b1b/packages/checkout/registry/index.js#L17)). +- `__experimentalValidateElementOrString` method included with `@woocommerce/blocks-checkout` package. Allows validating that a value is a valid element or a string ([experimental method](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/277ad369504c38abcc68a4cdbb034c421fe4d123/packages/checkout/registry/validations.js#L6)). - `__experimental_woocommerce_blocks_hidden` property in a Cart item data array that allows overwriting the `hidden` property. This is useful to make some cart item data visible/hidden depending if it needs to be displayed in Blocks or shortcode ([experimental property](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/9c4288b0ee46960bdc2bf8ef351d05ac23073b0c/src/StoreApi/Schemas/CartItemSchema.php#L439-L441)). From 4cfd9b23dbe5a1284d2f118b6af0330c2a5022f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 2 Feb 2021 17:09:12 +0100 Subject: [PATCH 09/16] Typo --- .../base/components/cart-checkout/totals/footer-item/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/js/base/components/cart-checkout/totals/footer-item/index.js b/assets/js/base/components/cart-checkout/totals/footer-item/index.js index d87fac874b2..44650516cbb 100644 --- a/assets/js/base/components/cart-checkout/totals/footer-item/index.js +++ b/assets/js/base/components/cart-checkout/totals/footer-item/index.js @@ -11,7 +11,7 @@ import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-mone import PropTypes from 'prop-types'; import { __experimentalApplyCheckoutFilter, - __experimentalValidatedElementOrString, + __experimentalValidateElementOrString, TotalsItem, } from '@woocommerce/blocks-checkout'; import { useStoreCart } from '@woocommerce/base-hooks'; @@ -32,7 +32,7 @@ const TotalsFooterItem = ( { currency, values } ) => { { extensions, }, - __experimentalValidatedElementOrString + __experimentalValidateElementOrString ); return ( From e36c22c961afb8f74e266a60560133b7e12dac3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 2 Feb 2021 18:17:28 +0100 Subject: [PATCH 10/16] Update comment --- packages/checkout/registry/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/checkout/registry/index.js b/packages/checkout/registry/index.js index 4469b0cfef2..bec3f555be7 100644 --- a/packages/checkout/registry/index.js +++ b/packages/checkout/registry/index.js @@ -28,7 +28,7 @@ const getCheckoutFilters = ( filterName ) => { }; /** - * Register a regular payment method. + * Apply a filter. * * @param {string} filterName Name of the filter to apply. * @param {any} defaultValue Default value to filter. From 1d89ef8a60c872e3340cd19c78da274d7ed7f865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Fri, 5 Feb 2021 11:15:21 +0100 Subject: [PATCH 11/16] Update JS docs --- packages/checkout/registry/index.js | 15 +++++++++------ packages/checkout/registry/validations.js | 6 ++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/checkout/registry/index.js b/packages/checkout/registry/index.js index bec3f555be7..c5bb1173420 100644 --- a/packages/checkout/registry/index.js +++ b/packages/checkout/registry/index.js @@ -18,6 +18,8 @@ export const __experimentalRegisterCheckoutFilters = ( namespace, filters ) => { * Get all filters with a specific name. * * @param {string} filterName Name of the filter to search for. + * @return {Function[]} Array of functions that are registered for that filter + * name. */ const getCheckoutFilters = ( filterName ) => { const namespaces = Object.keys( checkoutFilters ); @@ -30,12 +32,13 @@ const getCheckoutFilters = ( filterName ) => { /** * Apply a filter. * - * @param {string} filterName Name of the filter to apply. - * @param {any} defaultValue Default value to filter. - * @param {any} args Arguments to pass to registered functions. - * @param {any} [validate] Function that needs to return true when the - * filtered value is passed in order for the filter - * to be applied. + * @param {string} filterName Name of the filter to apply. + * @param {any} defaultValue Default value to filter. + * @param {Object} [args] Arguments to pass to registered functions. + * @param {Function} [validate] Function that needs to return true when the + * filtered value is passed in order for the + * filter to be applied. + * @return {any} Filtered value. */ export const __experimentalApplyCheckoutFilter = ( filterName, diff --git a/packages/checkout/registry/validations.js b/packages/checkout/registry/validations.js index 00691ec39c0..11e0ffadc9e 100644 --- a/packages/checkout/registry/validations.js +++ b/packages/checkout/registry/validations.js @@ -3,6 +3,12 @@ */ import { isValidElement } from '@wordpress/element'; +/** + * Checks if the provided value is a React element, a string or null. + * + * @param {any} value Value to check. + * @return {boolean} Whether the value is a valid element, string or null. + */ export const __experimentalValidateElementOrString = ( value ) => { return ( value === null || isValidElement( value ) || typeof value === 'string' From 955bf18948193d3e3bc022cb95a6915dbab32e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Mon, 8 Feb 2021 15:19:45 +0100 Subject: [PATCH 12/16] Use an object for applyCheckoutFilter args --- .../cart-checkout/totals/footer-item/index.js | 12 +++--- packages/checkout/registry/index.js | 19 ++++----- packages/checkout/registry/test/index.js | 40 ++++++++++--------- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/assets/js/base/components/cart-checkout/totals/footer-item/index.js b/assets/js/base/components/cart-checkout/totals/footer-item/index.js index 44650516cbb..17c89efd35f 100644 --- a/assets/js/base/components/cart-checkout/totals/footer-item/index.js +++ b/assets/js/base/components/cart-checkout/totals/footer-item/index.js @@ -26,14 +26,14 @@ const SHOW_TAXES = TAXES_ENABLED && DISPLAY_CART_PRICES_INCLUDING_TAX; const TotalsFooterItem = ( { currency, values } ) => { const { total_price: totalPrice, total_tax: totalTax } = values; const { extensions } = useStoreCart(); - const label = __experimentalApplyCheckoutFilter( - 'totalLabel', - __( 'Total', 'woo-gutenberg-products-block' ), - { + const label = __experimentalApplyCheckoutFilter( { + filterName: 'totalLabel', + defaultValue: __( 'Total', 'woo-gutenberg-products-block' ), + args: { extensions, }, - __experimentalValidateElementOrString - ); + validate: __experimentalValidateElementOrString, + } ); return ( { /** * Apply a filter. * - * @param {string} filterName Name of the filter to apply. - * @param {any} defaultValue Default value to filter. - * @param {Object} [args] Arguments to pass to registered functions. - * @param {Function} [validate] Function that needs to return true when the - * filtered value is passed in order for the - * filter to be applied. + * @param {Object} o Object of arguments. + * @param {string} o.filterName Name of the filter to apply. + * @param {any} o.defaultValue Default value to filter. + * @param {Object} [o.args] Arguments to pass to registered functions. + * @param {Function} [o.validate] Function that needs to return true when the + * filtered value is passed in order for the + * filter to be applied. * @return {any} Filtered value. */ -export const __experimentalApplyCheckoutFilter = ( +export const __experimentalApplyCheckoutFilter = ( { filterName, defaultValue, args = {}, - validate = () => true -) => { + validate = () => true, +} ) => { const filters = getCheckoutFilters( filterName ); let value = defaultValue; filters.forEach( ( filter ) => { diff --git a/packages/checkout/registry/test/index.js b/packages/checkout/registry/test/index.js index 437c26a7b1d..0b3bcc4e136 100644 --- a/packages/checkout/registry/test/index.js +++ b/packages/checkout/registry/test/index.js @@ -12,7 +12,10 @@ describe( 'Checkout registry', () => { test( 'should return default value if there are no filters', () => { const value = 'Hello World'; - const newValue = __experimentalApplyCheckoutFilter( filterName, value ); + const newValue = __experimentalApplyCheckoutFilter( { + filterName, + defaultValue: value, + } ); expect( newValue ).toBe( value ); } ); @@ -23,8 +26,12 @@ describe( 'Checkout registry', () => { [ filterName ]: ( val, args ) => val.toUpperCase() + args.punctuationSign, } ); - const newValue = __experimentalApplyCheckoutFilter( filterName, value, { - punctuationSign: '!', + const newValue = __experimentalApplyCheckoutFilter( { + filterName, + defaultValue: value, + args: { + punctuationSign: '!', + }, } ); expect( newValue ).toBe( 'HELLO WORLD!' ); @@ -35,12 +42,11 @@ describe( 'Checkout registry', () => { __experimentalRegisterCheckoutFilters( filterName, { [ filterName ]: ( val ) => val.toUpperCase(), } ); - const newValue = __experimentalApplyCheckoutFilter( + const newValue = __experimentalApplyCheckoutFilter( { filterName, - value, - {}, - ( val ) => ! val.includes( 'HELLO' ) - ); + defaultValue: value, + validate: ( val ) => ! val.includes( 'HELLO' ), + } ); expect( newValue ).toBe( value ); } ); @@ -55,21 +61,19 @@ describe( 'Checkout registry', () => { return [ 'invalid-value' ]; }, } ); - const validValue = __experimentalApplyCheckoutFilter( + const validValue = __experimentalApplyCheckoutFilter( { filterName, - 'Hello World', - {}, - __experimentalValidateElementOrString - ); + defaultValue: 'Hello World', + validate: __experimentalValidateElementOrString, + } ); expect( validValue ).toBe( 'Valid value' ); - const invalidValue = __experimentalApplyCheckoutFilter( + const invalidValue = __experimentalApplyCheckoutFilter( { filterName, - 'Hello Earth', - {}, - __experimentalValidateElementOrString - ); + defaultValue: 'Hello Earth', + validate: __experimentalValidateElementOrString, + } ); expect( invalidValue ).toBe( 'Hello Earth' ); } ); From 957ee96ee83992cb6c14ec91c6be9dceecb1ccf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Mon, 8 Feb 2021 15:20:53 +0100 Subject: [PATCH 13/16] Args doesn't need to be an object --- packages/checkout/registry/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/checkout/registry/index.js b/packages/checkout/registry/index.js index 7d2e8db30d6..4fff219e87f 100644 --- a/packages/checkout/registry/index.js +++ b/packages/checkout/registry/index.js @@ -35,7 +35,9 @@ const getCheckoutFilters = ( filterName ) => { * @param {Object} o Object of arguments. * @param {string} o.filterName Name of the filter to apply. * @param {any} o.defaultValue Default value to filter. - * @param {Object} [o.args] Arguments to pass to registered functions. + * @param {any} [o.args] Argument to pass to registered functions. If + * several arguments need to be passed, use an + * object. * @param {Function} [o.validate] Function that needs to return true when the * filtered value is passed in order for the * filter to be applied. From 2add635ab1e725a5e1ea68361c62286ef67a2450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Mon, 8 Feb 2021 15:25:19 +0100 Subject: [PATCH 14/16] Wrap validation function execution in a try/catch block --- packages/checkout/registry/index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/checkout/registry/index.js b/packages/checkout/registry/index.js index 4fff219e87f..16e503f134e 100644 --- a/packages/checkout/registry/index.js +++ b/packages/checkout/registry/index.js @@ -52,8 +52,13 @@ export const __experimentalApplyCheckoutFilter = ( { const filters = getCheckoutFilters( filterName ); let value = defaultValue; filters.forEach( ( filter ) => { - const newValue = filter( value, args ); - value = validate( newValue ) ? newValue : value; + try { + const newValue = filter( value, args ); + value = validate( newValue ) ? newValue : value; + } catch ( e ) { + // eslint-disable-next-line no-console + console.log( e ); + } } ); return value; }; From 9dc169072035bd7b6572c6cff336d8f89d87b9a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Mon, 8 Feb 2021 15:34:55 +0100 Subject: [PATCH 15/16] Only accept strings for the totalLabel filter --- .../cart-checkout/totals/footer-item/index.js | 4 +-- ...ature-flags-and-experimental-interfaces.md | 1 - packages/checkout/index.js | 1 - packages/checkout/registry/test/index.js | 29 ------------------- packages/checkout/registry/validations.js | 16 ---------- 5 files changed, 2 insertions(+), 49 deletions(-) delete mode 100644 packages/checkout/registry/validations.js diff --git a/assets/js/base/components/cart-checkout/totals/footer-item/index.js b/assets/js/base/components/cart-checkout/totals/footer-item/index.js index 17c89efd35f..48f2f7ba0b9 100644 --- a/assets/js/base/components/cart-checkout/totals/footer-item/index.js +++ b/assets/js/base/components/cart-checkout/totals/footer-item/index.js @@ -11,7 +11,6 @@ import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-mone import PropTypes from 'prop-types'; import { __experimentalApplyCheckoutFilter, - __experimentalValidateElementOrString, TotalsItem, } from '@woocommerce/blocks-checkout'; import { useStoreCart } from '@woocommerce/base-hooks'; @@ -32,7 +31,8 @@ const TotalsFooterItem = ( { currency, values } ) => { args: { extensions, }, - validate: __experimentalValidateElementOrString, + // Only accept strings. + validate: ( value ) => typeof value === 'string', } ); return ( diff --git a/docs/blocks/feature-flags-and-experimental-interfaces.md b/docs/blocks/feature-flags-and-experimental-interfaces.md index 85e6d346bbf..b9f6152492d 100644 --- a/docs/blocks/feature-flags-and-experimental-interfaces.md +++ b/docs/blocks/feature-flags-and-experimental-interfaces.md @@ -58,5 +58,4 @@ We also have individual features or code blocks behind a feature flag, this is a - `__experimentalDeRegisterPaymentMethod` function used to deregister a payment method, only used in tests ([experimental function](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/b07883b8b76feeb439d655b255507b24fc59e091/assets/js/blocks-registry/payment-methods/registry.js#L70)). - `__experimentalDeRegisterExpressPaymentMethod` function used to deregister an express payment method, only used in tests ([experimental function](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/b07883b8b76feeb439d655b255507b24fc59e091/assets/js/blocks-registry/payment-methods/registry.js#L74)). - `__experimentalRegisterCheckoutFilters` and `__experimentalApplyCheckoutFilter` methods included with `@woocommerce/blocks-checkout` package. They allow registering and applying a filter to certain parts of the Cart and Checkout blocks ([experimental method 1](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/3e59ec9842464f783f6e087947e717fa0b0a7b1b/packages/checkout/registry/index.js#L2) | [experimental method 2](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/3e59ec9842464f783f6e087947e717fa0b0a7b1b/packages/checkout/registry/index.js#L17)). -- `__experimentalValidateElementOrString` method included with `@woocommerce/blocks-checkout` package. Allows validating that a value is a valid element or a string ([experimental method](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/277ad369504c38abcc68a4cdbb034c421fe4d123/packages/checkout/registry/validations.js#L6)). - `__experimental_woocommerce_blocks_hidden` property in a Cart item data array that allows overwriting the `hidden` property. This is useful to make some cart item data visible/hidden depending if it needs to be displayed in Blocks or shortcode ([experimental property](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/9c4288b0ee46960bdc2bf8ef351d05ac23073b0c/src/StoreApi/Schemas/CartItemSchema.php#L439-L441)). diff --git a/packages/checkout/index.js b/packages/checkout/index.js index f393a5bb6ba..6025a694b8a 100644 --- a/packages/checkout/index.js +++ b/packages/checkout/index.js @@ -2,7 +2,6 @@ export * from './totals'; export * from './shipping'; export * from './slot'; export * from './registry'; -export * from './registry/validations.js'; export { default as ExperimentalOrderMeta } from './order-meta'; export { default as ExperimentalOrderShippingPackages } from './order-shipping-packages'; export { default as Panel } from './panel'; diff --git a/packages/checkout/registry/test/index.js b/packages/checkout/registry/test/index.js index 0b3bcc4e136..84d5549067c 100644 --- a/packages/checkout/registry/test/index.js +++ b/packages/checkout/registry/test/index.js @@ -5,7 +5,6 @@ import { __experimentalRegisterCheckoutFilters, __experimentalApplyCheckoutFilter, } from '../'; -import { __experimentalValidateElementOrString } from '../validations'; describe( 'Checkout registry', () => { const filterName = 'loremIpsum'; @@ -50,32 +49,4 @@ describe( 'Checkout registry', () => { expect( newValue ).toBe( value ); } ); - - describe( 'validations', () => { - test( '__experimentalValidateElementOrString should invalidate if filtered value is not an element or string', () => { - __experimentalRegisterCheckoutFilters( filterName, { - [ filterName ]: ( val ) => { - if ( val === 'Hello World' ) { - return 'Valid value'; - } - return [ 'invalid-value' ]; - }, - } ); - const validValue = __experimentalApplyCheckoutFilter( { - filterName, - defaultValue: 'Hello World', - validate: __experimentalValidateElementOrString, - } ); - - expect( validValue ).toBe( 'Valid value' ); - - const invalidValue = __experimentalApplyCheckoutFilter( { - filterName, - defaultValue: 'Hello Earth', - validate: __experimentalValidateElementOrString, - } ); - - expect( invalidValue ).toBe( 'Hello Earth' ); - } ); - } ); } ); diff --git a/packages/checkout/registry/validations.js b/packages/checkout/registry/validations.js deleted file mode 100644 index 11e0ffadc9e..00000000000 --- a/packages/checkout/registry/validations.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * External dependencies - */ -import { isValidElement } from '@wordpress/element'; - -/** - * Checks if the provided value is a React element, a string or null. - * - * @param {any} value Value to check. - * @return {boolean} Whether the value is a valid element, string or null. - */ -export const __experimentalValidateElementOrString = ( value ) => { - return ( - value === null || isValidElement( value ) || typeof value === 'string' - ); -}; From 73cb8e49c12c59ef023510ee401da52cfc543ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Mon, 8 Feb 2021 15:38:34 +0100 Subject: [PATCH 16/16] Change applyCheckoutFilter signature --- .../cart-checkout/totals/footer-item/index.js | 4 ++-- packages/checkout/registry/index.js | 12 ++++++------ packages/checkout/registry/test/index.js | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/assets/js/base/components/cart-checkout/totals/footer-item/index.js b/assets/js/base/components/cart-checkout/totals/footer-item/index.js index 48f2f7ba0b9..b86297838c1 100644 --- a/assets/js/base/components/cart-checkout/totals/footer-item/index.js +++ b/assets/js/base/components/cart-checkout/totals/footer-item/index.js @@ -28,11 +28,11 @@ const TotalsFooterItem = ( { currency, values } ) => { const label = __experimentalApplyCheckoutFilter( { filterName: 'totalLabel', defaultValue: __( 'Total', 'woo-gutenberg-products-block' ), - args: { + arg: { extensions, }, // Only accept strings. - validate: ( value ) => typeof value === 'string', + validation: ( value ) => typeof value === 'string', } ); return ( diff --git a/packages/checkout/registry/index.js b/packages/checkout/registry/index.js index 16e503f134e..edd1115aeb4 100644 --- a/packages/checkout/registry/index.js +++ b/packages/checkout/registry/index.js @@ -35,10 +35,10 @@ const getCheckoutFilters = ( filterName ) => { * @param {Object} o Object of arguments. * @param {string} o.filterName Name of the filter to apply. * @param {any} o.defaultValue Default value to filter. - * @param {any} [o.args] Argument to pass to registered functions. If + * @param {any} [o.arg] Argument to pass to registered functions. If * several arguments need to be passed, use an * object. - * @param {Function} [o.validate] Function that needs to return true when the + * @param {Function} [o.validation] Function that needs to return true when the * filtered value is passed in order for the * filter to be applied. * @return {any} Filtered value. @@ -46,15 +46,15 @@ const getCheckoutFilters = ( filterName ) => { export const __experimentalApplyCheckoutFilter = ( { filterName, defaultValue, - args = {}, - validate = () => true, + arg = null, + validation = () => true, } ) => { const filters = getCheckoutFilters( filterName ); let value = defaultValue; filters.forEach( ( filter ) => { try { - const newValue = filter( value, args ); - value = validate( newValue ) ? newValue : value; + const newValue = filter( value, arg ); + value = validation( newValue ) ? newValue : value; } catch ( e ) { // eslint-disable-next-line no-console console.log( e ); diff --git a/packages/checkout/registry/test/index.js b/packages/checkout/registry/test/index.js index 84d5549067c..3f9c2919dff 100644 --- a/packages/checkout/registry/test/index.js +++ b/packages/checkout/registry/test/index.js @@ -28,7 +28,7 @@ describe( 'Checkout registry', () => { const newValue = __experimentalApplyCheckoutFilter( { filterName, defaultValue: value, - args: { + arg: { punctuationSign: '!', }, } ); @@ -44,7 +44,7 @@ describe( 'Checkout registry', () => { const newValue = __experimentalApplyCheckoutFilter( { filterName, defaultValue: value, - validate: ( val ) => ! val.includes( 'HELLO' ), + validation: ( val ) => ! val.includes( 'HELLO' ), } ); expect( newValue ).toBe( value );