From b6167bc1791563c875ca4cc37f6de3d9fc999724 Mon Sep 17 00:00:00 2001 From: Seghir Nadir Date: Thu, 23 Sep 2021 16:38:30 +0100 Subject: [PATCH] Cart i2: Render filled and empty Carts on frontend (https://github.com/woocommerce/woocommerce-blocks/pull/4802) * WIP getting to work on frontend * restore frontend.tsx * fix layout * remove unit tests living where they shouldn't be living * remove skeleton * support emtpy cart in frontend * remove extra todo * use fragment instead of div * Add empty cart event * Remove extra fragment --- .../assets/js/base/utils/legacy-events.ts | 2 +- .../js/blocks/cart-checkout/cart-i2/block.js | 83 +- .../blocks/cart-checkout/cart-i2/frontend.js | 66 +- .../empty-cart-block/frontend.tsx | 27 + .../filled-cart-block/frontend.tsx | 30 + .../inner-blocks/register-components.ts | 92 + .../cart-i2/test/__snapshots__/block.js.snap | 2057 ----------------- .../cart-checkout/cart-i2/test/block.js | 165 -- .../checkout/blocks-registry/types.ts | 1 + .../src/BlockTypes/CartI2.php | 83 +- 10 files changed, 237 insertions(+), 2369 deletions(-) create mode 100644 plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/inner-blocks/empty-cart-block/frontend.tsx create mode 100644 plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/inner-blocks/filled-cart-block/frontend.tsx create mode 100644 plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/inner-blocks/register-components.ts delete mode 100644 plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/test/__snapshots__/block.js.snap delete mode 100644 plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/test/block.js diff --git a/plugins/woocommerce-blocks/assets/js/base/utils/legacy-events.ts b/plugins/woocommerce-blocks/assets/js/base/utils/legacy-events.ts index f69ef305009..8fcaad476ed 100644 --- a/plugins/woocommerce-blocks/assets/js/base/utils/legacy-events.ts +++ b/plugins/woocommerce-blocks/assets/js/base/utils/legacy-events.ts @@ -13,7 +13,7 @@ interface DispatchedEventProperties { // See https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail detail?: unknown; // Element that dispatches the event. By default, the body. - element?: HTMLElement; + element?: Element | null; } /** diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/block.js b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/block.js index 2fe83dca617..33d29a7bae0 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/block.js +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/block.js @@ -1,37 +1,37 @@ /** * External dependencies */ +import { __ } from '@wordpress/i18n'; import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data'; import { dispatch } from '@wordpress/data'; import { useStoreCart } from '@woocommerce/base-context/hooks'; -import { useEffect, RawHTML } from '@wordpress/element'; +import { useEffect } from '@wordpress/element'; import LoadingMask from '@woocommerce/base-components/loading-mask'; import { ValidationContextProvider } from '@woocommerce/base-context'; -import { - dispatchEvent, - translateJQueryEventToNative, -} from '@woocommerce/base-utils'; +import { CURRENT_USER_IS_ADMIN } from '@woocommerce/settings'; +import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary'; +import { translateJQueryEventToNative } from '@woocommerce/base-utils'; import withScrollToTop from '@woocommerce/base-hocs/with-scroll-to-top'; +import { + StoreNoticesProvider, + StoreSnackbarNoticesProvider, + CartProvider, +} from '@woocommerce/base-context/providers'; +import { SlotFillProvider } from '@woocommerce/blocks-checkout'; -/** - * Internal dependencies - */ -import FullCart from './full-cart'; +const reloadPage = () => void window.location.reload( true ); -const EmptyCart = ( { content } ) => { - useEffect( () => { - dispatchEvent( 'wc-blocks_render_blocks_frontend', { - element: document.body.querySelector( - '.wp-block-woocommerce-cart' - ), - } ); - }, [] ); - return { content }; +const Cart = ( { children } ) => { + const { cartIsLoading } = useStoreCart(); + + return ( + + { children } + + ); }; -const Block = ( { emptyCart, attributes, scrollToTop } ) => { - const { cartItems, cartIsLoading } = useStoreCart(); - +const ScrollOnError = ( { scrollToTop } ) => { useEffect( () => { const invalidateCartData = () => { dispatch( storeKey ).invalidateResolutionForStore(); @@ -72,19 +72,32 @@ const Block = ( { emptyCart, attributes, scrollToTop } ) => { }; }, [ scrollToTop ] ); - return ( - <> - { ! cartIsLoading && cartItems.length === 0 ? ( - - ) : ( - - - - - - ) } - - ); + return null; }; - +const Block = ( { attributes, children, scrollToTop } ) => ( + + { __( 'Reload the page', 'woo-gutenberg-products-block' ) } + + } + showErrorMessage={ CURRENT_USER_IS_ADMIN } + > + + + + + { children } + + + + + + +); export default withScrollToTop( Block ); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/frontend.js b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/frontend.js index b255032afe5..9fd8a32fee5 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/frontend.js +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/frontend.js @@ -5,25 +5,27 @@ import { withStoreCartApiHydration, withRestApiHydration, } from '@woocommerce/block-hocs'; -import { __ } from '@wordpress/i18n'; import { StoreNoticesProvider, StoreSnackbarNoticesProvider, CartProvider, } from '@woocommerce/base-context/providers'; import { SlotFillProvider } from '@woocommerce/blocks-checkout'; -import { CURRENT_USER_IS_ADMIN } from '@woocommerce/settings'; -import { - renderFrontend, - getValidBlockAttributes, -} from '@woocommerce/base-utils'; +import { getValidBlockAttributes } from '@woocommerce/base-utils'; +import { Children, cloneElement, isValidElement } from '@wordpress/element'; +import { useStoreCart } from '@woocommerce/base-context'; +import { useValidation } from '@woocommerce/base-context/hooks'; +import { getRegisteredBlockComponents } from '@woocommerce/blocks-registry'; + +import { renderParentBlock } from '@woocommerce/atomic-utils'; + /** * Internal dependencies */ -import Block from './block.js'; -import { blockAttributes } from './attributes'; +import './inner-blocks/register-components'; +import Block from './block'; +import { blockName, blockAttributes } from './attributes'; -const reloadPage = () => void window.location.reload( true ); /** * Wrapper component to supply API data and show empty cart view as needed. * @@ -45,30 +47,36 @@ const CartFrontend = ( props ) => { const getProps = ( el ) => { return { - emptyCart: el.innerHTML, - attributes: getValidBlockAttributes( blockAttributes, el.dataset ), - }; -}; - -const getErrorBoundaryProps = () => { - return { - header: __( 'Something went wrong…', 'woo-gutenberg-products-block' ), - text: __( - 'The cart has encountered an unexpected error. If the error persists, please get in touch with us for help.', - 'woo-gutenberg-products-block' - ), - showErrorMessage: CURRENT_USER_IS_ADMIN, - button: ( - + attributes: getValidBlockAttributes( + blockAttributes, + !! el ? el.dataset : {} ), }; }; -renderFrontend( { - selector: '.wp-block-woocommerce-cart-i2', +const Wrapper = ( { children } ) => { + // we need to pluck out receiveCart. + // eslint-disable-next-line no-unused-vars + const { extensions, receiveCart, ...cart } = useStoreCart(); + const validation = useValidation(); + return Children.map( children, ( child ) => { + if ( isValidElement( child ) ) { + const componentProps = { + extensions, + cart, + validation, + }; + return cloneElement( child, componentProps ); + } + return child; + } ); +}; + +renderParentBlock( { Block: withStoreCartApiHydration( withRestApiHydration( CartFrontend ) ), + blockName, + selector: '.wp-block-woocommerce-cart-i2', getProps, - getErrorBoundaryProps, + blockMap: getRegisteredBlockComponents( blockName ), + blockWrapper: Wrapper, } ); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/inner-blocks/empty-cart-block/frontend.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/inner-blocks/empty-cart-block/frontend.tsx new file mode 100644 index 00000000000..eb9b8c47bf8 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/inner-blocks/empty-cart-block/frontend.tsx @@ -0,0 +1,27 @@ +/** + * External dependencies + */ +import { useStoreCart } from '@woocommerce/base-context/hooks'; +import { useEffect } from '@wordpress/element'; +import { dispatchEvent } from '@woocommerce/base-utils'; + +const FrontendBlock = ( { + children, +}: { + children: JSX.Element; +} ): JSX.Element | null => { + const { cartItems, cartIsLoading } = useStoreCart(); + useEffect( () => { + dispatchEvent( 'wc-blocks_render_blocks_frontend', { + element: document.body.querySelector( + '.wp-block-woocommerce-cart' + ), + } ); + }, [] ); + if ( ! cartIsLoading && cartItems.length === 0 ) { + return <>{ children }; + } + return null; +}; + +export default FrontendBlock; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/inner-blocks/filled-cart-block/frontend.tsx b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/inner-blocks/filled-cart-block/frontend.tsx new file mode 100644 index 00000000000..0b0bedf9b68 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/inner-blocks/filled-cart-block/frontend.tsx @@ -0,0 +1,30 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { SidebarLayout } from '@woocommerce/base-components/sidebar-layout'; +import { useStoreCart } from '@woocommerce/base-context/hooks'; + +const FrontendBlock = ( { + children, +}: { + children: JSX.Element; +} ): JSX.Element | null => { + const { cartItems, cartIsLoading } = useStoreCart(); + // @todo pass attributes to inner most blocks. + const hasDarkControls = false; + if ( cartIsLoading || cartItems.length >= 1 ) { + return ( + + { children } + + ); + } + return null; +}; + +export default FrontendBlock; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/inner-blocks/register-components.ts b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/inner-blocks/register-components.ts new file mode 100644 index 00000000000..2cd401e50a3 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/inner-blocks/register-components.ts @@ -0,0 +1,92 @@ +/** + * External dependencies + */ +import { lazy } from '@wordpress/element'; +import { WC_BLOCKS_BUILD_URL } from '@woocommerce/block-settings'; +import { registerCheckoutBlock } from '@woocommerce/blocks-checkout'; + +// Modify webpack publicPath at runtime based on location of WordPress Plugin. +// eslint-disable-next-line no-undef,camelcase +__webpack_public_path__ = WC_BLOCKS_BUILD_URL; + +/** + * Internal dependencies + */ +import filledCartMetadata from './filled-cart-block/block.json'; +import emptyCartMetadata from './empty-cart-block/block.json'; +import cartItemsMetadata from './cart-items-block/block.json'; +import cartExpressPaymentMetadata from './cart-express-payment-block/block.json'; +import cartLineItemsMetadata from './cart-line-items-block/block.json'; +import cartOrderSummaryMetadata from './cart-order-summary-block/block.json'; +import cartTotalsMetadata from './cart-totals-block/block.json'; +import cartProceedToCheckoutMetadata from './proceed-to-checkout-block/block.json'; + +registerCheckoutBlock( { + metadata: filledCartMetadata, + component: lazy( () => + import( + /* webpackChunkName: "cart-blocks/filled-cart" */ './filled-cart-block/frontend' + ) + ), +} ); +registerCheckoutBlock( { + metadata: emptyCartMetadata, + component: lazy( () => + import( + /* webpackChunkName: "cart-blocks/empty-cart" */ './empty-cart-block/frontend' + ) + ), +} ); +registerCheckoutBlock( { + metadata: cartItemsMetadata, + component: lazy( () => + import( + /* webpackChunkName: "cart-blocks/items" */ './cart-items-block/frontend' + ) + ), +} ); + +registerCheckoutBlock( { + metadata: cartLineItemsMetadata, + component: lazy( () => + import( + /* webpackChunkName: "cart-blocks/line-items" */ './cart-line-items-block/block' + ) + ), +} ); + +registerCheckoutBlock( { + metadata: cartTotalsMetadata, + component: lazy( () => + import( + /* webpackChunkName: "cart-blocks/totals" */ './cart-totals-block/frontend' + ) + ), +} ); + +registerCheckoutBlock( { + metadata: cartOrderSummaryMetadata, + component: lazy( () => + import( + /* webpackChunkName: "cart-blocks/order-summary" */ './cart-order-summary-block/block' + ) + ), +} ); + +registerCheckoutBlock( { + metadata: cartExpressPaymentMetadata, + component: lazy( () => + import( + /* webpackChunkName: "cart-blocks/express-payment" */ './cart-express-payment-block/block' + ) + ), +} ); + +registerCheckoutBlock( { + metadata: cartProceedToCheckoutMetadata, + component: lazy( () => + import( + /* webpackChunkName: "cart-blocks/checkout-button" */ './proceed-to-checkout-block/frontend' + ) + ), +} ); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/test/__snapshots__/block.js.snap b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/test/__snapshots__/block.js.snap deleted file mode 100644 index 452f531e9de..00000000000 --- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout/cart-i2/test/__snapshots__/block.js.snap +++ /dev/null @@ -1,2057 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Testing cart Contains a Taxes section if Core options are set to show it 1`] = ` -
-