From 1b7320b785b15d0c8159cb8617151ce5c89e52a9 Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 27 Jul 2023 14:44:08 +0100 Subject: [PATCH 1/5] Footnotes/RichText: fix getRichTextValues for deeply nested blocks --- .../rich-text/get-rich-text-values.js | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/packages/block-editor/src/components/rich-text/get-rich-text-values.js b/packages/block-editor/src/components/rich-text/get-rich-text-values.js index 9af2eb054cf6c..9378c56c937f4 100644 --- a/packages/block-editor/src/components/rich-text/get-rich-text-values.js +++ b/packages/block-editor/src/components/rich-text/get-rich-text-values.js @@ -13,18 +13,20 @@ import { import InnerBlocks from '../inner-blocks'; import { Content } from './content'; +const elementToInnerBlocksMap = new WeakMap(); + /* * This function is similar to `@wordpress/element`'s `renderToString` function, * except that it does not render the elements to a string, but instead collects * the values of all rich text `Content` elements. */ -function addValuesForElement( element, ...args ) { +function addValuesForElement( element, values, innerBlocks ) { if ( null === element || undefined === element || false === element ) { return; } if ( Array.isArray( element ) ) { - return addValuesForElements( element, ...args ); + return addValuesForElements( element, values, innerBlocks ); } switch ( typeof element ) { @@ -33,18 +35,21 @@ function addValuesForElement( element, ...args ) { return; } + if ( elementToInnerBlocksMap.has( element ) ) { + innerBlocks = elementToInnerBlocksMap.get( element ).innerBlocks; + } + const { type, props } = element; switch ( type ) { case StrictMode: case Fragment: - return addValuesForElements( props.children, ...args ); + return addValuesForElements( props.children, values, innerBlocks ); case RawHTML: return; case InnerBlocks.Content: - return addValuesForBlocks( ...args ); + return addValuesForBlocks( values, innerBlocks ); case Content: - const [ values ] = args; values.push( props.value ); return; } @@ -52,21 +57,19 @@ function addValuesForElement( element, ...args ) { switch ( typeof type ) { case 'string': if ( typeof props.children !== 'undefined' ) { - return addValuesForElements( props.children, ...args ); + return addValuesForElements( + props.children, + values, + innerBlocks + ); } return; case 'function': - if ( - type.prototype && - typeof type.prototype.render === 'function' - ) { - return addValuesForElement( - new type( props ).render(), - ...args - ); - } - - return addValuesForElement( type( props ), ...args ); + const el = + type.prototype && typeof type.prototype.render === 'function' + ? new type( props ).render() + : type( props ); + return addValuesForElement( el, values, innerBlocks ); } } @@ -82,9 +85,15 @@ function _getSaveElement( name, attributes, innerBlocks ) { return getSaveElement( name, attributes, - innerBlocks.map( ( block ) => - _getSaveElement( block.name, block.attributes, block.innerBlocks ) - ) + innerBlocks.map( ( block ) => { + const saveElement = _getSaveElement( + block.name, + block.attributes, + block.innerBlocks + ); + elementToInnerBlocksMap.set( saveElement, block ); + return saveElement; + } ) ); } From d4b33b96ce7eff533b9ad4f4f71642017e752a3e Mon Sep 17 00:00:00 2001 From: Ella Date: Fri, 28 Jul 2023 15:22:01 +0100 Subject: [PATCH 2/5] Early return if saveElement is null --- .../src/components/rich-text/get-rich-text-values.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/block-editor/src/components/rich-text/get-rich-text-values.js b/packages/block-editor/src/components/rich-text/get-rich-text-values.js index 9378c56c937f4..9a65d8034d433 100644 --- a/packages/block-editor/src/components/rich-text/get-rich-text-values.js +++ b/packages/block-editor/src/components/rich-text/get-rich-text-values.js @@ -91,6 +91,9 @@ function _getSaveElement( name, attributes, innerBlocks ) { block.attributes, block.innerBlocks ); + if ( ! saveElement ) { + return null; + } elementToInnerBlocksMap.set( saveElement, block ); return saveElement; } ) From 7b06d260c0e2d8d6b3c9ebeaa6bbcdc83e9216b1 Mon Sep 17 00:00:00 2001 From: Ella Date: Fri, 28 Jul 2023 15:38:21 +0100 Subject: [PATCH 3/5] Add comment --- .../src/components/rich-text/get-rich-text-values.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/block-editor/src/components/rich-text/get-rich-text-values.js b/packages/block-editor/src/components/rich-text/get-rich-text-values.js index 9a65d8034d433..8b44e9ad3eb2c 100644 --- a/packages/block-editor/src/components/rich-text/get-rich-text-values.js +++ b/packages/block-editor/src/components/rich-text/get-rich-text-values.js @@ -85,6 +85,11 @@ function _getSaveElement( name, attributes, innerBlocks ) { return getSaveElement( name, attributes, + // We need to pass the inner blocks as save elements so that these + // elements can be used by `useInnerBlocksProps.save` + // (getInnerBlocksProps in the blocks package). The save element tree + // could in turn have inner blocks, which we may need when rendering + // `InnerBlocks.Content`. innerBlocks.map( ( block ) => { const saveElement = _getSaveElement( block.name, From 194364ec92edffdf48e40b5f6b705f7973daf29d Mon Sep 17 00:00:00 2001 From: Ella Date: Fri, 28 Jul 2023 15:56:25 +0100 Subject: [PATCH 4/5] Alternate approach --- .../rich-text/get-rich-text-values.js | 39 ++++--------------- packages/blocks/src/api/serializer.js | 9 ++--- 2 files changed, 11 insertions(+), 37 deletions(-) diff --git a/packages/block-editor/src/components/rich-text/get-rich-text-values.js b/packages/block-editor/src/components/rich-text/get-rich-text-values.js index 8b44e9ad3eb2c..bd1c62ea5e6f6 100644 --- a/packages/block-editor/src/components/rich-text/get-rich-text-values.js +++ b/packages/block-editor/src/components/rich-text/get-rich-text-values.js @@ -13,8 +13,6 @@ import { import InnerBlocks from '../inner-blocks'; import { Content } from './content'; -const elementToInnerBlocksMap = new WeakMap(); - /* * This function is similar to `@wordpress/element`'s `renderToString` function, * except that it does not render the elements to a string, but instead collects @@ -35,10 +33,6 @@ function addValuesForElement( element, values, innerBlocks ) { return; } - if ( elementToInnerBlocksMap.has( element ) ) { - innerBlocks = elementToInnerBlocksMap.get( element ).innerBlocks; - } - const { type, props } = element; switch ( type ) { @@ -81,34 +75,17 @@ function addValuesForElements( children, ...args ) { } } -function _getSaveElement( name, attributes, innerBlocks ) { - return getSaveElement( - name, - attributes, - // We need to pass the inner blocks as save elements so that these - // elements can be used by `useInnerBlocksProps.save` - // (getInnerBlocksProps in the blocks package). The save element tree - // could in turn have inner blocks, which we may need when rendering - // `InnerBlocks.Content`. - innerBlocks.map( ( block ) => { - const saveElement = _getSaveElement( - block.name, - block.attributes, - block.innerBlocks - ); - if ( ! saveElement ) { - return null; - } - elementToInnerBlocksMap.set( saveElement, block ); - return saveElement; - } ) - ); -} - function addValuesForBlocks( values, blocks ) { for ( let i = 0; i < blocks.length; i++ ) { const { name, attributes, innerBlocks } = blocks[ i ]; - const saveElement = _getSaveElement( name, attributes, innerBlocks ); + const saveElement = getSaveElement( + name, + attributes, + // Instead of letting save elements use `useInnerBlocksProps.save`, + // force them to use InnerBlocks.Content instead so we can intercept + // a single component. + + ); addValuesForElement( saveElement, values, innerBlocks ); } } diff --git a/packages/blocks/src/api/serializer.js b/packages/blocks/src/api/serializer.js index 92559b1a0e717..0fdb4f60a24a8 100644 --- a/packages/blocks/src/api/serializer.js +++ b/packages/blocks/src/api/serializer.js @@ -98,12 +98,9 @@ export function getBlockProps( props = {} ) { */ export function getInnerBlocksProps( props = {} ) { const { innerBlocks } = innerBlocksPropsProvider; - const [ firstBlock ] = innerBlocks ?? []; - if ( ! firstBlock ) return props; - // If the innerBlocks passed to `getSaveElement` are not blocks but already - // components, return the props as is. This is the case for - // `getRichTextValues`. - if ( ! firstBlock.clientId ) return { ...props, children: innerBlocks }; + if ( ! Array.isArray( innerBlocks ) ) { + return { ...props, children: innerBlocks }; + } // Value is an array of blocks, so defer to block serializer. const html = serialize( innerBlocks, { isInnerBlocks: true } ); // Use special-cased raw HTML tag to avoid default escaping. From c4bc90b47ebcfaaa425a93b0bde055ea24589600 Mon Sep 17 00:00:00 2001 From: Ella Date: Fri, 28 Jul 2023 16:01:25 +0100 Subject: [PATCH 5/5] Add comment --- packages/blocks/src/api/serializer.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/blocks/src/api/serializer.js b/packages/blocks/src/api/serializer.js index 0fdb4f60a24a8..6c45f08ee6aab 100644 --- a/packages/blocks/src/api/serializer.js +++ b/packages/blocks/src/api/serializer.js @@ -98,6 +98,8 @@ export function getBlockProps( props = {} ) { */ export function getInnerBlocksProps( props = {} ) { const { innerBlocks } = innerBlocksPropsProvider; + // Allow a different component to be passed to getSaveElement to handle + // inner blocks, bypassing the default serialisation. if ( ! Array.isArray( innerBlocks ) ) { return { ...props, children: innerBlocks }; }