Refactor Render Parent Block (https://github.com/woocommerce/woocommerce-blocks/pull/4325)
This commit is contained in:
parent
44b2003ec5
commit
a815993b40
|
@ -1,6 +1,5 @@
|
||||||
export * from './get-block-map.js';
|
export * from './get-block-map';
|
||||||
export * from './create-blocks-from-template.js';
|
export * from './create-blocks-from-template';
|
||||||
export * from './render-parent-block.js';
|
export * from './render-parent-block';
|
||||||
export * from './render-inner-blocks.js';
|
|
||||||
export * from './block-styling.js';
|
export * from './block-styling.js';
|
||||||
export * from './render-standalone-blocks.js';
|
export * from './render-standalone-blocks';
|
||||||
|
|
|
@ -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 (
|
|
||||||
<Suspense
|
|
||||||
key={ `${ parentBlockName }_${ depth }_${ index }_suspense` }
|
|
||||||
fallback={ <div className="wc-block-placeholder" /> }
|
|
||||||
>
|
|
||||||
<LayoutComponent { ...componentProps }>
|
|
||||||
{ componentChildren }
|
|
||||||
</LayoutComponent>
|
|
||||||
</Suspense>
|
|
||||||
);
|
|
||||||
} );
|
|
||||||
};
|
|
|
@ -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,
|
|
||||||
} );
|
|
||||||
};
|
|
|
@ -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 (
|
||||||
|
<Suspense
|
||||||
|
key={ `${ parentBlockName }_${ depth }_${ index }_suspense` }
|
||||||
|
fallback={ <div className="wc-block-placeholder" /> }
|
||||||
|
>
|
||||||
|
<LayoutComponentWrapper>
|
||||||
|
<LayoutComponent { ...componentProps }>
|
||||||
|
{ componentChildren }
|
||||||
|
</LayoutComponent>
|
||||||
|
</LayoutComponentWrapper>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
} );
|
||||||
|
};
|
|
@ -3,6 +3,7 @@
|
||||||
*/
|
*/
|
||||||
import { getValidBlockAttributes } from '@woocommerce/base-utils';
|
import { getValidBlockAttributes } from '@woocommerce/base-utils';
|
||||||
import {
|
import {
|
||||||
|
getBlockMap,
|
||||||
renderParentBlock,
|
renderParentBlock,
|
||||||
renderStandaloneBlocks,
|
renderStandaloneBlocks,
|
||||||
} from '@woocommerce/atomic-utils';
|
} from '@woocommerce/atomic-utils';
|
||||||
|
@ -25,6 +26,7 @@ renderParentBlock( {
|
||||||
blockName: BLOCK_NAME,
|
blockName: BLOCK_NAME,
|
||||||
selector: '.wp-block-woocommerce-single-product',
|
selector: '.wp-block-woocommerce-single-product',
|
||||||
getProps,
|
getProps,
|
||||||
|
blockMap: getBlockMap( BLOCK_NAME ),
|
||||||
} );
|
} );
|
||||||
|
|
||||||
renderStandaloneBlocks();
|
renderStandaloneBlocks();
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
declare let __webpack_public_path__: string;
|
Loading…
Reference in New Issue