Checkout: Collapsible Order Summary in mobile view (#52253)

This commit is contained in:
Sam Seay 2024-11-01 05:48:54 +13:00 committed by GitHub
parent f5d5caa3f0
commit 601e14a253
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 2330 additions and 480 deletions

View File

@ -54,6 +54,7 @@
"@wordpress/data", "@wordpress/data",
"@wordpress/data-controls", "@wordpress/data-controls",
"@wordpress/date", "@wordpress/date",
"@wordpress/editor",
"@wordpress/dependency-extraction-webpack-plugin", "@wordpress/dependency-extraction-webpack-plugin",
"@wordpress/deprecated", "@wordpress/deprecated",
"@wordpress/dom", "@wordpress/dom",

View File

@ -1,10 +1,9 @@
/** /**
* External dependencies * External dependencies
*/ */
import { __ } from '@wordpress/i18n';
import { useContainerWidthContext } from '@woocommerce/base-context'; import { useContainerWidthContext } from '@woocommerce/base-context';
import { Panel } from '@woocommerce/blocks-components';
import type { CartItem } from '@woocommerce/types'; import type { CartItem } from '@woocommerce/types';
import clsx from 'clsx';
/** /**
* Internal dependencies * Internal dependencies
@ -26,15 +25,10 @@ const OrderSummary = ( {
} }
return ( return (
<Panel <div
className="wc-block-components-order-summary" className={ clsx( 'wc-block-components-order-summary', {
initialOpen={ isLarge } 'is-large': isLarge,
hasBorder={ false } } ) }
title={
<span className="wc-block-components-order-summary__button-text">
{ __( 'Order summary', 'woocommerce' ) }
</span>
}
> >
<div className="wc-block-components-order-summary__content"> <div className="wc-block-components-order-summary__content">
{ cartItems.map( ( cartItem ) => { { cartItems.map( ( cartItem ) => {
@ -46,7 +40,7 @@ const OrderSummary = ( {
); );
} ) } } ) }
</div> </div>
</Panel> </div>
); );
}; };

View File

@ -1,4 +1,7 @@
.wc-block-components-order-summary { .wc-block-components-order-summary {
// Compensate for removing Panel.
padding: 0 $gap;
.wc-block-components-order-summary__button-text { .wc-block-components-order-summary__button-text {
font-weight: 500; font-weight: 500;
} }

View File

@ -16,6 +16,10 @@
gap: $gap-smaller; gap: $gap-smaller;
flex-wrap: wrap; flex-wrap: wrap;
.wc-block-components-text-input.wc-block-components-totals-coupon__input {
margin: 0;
}
.wc-block-components-totals-coupon__input, .wc-block-components-totals-coupon__input,
.wc-block-components-totals-coupon__button { .wc-block-components-totals-coupon__button {
margin: 0; margin: 0;

View File

@ -20,8 +20,11 @@ import { previewCart } from '@woocommerce/resource-previews';
*/ */
import { useStoreEvents } from '../use-store-events'; import { useStoreEvents } from '../use-store-events';
import type { ShippingData } from './types'; import type { ShippingData } from './types';
import { useEditorContext } from '../../providers';
export const useShippingData = (): ShippingData => { export const useShippingData = (): ShippingData => {
const { isEditor } = useEditorContext();
const { const {
shippingRates, shippingRates,
needsShipping, needsShipping,
@ -29,32 +32,37 @@ export const useShippingData = (): ShippingData => {
isLoadingRates, isLoadingRates,
isCollectable, isCollectable,
isSelectingRate, isSelectingRate,
} = useSelect( ( select ) => { } = useSelect(
const isEditor = !! select( 'core/editor' ); ( select ) => {
const store = select( storeKey ); const store = select( storeKey );
const rates = isEditor const rates = isEditor
? previewCart.shipping_rates ? previewCart.shipping_rates
: store.getShippingRates(); : store.getShippingRates();
return { return {
shippingRates: rates, shippingRates: rates,
needsShipping: isEditor needsShipping: isEditor
? previewCart.needs_shipping ? previewCart.needs_shipping
: store.getNeedsShipping(), : store.getNeedsShipping(),
hasCalculatedShipping: isEditor hasCalculatedShipping: isEditor
? previewCart.has_calculated_shipping ? previewCart.has_calculated_shipping
: store.getHasCalculatedShipping(), : store.getHasCalculatedShipping(),
isLoadingRates: isEditor ? false : store.isCustomerDataUpdating(), isLoadingRates: isEditor
isCollectable: rates.every( ? false
( { shipping_rates: packageShippingRates } ) => : store.isCustomerDataUpdating(),
packageShippingRates.find( ( { method_id: methodId } ) => isCollectable: rates.every(
hasCollectableRate( methodId ) ( { shipping_rates: packageShippingRates } ) =>
) packageShippingRates.find(
), ( { method_id: methodId } ) =>
isSelectingRate: isEditor hasCollectableRate( methodId )
? false )
: store.isShippingRateBeingSelected(), ),
}; isSelectingRate: isEditor
} ); ? false
: store.isShippingRateBeingSelected(),
};
},
[ isEditor ]
);
// set selected rates on ref so it's always current. // set selected rates on ref so it's always current.
const selectedRates = useRef< Record< string, string > >( {} ); const selectedRates = useRef< Record< string, string > >( {} );

View File

@ -62,6 +62,7 @@
} }
.wc-block-components-address-form__address_2-toggle { .wc-block-components-address-form__address_2-toggle {
display: inline-block;
background: none; background: none;
border: none; border: none;
color: inherit; color: inherit;

View File

@ -7,6 +7,13 @@ import { innerBlockAreas } from '@woocommerce/blocks-checkout';
import { TotalsFooterItem } from '@woocommerce/base-components/cart-checkout'; import { TotalsFooterItem } from '@woocommerce/base-components/cart-checkout';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format'; import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import { useStoreCart } from '@woocommerce/base-context/hooks'; import { useStoreCart } from '@woocommerce/base-context/hooks';
import { __ } from '@wordpress/i18n';
import { useId, useState } from '@wordpress/element';
import { Icon } from '@wordpress/components';
import { chevronDown, chevronUp } from '@wordpress/icons';
import clsx from 'clsx';
import { FormattedMonetaryAmount } from '@woocommerce/blocks-components';
import { useContainerWidthContext } from '@woocommerce/base-context';
/** /**
* Internal dependencies * Internal dependencies
@ -21,9 +28,29 @@ export const Edit = ( { clientId }: { clientId: string } ): JSX.Element => {
const blockProps = useBlockProps(); const blockProps = useBlockProps();
const { cartTotals } = useStoreCart(); const { cartTotals } = useStoreCart();
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals ); const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
const totalPrice = parseInt( cartTotals.total_price, 10 );
const allowedBlocks = getAllowedBlocks( const allowedBlocks = getAllowedBlocks(
innerBlockAreas.CHECKOUT_ORDER_SUMMARY innerBlockAreas.CHECKOUT_ORDER_SUMMARY
); );
const { isLarge } = useContainerWidthContext();
const [ isOpen, setIsOpen ] = useState( false );
const ariaControlsId = useId();
const orderSummaryProps = ! isLarge
? {
role: 'button',
onClick: () => setIsOpen( ! isOpen ),
'aria-expanded': isOpen,
'aria-controls': ariaControlsId,
tabIndex: 0,
onKeyDown: ( event: React.KeyboardEvent ) => {
if ( event.key === 'Enter' || event.key === ' ' ) {
setIsOpen( ! isOpen );
}
},
}
: {};
const defaultTemplate = [ const defaultTemplate = [
[ 'woocommerce/checkout-order-summary-cart-items-block', {}, [] ], [ 'woocommerce/checkout-order-summary-cart-items-block', {}, [] ],
[ 'woocommerce/checkout-order-summary-coupon-form-block', {}, [] ], [ 'woocommerce/checkout-order-summary-coupon-form-block', {}, [] ],
@ -38,17 +65,48 @@ export const Edit = ( { clientId }: { clientId: string } ): JSX.Element => {
return ( return (
<div { ...blockProps }> <div { ...blockProps }>
<InnerBlocks <div
allowedBlocks={ allowedBlocks } className="wc-block-components-checkout-order-summary__title"
template={ defaultTemplate } { ...orderSummaryProps }
/> >
<div className="wc-block-components-totals-wrapper"> <p
<TotalsFooterItem className="wc-block-components-checkout-order-summary__title-text"
currency={ totalsCurrency } role="heading"
values={ cartTotals } >
/> { __( 'Order summary', 'woocommerce' ) }
</p>
{ ! isLarge && (
<>
<FormattedMonetaryAmount
currency={ totalsCurrency }
value={ totalPrice }
/>
<Icon icon={ isOpen ? chevronUp : chevronDown } />
</>
) }
</div>
<div
className={ clsx(
'wc-block-components-checkout-order-summary__content',
{
'is-open': isOpen,
}
) }
id={ ariaControlsId }
>
<InnerBlocks
allowedBlocks={ allowedBlocks }
template={ defaultTemplate }
/>
<div className="wc-block-components-totals-wrapper">
<TotalsFooterItem
currency={ totalsCurrency }
values={ cartTotals }
/>
</div>
<OrderMetaSlotFill />
</div> </div>
<OrderMetaSlotFill />
</div> </div>
); );
}; };

View File

@ -4,11 +4,17 @@
import { TotalsFooterItem } from '@woocommerce/base-components/cart-checkout'; import { TotalsFooterItem } from '@woocommerce/base-components/cart-checkout';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format'; import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import { useStoreCart } from '@woocommerce/base-context/hooks'; import { useStoreCart } from '@woocommerce/base-context/hooks';
import { __ } from '@wordpress/i18n';
import { Icon, chevronDown, chevronUp } from '@wordpress/icons';
import { useId, useState } from '@wordpress/element';
import clsx from 'clsx';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { OrderMetaSlotFill } from './slotfills'; import { OrderMetaSlotFill, CheckoutOrderSummaryFill } from './slotfills';
import { useContainerWidthContext } from '../../../../base/context';
import { FormattedMonetaryAmount } from '../../../../../../packages/components';
import { FormStepHeading } from '../../form-step';
const FrontendBlock = ( { const FrontendBlock = ( {
children, children,
@ -18,19 +24,104 @@ const FrontendBlock = ( {
className?: string; className?: string;
} ): JSX.Element | null => { } ): JSX.Element | null => {
const { cartTotals } = useStoreCart(); const { cartTotals } = useStoreCart();
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals ); const { isLarge } = useContainerWidthContext();
const [ isOpen, setIsOpen ] = useState( false );
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
const totalPrice = parseInt( cartTotals.total_price, 10 );
const ariaControlsId = useId();
const orderSummaryProps = ! isLarge
? {
role: 'button',
onClick: () => setIsOpen( ! isOpen ),
'aria-expanded': isOpen,
'aria-controls': ariaControlsId,
tabIndex: 0,
onKeyDown: ( event: React.KeyboardEvent ) => {
if ( event.key === 'Enter' || event.key === ' ' ) {
setIsOpen( ! isOpen );
}
},
}
: {};
// Render the summary once here in the block and once in the fill. The fill can be slotted once elsewhere. The fill is only
// rendered on small and mobile screens.
return ( return (
<div className={ className }> <>
{ children } <div className={ className }>
<div className="wc-block-components-totals-wrapper"> <div
<TotalsFooterItem className={ clsx(
currency={ totalsCurrency } 'wc-block-components-checkout-order-summary__title',
values={ cartTotals } {
/> 'is-open': isOpen,
}
) }
{ ...orderSummaryProps }
>
<p
className="wc-block-components-checkout-order-summary__title-text"
role="heading"
>
{ __( 'Order summary', 'woocommerce' ) }
</p>
{ ! isLarge && (
<>
<FormattedMonetaryAmount
currency={ totalsCurrency }
value={ totalPrice }
/>
<Icon
className="wc-block-components-checkout-order-summary__title-icon"
icon={ isOpen ? chevronUp : chevronDown }
/>
</>
) }
</div>
<div
className={ clsx(
'wc-block-components-checkout-order-summary__content',
{
'is-open': isOpen,
}
) }
id={ ariaControlsId }
>
{ children }
<div className="wc-block-components-totals-wrapper">
<TotalsFooterItem
currency={ totalsCurrency }
values={ cartTotals }
/>
</div>
<OrderMetaSlotFill />
</div>
</div> </div>
<OrderMetaSlotFill />
</div> { ! isLarge && (
<CheckoutOrderSummaryFill>
<div
className={ `${ className } checkout-order-summary-block-fill-wrapper` }
>
<FormStepHeading>
<>{ __( 'Order summary', 'woocommerce' ) }</>
</FormStepHeading>
<div className="checkout-order-summary-block-fill">
{ children }
<div className="wc-block-components-totals-wrapper">
<TotalsFooterItem
currency={ totalsCurrency }
values={ cartTotals }
/>
</div>
<OrderMetaSlotFill />
</div>
</div>
</CheckoutOrderSummaryFill>
) }
</>
); );
}; };

View File

@ -11,6 +11,7 @@ import { registerBlockType } from '@wordpress/blocks';
import { Edit, Save } from './edit'; import { Edit, Save } from './edit';
import attributes from './attributes'; import attributes from './attributes';
import deprecated from './deprecated'; import deprecated from './deprecated';
import './style.scss';
registerBlockType( 'woocommerce/checkout-order-summary-block', { registerBlockType( 'woocommerce/checkout-order-summary-block', {
icon: { icon: {

View File

@ -1,7 +1,10 @@
/** /**
* External dependencies * External dependencies
*/ */
import { ExperimentalOrderMeta } from '@woocommerce/blocks-checkout'; import {
ExperimentalOrderMeta,
createSlotFill,
} from '@woocommerce/blocks-checkout';
import { useStoreCart } from '@woocommerce/base-context/hooks'; import { useStoreCart } from '@woocommerce/base-context/hooks';
// @todo Consider deprecating OrderMetaSlotFill and DiscountSlotFill in favour of inner block areas. // @todo Consider deprecating OrderMetaSlotFill and DiscountSlotFill in favour of inner block areas.
@ -17,3 +20,10 @@ export const OrderMetaSlotFill = (): JSX.Element => {
return <ExperimentalOrderMeta.Slot { ...slotFillProps } />; return <ExperimentalOrderMeta.Slot { ...slotFillProps } />;
}; };
const checkoutOrderSummarySlotName = 'checkoutOrderSummaryActionArea';
export const {
Fill: CheckoutOrderSummaryFill,
Slot: CheckoutOrderSummarySlot,
} = createSlotFill( checkoutOrderSummarySlotName );

View File

@ -0,0 +1,98 @@
.wp-block-woocommerce-checkout-order-summary-block {
border: 1px solid $universal-border-light;
border-radius: 5px;
.wc-block-components-formatted-money-amount {
font-weight: 600;
}
.wc-block-components-totals-wrapper:first-of-type {
border-top: 0;
}
.wc-block-components-checkout-order-summary__title {
margin-top: $gap;
display: flex;
justify-content: space-between;
align-items: center;
.wc-block-components-checkout-order-summary__title-text {
margin: 0 0 $gap $gap;
flex-grow: 1;
font-weight: 500;
}
.wc-block-components-checkout-order-summary__title-open-close {
cursor: pointer;
}
}
.checkout-order-summary-block-fill {
border: 1px solid $universal-border-light;
border-radius: 5px;
.wc-block-components-totals-wrapper:first-of-type {
border-top: 0;
}
.wc-block-components-totals-item {
padding-left: $gap;
padding-right: $gap;
}
.wc-block-components-totals-coupon {
padding-left: $gap;
padding-right: $gap;
}
}
}
.has-dark-controls {
.wp-block-woocommerce-checkout-order-summary-block {
border: 1px solid $input-border-dark;
}
}
.is-small,
.is-medium,
.is-mobile {
.wp-block-woocommerce-checkout-order-summary-block {
margin-top: 0;
border: none;
&.checkout-order-summary-block-fill-wrapper {
padding-top: $gap-larger;
}
.wc-block-components-checkout-order-summary__title {
padding: 20px 0;
cursor: pointer;
border-top: 1px solid $universal-border-light;
border-bottom: 1px solid $universal-border-light;
&.is-open {
border-bottom: none;
}
.wc-block-components-checkout-order-summary__title-text {
margin: 0;
}
.wc-block-components-checkout-order-summary__title-icon {
fill: currentColor;
}
}
.wc-block-components-checkout-order-summary__content {
display: none;
&.is-open {
.wc-block-components-totals-wrapper:first-child {
border-top: none;
}
display: block;
}
}
}
}

View File

@ -14,6 +14,7 @@ import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';
* Internal dependencies * Internal dependencies
*/ */
import { termsConsentDefaultText, termsCheckboxDefaultText } from './constants'; import { termsConsentDefaultText, termsCheckboxDefaultText } from './constants';
import { CheckoutOrderSummarySlot } from '../checkout-order-summary-block/slotfills';
const FrontendBlock = ( { const FrontendBlock = ( {
text, text,
@ -73,41 +74,47 @@ const FrontendBlock = ( {
] ); ] );
return ( return (
<div <>
className={ clsx( <CheckoutOrderSummarySlot />
'wc-block-checkout__terms', <div
{ className={ clsx(
'wc-block-checkout__terms--disabled': isDisabled, 'wc-block-checkout__terms',
'wc-block-checkout__terms--with-separator': {
showSeparator !== 'false' && showSeparator !== false, 'wc-block-checkout__terms--disabled': isDisabled,
}, 'wc-block-checkout__terms--with-separator':
className showSeparator !== 'false' &&
) } showSeparator !== false,
> },
{ checkbox ? ( className
<> ) }
<CheckboxControl >
id="terms-and-conditions" { checkbox ? (
checked={ checked } <>
onChange={ () => setChecked( ( value ) => ! value ) } <CheckboxControl
hasError={ hasError } id="terms-and-conditions"
disabled={ isDisabled } checked={ checked }
> onChange={ () =>
<span setChecked( ( value ) => ! value )
dangerouslySetInnerHTML={ { }
__html: text || termsCheckboxDefaultText, hasError={ hasError }
} } disabled={ isDisabled }
/> >
</CheckboxControl> <span
</> dangerouslySetInnerHTML={ {
) : ( __html: text || termsCheckboxDefaultText,
<span } }
dangerouslySetInnerHTML={ { />
__html: text || termsConsentDefaultText, </CheckboxControl>
} } </>
/> ) : (
) } <span
</div> dangerouslySetInnerHTML={ {
__html: text || termsConsentDefaultText,
} }
/>
) }
</div>
</>
); );
}; };

View File

@ -14,8 +14,8 @@
padding-top: $gap-largest; padding-top: $gap-largest;
border-top: 1px solid $universal-border-light; border-top: 1px solid $universal-border-light;
.is-mobile &, .is-mobile &,
.is-medium &,
.is-small & { .is-small & {
border-top: 0; border-top: 0;
} }

View File

@ -5,6 +5,8 @@ import clsx from 'clsx';
import { Sidebar } from '@woocommerce/base-components/sidebar-layout'; import { Sidebar } from '@woocommerce/base-components/sidebar-layout';
import { StoreNoticesContainer } from '@woocommerce/blocks-components'; import { StoreNoticesContainer } from '@woocommerce/blocks-components';
import { useObservedViewport } from '@woocommerce/base-hooks'; import { useObservedViewport } from '@woocommerce/base-hooks';
import { useContainerWidthContext } from '@woocommerce/base-context';
const FrontendBlock = ( { const FrontendBlock = ( {
children, children,
className, className,
@ -15,11 +17,14 @@ const FrontendBlock = ( {
const [ observedRef, observedElement, viewWindow ] = const [ observedRef, observedElement, viewWindow ] =
useObservedViewport< HTMLDivElement >(); useObservedViewport< HTMLDivElement >();
const isSticky = observedElement.height < viewWindow.height; const isSticky = observedElement.height < viewWindow.height;
const { isLarge } = useContainerWidthContext();
return ( return (
<Sidebar <Sidebar
ref={ observedRef } ref={ observedRef }
className={ clsx( 'wc-block-checkout__sidebar', className, { className={ clsx( 'wc-block-checkout__sidebar', className, {
'is-sticky': isSticky, 'is-sticky': isSticky,
'is-large': isLarge,
} ) } } ) }
> >
<StoreNoticesContainer <StoreNoticesContainer

View File

@ -27,23 +27,6 @@
} }
} }
.is-large {
.wp-block-woocommerce-checkout-order-summary-block {
border: 1px solid $universal-border-light;
border-radius: 5px;
.wc-block-components-totals-wrapper:first-of-type {
border-top: 0;
}
}
&.has-dark-controls {
.wp-block-woocommerce-checkout-order-summary-block {
border-color: $input-border-dark;
}
}
}
.wp-block-woocommerce-checkout.is-loading { .wp-block-woocommerce-checkout.is-loading {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;

View File

@ -170,6 +170,7 @@
"@wordpress/data": "6.15.0", "@wordpress/data": "6.15.0",
"@wordpress/data-controls": "2.2.7", "@wordpress/data-controls": "2.2.7",
"@wordpress/date": "4.44.0", "@wordpress/date": "4.44.0",
"@wordpress/editor": "wp-6.7",
"@wordpress/dependency-extraction-webpack-plugin": "4.28.0", "@wordpress/dependency-extraction-webpack-plugin": "4.28.0",
"@wordpress/dom": "3.27.0", "@wordpress/dom": "3.27.0",
"@wordpress/dom-ready": "3.27.0", "@wordpress/dom-ready": "3.27.0",

View File

@ -23,11 +23,26 @@ import BlockErrorBoundary from '../components/error-boundary';
* @param {Array} fills The list of fills to check for a valid one in. * @param {Array} fills The list of fills to check for a valid one in.
* @return {boolean} True if this slot contains any valid fills. * @return {boolean} True if this slot contains any valid fills.
*/ */
export const hasValidFills = ( fills ) => export const hasValidFills = ( fills: [] ) =>
Array.isArray( fills ) && fills.filter( Boolean ).length > 0; Array.isArray( fills ) && fills.filter( Boolean ).length > 0;
export { useSlot, useSlotFills }; export { useSlot, useSlotFills };
type SlotFill = {
Fill: (
props: Partial< {
bubblesVirtually: boolean;
children: React.ReactNode;
} >
) => JSX.Element;
Slot: (
props: Partial< {
bubblesVirtually: boolean;
children: React.ReactNode;
} >
) => JSX.Element;
};
/** /**
* Abstracts @wordpress/components createSlotFill, wraps Fill in an error boundary and passes down fillProps. * Abstracts @wordpress/components createSlotFill, wraps Fill in an error boundary and passes down fillProps.
* *
@ -36,8 +51,10 @@ export { useSlot, useSlotFills };
* *
* @return {Object} Returns a newly wrapped Fill and Slot. * @return {Object} Returns a newly wrapped Fill and Slot.
*/ */
export const createSlotFill = ( slotName, onError = null ) => { export const createSlotFill = ( slotName: string, onError = null ) => {
const { Fill: BaseFill, Slot: BaseSlot } = baseCreateSlotFill( slotName ); const { Fill: BaseFill, Slot: BaseSlot } = baseCreateSlotFill(
slotName
) as SlotFill;
/** /**
* A Fill that will get rendered inside associate slot. * A Fill that will get rendered inside associate slot.
@ -47,9 +64,9 @@ export const createSlotFill = ( slotName, onError = null ) => {
* @param {Object} props Items props. * @param {Object} props Items props.
* @param {Array} props.children Children to be rendered. * @param {Array} props.children Children to be rendered.
*/ */
const Fill = ( { children } ) => ( const Fill = ( { children }: { children: React.ReactNode } ) => (
<BaseFill> <BaseFill>
{ ( fillProps ) => { ( fillProps: unknown ) =>
Children.map( children, ( fill ) => ( Children.map( children, ( fill ) => (
<BlockErrorBoundary <BlockErrorBoundary
/* Returning null would trigger the default error display. /* Returning null would trigger the default error display.
@ -59,6 +76,7 @@ export const createSlotFill = ( slotName, onError = null ) => {
CURRENT_USER_IS_ADMIN ? onError : () => null CURRENT_USER_IS_ADMIN ? onError : () => null
} }
> >
{ /* @ts-expect-error It's not clear how to accurately type `fill`. */ }
{ cloneElement( fill, fillProps ) } { cloneElement( fill, fillProps ) }
</BlockErrorBoundary> </BlockErrorBoundary>
) ) ) )
@ -76,7 +94,9 @@ export const createSlotFill = ( slotName, onError = null ) => {
* @param {Element|string} props.as Element used to render the slot, defaults to div. * @param {Element|string} props.as Element used to render the slot, defaults to div.
* *
*/ */
const Slot = ( props ) => <BaseSlot { ...props } bubblesVirtually />; const Slot = ( props: object ) => (
<BaseSlot { ...props } bubblesVirtually />
);
return { return {
Fill, Fill,

View File

@ -99,7 +99,7 @@ test.describe( 'Shopper → Translations', () => {
).toBeVisible(); ).toBeVisible();
await expect( await expect(
page.getByRole( 'button', { page.getByRole( 'heading', {
name: 'Besteloverzicht', name: 'Besteloverzicht',
} ) } )
).toBeVisible(); ).toBeVisible();

View File

@ -1,3 +1,10 @@
const { webcrypto } = require( 'node:crypto' );
global.crypto = webcrypto;
global.TextEncoder = require( 'util' ).TextEncoder;
global.TextDecoder = require( 'util' ).TextDecoder;
// Set up `wp.*` aliases. Doing this because any tests importing wp stuff will likely run into this. // Set up `wp.*` aliases. Doing this because any tests importing wp stuff will likely run into this.
global.wp = {}; global.wp = {};
require( '@wordpress/data' ); require( '@wordpress/data' );

View File

@ -0,0 +1,4 @@
Significance: minor
Type: enhancement
Checkout: Add a collapsible order summary for smaller screens.

View File

@ -786,10 +786,8 @@
"node_modules/@woocommerce/e2e-core-tests/CHANGELOG.md", "node_modules/@woocommerce/e2e-core-tests/CHANGELOG.md",
"node_modules/@woocommerce/api/dist/", "node_modules/@woocommerce/api/dist/",
"node_modules/@woocommerce/admin-e2e-tests/build", "node_modules/@woocommerce/admin-e2e-tests/build",
"node_modules/@woocommerce/classic-assets/build",
"node_modules/@woocommerce/block-library/build", "node_modules/@woocommerce/block-library/build",
"node_modules/@woocommerce/block-library/blocks.ini", "node_modules/@woocommerce/block-library/blocks.ini",
"node_modules/@woocommerce/admin-library/build",
"package.json", "package.json",
"!node_modules/@woocommerce/admin-e2e-tests/*.ts.map", "!node_modules/@woocommerce/admin-e2e-tests/*.ts.map",
"!node_modules/@woocommerce/admin-e2e-tests/*.tsbuildinfo", "!node_modules/@woocommerce/admin-e2e-tests/*.tsbuildinfo",

File diff suppressed because it is too large Load Diff