diff --git a/packages/block-editor/src/components/rich-text/content.js b/packages/block-editor/src/components/rich-text/content.js
index dfd206a1ddb7e..9762582f86f14 100644
--- a/packages/block-editor/src/components/rich-text/content.js
+++ b/packages/block-editor/src/components/rich-text/content.js
@@ -2,11 +2,7 @@
* WordPress dependencies
*/
import { RawHTML } from '@wordpress/element';
-import {
- children as childrenSource,
- getSaveElement,
- __unstableGetBlockProps as getBlockProps,
-} from '@wordpress/blocks';
+import { children as childrenSource } from '@wordpress/blocks';
import deprecated from '@wordpress/deprecated';
/**
@@ -42,44 +38,3 @@ export const Content = ( { value, tagName: Tag, multiline, ...props } ) => {
return content;
};
-
-Content.__unstableIsRichTextContent = {};
-
-function findContent( blocks, richTextValues = [] ) {
- if ( ! Array.isArray( blocks ) ) {
- blocks = [ blocks ];
- }
-
- for ( const block of blocks ) {
- if (
- block?.type?.__unstableIsRichTextContent ===
- Content.__unstableIsRichTextContent
- ) {
- richTextValues.push( block.props.value );
- continue;
- }
-
- if ( block?.props?.children ) {
- findContent( block.props.children, richTextValues );
- }
- }
-
- return richTextValues;
-}
-
-function _getSaveElement( { name, attributes, innerBlocks } ) {
- return getSaveElement(
- name,
- attributes,
- innerBlocks.map( _getSaveElement )
- );
-}
-
-export function getRichTextValues( blocks = [] ) {
- getBlockProps.skipFilters = true;
- const values = findContent(
- ( Array.isArray( blocks ) ? blocks : [ blocks ] ).map( _getSaveElement )
- );
- getBlockProps.skipFilters = false;
- return values;
-}
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
new file mode 100644
index 0000000000000..4ecee9b76530e
--- /dev/null
+++ b/packages/block-editor/src/components/rich-text/get-rich-text-values.js
@@ -0,0 +1,95 @@
+/**
+ * WordPress dependencies
+ */
+import { RawHTML, StrictMode, Fragment } from '@wordpress/element';
+import {
+ getSaveElement,
+ __unstableGetBlockProps as getBlockProps,
+} from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import InnerBlocks from '../inner-blocks';
+import { Content } from './content';
+
+/*
+ * 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 ) {
+ if ( null === element || undefined === element || false === element ) {
+ return;
+ }
+
+ if ( Array.isArray( element ) ) {
+ return addValuesForElements( element, ...args );
+ }
+
+ switch ( typeof element ) {
+ case 'string':
+ case 'number':
+ return;
+ }
+
+ const { type, props } = element;
+
+ switch ( type ) {
+ case StrictMode:
+ case Fragment:
+ return addValuesForElements( props.children, ...args );
+ case RawHTML:
+ return;
+ case InnerBlocks.Content:
+ return addValuesForBlocks( ...args );
+ case Content:
+ const [ values ] = args;
+ values.push( props.value );
+ return;
+ }
+
+ switch ( typeof type ) {
+ case 'string':
+ if ( typeof props.children !== 'undefined' ) {
+ return addValuesForElements( props.children, ...args );
+ }
+ return;
+ case 'function':
+ if (
+ type.prototype &&
+ typeof type.prototype.render === 'function'
+ ) {
+ return addValuesForElement(
+ new type( props ).render(),
+ ...args
+ );
+ }
+
+ return addValuesForElement( type( props ), ...args );
+ }
+}
+
+function addValuesForElements( children, ...args ) {
+ children = Array.isArray( children ) ? children : [ children ];
+
+ for ( let i = 0; i < children.length; i++ ) {
+ addValuesForElement( children[ i ], ...args );
+ }
+}
+
+function addValuesForBlocks( values, blocks ) {
+ for ( let i = 0; i < blocks.length; i++ ) {
+ const { name, attributes, innerBlocks } = blocks[ i ];
+ const saveElement = getSaveElement( name, attributes );
+ addValuesForElement( saveElement, values, innerBlocks );
+ }
+}
+
+export function getRichTextValues( blocks = [] ) {
+ getBlockProps.skipFilters = true;
+ const values = [];
+ addValuesForBlocks( values, blocks );
+ getBlockProps.skipFilters = false;
+ return values;
+}
diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js
index 1200dee367d24..453fbd7ce63eb 100644
--- a/packages/block-editor/src/private-apis.js
+++ b/packages/block-editor/src/private-apis.js
@@ -4,7 +4,7 @@
import * as globalStyles from './components/global-styles';
import { ExperimentalBlockEditorProvider } from './components/provider';
import { lock } from './lock-unlock';
-import { getRichTextValues } from './components/rich-text/content';
+import { getRichTextValues } from './components/rich-text/get-rich-text-values';
import { kebabCase } from './utils/object';
import ResizableBoxPopover from './components/resizable-box-popover';
import { ComposedPrivateInserter as PrivateInserter } from './components/inserter';
diff --git a/test/e2e/specs/editor/various/footnotes.spec.js b/test/e2e/specs/editor/various/footnotes.spec.js
index e65eee6d3d08e..1f2fd33f23f73 100644
--- a/test/e2e/specs/editor/various/footnotes.spec.js
+++ b/test/e2e/specs/editor/various/footnotes.spec.js
@@ -182,4 +182,100 @@ test.describe( 'Footnotes', () => {
expect( await getFootnotes( page ) ).toMatchObject( [] );
} );
+
+ test( 'can be inserted in a list', async ( { editor, page } ) => {
+ await editor.canvas.click( 'role=button[name="Add default block"i]' );
+ await page.keyboard.type( '* 1' );
+ await editor.clickBlockToolbarButton( 'More' );
+ await page.locator( 'button:text("Footnote")' ).click();
+
+ await page.keyboard.type( 'a' );
+
+ const id1 = await editor.canvas.evaluate( () => {
+ return document.activeElement.id;
+ } );
+
+ expect( await editor.getBlocks() ).toMatchObject( [
+ {
+ name: 'core/list',
+ innerBlocks: [
+ {
+ name: 'core/list-item',
+ attributes: {
+ content: `1*`,
+ },
+ },
+ ],
+ },
+ {
+ name: 'core/footnotes',
+ },
+ ] );
+
+ expect( await getFootnotes( page ) ).toMatchObject( [
+ {
+ content: 'a',
+ id: id1,
+ },
+ ] );
+ } );
+
+ test( 'can be inserted in a table', async ( { editor, page } ) => {
+ await editor.insertBlock( { name: 'core/table' } );
+ await editor.canvas.click( 'role=button[name="Create Table"i]' );
+ await page.keyboard.type( '1' );
+ await editor.showBlockToolbar();
+ await editor.clickBlockToolbarButton( 'More' );
+ await page.locator( 'button:text("Footnote")' ).click();
+
+ await page.keyboard.type( 'a' );
+
+ const id1 = await editor.canvas.evaluate( () => {
+ return document.activeElement.id;
+ } );
+
+ expect( await editor.getBlocks() ).toMatchObject( [
+ {
+ name: 'core/table',
+ attributes: {
+ body: [
+ {
+ cells: [
+ {
+ content: `1*`,
+ tag: 'td',
+ },
+ {
+ content: '',
+ tag: 'td',
+ },
+ ],
+ },
+ {
+ cells: [
+ {
+ content: '',
+ tag: 'td',
+ },
+ {
+ content: '',
+ tag: 'td',
+ },
+ ],
+ },
+ ],
+ },
+ },
+ {
+ name: 'core/footnotes',
+ },
+ ] );
+
+ expect( await getFootnotes( page ) ).toMatchObject( [
+ {
+ content: 'a',
+ id: id1,
+ },
+ ] );
+ } );
} );