Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Update filter usage - remove useMemo so filter can work inline
Browse files Browse the repository at this point in the history
  • Loading branch information
mikejolley committed Dec 5, 2022
1 parent 29d28f2 commit ad756b9
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 145 deletions.
54 changes: 19 additions & 35 deletions assets/js/base/context/hooks/cart/use-store-cart-coupons.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -22,39 +17,21 @@ 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();
const { createErrorNotice } = useDispatch( 'core/notices' );
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 ]
Expand All @@ -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',
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@

## Table of Contents <!-- omit in toc -->

- [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.

Expand All @@ -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 `<price/>` where the price should appear. |
| `cartItemClass` | This is the className of the item cell. | `string` |
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)

<!-- /FEEDBACK -->

85 changes: 35 additions & 50 deletions packages/checkout/filter-registry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;
};

0 comments on commit ad756b9

Please sign in to comment.