From ad3135529ff7d589de1c25a4e2b39486c3272924 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 22 Apr 2024 21:13:39 +0400 Subject: [PATCH] Blocks: Merge variations bootstrapped from a server with the client definitions (#60832) Co-authored-by: Mamaduka Co-authored-by: jsnajdr Co-authored-by: tyxla Co-authored-by: t-hamano Co-authored-by: joemcgill --- packages/blocks/src/api/test/registration.js | 45 +++++++++++++++++++ .../blocks/src/store/process-block-type.js | 40 ++++++++++++++++- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/packages/blocks/src/api/test/registration.js b/packages/blocks/src/api/test/registration.js index a97a456e0e22fa..f9a62346a72e41 100644 --- a/packages/blocks/src/api/test/registration.js +++ b/packages/blocks/src/api/test/registration.js @@ -398,6 +398,51 @@ describe( 'blocks', () => { } ); } ); + it( 'should merge settings provided by server and client', () => { + const blockName = 'core/test-block-with-merged-settings'; + unstable__bootstrapServerSideBlockDefinitions( { + [ blockName ]: { + variations: [ + { name: 'foo', label: 'Foo' }, + { name: 'baz', label: 'Baz', description: 'Testing' }, + ], + }, + } ); + + const blockType = { + title: 'block settings merge', + variations: [ + { name: 'bar', label: 'Bar' }, + { name: 'baz', label: 'Baz', icon: 'layout' }, + ], + }; + registerBlockType( blockName, blockType ); + expect( getBlockType( blockName ) ).toEqual( { + name: blockName, + save: expect.any( Function ), + title: 'block settings merge', + icon: { src: BLOCK_ICON_DEFAULT }, + attributes: {}, + providesContext: {}, + usesContext: [], + keywords: [], + selectors: {}, + supports: {}, + styles: [], + variations: [ + { name: 'foo', label: 'Foo' }, + { + description: 'Testing', + name: 'baz', + label: 'Baz', + icon: 'layout', + }, + { name: 'bar', label: 'Bar' }, + ], + blockHooks: {}, + } ); + } ); + // This test can be removed once the polyfill for blockHooks gets removed. it( 'should polyfill blockHooks using metadata on the client when not set on the server', () => { const blockName = 'tests/hooked-block'; diff --git a/packages/blocks/src/store/process-block-type.js b/packages/blocks/src/store/process-block-type.js index 889f59b55e392e..154f74c6ab7296 100644 --- a/packages/blocks/src/store/process-block-type.js +++ b/packages/blocks/src/store/process-block-type.js @@ -33,6 +33,37 @@ const LEGACY_CATEGORY_MAPPING = { layout: 'design', }; +/** + * Merge block variations bootstrapped from the server and client. + * + * When a variation is registered in both places, its properties are merged. + * + * @param {Array} bootstrappedVariations - A block type variations from the server. + * @param {Array} clientVariations - A block type variations from the client. + * @return {Array} The merged array of block variations. + */ +function mergeBlockVariations( + bootstrappedVariations = [], + clientVariations = [] +) { + const result = [ ...bootstrappedVariations ]; + + clientVariations.forEach( ( clientVariation ) => { + const index = result.findIndex( + ( bootstrappedVariation ) => + bootstrappedVariation.name === clientVariation.name + ); + + if ( index !== -1 ) { + result[ index ] = { ...result[ index ], ...clientVariation }; + } else { + result.push( clientVariation ); + } + } ); + + return result; +} + /** * Takes the unprocessed block type settings, merges them with block type metadata * and applies all the existing filters for the registered block type. @@ -46,6 +77,8 @@ const LEGACY_CATEGORY_MAPPING = { export const processBlockType = ( name, blockSettings ) => ( { select } ) => { + const bootstrappedBlockType = select.getBootstrappedBlockType( name ); + const blockType = { name, icon: BLOCK_ICON_DEFAULT, @@ -56,11 +89,14 @@ export const processBlockType = selectors: {}, supports: {}, styles: [], - variations: [], blockHooks: {}, save: () => null, - ...select.getBootstrappedBlockType( name ), + ...bootstrappedBlockType, ...blockSettings, + variations: mergeBlockVariations( + bootstrappedBlockType?.variations, + blockSettings?.variations + ), }; const settings = applyFilters(