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
index 8a15b75b55f..03614c7b3bd 100644
--- a/plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.tsx
+++ b/plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.tsx
@@ -2,6 +2,7 @@
* External dependencies
*/
import { renderFrontend } from '@woocommerce/base-utils';
+import { CURRENT_USER_IS_ADMIN } from '@woocommerce/settings';
import {
Fragment,
Suspense,
@@ -13,6 +14,7 @@ import {
getRegisteredBlocks,
hasInnerBlocks,
} from '@woocommerce/blocks-checkout';
+import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary';
/**
* This file contains logic used on the frontend to convert DOM elements (saved by the block editor) to React
@@ -53,7 +55,7 @@ const renderForcedBlocks = (
block: string,
blockMap: Record< string, React.ReactNode >,
// Current children from the parent (siblings of the forced block)
- blockChildren: HTMLCollection | null,
+ blockChildren: NodeListOf< ChildNode > | null,
// Wrapper for inner components.
blockWrapper?: React.ElementType
) => {
@@ -63,9 +65,9 @@ const renderForcedBlocks = (
const currentBlocks = blockChildren
? ( Array.from( blockChildren )
- .map( ( element: Element ) =>
- element instanceof HTMLElement
- ? element?.dataset.blockName || null
+ .map( ( node: Node ) =>
+ node instanceof HTMLElement
+ ? node?.dataset.blockName || null
: null
)
.filter( Boolean ) as string[] )
@@ -80,7 +82,7 @@ const renderForcedBlocks = (
const InnerBlockComponentWrapper = blockWrapper ? blockWrapper : Fragment;
return (
-
+ <>
{ forcedBlocks.map(
(
{ blockName, component },
@@ -90,16 +92,36 @@ const renderForcedBlocks = (
? component
: getBlockComponentFromMap( blockName, blockMap );
return ForcedComponent ? (
-
+
+
+
+
+
) : null;
}
) }
-
+ >
);
};
+interface renderInnerBlocksProps {
+ // Block (parent) being rendered. Used for inner block component mapping.
+ block: string;
+ // Map of block names to block components for children.
+ blockMap: Record< string, React.ReactNode >;
+ // Wrapper for inner components.
+ blockWrapper?: React.ElementType | undefined;
+ // Elements from the DOM being converted to components.
+ children: HTMLCollection | NodeList;
+ // Depth within the DOM hierarchy.
+ depth?: number;
+}
+
/**
* Recursively replace block markup in the DOM with React Components.
*/
@@ -114,30 +136,19 @@ const renderInnerBlocks = ( {
children,
// Current depth of the children. Used to ensure keys are unique.
depth = 1,
-}: {
- // Block (parent) being rendered. Used for inner block component mapping.
- block: string;
- // Map of block names to block components for children.
- blockMap: Record< string, React.ReactNode >;
- // Wrapper for inner components.
- blockWrapper?: React.ElementType;
- // Elements from the DOM being converted to components.
- children: HTMLCollection | NodeList;
- // Depth within the DOM hierarchy.
- depth?: number;
-} ): ( JSX.Element | null )[] | null => {
+}: renderInnerBlocksProps ): ( JSX.Element | null )[] | null => {
if ( ! children || children.length === 0 ) {
return null;
}
- return Array.from( children ).map( ( element: Element, index: number ) => {
+ return Array.from( children ).map( ( node: Node, index: number ) => {
/**
* This will grab the blockName from the data- attributes stored in block markup. Without a blockName, we cannot
* convert the HTMLElement to a React component.
*/
const { blockName = '', ...componentProps } = {
key: `${ block }_${ depth }_${ index }`,
- ...( element instanceof HTMLElement ? element.dataset : {} ),
- className: element.className || '',
+ ...( node instanceof HTMLElement ? node.dataset : {} ),
+ className: node instanceof Element ? node?.className : '',
};
const InnerBlockComponent = getBlockComponentFromMap(
@@ -153,7 +164,9 @@ const renderInnerBlocks = ( {
*/
if ( ! InnerBlockComponent ) {
const parsedElement = parse(
- element?.outerHTML || element?.textContent || ''
+ ( node instanceof Element && node?.outerHTML ) ||
+ node?.textContent ||
+ ''
);
// Returns text nodes without manipulation.
@@ -166,11 +179,11 @@ const renderInnerBlocks = ( {
return null;
}
- const renderedChildren = element.childNodes.length
+ const renderedChildren = node.childNodes.length
? renderInnerBlocks( {
block,
blockMap,
- children: element.childNodes,
+ children: node.childNodes,
depth: depth + 1,
blockWrapper,
} )
@@ -195,39 +208,45 @@ const renderInnerBlocks = ( {
key={ `${ block }_${ depth }_${ index }_suspense` }
fallback={
}
>
-
-
- {
- /**
- * Within this Inner Block Component we also need to recursively render it's children. This
- * is done here with a depth+1. The same block map and parent is used, but we pass new
- * children from this element.
- */
- renderInnerBlocks( {
- block,
- blockMap,
- children: element.children,
- depth: depth + 1,
- blockWrapper,
- } )
- }
- {
- /**
- * In addition to the inner blocks, we may also need to render FORCED blocks which have not
- * yet been added to the inner block template. We do this by comparing the current children
- * to the list of registered forced blocks.
- *
- * @see registerCheckoutBlock
- */
- renderForcedBlocks(
- blockName,
- blockMap,
- element.children,
- blockWrapper
- )
- }
-
-
+ { /* Prevent third party components from breaking the entire checkout */ }
+
+
+
+ {
+ /**
+ * Within this Inner Block Component we also need to recursively render it's children. This
+ * is done here with a depth+1. The same block map and parent is used, but we pass new
+ * children from this element.
+ */
+ renderInnerBlocks( {
+ block,
+ blockMap,
+ children: node.childNodes,
+ depth: depth + 1,
+ blockWrapper,
+ } )
+ }
+ {
+ /**
+ * In addition to the inner blocks, we may also need to render FORCED blocks which have not
+ * yet been added to the inner block template. We do this by comparing the current children
+ * to the list of registered forced blocks.
+ *
+ * @see registerCheckoutBlock
+ */
+ renderForcedBlocks(
+ blockName,
+ blockMap,
+ node.childNodes,
+ blockWrapper
+ )
+ }
+
+
+
);
} );
diff --git a/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/block-error.tsx b/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/block-error.tsx
index 49b5443de15..e09f18fd4c9 100644
--- a/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/block-error.tsx
+++ b/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/block-error.tsx
@@ -18,8 +18,9 @@ const BlockError = ( {
errorMessage,
errorMessagePrefix = __( 'Error:', 'woo-gutenberg-products-block' ),
button,
-}: BlockErrorProps ): JSX.Element => {
- return (
+ showErrorBlock = true,
+}: BlockErrorProps ): React.ReactNode => {
+ return showErrorBlock ? (
{ imageUrl && (
- );
+ ) : null;
};
export default BlockError;
diff --git a/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/index.tsx b/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/index.tsx
index 47df319e96b..cbc7f085bb9 100644
--- a/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/index.tsx
+++ b/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/index.tsx
@@ -42,6 +42,7 @@ class BlockErrorBoundary extends Component< BlockErrorBoundaryProps > {
header,
imageUrl,
showErrorMessage = true,
+ showErrorBlock = true,
text,
errorMessagePrefix,
renderError,
@@ -55,6 +56,7 @@ class BlockErrorBoundary extends Component< BlockErrorBoundaryProps > {
}
return (
React.ReactNode;
+ renderError?: ( props: RenderErrorProps ) => React.ReactNode;
+ showErrorMessage?: boolean;
}
export interface DerivedStateReturn {