Skip to content

Commit

Permalink
RichText/Footnotes: make getRichTextValues work with InnerBlocks.Cont…
Browse files Browse the repository at this point in the history
…ent (#52241)

* RichText/Footnotes: make getRichTextValues work with InnerBlocks.Content

---------

Co-authored-by: Miguel Fonseca <150562+mcsf@users.noreply.github.com>
  • Loading branch information
2 people authored and ockham committed Jul 10, 2023
1 parent 1ea0126 commit 0bde5ae
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 47 deletions.
47 changes: 1 addition & 46 deletions packages/block-editor/src/components/rich-text/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand Down Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 1 addition & 1 deletion packages/block-editor/src/private-apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
96 changes: 96 additions & 0 deletions test/e2e/specs/editor/various/footnotes.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<a href="#${ id1 }" id="${ id1 }-link" data-fn="${ id1 }" class="fn">*</a>`,
},
},
],
},
{
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<a href="#${ id1 }" id="${ id1 }-link" data-fn="${ id1 }" class="fn">*</a>`,
tag: 'td',
},
{
content: '',
tag: 'td',
},
],
},
{
cells: [
{
content: '',
tag: 'td',
},
{
content: '',
tag: 'td',
},
],
},
],
},
},
{
name: 'core/footnotes',
},
] );

expect( await getFootnotes( page ) ).toMatchObject( [
{
content: 'a',
id: id1,
},
] );
} );
} );

0 comments on commit 0bde5ae

Please sign in to comment.