Skip to content

Commit

Permalink
Preserve bindings metadata in block transforms (#59179)
Browse files Browse the repository at this point in the history
* Add metadata in heading transforms

* Add metadata in button transforms

* Add heading and button transform from/to paragraph

* Revert "Add heading and button transform from/to paragraph"

This reverts commit c3138e3.

* Move tests to individual block specs

* Explicitly transform metadata in button block

* Explicitly transform metadata in heading block

* Make bindings transform explicit in heading block

* Use util to transform metadata

* Use metadata util in code block to keep id and name

* Move util to block-library package

* Change code transforms util path

* Inherit transform metadata props in the util

* Add transform tests for code block

* Return early if no supported properties

* Use bindings callback for mapping attributes
  • Loading branch information
SantosGuillamot authored and getdave committed Feb 27, 2024
1 parent 667797e commit 6a8bf9d
Show file tree
Hide file tree
Showing 7 changed files with 498 additions and 17 deletions.
18 changes: 14 additions & 4 deletions packages/block-library/src/buttons/transforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import { createBlock } from '@wordpress/blocks';
import { __unstableCreateElement as createElement } from '@wordpress/rich-text';

/**
* Internal dependencies
*/
import { getTransformedMetadata } from '../utils/get-transformed-metadata';

const transforms = {
from: [
{
Expand Down Expand Up @@ -33,10 +38,8 @@ const transforms = {
{},
// Loop the selected buttons.
buttons.map( ( attributes ) => {
const element = createElement(
document,
attributes.content
);
const { content, metadata } = attributes;
const element = createElement( document, content );
// Remove any HTML tags.
const text = element.innerText || '';
// Get first url.
Expand All @@ -46,6 +49,13 @@ const transforms = {
return createBlock( 'core/button', {
text,
url,
metadata: getTransformedMetadata(
metadata,
'core/button',
( { content: contentBinding } ) => ( {
text: contentBinding,
} )
),
} );
} )
),
Expand Down
25 changes: 20 additions & 5 deletions packages/block-library/src/code/transforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import { createBlock } from '@wordpress/blocks';
import { create, toHTMLString } from '@wordpress/rich-text';

/**
* Internal dependencies
*/
import { getTransformedMetadata } from '../utils/get-transformed-metadata';

const transforms = {
from: [
{
Expand All @@ -14,17 +19,21 @@ const transforms = {
{
type: 'block',
blocks: [ 'core/paragraph' ],
transform: ( { content } ) =>
createBlock( 'core/code', { content } ),
transform: ( { content, metadata } ) =>
createBlock( 'core/code', {
content,
metadata: getTransformedMetadata( metadata, 'core/code' ),
} ),
},
{
type: 'block',
blocks: [ 'core/html' ],
transform: ( { content: text } ) => {
transform: ( { content: text, metadata } ) => {
return createBlock( 'core/code', {
// The HTML is plain text (with plain line breaks), so
// convert it to rich text.
content: toHTMLString( { value: create( { text } ) } ),
metadata: getTransformedMetadata( metadata, 'core/code' ),
} );
},
},
Expand All @@ -51,8 +60,14 @@ const transforms = {
{
type: 'block',
blocks: [ 'core/paragraph' ],
transform: ( { content } ) =>
createBlock( 'core/paragraph', { content } ),
transform: ( { content, metadata } ) =>
createBlock( 'core/paragraph', {
content,
metadata: getTransformedMetadata(
metadata,
'core/paragraph'
),
} ),
},
],
};
Expand Down
35 changes: 27 additions & 8 deletions packages/block-library/src/heading/transforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createBlock, getBlockAttributes } from '@wordpress/blocks';
* Internal dependencies
*/
import { getLevelFromHeadingNodeName } from './shared';
import { getTransformedMetadata } from '../utils/get-transformed-metadata';

const transforms = {
from: [
Expand All @@ -15,12 +16,20 @@ const transforms = {
isMultiBlock: true,
blocks: [ 'core/paragraph' ],
transform: ( attributes ) =>
attributes.map( ( { content, anchor, align: textAlign } ) =>
createBlock( 'core/heading', {
content,
anchor,
textAlign,
} )
attributes.map(
( { content, anchor, align: textAlign, metadata } ) =>
createBlock( 'core/heading', {
content,
anchor,
textAlign,
metadata: getTransformedMetadata(
metadata,
'core/heading',
( { content: contentBinding } ) => ( {
content: contentBinding,
} )
),
} )
),
},
{
Expand Down Expand Up @@ -82,8 +91,18 @@ const transforms = {
isMultiBlock: true,
blocks: [ 'core/paragraph' ],
transform: ( attributes ) =>
attributes.map( ( { content, textAlign: align } ) =>
createBlock( 'core/paragraph', { content, align } )
attributes.map( ( { content, textAlign: align, metadata } ) =>
createBlock( 'core/paragraph', {
content,
align,
metadata: getTransformedMetadata(
metadata,
'core/paragraph',
( { content: contentBinding } ) => ( {
content: contentBinding,
} )
),
} )
),
},
],
Expand Down
65 changes: 65 additions & 0 deletions packages/block-library/src/utils/get-transformed-metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* WordPress dependencies
*/
import { getBlockType } from '@wordpress/blocks';

/**
* Transform the metadata attribute with only the values and bindings specified by each transform.
* Returns `undefined` if the input metadata is falsy.
*
* @param {Object} metadata Original metadata attribute from the block that is being transformed.
* @param {Object} newBlockName Name of the final block after the transformation.
* @param {Function} bindingsCallback Optional callback to transform the `bindings` property object.
* @return {Object|undefined} New metadata object only with the relevant properties.
*/
export function getTransformedMetadata(
metadata,
newBlockName,
bindingsCallback
) {
if ( ! metadata ) {
return;
}
const { supports } = getBlockType( newBlockName );
// Fixed until an opt-in mechanism is implemented.
const BLOCK_BINDINGS_SUPPORTED_BLOCKS = [
'core/paragraph',
'core/heading',
'core/image',
'core/button',
];
// The metadata properties that should be preserved after the transform.
const transformSupportedProps = [];
// If it support bindings, and there is a transform bindings callback, add the `id` and `bindings` properties.
if (
BLOCK_BINDINGS_SUPPORTED_BLOCKS.includes( newBlockName ) &&
bindingsCallback
) {
transformSupportedProps.push( 'id', 'bindings' );
}
// If it support block naming (true by default), add the `name` property.
if ( supports.renaming !== false ) {
transformSupportedProps.push( 'name' );
}

// Return early if no supported properties.
if ( ! transformSupportedProps.length ) {
return;
}

const newMetadata = Object.entries( metadata ).reduce(
( obj, [ prop, value ] ) => {
// If prop is not supported, don't add it to the new metadata object.
if ( ! transformSupportedProps.includes( prop ) ) {
return obj;
}
obj[ prop ] =
prop === 'bindings' ? bindingsCallback( value ) : value;
return obj;
},
{}
);

// Return undefined if object is empty.
return Object.keys( newMetadata ).length ? newMetadata : undefined;
}
76 changes: 76 additions & 0 deletions test/e2e/specs/editor/blocks/buttons.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -405,4 +405,80 @@ test.describe( 'Buttons', () => {
<!-- /wp:buttons -->`
);
} );

test.describe( 'Block transforms', () => {
test.describe( 'FROM paragraph', () => {
test( 'should preserve the content', async ( { editor } ) => {
await editor.insertBlock( {
name: 'core/paragraph',
attributes: {
content: 'initial content',
},
} );
await editor.transformBlockTo( 'core/buttons' );
const buttonBlock = ( await editor.getBlocks() )[ 0 ]
.innerBlocks[ 0 ];
expect( buttonBlock.name ).toBe( 'core/button' );
expect( buttonBlock.attributes.text ).toBe( 'initial content' );
} );

test( 'should preserve the metadata attribute', async ( {
editor,
} ) => {
await editor.insertBlock( {
name: 'core/paragraph',
attributes: {
content: 'initial content',
metadata: {
name: 'Custom name',
},
},
} );

await editor.transformBlockTo( 'core/buttons' );
const buttonBlock = ( await editor.getBlocks() )[ 0 ]
.innerBlocks[ 0 ];
expect( buttonBlock.name ).toBe( 'core/button' );
expect( buttonBlock.attributes.metadata ).toMatchObject( {
name: 'Custom name',
} );
} );

test( 'should preserve the block bindings', async ( {
editor,
} ) => {
await editor.insertBlock( {
name: 'core/paragraph',
attributes: {
content: 'initial content',
metadata: {
bindings: {
content: {
source: 'core/post-meta',
args: {
key: 'custom_field',
},
},
},
},
},
} );

await editor.transformBlockTo( 'core/buttons' );
const buttonBlock = ( await editor.getBlocks() )[ 0 ]
.innerBlocks[ 0 ];
expect( buttonBlock.name ).toBe( 'core/button' );
expect(
buttonBlock.attributes.metadata.bindings
).toMatchObject( {
text: {
source: 'core/post-meta',
args: {
key: 'custom_field',
},
},
} );
} );
} );
} );
} );
Loading

0 comments on commit 6a8bf9d

Please sign in to comment.