From 6e2d826cdcd4549394be78e345bb13fea0f7a990 Mon Sep 17 00:00:00 2001 From: Nadir Seghir Date: Mon, 6 Feb 2023 17:59:41 +0100 Subject: [PATCH 1/6] Capture notices from hidden block into siblings block --- .../checkout-billing-address-block/block.tsx | 9 ++++++- .../checkout-shipping-address-block/block.tsx | 5 ++++ .../store-notices-container/index.tsx | 27 ++++++++++++++++--- .../store-notices-container/store-notices.tsx | 10 +++---- .../store-notices-container/types.ts | 1 + 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx index 3d42b8ebc6a..f5b4afe9b24 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx @@ -91,7 +91,14 @@ const Block = ( { return ( - + { } ) ) as StoreNotice[]; }; +const removeDuplicateNotices = ( + notice: Notice, + noticeIndex: number, + noticesArray: Notice[] +) => + noticesArray.findIndex( + ( _notice: Notice ) => _notice.content === notice.content + ) === noticeIndex; + const StoreNoticesContainer = ( { className = '', context, + capturedContexts = [], additionalNotices = [], }: StoreNoticesContainerProps ): JSX.Element | null => { const { suppressNotices, registeredContainers } = useSelect( @@ -46,9 +57,13 @@ const StoreNoticesContainer = ( { subContext.includes( context + '/' ) && ! registeredContainers.includes( subContext ) ); - + const contexts = useMemo( + () => [ context, ...capturedContexts ], + [ capturedContexts, context ] + ); // Get notices from the current context and any sub-contexts and append the name of the context to the notice // objects for later reference. + const notices = useSelect< StoreNotice[] >( ( select ) => { const { getNotices } = select( 'core/notices' ); @@ -56,13 +71,17 @@ const StoreNoticesContainer = ( { ...unregisteredSubContexts.flatMap( ( subContext: string ) => formatNotices( getNotices( subContext ), subContext ) ), + ...capturedContexts.flatMap( ( capturedNotice: string ) => + formatNotices( getNotices( capturedNotice ), capturedNotice ) + ), ...formatNotices( getNotices( context ).concat( additionalNotices ), context ), - ].filter( Boolean ) as StoreNotice[]; + ] + .filter( removeDuplicateNotices ) + .filter( Boolean ) as StoreNotice[]; } ); - if ( suppressNotices || ! notices.length ) { return null; } @@ -71,7 +90,7 @@ const StoreNoticesContainer = ( { <> notice.type === 'default' ) } diff --git a/packages/checkout/components/store-notices-container/store-notices.tsx b/packages/checkout/components/store-notices-container/store-notices.tsx index 8262ff18764..ffab8455674 100644 --- a/packages/checkout/components/store-notices-container/store-notices.tsx +++ b/packages/checkout/components/store-notices-container/store-notices.tsx @@ -17,11 +17,11 @@ import { getClassNameFromStatus } from './utils'; import type { StoreNotice } from './types'; const StoreNotices = ( { - context, + contexts, className, notices, }: { - context: string; + contexts: string[]; className: string; notices: StoreNotice[]; } ): JSX.Element => { @@ -67,11 +67,11 @@ const StoreNotices = ( { // Register the container context with the parent. useEffect( () => { - registerContainer( context ); + contexts.map( ( context ) => registerContainer( context ) ); return () => { - unregisterContainer( context ); + contexts.map( ( context ) => unregisterContainer( context ) ); }; - }, [ context, registerContainer, unregisterContainer ] ); + }, [ contexts, registerContainer, unregisterContainer ] ); // Group notices by whether or not they are dismissible. Dismissible notices can be grouped. const dismissibleNotices = notices.filter( diff --git a/packages/checkout/components/store-notices-container/types.ts b/packages/checkout/components/store-notices-container/types.ts index 332ea079f5d..ecbf839a921 100644 --- a/packages/checkout/components/store-notices-container/types.ts +++ b/packages/checkout/components/store-notices-container/types.ts @@ -11,6 +11,7 @@ export interface StoreNoticesContainerProps { context: string; // List of additional notices that were added inline and not stored in the `core/notices` store. additionalNotices?: ( NoticeType & NoticeOptions )[]; + capturedContexts: string[]; } export type StoreNotice = NoticeType & NoticeOptions; From 708c1b59aefadfaac6d2c783db7cdec0072ffa67 Mon Sep 17 00:00:00 2001 From: Nadir Seghir Date: Thu, 9 Feb 2023 13:11:08 +0100 Subject: [PATCH 2/6] switch to using a single context --- .../shipping-calculator/index.tsx | 5 ++-- .../express-payment/cart-express-payment.js | 2 +- .../checkout-express-payment.js | 4 +-- .../payment-method-error-boundary.js | 2 +- assets/js/blocks/cart/block.js | 2 +- assets/js/blocks/checkout/block.tsx | 5 ++-- .../checkout-actions-block/block.tsx | 2 +- .../checkout-billing-address-block/block.tsx | 12 +++------ .../block.tsx | 2 +- .../checkout-payment-block/frontend.tsx | 2 +- .../checkout-shipping-address-block/block.tsx | 12 +++------ .../checkout-shipping-methods-block/block.tsx | 2 +- .../js/blocks/products/all-products/block.js | 2 +- assets/js/blocks/single-product/block.tsx | 2 +- .../store-notices-container/index.tsx | 26 +++++++------------ .../store-notices-container/types.ts | 3 +-- 16 files changed, 36 insertions(+), 49 deletions(-) diff --git a/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx b/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx index 108fb33f25a..8ff3eebf8b0 100644 --- a/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx +++ b/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx @@ -30,10 +30,11 @@ const ShippingCalculator = ( { addressFields = [ 'country', 'state', 'city', 'postcode' ], }: ShippingCalculatorProps ): JSX.Element => { const { shippingAddress } = useCustomerData(); - const noticeContext = 'wc/cart/shipping-calculator'; return (
- + {
diff --git a/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment/checkout-express-payment.js b/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment/checkout-express-payment.js index 957e90fdc98..0f22d3713dd 100644 --- a/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment/checkout-express-payment.js +++ b/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment/checkout-express-payment.js @@ -61,7 +61,7 @@ const CheckoutExpressPayment = () => { if ( isEditor || CURRENT_USER_IS_ADMIN ) { return ( ); } @@ -98,7 +98,7 @@ const CheckoutExpressPayment = () => {
diff --git a/assets/js/blocks/cart-checkout-shared/payment-methods/payment-method-error-boundary.js b/assets/js/blocks/cart-checkout-shared/payment-methods/payment-method-error-boundary.js index 42f90ed26d3..52bfccb285b 100644 --- a/assets/js/blocks/cart-checkout-shared/payment-methods/payment-method-error-boundary.js +++ b/assets/js/blocks/cart-checkout-shared/payment-methods/payment-method-error-boundary.js @@ -48,7 +48,7 @@ class PaymentMethodErrorBoundary extends Component { return ( ); } diff --git a/assets/js/blocks/cart/block.js b/assets/js/blocks/cart/block.js index e2b2fc829e6..2fa8ad04245 100644 --- a/assets/js/blocks/cart/block.js +++ b/assets/js/blocks/cart/block.js @@ -82,7 +82,7 @@ const Block = ( { attributes, children, scrollToTop } ) => ( } showErrorMessage={ CURRENT_USER_IS_ADMIN } > - + { children } diff --git a/assets/js/blocks/checkout/block.tsx b/assets/js/blocks/checkout/block.tsx index 1cce8fc71d0..977fd18e264 100644 --- a/assets/js/blocks/checkout/block.tsx +++ b/assets/js/blocks/checkout/block.tsx @@ -183,8 +183,9 @@ const Block = ( { ) } showErrorMessage={ CURRENT_USER_IS_ADMIN } > - - + { /* SlotFillProvider need to be defined before CheckoutProvider so fills have the SlotFill context ready when they mount. */ } diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-actions-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-actions-block/block.tsx index 4ad6356032a..3918d15fe88 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-actions-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-actions-block/block.tsx @@ -45,7 +45,7 @@ const Block = ( { className={ classnames( 'wc-block-checkout__actions', className ) } >
{ showReturnToCart && ( diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx index f5b4afe9b24..02eaacd77aa 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx @@ -88,17 +88,13 @@ const Block = ( { ] ) as Record< keyof AddressFields, Partial< AddressField > >; const AddressFormWrapperComponent = isEditor ? Noninteractive : Fragment; + const noticeContext = useBillingAsShipping + ? [ noticeContexts.BILLING_ADDRESS, noticeContexts.SHIPPING_ADDRESS ] + : [ noticeContexts.BILLING_ADDRESS ]; return ( - + { return ( <> - + { children } diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx index 8a9ea5b414a..47e03839399 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx @@ -97,18 +97,14 @@ const Block = ( { ] ) as Record< keyof AddressFields, Partial< AddressField > >; const AddressFormWrapperComponent = isEditor ? Noninteractive : Fragment; + const noticeContext = useShippingAsBilling + ? [ noticeContexts.SHIPPING_ADDRESS, noticeContexts.BILLING_ADDRESS ] + : [ noticeContexts.SHIPPING_ADDRESS ]; return ( <> - + { return ( <> { isEditor && ! shippingRatesPackageCount ? ( diff --git a/assets/js/blocks/products/all-products/block.js b/assets/js/blocks/products/all-products/block.js index 26e92ca63a9..8c29b503b57 100644 --- a/assets/js/blocks/products/all-products/block.js +++ b/assets/js/blocks/products/all-products/block.js @@ -37,7 +37,7 @@ class Block extends Component { parentName="woocommerce/all-products" parentClassName="wc-block-grid" > - + { product={ product } isLoading={ isLoading } > - +
{ children }
diff --git a/packages/checkout/components/store-notices-container/index.tsx b/packages/checkout/components/store-notices-container/index.tsx index 7506480bfb0..137a93090c0 100644 --- a/packages/checkout/components/store-notices-container/index.tsx +++ b/packages/checkout/components/store-notices-container/index.tsx @@ -8,7 +8,6 @@ import { } from '@woocommerce/block-data'; import { getNoticeContexts } from '@woocommerce/base-utils'; import type { Notice } from '@wordpress/notices'; -import { useMemo } from '@wordpress/element'; /** * Internal dependencies @@ -36,8 +35,7 @@ const removeDuplicateNotices = ( const StoreNoticesContainer = ( { className = '', - context, - capturedContexts = [], + contexts, additionalNotices = [], }: StoreNoticesContainerProps ): JSX.Element | null => { const { suppressNotices, registeredContainers } = useSelect( @@ -54,16 +52,13 @@ const StoreNoticesContainer = ( { const allContexts = getNoticeContexts(); const unregisteredSubContexts = allContexts.filter( ( subContext: string ) => - subContext.includes( context + '/' ) && - ! registeredContainers.includes( subContext ) - ); - const contexts = useMemo( - () => [ context, ...capturedContexts ], - [ capturedContexts, context ] + contexts.some( ( context ) => + subContext.includes( context + '/' ) + ) && ! registeredContainers.includes( subContext ) ); + // Get notices from the current context and any sub-contexts and append the name of the context to the notice // objects for later reference. - const notices = useSelect< StoreNotice[] >( ( select ) => { const { getNotices } = select( 'core/notices' ); @@ -71,12 +66,11 @@ const StoreNoticesContainer = ( { ...unregisteredSubContexts.flatMap( ( subContext: string ) => formatNotices( getNotices( subContext ), subContext ) ), - ...capturedContexts.flatMap( ( capturedNotice: string ) => - formatNotices( getNotices( capturedNotice ), capturedNotice ) - ), - ...formatNotices( - getNotices( context ).concat( additionalNotices ), - context + ...contexts.flatMap( ( subContext: string ) => + formatNotices( + getNotices( subContext ).concat( additionalNotices ), + subContext + ) ), ] .filter( removeDuplicateNotices ) diff --git a/packages/checkout/components/store-notices-container/types.ts b/packages/checkout/components/store-notices-container/types.ts index ecbf839a921..51ab421edba 100644 --- a/packages/checkout/components/store-notices-container/types.ts +++ b/packages/checkout/components/store-notices-container/types.ts @@ -8,10 +8,9 @@ import type { export interface StoreNoticesContainerProps { className?: string | undefined; - context: string; + contexts: string[]; // List of additional notices that were added inline and not stored in the `core/notices` store. additionalNotices?: ( NoticeType & NoticeOptions )[]; - capturedContexts: string[]; } export type StoreNotice = NoticeType & NoticeOptions; From e3179cf71ac01334053d6c160f88cc2e4b3e7b1d Mon Sep 17 00:00:00 2001 From: Nadir Seghir Date: Thu, 9 Feb 2023 13:24:59 +0100 Subject: [PATCH 3/6] make change bwc --- .../cart-checkout/shipping-calculator/index.tsx | 4 +--- .../express-payment/cart-express-payment.js | 2 +- .../express-payment/checkout-express-payment.js | 4 ++-- .../payment-methods/payment-method-error-boundary.js | 2 +- assets/js/blocks/cart/block.js | 2 +- assets/js/blocks/checkout/block.tsx | 2 +- .../inner-blocks/checkout-actions-block/block.tsx | 2 +- .../inner-blocks/checkout-billing-address-block/block.tsx | 2 +- .../checkout-contact-information-block/block.tsx | 2 +- .../inner-blocks/checkout-payment-block/frontend.tsx | 2 +- .../checkout-shipping-address-block/block.tsx | 2 +- .../checkout-shipping-methods-block/block.tsx | 2 +- assets/js/blocks/single-product/block.tsx | 2 +- .../checkout/components/store-notices-container/index.tsx | 8 ++++---- .../checkout/components/store-notices-container/types.ts | 2 +- 15 files changed, 19 insertions(+), 21 deletions(-) diff --git a/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx b/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx index 8ff3eebf8b0..204596bffd0 100644 --- a/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx +++ b/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx @@ -32,9 +32,7 @@ const ShippingCalculator = ( { const { shippingAddress } = useCustomerData(); return (
- + {
diff --git a/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment/checkout-express-payment.js b/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment/checkout-express-payment.js index 0f22d3713dd..957e90fdc98 100644 --- a/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment/checkout-express-payment.js +++ b/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment/checkout-express-payment.js @@ -61,7 +61,7 @@ const CheckoutExpressPayment = () => { if ( isEditor || CURRENT_USER_IS_ADMIN ) { return ( ); } @@ -98,7 +98,7 @@ const CheckoutExpressPayment = () => {
diff --git a/assets/js/blocks/cart-checkout-shared/payment-methods/payment-method-error-boundary.js b/assets/js/blocks/cart-checkout-shared/payment-methods/payment-method-error-boundary.js index 52bfccb285b..42f90ed26d3 100644 --- a/assets/js/blocks/cart-checkout-shared/payment-methods/payment-method-error-boundary.js +++ b/assets/js/blocks/cart-checkout-shared/payment-methods/payment-method-error-boundary.js @@ -48,7 +48,7 @@ class PaymentMethodErrorBoundary extends Component { return ( ); } diff --git a/assets/js/blocks/cart/block.js b/assets/js/blocks/cart/block.js index 2fa8ad04245..e2b2fc829e6 100644 --- a/assets/js/blocks/cart/block.js +++ b/assets/js/blocks/cart/block.js @@ -82,7 +82,7 @@ const Block = ( { attributes, children, scrollToTop } ) => ( } showErrorMessage={ CURRENT_USER_IS_ADMIN } > - + { children } diff --git a/assets/js/blocks/checkout/block.tsx b/assets/js/blocks/checkout/block.tsx index 977fd18e264..18d1a0ac174 100644 --- a/assets/js/blocks/checkout/block.tsx +++ b/assets/js/blocks/checkout/block.tsx @@ -184,7 +184,7 @@ const Block = ( { showErrorMessage={ CURRENT_USER_IS_ADMIN } > { /* SlotFillProvider need to be defined before CheckoutProvider so fills have the SlotFill context ready when they mount. */ } diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-actions-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-actions-block/block.tsx index 3918d15fe88..4ad6356032a 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-actions-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-actions-block/block.tsx @@ -45,7 +45,7 @@ const Block = ( { className={ classnames( 'wc-block-checkout__actions', className ) } >
{ showReturnToCart && ( diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx index 02eaacd77aa..1d372046da0 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx @@ -94,7 +94,7 @@ const Block = ( { return ( - + { return ( <> - + { children } diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx index 47e03839399..e1fdfa98f02 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx @@ -104,7 +104,7 @@ const Block = ( { return ( <> - + { return ( <> { isEditor && ! shippingRatesPackageCount ? ( diff --git a/assets/js/blocks/single-product/block.tsx b/assets/js/blocks/single-product/block.tsx index 1ff36b84490..032a2184003 100644 --- a/assets/js/blocks/single-product/block.tsx +++ b/assets/js/blocks/single-product/block.tsx @@ -43,7 +43,7 @@ const Block = ( { isLoading, product, children }: BlockProps ) => { product={ product } isLoading={ isLoading } > - +
{ children }
diff --git a/packages/checkout/components/store-notices-container/index.tsx b/packages/checkout/components/store-notices-container/index.tsx index 137a93090c0..bc1925f336b 100644 --- a/packages/checkout/components/store-notices-container/index.tsx +++ b/packages/checkout/components/store-notices-container/index.tsx @@ -35,7 +35,7 @@ const removeDuplicateNotices = ( const StoreNoticesContainer = ( { className = '', - contexts, + context, additionalNotices = [], }: StoreNoticesContainerProps ): JSX.Element | null => { const { suppressNotices, registeredContainers } = useSelect( @@ -47,13 +47,13 @@ const StoreNoticesContainer = ( { ).getRegisteredContainers(), } ) ); - + const contexts = Array.isArray( context ) ? context : [ context ]; // Find sub-contexts that have not been registered. We will show notices from those contexts here too. const allContexts = getNoticeContexts(); const unregisteredSubContexts = allContexts.filter( ( subContext: string ) => - contexts.some( ( context ) => - subContext.includes( context + '/' ) + contexts.some( ( _context: string ) => + subContext.includes( _context + '/' ) ) && ! registeredContainers.includes( subContext ) ); diff --git a/packages/checkout/components/store-notices-container/types.ts b/packages/checkout/components/store-notices-container/types.ts index 51ab421edba..3c174d4ec69 100644 --- a/packages/checkout/components/store-notices-container/types.ts +++ b/packages/checkout/components/store-notices-container/types.ts @@ -8,7 +8,7 @@ import type { export interface StoreNoticesContainerProps { className?: string | undefined; - contexts: string[]; + contexts: string | string[]; // List of additional notices that were added inline and not stored in the `core/notices` store. additionalNotices?: ( NoticeType & NoticeOptions )[]; } From 4abc45344c2a5492c4209e82951ea397d6faac0a Mon Sep 17 00:00:00 2001 From: Nadir Seghir Date: Thu, 9 Feb 2023 13:38:15 +0100 Subject: [PATCH 4/6] add tests --- .../block-client-apis/notices.md | 21 ++++++ .../store-notices-container/test/index.tsx | 75 +++++++++++++++++++ .../store-notices-container/types.ts | 2 +- 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/docs/internal-developers/block-client-apis/notices.md b/docs/internal-developers/block-client-apis/notices.md index 382f6d84e44..1758001fd30 100644 --- a/docs/internal-developers/block-client-apis/notices.md +++ b/docs/internal-developers/block-client-apis/notices.md @@ -19,6 +19,10 @@ The below example will show all notices with type `default` that are in the `wc/ On the Cart Block, a `StoreNoticesContainer` is already rendered with the `wc/cart` context, and on the Checkout Block, a `StoreNoticesContainer` is already rendered with the `wc/checkout` context. To display errors from other contexts, you can use the `StoreNoticesContainer` component with context passed as a prop. +`StoreNoticesContainer` also support passing an array of context strings to it, this allows you to capture several contexts at once, while filtering out similar notices. + +#### Single context + ```jsx import { StoreNoticesContainer } from '@woocommerce/blocks-checkout'; @@ -27,6 +31,23 @@ const PaymentErrors = () => { }; ``` +#### Multiple contexts + +```jsx +import { StoreNoticesContainer } from '@woocommerce/blocks-checkout'; + +const AddressForm = () => { + return ( + + ); +}; +``` + ## Snackbar notices in WooCommerce Blocks WooCommerce Blocks also shows snackbar notices, to add a snackbar notice you need to create a notice with `type:snackbar` in the options object. diff --git a/packages/checkout/components/store-notices-container/test/index.tsx b/packages/checkout/components/store-notices-container/test/index.tsx index 5dab1af4546..0617dc51b1b 100644 --- a/packages/checkout/components/store-notices-container/test/index.tsx +++ b/packages/checkout/components/store-notices-container/test/index.tsx @@ -137,4 +137,79 @@ describe( 'StoreNoticesContainer', () => { ) ); } ); + + it( 'Shows notices from several contexts', async () => { + dispatch( noticesStore ).createErrorNotice( 'Custom shipping error', { + id: 'custom-subcontext-test-error', + context: 'wc/checkout/shipping-address', + } ); + dispatch( noticesStore ).createErrorNotice( 'Custom billing error', { + id: 'custom-subcontext-test-error', + context: 'wc/checkout/billing-address', + } ); + render( + + ); + // This should match against 4 elements; A written and spoken message for each error. + expect( screen.getAllByText( /Custom shipping error/i ) ).toHaveLength( + 2 + ); + expect( screen.getAllByText( /Custom billing error/i ) ).toHaveLength( + 2 + ); + // Clean up notices. + await act( () => + dispatch( noticesStore ).removeNotice( + 'custom-subcontext-test-error', + 'wc/checkout/shipping-address' + ) + ); + await act( () => + dispatch( noticesStore ).removeNotice( + 'custom-subcontext-test-error', + 'wc/checkout/billing-address' + ) + ); + } ); + + it( 'Combine same notices from several contexts', async () => { + dispatch( noticesStore ).createErrorNotice( 'Custom generic error', { + id: 'custom-subcontext-test-error', + context: 'wc/checkout/shipping-address', + } ); + dispatch( noticesStore ).createErrorNotice( 'Custom generic error', { + id: 'custom-subcontext-test-error', + context: 'wc/checkout/billing-address', + } ); + render( + + ); + // This should match against 2 elements; A written and spoken message. + expect( screen.getAllByText( /Custom generic error/i ) ).toHaveLength( + 2 + ); + // Clean up notices. + await act( () => + dispatch( noticesStore ).removeNotice( + 'custom-subcontext-test-error', + 'wc/checkout/shipping-address' + ) + ); + await act( () => + dispatch( noticesStore ).removeNotice( + 'custom-subcontext-test-error', + 'wc/checkout/billing-address' + ) + ); + } ); } ); diff --git a/packages/checkout/components/store-notices-container/types.ts b/packages/checkout/components/store-notices-container/types.ts index 3c174d4ec69..63e1db419b7 100644 --- a/packages/checkout/components/store-notices-container/types.ts +++ b/packages/checkout/components/store-notices-container/types.ts @@ -8,7 +8,7 @@ import type { export interface StoreNoticesContainerProps { className?: string | undefined; - contexts: string | string[]; + context: string | string[]; // List of additional notices that were added inline and not stored in the `core/notices` store. additionalNotices?: ( NoticeType & NoticeOptions )[]; } From a99279d3386f9e30c80f50004434fca135bb339d Mon Sep 17 00:00:00 2001 From: Nadir Seghir Date: Thu, 9 Feb 2023 14:21:07 +0100 Subject: [PATCH 5/6] support context as array in StoreNotice --- assets/js/blocks/products/all-products/block.js | 2 +- .../block-client-apis/notices.md | 15 --------------- .../components/store-notices-container/index.tsx | 2 +- .../store-notices-container/store-notices.tsx | 12 ++++++------ 4 files changed, 8 insertions(+), 23 deletions(-) diff --git a/assets/js/blocks/products/all-products/block.js b/assets/js/blocks/products/all-products/block.js index 8c29b503b57..26e92ca63a9 100644 --- a/assets/js/blocks/products/all-products/block.js +++ b/assets/js/blocks/products/all-products/block.js @@ -37,7 +37,7 @@ class Block extends Component { parentName="woocommerce/all-products" parentClassName="wc-block-grid" > - + { - return ( - - ); -}; -``` diff --git a/packages/checkout/components/store-notices-container/index.tsx b/packages/checkout/components/store-notices-container/index.tsx index bc1925f336b..0348372bc53 100644 --- a/packages/checkout/components/store-notices-container/index.tsx +++ b/packages/checkout/components/store-notices-container/index.tsx @@ -84,7 +84,7 @@ const StoreNoticesContainer = ( { <> notice.type === 'default' ) } diff --git a/packages/checkout/components/store-notices-container/store-notices.tsx b/packages/checkout/components/store-notices-container/store-notices.tsx index ffab8455674..ccb865079d2 100644 --- a/packages/checkout/components/store-notices-container/store-notices.tsx +++ b/packages/checkout/components/store-notices-container/store-notices.tsx @@ -17,11 +17,11 @@ import { getClassNameFromStatus } from './utils'; import type { StoreNotice } from './types'; const StoreNotices = ( { - contexts, + context, className, notices, }: { - contexts: string[]; + context: string | string[]; className: string; notices: StoreNotice[]; } ): JSX.Element => { @@ -64,14 +64,14 @@ const StoreNotices = ( { } ); } }, [ noticeIds, previousNoticeIds, ref ] ); - // Register the container context with the parent. useEffect( () => { - contexts.map( ( context ) => registerContainer( context ) ); + const contexts = Array.isArray( context ) ? context : [ context ]; + contexts.map( ( _context ) => registerContainer( _context ) ); return () => { - contexts.map( ( context ) => unregisterContainer( context ) ); + contexts.map( ( _context ) => unregisterContainer( _context ) ); }; - }, [ contexts, registerContainer, unregisterContainer ] ); + }, [ context, registerContainer, unregisterContainer ] ); // Group notices by whether or not they are dismissible. Dismissible notices can be grouped. const dismissibleNotices = notices.filter( From 02b9cfa36447872eb8ef61c7a896e992c58e97ce Mon Sep 17 00:00:00 2001 From: Nadir Seghir Date: Thu, 9 Feb 2023 14:31:47 +0100 Subject: [PATCH 6/6] move filter logic to Notice component --- .../components/store-notices-container/index.tsx | 13 +------------ .../store-notices-container/store-notices.tsx | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/checkout/components/store-notices-container/index.tsx b/packages/checkout/components/store-notices-container/index.tsx index 0348372bc53..65ce1caa7d4 100644 --- a/packages/checkout/components/store-notices-container/index.tsx +++ b/packages/checkout/components/store-notices-container/index.tsx @@ -24,15 +24,6 @@ const formatNotices = ( notices: Notice[], context: string ): StoreNotice[] => { } ) ) as StoreNotice[]; }; -const removeDuplicateNotices = ( - notice: Notice, - noticeIndex: number, - noticesArray: Notice[] -) => - noticesArray.findIndex( - ( _notice: Notice ) => _notice.content === notice.content - ) === noticeIndex; - const StoreNoticesContainer = ( { className = '', context, @@ -72,9 +63,7 @@ const StoreNoticesContainer = ( { subContext ) ), - ] - .filter( removeDuplicateNotices ) - .filter( Boolean ) as StoreNotice[]; + ].filter( Boolean ) as StoreNotice[]; } ); if ( suppressNotices || ! notices.length ) { return null; diff --git a/packages/checkout/components/store-notices-container/store-notices.tsx b/packages/checkout/components/store-notices-container/store-notices.tsx index ccb865079d2..7957e1d3822 100644 --- a/packages/checkout/components/store-notices-container/store-notices.tsx +++ b/packages/checkout/components/store-notices-container/store-notices.tsx @@ -117,6 +117,17 @@ const StoreNotices = ( { if ( ! noticeGroup.length ) { return null; } + const uniqueNotices = noticeGroup.filter( + ( + notice: Notice, + noticeIndex: number, + noticesArray: Notice[] + ) => + noticesArray.findIndex( + ( _notice: Notice ) => + _notice.content === notice.content + ) === noticeIndex + ); return ( - { noticeGroup.length === 1 ? ( + { uniqueNotices.length === 1 ? ( <> { sanitizeHTML( decodeEntities( @@ -140,7 +151,7 @@ const StoreNotices = ( { ) : (
    - { noticeGroup.map( ( notice ) => ( + { uniqueNotices.map( ( notice ) => (