diff --git a/blocks/api/parser.js b/blocks/api/parser.js index 4cfb87094ce109..99d13fbb83e8fb 100644 --- a/blocks/api/parser.js +++ b/blocks/api/parser.js @@ -2,7 +2,7 @@ * External dependencies */ import { parse as hpqParse } from 'hpq'; -import { mapValues, omit } from 'lodash'; +import { castArray, mapValues, omit } from 'lodash'; /** * WordPress dependencies @@ -155,13 +155,14 @@ export function getBlockAttributes( blockType, innerHTML, attributes ) { * Attempt to parse the innerHTML using using a supplied `deprecated` * definition. * - * @param {?Object} blockType Block type. - * @param {string} innerHTML Raw block content. - * @param {?Object} attributes Known block attributes (from delimiters). + * @param {?Object} blockType Block type. + * @param {string} innerHTML Raw block content. + * @param {?Object} attributes Known block attributes (from delimiters). + * @param {?Array} innerBlocks Array of innerBlocks. * * @return {Object} Block attributes. */ -export function getAttributesFromDeprecatedVersion( blockType, innerHTML, attributes ) { +export function getAttributesAndInnerBlocksFromDeprecatedVersion( blockType, innerHTML, attributes, innerBlocks ) { // Not all blocks need a deprecated definition so avoid unnecessary computational cycles // as early as possible when `deprecated` property is not supplied. if ( ! blockType.deprecated || ! blockType.deprecated.length ) { @@ -181,12 +182,22 @@ export function getAttributesFromDeprecatedVersion( blockType, innerHTML, attrib try { // Handle migration of older attributes into current version if necessary. const deprecatedBlockAttributes = getBlockAttributes( deprecatedBlockType, innerHTML, attributes ); - const migratedBlockAttributes = deprecatedBlockType.migrate ? deprecatedBlockType.migrate( deprecatedBlockAttributes ) : deprecatedBlockAttributes; // Attempt to validate the parsed block. Ignore if the the validation step fails. const isValid = isValidBlock( innerHTML, deprecatedBlockType, deprecatedBlockAttributes ); if ( isValid ) { - return migratedBlockAttributes; + const migratedBlockAttributesAndInnerBlocks = deprecatedBlockType.migrate && + deprecatedBlockType.migrate( deprecatedBlockAttributes, innerBlocks ); + + if ( migratedBlockAttributesAndInnerBlocks ) { + const [ + migratedAttributes, + migratedInnerBlocks = innerBlocks, + ] = castArray( migratedBlockAttributesAndInnerBlocks ); + return { attributes: migratedAttributes, innerBlocks: migratedInnerBlocks }; + } + + return { attributes: deprecatedBlockAttributes, innerBlocks }; } } catch ( error ) { // Ignore error, it means this deprecated version is invalid. @@ -275,13 +286,14 @@ export function createBlockWithFallback( blockNode ) { // This enables blocks to modify its attributes and markup structure without // invalidating content written in previous formats. if ( ! block.isValid ) { - const attributesParsedWithDeprecatedVersion = getAttributesFromDeprecatedVersion( - blockType, innerHTML, attributes + const attributesAndInnerBlocksParsedWithDeprecatedVersion = getAttributesAndInnerBlocksFromDeprecatedVersion( + blockType, innerHTML, attributes, block.innerBlocks ); - if ( attributesParsedWithDeprecatedVersion ) { + if ( attributesAndInnerBlocksParsedWithDeprecatedVersion ) { block.isValid = true; - block.attributes = attributesParsedWithDeprecatedVersion; + block.attributes = attributesAndInnerBlocksParsedWithDeprecatedVersion.attributes; + block.innerBlocks = attributesAndInnerBlocksParsedWithDeprecatedVersion.innerBlocks || []; } } diff --git a/blocks/api/test/parser.js b/blocks/api/test/parser.js index ac304aa1a12a72..36208909138aa5 100644 --- a/blocks/api/test/parser.js +++ b/blocks/api/test/parser.js @@ -6,7 +6,7 @@ import { getBlockAttributes, asType, createBlockWithFallback, - getAttributesFromDeprecatedVersion, + getAttributesAndInnerBlocksFromDeprecatedVersion, default as parse, parseWithAttributeSchema, } from '../parser'; @@ -185,18 +185,18 @@ describe( 'block parser', () => { } ); } ); - describe( 'getAttributesFromDeprecatedVersion', () => { + describe( 'getAttributesAndInnerBlocksFromDeprecatedVersion', () => { it( 'should return undefined if the block has no deprecated versions', () => { - const attributes = getAttributesFromDeprecatedVersion( + const attributesAndInnerBlocks = getAttributesAndInnerBlocksFromDeprecatedVersion( defaultBlockSettings, 'Bananas', {}, ); - expect( attributes ).toBeUndefined(); + expect( attributesAndInnerBlocks ).toBeUndefined(); } ); it( 'should return undefined if no valid deprecated version found', () => { - const attributes = getAttributesFromDeprecatedVersion( + const attributesAndInnerBlocks = getAttributesAndInnerBlocksFromDeprecatedVersion( { name: 'core/test-block', ...defaultBlockSettings, @@ -211,13 +211,13 @@ describe( 'block parser', () => { 'Bananas', {}, ); - expect( attributes ).toBeUndefined(); + expect( attributesAndInnerBlocks ).toBeUndefined(); expect( console ).toHaveErrored(); expect( console ).toHaveWarned(); } ); it( 'should return the attributes parsed by the deprecated version', () => { - const attributes = getAttributesFromDeprecatedVersion( + const attributesAndInnerBlocks = getAttributesAndInnerBlocksFromDeprecatedVersion( { name: 'core/test-block', ...defaultBlockSettings, @@ -238,7 +238,77 @@ describe( 'block parser', () => { 'Bananas', {}, ); - expect( attributes ).toEqual( { fruit: 'Bananas' } ); + expect( attributesAndInnerBlocks.attributes ).toEqual( { fruit: 'Bananas' } ); + } ); + + it( 'should return the innerBlocks if they are passed', () => { + const attributesAndInnerBlocks = getAttributesAndInnerBlocksFromDeprecatedVersion( + { + name: 'core/test-block', + ...defaultBlockSettings, + save: ( props ) =>
{ props.attributes.title }; + }, + + deprecated: [ + { + attributes: { + title: { + type: 'array', + source: 'children', + selector: 'p', + }, + }, + + migrate( attributes, innerBlocks ) { + return [ + omit( attributes, 'title' ), + [ + createBlock( 'core/paragraph', { + content: attributes.title, + fontSize: 'large', + } ), + ...innerBlocks, + ], + ]; + }, + + save( props ) { + return
{ props.attributes.title }; + }, + } + ] +} ); +``` +{% end %} + +In the example above we updated the block to use an inner paragraph block with a title instead of a title attribute.