From a815993b4049dde1515db73fc792943276c3f564 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jun 2021 09:54:18 +0100 Subject: [PATCH] Refactor Render Parent Block (https://github.com/woocommerce/woocommerce-blocks/pull/4325) --- .../assets/js/atomic/utils/index.js | 9 +- .../js/atomic/utils/render-inner-blocks.js | 69 ---------- .../js/atomic/utils/render-parent-block.js | 41 ------ .../js/atomic/utils/render-parent-block.tsx | 126 ++++++++++++++++++ .../js/blocks/single-product/frontend.js | 2 + plugins/woocommerce-blocks/global.d.ts | 2 + 6 files changed, 134 insertions(+), 115 deletions(-) delete mode 100644 plugins/woocommerce-blocks/assets/js/atomic/utils/render-inner-blocks.js delete mode 100644 plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.js create mode 100644 plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.tsx create mode 100644 plugins/woocommerce-blocks/global.d.ts diff --git a/plugins/woocommerce-blocks/assets/js/atomic/utils/index.js b/plugins/woocommerce-blocks/assets/js/atomic/utils/index.js index 006dc93e745..111fae8fd7d 100644 --- a/plugins/woocommerce-blocks/assets/js/atomic/utils/index.js +++ b/plugins/woocommerce-blocks/assets/js/atomic/utils/index.js @@ -1,6 +1,5 @@ -export * from './get-block-map.js'; -export * from './create-blocks-from-template.js'; -export * from './render-parent-block.js'; -export * from './render-inner-blocks.js'; +export * from './get-block-map'; +export * from './create-blocks-from-template'; +export * from './render-parent-block'; export * from './block-styling.js'; -export * from './render-standalone-blocks.js'; +export * from './render-standalone-blocks'; diff --git a/plugins/woocommerce-blocks/assets/js/atomic/utils/render-inner-blocks.js b/plugins/woocommerce-blocks/assets/js/atomic/utils/render-inner-blocks.js deleted file mode 100644 index 75005fcc9ee..00000000000 --- a/plugins/woocommerce-blocks/assets/js/atomic/utils/render-inner-blocks.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * External dependencies - */ -import { Suspense, cloneElement, isValidElement } from '@wordpress/element'; -import parse from 'html-react-parser'; - -/** - * Internal dependencies - */ -import { getBlockMap } from './get-block-map'; - -/** - * Replaces saved block HTML markup with Inner Block Components. - * - * @param {Object} props Render props. - * @param {Array} props.children Children/inner blocks to render. - * @param {string} props.blockName Parent Block Name used to get the block map and for keys. - * @param {number} [props.depth] Depth of inner blocks being rendered. - */ -export const renderInnerBlocks = ( { - children, - blockName: parentBlockName, - depth = 1, -} ) => { - const blockMap = getBlockMap( parentBlockName ); - - return Array.from( children ).map( ( el, index ) => { - const componentProps = { - ...el.dataset, - key: `${ parentBlockName }_${ depth }_${ index }`, - }; - - const componentChildren = - el.children && el.children.length - ? renderInnerBlocks( { - children: el.children, - blockName: parentBlockName, - depth: depth + 1, - } ) - : null; - - const LayoutComponent = - componentProps.blockName && blockMap[ componentProps.blockName ] - ? blockMap[ componentProps.blockName ] - : null; - - if ( ! LayoutComponent ) { - const element = parse( el.outerHTML ); - - if ( isValidElement( element ) ) { - return componentChildren - ? cloneElement( element, componentProps, componentChildren ) - : cloneElement( element, componentProps ); - } - return null; - } - - return ( - } - > - - { componentChildren } - - - ); - } ); -}; diff --git a/plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.js b/plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.js deleted file mode 100644 index cf32128087b..00000000000 --- a/plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * External dependencies - */ -import { renderFrontend } from '@woocommerce/base-utils'; - -/** - * Internal dependencies - */ -import { renderInnerBlocks } from './render-inner-blocks'; - -/** - * Renders a block component in the place of a specified set of selectors. - * - * @param {Object} props Render props. - * @param {Function} props.Block React component to use as a replacement. - * @param {string} props.selector CSS selector to match the elements to replace. - * @param {string} [props.blockName] Optional Block Name. Used for inner block component mapping. - * @param {Function} [props.getProps] Function to generate the props object for the block. - */ -export const renderParentBlock = ( { - Block, - selector, - blockName = '', - getProps = () => {}, -} ) => { - const getPropsWithChildren = ( el, i ) => { - const children = - el.children && el.children.length - ? renderInnerBlocks( { - blockName, - children: el.children, - } ) - : null; - return { ...getProps( el, i ), children }; - }; - renderFrontend( { - Block, - selector, - getProps: getPropsWithChildren, - } ); -}; diff --git a/plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.tsx b/plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.tsx new file mode 100644 index 00000000000..59c32a04cb7 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.tsx @@ -0,0 +1,126 @@ +/** + * External dependencies + */ +import { renderFrontend } from '@woocommerce/base-utils'; +import { + Fragment, + Suspense, + cloneElement, + isValidElement, +} from '@wordpress/element'; +import parse from 'html-react-parser'; + +interface renderBlockProps { + // Parent Block Name. Used for inner block component mapping. + blockName: string; + // Map of block names to block components for children. + blockMap: Record< string, React.ReactNode >; + // Wrapper for inner components. + blockWrapper?: React.ReactNode; +} + +interface renderParentBlockProps extends renderBlockProps { + // React component to use as a replacement. + Block: React.FunctionComponent; + // CSS selector to match the elements to replace. + selector: string; + // Function to generate the props object for the block. + getProps: ( el: Element, i: number ) => Record< string, unknown >; +} + +interface renderInnerBlockProps extends renderBlockProps { + children: HTMLCollection; + depth?: number; +} + +/** + * Replaces saved block HTML markup with Inner Block Components. + */ +const renderInnerBlocks = ( { + blockName: parentBlockName, + blockMap, + blockWrapper, + depth = 1, + children, +}: renderInnerBlockProps ): ( JSX.Element | null )[] | null => { + return Array.from( children ).map( ( el: Element, index: number ) => { + const { blockName = '', ...componentProps } = { + key: `${ parentBlockName }_${ depth }_${ index }`, + ...( el instanceof HTMLElement ? el.dataset : {} ), + }; + + const componentChildren = + el.children && el.children.length + ? renderInnerBlocks( { + children: el.children, + blockName: parentBlockName, + blockMap, + depth: depth + 1, + blockWrapper, + } ) + : null; + + const LayoutComponent = + blockName && blockMap[ blockName ] + ? ( blockMap[ blockName ] as React.ElementType ) + : null; + + if ( ! LayoutComponent ) { + const element = parse( el.outerHTML ); + + if ( isValidElement( element ) ) { + return componentChildren + ? cloneElement( element, componentProps, componentChildren ) + : cloneElement( element, componentProps ); + } + return null; + } + + const LayoutComponentWrapper = ( blockWrapper + ? blockWrapper + : Fragment ) as React.ElementType; + + return ( + } + > + + + { componentChildren } + + + + ); + } ); +}; + +/** + * Renders a block component in the place of a specified set of selectors. + */ +export const renderParentBlock = ( { + Block, + selector, + blockName, + getProps = () => ( {} ), + blockMap, + blockWrapper, +}: renderParentBlockProps ): void => { + const getPropsWithChildren = ( el: Element, i: number ) => { + const children = + el.children && el.children.length + ? renderInnerBlocks( { + blockName, + blockMap, + children: el.children, + blockWrapper, + } ) + : null; + return { ...getProps( el, i ), children }; + }; + renderFrontend( { + Block, + selector, + getProps: getPropsWithChildren, + } ); +}; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/single-product/frontend.js b/plugins/woocommerce-blocks/assets/js/blocks/single-product/frontend.js index f96bc4e41d9..0c471deb9bc 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/single-product/frontend.js +++ b/plugins/woocommerce-blocks/assets/js/blocks/single-product/frontend.js @@ -3,6 +3,7 @@ */ import { getValidBlockAttributes } from '@woocommerce/base-utils'; import { + getBlockMap, renderParentBlock, renderStandaloneBlocks, } from '@woocommerce/atomic-utils'; @@ -25,6 +26,7 @@ renderParentBlock( { blockName: BLOCK_NAME, selector: '.wp-block-woocommerce-single-product', getProps, + blockMap: getBlockMap( BLOCK_NAME ), } ); renderStandaloneBlocks(); diff --git a/plugins/woocommerce-blocks/global.d.ts b/plugins/woocommerce-blocks/global.d.ts new file mode 100644 index 00000000000..12cbebc2079 --- /dev/null +++ b/plugins/woocommerce-blocks/global.d.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line camelcase +declare let __webpack_public_path__: string;