Add optimizations around checkout filter calls (https://github.com/woocommerce/woocommerce-blocks/pull/4570)

* Extract productPriceValidation out of the component

* Extract checkout filter args outside of component

* Remove unnecessary default fallback

* Mark ProductName optional props as not required

* Use an empty array constant for the filteredNotices default value when there are no notices

* Refactor Panel component so hidden contents are not rendered

* Prevent extensions changing on each call of __experimentalApplyCheckoutFilter if it was an empty object

* Add missing line break

* Update tests
This commit is contained in:
Albert Juhé Lluveras 2021-08-13 15:42:09 +02:00 committed by GitHub
parent ff9c73bec5
commit 70ec1f8b31
10 changed files with 38 additions and 241 deletions

View File

@ -13,7 +13,7 @@ import {
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Dinero from 'dinero.js'; import Dinero from 'dinero.js';
import { getSetting } from '@woocommerce/settings'; import { getSetting } from '@woocommerce/settings';
import { useCallback, useMemo } from '@wordpress/element'; import { useMemo } from '@wordpress/element';
import { useStoreCart } from '@woocommerce/base-context/hooks'; import { useStoreCart } from '@woocommerce/base-context/hooks';
/** /**
@ -24,21 +24,23 @@ import ProductImage from '../product-image';
import ProductLowStockBadge from '../product-low-stock-badge'; import ProductLowStockBadge from '../product-low-stock-badge';
import ProductMetadata from '../product-metadata'; import ProductMetadata from '../product-metadata';
const productPriceValidation = ( value ) => mustContain( value, '<price/>' );
const OrderSummaryItem = ( { cartItem } ) => { const OrderSummaryItem = ( { cartItem } ) => {
const { const {
images, images,
low_stock_remaining: lowStockRemaining = null, low_stock_remaining: lowStockRemaining,
show_backorder_badge: showBackorderBadge = false, show_backorder_badge: showBackorderBadge,
name: initialName, name: initialName,
permalink, permalink,
prices, prices,
quantity, quantity,
short_description: shortDescription, short_description: shortDescription,
description: fullDescription, description: fullDescription,
item_data: itemData = [], item_data: itemData,
variation, variation,
totals, totals,
extensions = {}, extensions,
} = cartItem; } = cartItem;
// Prepare props to pass to the __experimentalApplyCheckoutFilter filter. // Prepare props to pass to the __experimentalApplyCheckoutFilter filter.
@ -46,11 +48,6 @@ const OrderSummaryItem = ( { cartItem } ) => {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const { receiveCart, ...cart } = useStoreCart(); const { receiveCart, ...cart } = useStoreCart();
const productPriceValidation = useCallback(
( value ) => mustContain( value, '<price/>' ),
[]
);
const arg = useMemo( const arg = useMemo(
() => ( { () => ( {
context: 'summary', context: 'summary',

View File

@ -12,7 +12,7 @@ import OrderSummary from '../index';
jest.mock( '@woocommerce/base-context', () => ( { jest.mock( '@woocommerce/base-context', () => ( {
...jest.requireActual( '@woocommerce/base-context' ), ...jest.requireActual( '@woocommerce/base-context' ),
useContainerWidthContext: () => ( { useContainerWidthContext: () => ( {
isLarge: false, isLarge: true,
hasContainerWidth: true, hasContainerWidth: true,
} ), } ),
} ) ); } ) );

View File

@ -16,6 +16,10 @@ import { getSetting } from '@woocommerce/settings';
*/ */
import './style.scss'; import './style.scss';
const filteredCartCouponsFilterArg = {
context: 'summary',
};
const TotalsDiscount = ( { const TotalsDiscount = ( {
cartCoupons = [], cartCoupons = [],
currency, currency,
@ -42,9 +46,7 @@ const TotalsDiscount = ( {
: discountValue; : discountValue;
const filteredCartCoupons = __experimentalApplyCheckoutFilter( { const filteredCartCoupons = __experimentalApplyCheckoutFilter( {
arg: { arg: filteredCartCouponsFilterArg,
context: 'summary',
},
filterName: 'coupons', filterName: 'coupons',
defaultValue: cartCoupons, defaultValue: cartCoupons,
} ); } );

View File

@ -21,10 +21,10 @@ export default ( {
permalink = '', permalink = '',
...props ...props
}: { }: {
className: string; className?: string;
disabled: boolean; disabled?: boolean;
name: string; name: string;
permalink: string; permalink?: string;
} ): JSX.Element => { } ): JSX.Element => {
const classes = classnames( 'wc-block-components-product-name', className ); const classes = classnames( 'wc-block-components-product-name', className );
return disabled ? ( return disabled ? (

View File

@ -5,6 +5,8 @@ import { SnackbarList } from 'wordpress-components';
import classnames from 'classnames'; import classnames from 'classnames';
import { __experimentalApplyCheckoutFilter } from '@woocommerce/blocks-checkout'; import { __experimentalApplyCheckoutFilter } from '@woocommerce/blocks-checkout';
const EMPTY_SNACKBAR_NOTICES = {};
const SnackbarNoticesContainer = ( { const SnackbarNoticesContainer = ( {
className, className,
notices, notices,
@ -19,10 +21,13 @@ const SnackbarNoticesContainer = ( {
( notice ) => notice.type === 'snackbar' ( notice ) => notice.type === 'snackbar'
); );
const noticeVisibility = snackbarNotices.reduce( ( acc, { content } ) => { const noticeVisibility =
acc[ content ] = true; snackbarNotices.length > 0
return acc; ? snackbarNotices.reduce( ( acc, { content } ) => {
}, {} ); acc[ content ] = true;
return acc;
}, {} )
: EMPTY_SNACKBAR_NOTICES;
const filteredNotices = __experimentalApplyCheckoutFilter( { const filteredNotices = __experimentalApplyCheckoutFilter( {
filterName: 'snackbarNoticeVisibility', filterName: 'snackbarNoticeVisibility',

View File

@ -27,7 +27,7 @@ import {
mustContain, mustContain,
} from '@woocommerce/blocks-checkout'; } from '@woocommerce/blocks-checkout';
import Dinero from 'dinero.js'; import Dinero from 'dinero.js';
import { useCallback, useMemo } from '@wordpress/element'; import { useMemo } from '@wordpress/element';
import type { CartItem } from '@woocommerce/type-defs/cart'; import type { CartItem } from '@woocommerce/type-defs/cart';
import { objectHasProp } from '@woocommerce/types'; import { objectHasProp } from '@woocommerce/types';
import { getSetting } from '@woocommerce/settings'; import { getSetting } from '@woocommerce/settings';
@ -46,6 +46,8 @@ const getAmountFromRawPrice = (
return priceObject.convertPrecision( currency.minorUnit ).getAmount(); return priceObject.convertPrecision( currency.minorUnit ).getAmount();
}; };
const productPriceValidation = ( value ) => mustContain( value, '<price/>' );
/** /**
* Cart line item table row component. * Cart line item table row component.
* *
@ -99,7 +101,7 @@ const CartLineItemRow = ( {
line_subtotal: '0', line_subtotal: '0',
line_subtotal_tax: '0', line_subtotal_tax: '0',
}, },
extensions = {}, extensions,
} = lineItem; } = lineItem;
const { const {
@ -110,11 +112,6 @@ const CartLineItemRow = ( {
} = useStoreCartItemQuantity( lineItem ); } = useStoreCartItemQuantity( lineItem );
const { dispatchStoreEvent } = useStoreEvents(); const { dispatchStoreEvent } = useStoreEvents();
const productPriceValidation = useCallback(
( value ) => mustContain( value, '<price/>' ),
[]
);
// Prepare props to pass to the __experimentalApplyCheckoutFilter filter. // Prepare props to pass to the __experimentalApplyCheckoutFilter filter.
// We need to pluck out receiveCart. // We need to pluck out receiveCart.
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
@ -308,4 +305,5 @@ const CartLineItemRow = ( {
</tr> </tr>
); );
}; };
export default CartLineItemRow; export default CartLineItemRow;

View File

@ -427,57 +427,6 @@ exports[`Testing cart Contains a Taxes section if Core options are set to show i
</span> </span>
</button> </button>
</div> </div>
<div
class="wc-block-components-panel__content"
hidden=""
>
<div
class=""
>
<div
aria-hidden="false"
class=""
>
<div
class="wc-block-components-totals-coupon__content"
>
<form
class="wc-block-components-totals-coupon__form"
>
<div
class="wc-block-components-text-input wc-block-components-totals-coupon__input is-active"
>
<input
aria-describedby=""
aria-label="Enter code"
autocapitalize="off"
autocomplete="off"
id="wc-block-components-totals-coupon__input-1"
type="text"
value=""
/>
<label
for="wc-block-components-totals-coupon__input-1"
>
Enter code
</label>
</div>
<button
class="components-button wc-block-components-button wc-block-components-totals-coupon__button"
disabled=""
type="submit"
>
<span
class="wc-block-components-button__text"
>
Apply
</span>
</button>
</form>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div <div
@ -1160,57 +1109,6 @@ exports[`Testing cart Shows individual tax lines if the store is set to do so 1`
</span> </span>
</button> </button>
</div> </div>
<div
class="wc-block-components-panel__content"
hidden=""
>
<div
class=""
>
<div
aria-hidden="false"
class=""
>
<div
class="wc-block-components-totals-coupon__content"
>
<form
class="wc-block-components-totals-coupon__form"
>
<div
class="wc-block-components-text-input wc-block-components-totals-coupon__input is-active"
>
<input
aria-describedby=""
aria-label="Enter code"
autocapitalize="off"
autocomplete="off"
id="wc-block-components-totals-coupon__input-2"
type="text"
value=""
/>
<label
for="wc-block-components-totals-coupon__input-2"
>
Enter code
</label>
</div>
<button
class="components-button wc-block-components-button wc-block-components-totals-coupon__button"
disabled=""
type="submit"
>
<span
class="wc-block-components-button__text"
>
Apply
</span>
</button>
</form>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div <div
@ -1898,57 +1796,6 @@ exports[`Testing cart Shows rate percentages after tax lines if the block is set
</span> </span>
</button> </button>
</div> </div>
<div
class="wc-block-components-panel__content"
hidden=""
>
<div
class=""
>
<div
aria-hidden="false"
class=""
>
<div
class="wc-block-components-totals-coupon__content"
>
<form
class="wc-block-components-totals-coupon__form"
>
<div
class="wc-block-components-text-input wc-block-components-totals-coupon__input is-active"
>
<input
aria-describedby=""
aria-label="Enter code"
autocapitalize="off"
autocomplete="off"
id="wc-block-components-totals-coupon__input-3"
type="text"
value=""
/>
<label
for="wc-block-components-totals-coupon__input-3"
>
Enter code
</label>
</div>
<button
class="components-button wc-block-components-button wc-block-components-totals-coupon__button"
disabled=""
type="submit"
>
<span
class="wc-block-components-button__text"
>
Apply
</span>
</button>
</form>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div <div

View File

@ -63,57 +63,6 @@ exports[`Testing checkout sidebar Shows rate percentages after tax lines if the
</span> </span>
</button> </button>
</div> </div>
<div
class="wc-block-components-panel__content"
hidden=""
>
<div
class=""
>
<div
aria-hidden="false"
class=""
>
<div
class="wc-block-components-totals-coupon__content"
>
<form
class="wc-block-components-totals-coupon__form"
>
<div
class="wc-block-components-text-input wc-block-components-totals-coupon__input is-active"
>
<input
aria-describedby="wc-block-components-totals-coupon__input-0"
aria-label="Enter code"
autocapitalize="off"
autocomplete="off"
id="wc-block-components-totals-coupon__input-0"
type="text"
value=""
/>
<label
for="wc-block-components-totals-coupon__input-0"
>
Enter code
</label>
</div>
<button
class="components-button wc-block-components-button wc-block-components-totals-coupon__button"
disabled=""
type="submit"
>
<span
class="wc-block-components-button__text"
>
Apply
</span>
</button>
</form>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div <div

View File

@ -50,12 +50,11 @@ const Panel = ( {
{ title } { title }
</button> </button>
</TitleTag> </TitleTag>
<div { isOpen && (
className="wc-block-components-panel__content" <div className="wc-block-components-panel__content">
hidden={ ! isOpen } { children }
> </div>
{ children } ) }
</div>
</div> </div>
); );
}; };

View File

@ -90,7 +90,7 @@ const getCheckoutFilters = ( filterName: string ): CheckoutFilterFunction[] => {
export const __experimentalApplyCheckoutFilter = < T >( { export const __experimentalApplyCheckoutFilter = < T >( {
filterName, filterName,
defaultValue, defaultValue,
extensions = {}, extensions = null,
arg = null, arg = null,
validation = returnTrue, validation = returnTrue,
}: { }: {
@ -99,7 +99,7 @@ export const __experimentalApplyCheckoutFilter = < T >( {
/** Default value to filter. */ /** Default value to filter. */
defaultValue: T; defaultValue: T;
/** Values extend to REST API response. */ /** Values extend to REST API response. */
extensions?: Record< string, unknown >; extensions?: Record< string, unknown > | null;
/** Object containing arguments for the filter function. */ /** Object containing arguments for the filter function. */
arg?: CheckoutFilterArguments; arg?: CheckoutFilterArguments;
/** Function that needs to return true when the filtered value is passed in order for the filter to be applied. */ /** Function that needs to return true when the filtered value is passed in order for the filter to be applied. */
@ -111,7 +111,7 @@ export const __experimentalApplyCheckoutFilter = < T >( {
let value = defaultValue; let value = defaultValue;
filters.forEach( ( filter ) => { filters.forEach( ( filter ) => {
try { try {
const newValue = filter( value, extensions, arg ); const newValue = filter( value, extensions || {}, arg );
if ( typeof newValue !== typeof value ) { if ( typeof newValue !== typeof value ) {
throw new Error( throw new Error(
sprintf( sprintf(