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 f39acaaa46e..98f6ebba8e2 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 @@ -108,7 +108,7 @@ const renderInnerBlocks = ( { // Wrapper for inner components. blockWrapper?: React.ElementType; // Elements from the DOM being converted to components. - children: HTMLCollection; + children: HTMLCollection | NodeList; // Depth within the DOM hierarchy. depth?: number; } ): ( JSX.Element | null )[] | null => { @@ -133,27 +133,41 @@ const renderInnerBlocks = ( { /** * If the component cannot be found, or blockName is missing, return the original element. This also ensures * that children within the element are processed also, since it may be an element containing block markup. + * + * Note we use childNodes rather than children so that text nodes are also rendered. */ if ( ! InnerBlockComponent ) { - const parsedElement = parse( element.outerHTML ); + const parsedElement = parse( + element?.outerHTML || element?.textContent || '' + ); - if ( isValidElement( parsedElement ) ) { - const elementChildren = renderInnerBlocks( { - block, - blockMap, - children: element.children || [], - depth: depth + 1, - blockWrapper, - } ); - return elementChildren - ? cloneElement( - parsedElement, - componentProps, - elementChildren - ) - : cloneElement( parsedElement, componentProps ); + // Returns text nodes without manipulation. + if ( typeof parsedElement === 'string' && !! parsedElement ) { + return parsedElement; } - return null; + + // Do not render invalid elements. + if ( ! isValidElement( parsedElement ) ) { + return null; + } + + const renderedChildren = element.childNodes.length + ? renderInnerBlocks( { + block, + blockMap, + children: element.childNodes, + depth: depth + 1, + blockWrapper, + } ) + : undefined; + + return renderedChildren + ? cloneElement( + parsedElement, + componentProps, + renderedChildren + ) + : cloneElement( parsedElement, componentProps ); } // This will wrap inner blocks with the provided wrapper. If no wrapper is provided, we default to Fragment.