diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 4ce85afb8cfd1..02378e9e9bce9 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { + getBlockDefaultClassName, getBlockType, getBlockTypes, getBlockVariations, @@ -1862,6 +1863,14 @@ const canIncludeBlockTypeInInserter = ( state, blockType, rootClientId ) => { const getItemFromVariation = ( state, item ) => ( variation ) => { const variationId = `${ item.id }/${ variation.name }`; const { time, count = 0 } = getInsertUsage( state, variationId ) || {}; + const supportsAlias = variation?.supports?.alias ?? false; + const initialAttributes = { + ...item.initialAttributes, + ...variation.attributes, + ...( supportsAlias && { + className: getBlockDefaultClassName( variation.name ), + } ), + }; return { ...item, id: variationId, @@ -1874,8 +1883,13 @@ const getItemFromVariation = ( state, item ) => ( variation ) => { ? variation.example : item.example, initialAttributes: { - ...item.initialAttributes, - ...variation.attributes, + ...initialAttributes, + metadata: { + ...initialAttributes.metadata, + // If the variation supports alias, set the alias attribute to the variation name. + // This is needed for convertAliasBlockNameAndAttributes to convert it to/from the alias/canonical block name. + ...( supportsAlias && { alias: variation.name } ), + }, }, innerBlocks: variation.innerBlocks, keywords: variation.keywords || item.keywords, diff --git a/packages/blocks/src/api/parser/convert-alias-block.js b/packages/blocks/src/api/parser/convert-alias-block.js new file mode 100644 index 0000000000000..0f58649842da8 --- /dev/null +++ b/packages/blocks/src/api/parser/convert-alias-block.js @@ -0,0 +1,21 @@ +/** + * Convert alias blocks to their canonical form. This function is used + * both in the parser level for previous content and to convert such blocks + * used in Custom Post Types templates. + * + * @param {string} name The block's name + * @param {Object} attributes The block's attributes + * + * @return {[string, Object]} The block's name and attributes, changed accordingly if a match was found + */ +export function convertAliasBlockNameAndAttributes( name, attributes ) { + const canonicalBlockName = attributes.metadata?.alias; + let blockName = name; + const newAttributes = { ...attributes }; + if ( canonicalBlockName ) { + blockName = canonicalBlockName; + newAttributes.metadata.alias = name; + } + + return [ blockName, newAttributes ]; +} diff --git a/packages/blocks/src/api/parser/index.js b/packages/blocks/src/api/parser/index.js index f8ff0c68964dc..d5b1896e8b7ea 100644 --- a/packages/blocks/src/api/parser/index.js +++ b/packages/blocks/src/api/parser/index.js @@ -16,6 +16,7 @@ import { getSaveContent } from '../serializer'; import { validateBlock } from '../validation'; import { createBlock } from '../factory'; import { convertLegacyBlockNameAndAttributes } from './convert-legacy-block'; +import { convertAliasBlockNameAndAttributes } from './convert-alias-block'; import { serializeRawBlock } from './serialize-raw-block'; import { getBlockAttributes } from './get-block-attributes'; import { applyBlockDeprecatedVersions } from './apply-block-deprecated-versions'; @@ -78,6 +79,31 @@ function convertLegacyBlocks( rawBlock ) { }; } +/** + * Convert alias blocks to their canonical form. This function is used + * both in the parser level for previous content and to convert such blocks + * used in Custom Post Types templates. + * + * We are swapping the alias value with the block name depending on whether we are serializing or parsing. + * This is because the alias is used to serialize the block name, but when parsing, we need to convert the alias to the block name. + * + * @param {WPRawBlock} rawBlock + * + * @return {WPRawBlock} The block's name and attributes, changed accordingly if a match was found + */ +function convertAliasBlocks( rawBlock ) { + const [ correctName, correctedAttributes ] = + convertAliasBlockNameAndAttributes( + rawBlock.blockName, + rawBlock.attrs + ); + return { + ...rawBlock, + blockName: correctName, + attrs: correctedAttributes, + }; +} + /** * Normalize the raw block by applying the fallback block name if none given, * sanitize the parsed HTML... @@ -201,6 +227,9 @@ export function parseRawBlock( rawBlock, options ) { // we added this function to properly parse the old content. normalizedBlock = convertLegacyBlocks( normalizedBlock ); + // Convert alias blocks to their canonical form. + normalizedBlock = convertAliasBlocks( normalizedBlock ); + // Try finding the type for known block name. let blockType = getBlockType( normalizedBlock.blockName ); diff --git a/packages/blocks/src/api/serializer.js b/packages/blocks/src/api/serializer.js index 609e62fc7e84b..c2751f79e83e7 100644 --- a/packages/blocks/src/api/serializer.js +++ b/packages/blocks/src/api/serializer.js @@ -21,6 +21,7 @@ import { } from './registration'; import { serializeRawBlock } from './parser/serialize-raw-block'; import { isUnmodifiedDefaultBlock, normalizeBlockType } from './utils'; +import { convertAliasBlockNameAndAttributes } from './parser/convert-alias-block'; /** @typedef {import('./parser').WPBlock} WPBlock */ @@ -322,15 +323,18 @@ export function getCommentDelimitedContent( attributes, content ) { + const [ correctBlockName, correctedAttributes ] = + convertAliasBlockNameAndAttributes( rawBlockName, attributes ); + const serializedAttributes = - attributes && Object.entries( attributes ).length - ? serializeAttributes( attributes ) + ' ' + correctedAttributes && Object.entries( correctedAttributes ).length + ? serializeAttributes( correctedAttributes ) + ' ' : ''; // Strip core blocks of their namespace prefix. - const blockName = rawBlockName?.startsWith( 'core/' ) - ? rawBlockName.slice( 5 ) - : rawBlockName; + const blockName = correctBlockName?.startsWith( 'core/' ) + ? correctBlockName.slice( 5 ) + : correctBlockName; // @todo make the `wp:` prefix potentially configurable.