Skip to content

Commit 0bde5ae

Browse files
ellatrixmcsf
authored andcommitted
RichText/Footnotes: make getRichTextValues work with InnerBlocks.Content (#52241)
* RichText/Footnotes: make getRichTextValues work with InnerBlocks.Content --------- Co-authored-by: Miguel Fonseca <150562+mcsf@users.noreply.github.com>
1 parent 1ea0126 commit 0bde5ae

File tree

4 files changed

+193
-47
lines changed

4 files changed

+193
-47
lines changed

packages/block-editor/src/components/rich-text/content.js

+1-46
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22
* WordPress dependencies
33
*/
44
import { RawHTML } from '@wordpress/element';
5-
import {
6-
children as childrenSource,
7-
getSaveElement,
8-
__unstableGetBlockProps as getBlockProps,
9-
} from '@wordpress/blocks';
5+
import { children as childrenSource } from '@wordpress/blocks';
106
import deprecated from '@wordpress/deprecated';
117

128
/**
@@ -42,44 +38,3 @@ export const Content = ( { value, tagName: Tag, multiline, ...props } ) => {
4238

4339
return content;
4440
};
45-
46-
Content.__unstableIsRichTextContent = {};
47-
48-
function findContent( blocks, richTextValues = [] ) {
49-
if ( ! Array.isArray( blocks ) ) {
50-
blocks = [ blocks ];
51-
}
52-
53-
for ( const block of blocks ) {
54-
if (
55-
block?.type?.__unstableIsRichTextContent ===
56-
Content.__unstableIsRichTextContent
57-
) {
58-
richTextValues.push( block.props.value );
59-
continue;
60-
}
61-
62-
if ( block?.props?.children ) {
63-
findContent( block.props.children, richTextValues );
64-
}
65-
}
66-
67-
return richTextValues;
68-
}
69-
70-
function _getSaveElement( { name, attributes, innerBlocks } ) {
71-
return getSaveElement(
72-
name,
73-
attributes,
74-
innerBlocks.map( _getSaveElement )
75-
);
76-
}
77-
78-
export function getRichTextValues( blocks = [] ) {
79-
getBlockProps.skipFilters = true;
80-
const values = findContent(
81-
( Array.isArray( blocks ) ? blocks : [ blocks ] ).map( _getSaveElement )
82-
);
83-
getBlockProps.skipFilters = false;
84-
return values;
85-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { RawHTML, StrictMode, Fragment } from '@wordpress/element';
5+
import {
6+
getSaveElement,
7+
__unstableGetBlockProps as getBlockProps,
8+
} from '@wordpress/blocks';
9+
10+
/**
11+
* Internal dependencies
12+
*/
13+
import InnerBlocks from '../inner-blocks';
14+
import { Content } from './content';
15+
16+
/*
17+
* This function is similar to `@wordpress/element`'s `renderToString` function,
18+
* except that it does not render the elements to a string, but instead collects
19+
* the values of all rich text `Content` elements.
20+
*/
21+
function addValuesForElement( element, ...args ) {
22+
if ( null === element || undefined === element || false === element ) {
23+
return;
24+
}
25+
26+
if ( Array.isArray( element ) ) {
27+
return addValuesForElements( element, ...args );
28+
}
29+
30+
switch ( typeof element ) {
31+
case 'string':
32+
case 'number':
33+
return;
34+
}
35+
36+
const { type, props } = element;
37+
38+
switch ( type ) {
39+
case StrictMode:
40+
case Fragment:
41+
return addValuesForElements( props.children, ...args );
42+
case RawHTML:
43+
return;
44+
case InnerBlocks.Content:
45+
return addValuesForBlocks( ...args );
46+
case Content:
47+
const [ values ] = args;
48+
values.push( props.value );
49+
return;
50+
}
51+
52+
switch ( typeof type ) {
53+
case 'string':
54+
if ( typeof props.children !== 'undefined' ) {
55+
return addValuesForElements( props.children, ...args );
56+
}
57+
return;
58+
case 'function':
59+
if (
60+
type.prototype &&
61+
typeof type.prototype.render === 'function'
62+
) {
63+
return addValuesForElement(
64+
new type( props ).render(),
65+
...args
66+
);
67+
}
68+
69+
return addValuesForElement( type( props ), ...args );
70+
}
71+
}
72+
73+
function addValuesForElements( children, ...args ) {
74+
children = Array.isArray( children ) ? children : [ children ];
75+
76+
for ( let i = 0; i < children.length; i++ ) {
77+
addValuesForElement( children[ i ], ...args );
78+
}
79+
}
80+
81+
function addValuesForBlocks( values, blocks ) {
82+
for ( let i = 0; i < blocks.length; i++ ) {
83+
const { name, attributes, innerBlocks } = blocks[ i ];
84+
const saveElement = getSaveElement( name, attributes );
85+
addValuesForElement( saveElement, values, innerBlocks );
86+
}
87+
}
88+
89+
export function getRichTextValues( blocks = [] ) {
90+
getBlockProps.skipFilters = true;
91+
const values = [];
92+
addValuesForBlocks( values, blocks );
93+
getBlockProps.skipFilters = false;
94+
return values;
95+
}

packages/block-editor/src/private-apis.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import * as globalStyles from './components/global-styles';
55
import { ExperimentalBlockEditorProvider } from './components/provider';
66
import { lock } from './lock-unlock';
7-
import { getRichTextValues } from './components/rich-text/content';
7+
import { getRichTextValues } from './components/rich-text/get-rich-text-values';
88
import { kebabCase } from './utils/object';
99
import ResizableBoxPopover from './components/resizable-box-popover';
1010
import { ComposedPrivateInserter as PrivateInserter } from './components/inserter';

test/e2e/specs/editor/various/footnotes.spec.js

+96
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,100 @@ test.describe( 'Footnotes', () => {
182182

183183
expect( await getFootnotes( page ) ).toMatchObject( [] );
184184
} );
185+
186+
test( 'can be inserted in a list', async ( { editor, page } ) => {
187+
await editor.canvas.click( 'role=button[name="Add default block"i]' );
188+
await page.keyboard.type( '* 1' );
189+
await editor.clickBlockToolbarButton( 'More' );
190+
await page.locator( 'button:text("Footnote")' ).click();
191+
192+
await page.keyboard.type( 'a' );
193+
194+
const id1 = await editor.canvas.evaluate( () => {
195+
return document.activeElement.id;
196+
} );
197+
198+
expect( await editor.getBlocks() ).toMatchObject( [
199+
{
200+
name: 'core/list',
201+
innerBlocks: [
202+
{
203+
name: 'core/list-item',
204+
attributes: {
205+
content: `1<a href="#${ id1 }" id="${ id1 }-link" data-fn="${ id1 }" class="fn">*</a>`,
206+
},
207+
},
208+
],
209+
},
210+
{
211+
name: 'core/footnotes',
212+
},
213+
] );
214+
215+
expect( await getFootnotes( page ) ).toMatchObject( [
216+
{
217+
content: 'a',
218+
id: id1,
219+
},
220+
] );
221+
} );
222+
223+
test( 'can be inserted in a table', async ( { editor, page } ) => {
224+
await editor.insertBlock( { name: 'core/table' } );
225+
await editor.canvas.click( 'role=button[name="Create Table"i]' );
226+
await page.keyboard.type( '1' );
227+
await editor.showBlockToolbar();
228+
await editor.clickBlockToolbarButton( 'More' );
229+
await page.locator( 'button:text("Footnote")' ).click();
230+
231+
await page.keyboard.type( 'a' );
232+
233+
const id1 = await editor.canvas.evaluate( () => {
234+
return document.activeElement.id;
235+
} );
236+
237+
expect( await editor.getBlocks() ).toMatchObject( [
238+
{
239+
name: 'core/table',
240+
attributes: {
241+
body: [
242+
{
243+
cells: [
244+
{
245+
content: `1<a href="#${ id1 }" id="${ id1 }-link" data-fn="${ id1 }" class="fn">*</a>`,
246+
tag: 'td',
247+
},
248+
{
249+
content: '',
250+
tag: 'td',
251+
},
252+
],
253+
},
254+
{
255+
cells: [
256+
{
257+
content: '',
258+
tag: 'td',
259+
},
260+
{
261+
content: '',
262+
tag: 'td',
263+
},
264+
],
265+
},
266+
],
267+
},
268+
},
269+
{
270+
name: 'core/footnotes',
271+
},
272+
] );
273+
274+
expect( await getFootnotes( page ) ).toMatchObject( [
275+
{
276+
content: 'a',
277+
id: id1,
278+
},
279+
] );
280+
} );
185281
} );

0 commit comments

Comments
 (0)