From 39fa3afaaaca593de145f6977f9bd212ac3fa10e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 21 Nov 2022 13:38:31 +0000 Subject: [PATCH] Update filter usage - remove useMemo so filter can work inline --- .../hooks/cart/use-store-cart-coupons.ts | 54 +++++------- .../checkout-block/available-filters.md | 74 +++------------- packages/checkout/filter-registry/index.ts | 85 ++++++++----------- 3 files changed, 68 insertions(+), 145 deletions(-) diff --git a/assets/js/base/context/hooks/cart/use-store-cart-coupons.ts b/assets/js/base/context/hooks/cart/use-store-cart-coupons.ts index f72ffa10753..3fdc41b5c75 100644 --- a/assets/js/base/context/hooks/cart/use-store-cart-coupons.ts +++ b/assets/js/base/context/hooks/cart/use-store-cart-coupons.ts @@ -1,14 +1,9 @@ -/** @typedef { import('@woocommerce/type-defs/hooks').StoreCartCoupon } StoreCartCoupon */ - /** * External dependencies */ import { __, sprintf } from '@wordpress/i18n'; import { useDispatch, useSelect } from '@wordpress/data'; -import { - CART_STORE_KEY as storeKey, - VALIDATION_STORE_KEY, -} from '@woocommerce/block-data'; +import { CART_STORE_KEY, VALIDATION_STORE_KEY } from '@woocommerce/block-data'; import { decodeEntities } from '@wordpress/html-entities'; import type { StoreCartCoupon } from '@woocommerce/types'; import { __experimentalApplyCheckoutFilter } from '@woocommerce/blocks-checkout'; @@ -22,9 +17,6 @@ import { useStoreCart } from './use-store-cart'; * This is a custom hook for loading the Store API /cart/coupons endpoint and an * action for adding a coupon _to_ the cart. * See also: https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/trunk/src/RestApi/StoreApi - * - * @return {StoreCartCoupon} An object exposing data and actions from/for the - * store api /cart/coupons endpoint. */ export const useStoreCartCoupons = ( context = '' ): StoreCartCoupon => { const { cartCoupons, cartIsLoading } = useStoreCart(); @@ -32,29 +24,14 @@ export const useStoreCartCoupons = ( context = '' ): StoreCartCoupon => { const { createNotice } = useDispatch( 'core/notices' ); const { setValidationErrors } = useDispatch( VALIDATION_STORE_KEY ); - const { - applyCoupon, - removeCoupon, - isApplyingCoupon, - isRemovingCoupon, - }: Pick< - StoreCartCoupon, - | 'applyCoupon' - | 'removeCoupon' - | 'isApplyingCoupon' - | 'isRemovingCoupon' - | 'receiveApplyingCoupon' - > = useSelect( - ( select, { dispatch } ) => { - const store = select( storeKey ); - const actions = dispatch( storeKey ); - + const { applyCoupon, removeCoupon, receiveApplyingCoupon } = + useDispatch( CART_STORE_KEY ); + const { isApplyingCoupon, isRemovingCoupon } = useSelect( + ( select ) => { + const store = select( CART_STORE_KEY ); return { - applyCoupon: actions.applyCoupon, - removeCoupon: actions.removeCoupon, isApplyingCoupon: store.isApplyingCoupon(), isRemovingCoupon: store.isRemovingCoupon(), - receiveApplyingCoupon: actions.receiveApplyingCoupon, }; }, [ createErrorNotice, createNotice ] @@ -65,11 +42,11 @@ export const useStoreCartCoupons = ( context = '' ): StoreCartCoupon => { .then( ( result ) => { if ( result === true && - __experimentalApplyCheckoutFilter( - 'showApplyCouponNotice', - true, - { couponCode, context } - ) + __experimentalApplyCheckoutFilter( { + filterName: 'showApplyCouponNotice', + defaultValue: true, + arg: { couponCode, context }, + } ) ) { createNotice( 'info', @@ -104,7 +81,14 @@ export const useStoreCartCoupons = ( context = '' ): StoreCartCoupon => { const removeCouponWithNotices = ( couponCode: string ) => { removeCoupon( couponCode ) .then( ( result ) => { - if ( result === true ) { + if ( + result === true && + __experimentalApplyCheckoutFilter( { + filterName: 'showRemoveCouponNotice', + defaultValue: true, + arg: { couponCode, context }, + } ) + ) { createNotice( 'info', sprintf( diff --git a/docs/third-party-developers/extensibility/checkout-block/available-filters.md b/docs/third-party-developers/extensibility/checkout-block/available-filters.md index d9262204fe2..ee0dddf0139 100644 --- a/docs/third-party-developers/extensibility/checkout-block/available-filters.md +++ b/docs/third-party-developers/extensibility/checkout-block/available-filters.md @@ -2,19 +2,18 @@ ## Table of Contents -- [Cart Line Items](#cart-line-items) -- [Order Summary Items](#order-summary-items) -- [Totals footer item (in Mini Cart, Cart and Checkout)](#totals-footer-item-in-mini-cart-cart-and-checkout) -- [Coupons](#coupons) -- [Snackbar notices](#snackbar-notices) -- [Place Order Button Label](#place-order-button-label) -- [Examples](#examples) - - [Changing the wording of the Totals label in the Mini Cart, Cart and Checkout](#changing-the-wording-of-the-totals-label-in-the-mini-cart-cart-and-checkout) - - [Changing the format of the item's single price](#changing-the-format-of-the-items-single-price) - - [Change the name of a coupon](#change-the-name-of-a-coupon) - - [Hide a snackbar notice containing a certain string](#hide-a-snackbar-notice-containing-a-certain-string) - - [Change the label of the Place Order button](#change-the-label-of-the-place-order-button) -- [Troubleshooting](#troubleshooting) +- [Cart Line Items](#cart-line-items) +- [Order Summary Items](#order-summary-items) +- [Totals footer item (in Mini Cart, Cart and Checkout)](#totals-footer-item-in-mini-cart-cart-and-checkout) +- [Coupons](#coupons) +- [Place Order Button Label](#place-order-button-label) +- [Examples](#examples) + - [Changing the wording of the Totals label in the Mini Cart, Cart and Checkout](#changing-the-wording-of-the-totals-label-in-the-mini-cart-cart-and-checkout) + - [Changing the format of the item's single price](#changing-the-format-of-the-items-single-price) + - [Change the name of a coupon](#change-the-name-of-a-coupon) + - [Hide the "Remove item" link on a cart item](#hide-the-remove-item-link-on-a-cart-item) + - [Change the label of the Place Order button](#change-the-label-of-the-place-order-button) +- [Troubleshooting](#troubleshooting) This document lists the filters that are currently available to extensions and offers usage information for each one of them. Information on registering filters can be found on the [Checkout - Filter Registry](../../../../packages/checkout/filter-registry/README.md) page. @@ -27,7 +26,7 @@ Line items refer to each item listed in the cart or checkout. For instance, the The following filters are available for line items: | Filter name | Description | Return type | -| ---------------------- |----------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------| +| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | `itemName` | Used to change the name of the item before it is rendered onto the page | `string` | | `cartItemPrice` | This is the price of the item, multiplied by the number of items in the cart. | `string` and **must** contain the substring `` where the price should appear. | | `cartItemClass` | This is the className of the item cell. | `string` | @@ -91,31 +90,6 @@ CartCoupon { } ``` -## Snackbar notices - -There is a snackbar at the bottom of the page used to display notices to the customer, it looks like this: - -![Snackbar notices](https://user-images.githubusercontent.com/5656702/120882329-d573c100-c5ce-11eb-901b-d7f206f74a66.png) - -It may be desirable to hide this if there's a notice you don't want the shopper to see. - -| Filter name | Description | Return type | -| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -| `snackbarNoticeVisibility` | An object keyed by the content of the notices slated to be displayed. The value of each member of this object will initially be true. | `object` | - -The filter passes an object whose keys are the `content` of each notice. - -If there are two notices slated to be displayed ('Coupon code "10off" has been applied to your basket.', and 'Coupon code "50off" has been removed from your basket.'), the value passed to the filter would look like so: - -```js -{ - 'Coupon code "10off" has been applied to your basket.': true, - 'Coupon code "50off" has been removed from your basket.': true -} -``` - -To reiterate, the _value_ here will determine whether this notice gets displayed or not. It will display if true. - ## Place Order Button Label The Checkout block contains a button which is labelled 'Place Order' by default, but can be changed using the following filter. @@ -212,29 +186,10 @@ __experimentalRegisterCheckoutFilters( 'automatic-coupon-extension', { | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | ![image](https://user-images.githubusercontent.com/5656702/123768988-bc55eb80-d8c0-11eb-9262-5d629837706d.png) | ![image](https://user-images.githubusercontent.com/5656702/124126048-2c57a380-da72-11eb-9b45-b2cae0cffc37.png) | -### Hide a snackbar notice containing a certain string - -Let's say we want to hide all notices that contain the string `auto-generated-coupon`. We would do this by setting the value of the `snackbarNoticeVisibility` to false for the notices we would like to hide. - -```ts -import { __experimentalRegisterCheckoutFilters } from '@woocommerce/blocks-checkout'; - -__experimentalRegisterCheckoutFilters( 'automatic-coupon-extension', { - snackbarNoticeVisibility: ( value ) => { - // Copy the value so we don't mutate what is being passed by the filter. - const valueCopy = Object.assign( {}, value ); - Object.keys( value ).forEach( ( key ) => { - valueCopy[ key ] = key.indexOf( 'auto-generated-coupon' ) === -1; - } ); - return valueCopy; - }, -} ); -``` - ### Hide the "Remove item" link on a cart item If you want to stop customers from being able to remove a specific item from their cart **on the front end**, you can do -this by using the `showRemoveItemLink` filter. If it returns `false` for that line item the link will not show. +this by using the `showRemoveItemLink` filter. If it returns `false` for that line item the link will not show. An important caveat to note is this does _not_ prevent the item from being removed from the cart using StoreAPI or by removing it in the Mini Cart, or traditional shortcode cart. @@ -291,4 +246,3 @@ The error will also be shown in your console. 🐞 Found a mistake, or have a suggestion? [Leave feedback about this document here.](https://github.com/woocommerce/woocommerce-blocks/issues/new?assignees=&labels=type%3A+documentation&template=--doc-feedback.md&title=Feedback%20on%20./docs/third-party-developers/extensibility/checkout-block/available-filters.md) - diff --git a/packages/checkout/filter-registry/index.ts b/packages/checkout/filter-registry/index.ts index 312b5fd420a..058b7d70cc8 100644 --- a/packages/checkout/filter-registry/index.ts +++ b/packages/checkout/filter-registry/index.ts @@ -41,19 +41,6 @@ export const __experimentalRegisterCheckoutFilters = ( namespace: string, filters: Record< string, CheckoutFilterFunction > ): void => { - /** - * Let developers know snackbarNotices is no longer available as a filter. - * - * See: https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4417 - */ - if ( Object.keys( filters ).includes( 'couponName' ) ) { - deprecated( 'snackbarNotices', { - alternative: 'snackbarNoticeVisibility', - plugin: 'WooCommerce Blocks', - link: 'https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4417', - } ); - } - /** * Let the user know couponName is no longer available as a filter. * @@ -211,42 +198,40 @@ export const __experimentalApplyCheckoutFilter = < T >( { /** Function that needs to return true when the filtered value is passed in order for the filter to be applied. */ validation?: ( value: T ) => true | Error; } ): T => { - return useMemo( () => { - if ( - ! shouldReRunFilters( filterName, arg, extensions, defaultValue ) && - cachedValues[ filterName ] !== undefined - ) { - return cachedValues[ filterName ]; - } - const filters = getCheckoutFilters( filterName ); - let value = defaultValue; - filters.forEach( ( filter ) => { - try { - const newValue = filter( value, extensions || {}, arg ); - if ( typeof newValue !== typeof value ) { - throw new Error( - sprintf( - /* translators: %1$s is the type of the variable passed to the filter function, %2$s is the type of the value returned by the filter function. */ - __( - 'The type returned by checkout filters must be the same as the type they receive. The function received %1$s but returned %2$s.', - 'woo-gutenberg-products-block' - ), - typeof value, - typeof newValue - ) - ); - } - value = validation( newValue ) ? newValue : value; - } catch ( e ) { - if ( CURRENT_USER_IS_ADMIN ) { - throw e; - } else { - // eslint-disable-next-line no-console - console.error( e ); - } + if ( + ! shouldReRunFilters( filterName, arg, extensions, defaultValue ) && + cachedValues[ filterName ] !== undefined + ) { + return cachedValues[ filterName ]; + } + const filters = getCheckoutFilters( filterName ); + let value = defaultValue; + filters.forEach( ( filter ) => { + try { + const newValue = filter( value, extensions || {}, arg ); + if ( typeof newValue !== typeof value ) { + throw new Error( + sprintf( + /* translators: %1$s is the type of the variable passed to the filter function, %2$s is the type of the value returned by the filter function. */ + __( + 'The type returned by checkout filters must be the same as the type they receive. The function received %1$s but returned %2$s.', + 'woo-gutenberg-products-block' + ), + typeof value, + typeof newValue + ) + ); } - } ); - cachedValues[ filterName ] = value; - return value; - }, [ arg, defaultValue, extensions, filterName, validation ] ); + value = validation( newValue ) ? newValue : value; + } catch ( e ) { + if ( CURRENT_USER_IS_ADMIN ) { + throw e; + } else { + // eslint-disable-next-line no-console + console.error( e ); + } + } + } ); + cachedValues[ filterName ] = value; + return value; };