From afd2daf6f0a2adcc06310f462d75401e50ba0ac5 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 3 Mar 2025 17:30:14 +0400 Subject: [PATCH 1/4] Use stable reference fallback for getDownloadableBlocks --- packages/block-directory/src/store/selectors.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/block-directory/src/store/selectors.js b/packages/block-directory/src/store/selectors.js index f7e541dd14ed82..170659b829912c 100644 --- a/packages/block-directory/src/store/selectors.js +++ b/packages/block-directory/src/store/selectors.js @@ -9,6 +9,8 @@ import { store as blockEditorStore } from '@wordpress/block-editor'; */ import hasBlockType from './utils/has-block-type'; +const EMPTY_ARRAY = []; + /** * Returns true if application is requesting for downloadable blocks. * @@ -30,7 +32,7 @@ export function isRequestingDownloadableBlocks( state, filterValue ) { * @return {Array} Downloadable blocks. */ export function getDownloadableBlocks( state, filterValue ) { - return state.downloadableBlocks[ filterValue ]?.results ?? []; + return state.downloadableBlocks[ filterValue ]?.results ?? EMPTY_ARRAY; } /** From fbe9147362736bc6c3ff655ea5bba05e0f515c54 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 3 Mar 2025 17:33:31 +0400 Subject: [PATCH 2/4] Do nothing when there are no installed block types --- packages/block-directory/src/store/selectors.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/block-directory/src/store/selectors.js b/packages/block-directory/src/store/selectors.js index 170659b829912c..ad96886df8a37e 100644 --- a/packages/block-directory/src/store/selectors.js +++ b/packages/block-directory/src/store/selectors.js @@ -58,9 +58,12 @@ export function getInstalledBlockTypes( state ) { export const getNewBlockTypes = createRegistrySelector( ( select ) => createSelector( ( state ) => { - const usedBlockTree = select( blockEditorStore ).getBlocks(); const installedBlockTypes = getInstalledBlockTypes( state ); + if ( ! installedBlockTypes.length ) { + return EMPTY_ARRAY; + } + const usedBlockTree = select( blockEditorStore ).getBlocks(); return installedBlockTypes.filter( ( blockType ) => hasBlockType( blockType, usedBlockTree ) ); @@ -83,8 +86,12 @@ export const getNewBlockTypes = createRegistrySelector( ( select ) => export const getUnusedBlockTypes = createRegistrySelector( ( select ) => createSelector( ( state ) => { - const usedBlockTree = select( blockEditorStore ).getBlocks(); const installedBlockTypes = getInstalledBlockTypes( state ); + if ( ! installedBlockTypes.length ) { + return EMPTY_ARRAY; + } + + const usedBlockTree = select( blockEditorStore ).getBlocks(); return installedBlockTypes.filter( ( blockType ) => ! hasBlockType( blockType, usedBlockTree ) From 07a191231ba56323f5f18288111b1556d28a1d8f Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 3 Mar 2025 18:18:44 +0400 Subject: [PATCH 3/4] Update block directory selectors --- .../block-directory/src/store/selectors.js | 50 +++++++++++++------ .../src/store/test/fixtures/index.js | 6 +++ .../src/store/test/selectors.js | 23 +++++++-- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/packages/block-directory/src/store/selectors.js b/packages/block-directory/src/store/selectors.js index ad96886df8a37e..f40c6dc1209a54 100644 --- a/packages/block-directory/src/store/selectors.js +++ b/packages/block-directory/src/store/selectors.js @@ -4,11 +4,6 @@ import { createSelector, createRegistrySelector } from '@wordpress/data'; import { store as blockEditorStore } from '@wordpress/block-editor'; -/** - * Internal dependencies - */ -import hasBlockType from './utils/has-block-type'; - const EMPTY_ARRAY = []; /** @@ -63,14 +58,28 @@ export const getNewBlockTypes = createRegistrySelector( ( select ) => return EMPTY_ARRAY; } - const usedBlockTree = select( blockEditorStore ).getBlocks(); - return installedBlockTypes.filter( ( blockType ) => - hasBlockType( blockType, usedBlockTree ) + const { getBlockName, getClientIdsWithDescendants } = + select( blockEditorStore ); + const installedBlockNames = installedBlockTypes.map( + ( blockType ) => blockType.name + ); + const foundBlockNames = getClientIdsWithDescendants().flatMap( + ( clientId ) => { + const blockName = getBlockName( clientId ); + return installedBlockNames.includes( blockName ) + ? blockName + : []; + } + ); + const newBlockTypes = installedBlockTypes.filter( ( blockType ) => + foundBlockNames.includes( blockType.name ) ); + + return newBlockTypes.length > 0 ? newBlockTypes : EMPTY_ARRAY; }, ( state ) => [ getInstalledBlockTypes( state ), - select( blockEditorStore ).getBlocks(), + select( blockEditorStore ).getClientIdsWithDescendants(), ] ) ); @@ -91,15 +100,28 @@ export const getUnusedBlockTypes = createRegistrySelector( ( select ) => return EMPTY_ARRAY; } - const usedBlockTree = select( blockEditorStore ).getBlocks(); - - return installedBlockTypes.filter( - ( blockType ) => ! hasBlockType( blockType, usedBlockTree ) + const { getBlockName, getClientIdsWithDescendants } = + select( blockEditorStore ); + const installedBlockNames = installedBlockTypes.map( + ( blockType ) => blockType.name + ); + const foundBlockNames = getClientIdsWithDescendants().flatMap( + ( clientId ) => { + const blockName = getBlockName( clientId ); + return installedBlockNames.includes( blockName ) + ? blockName + : []; + } ); + const unusedBlockTypes = installedBlockTypes.filter( + ( blockType ) => ! foundBlockNames.includes( blockType.name ) + ); + + return unusedBlockTypes.length > 0 ? unusedBlockTypes : EMPTY_ARRAY; }, ( state ) => [ getInstalledBlockTypes( state ), - select( blockEditorStore ).getBlocks(), + select( blockEditorStore ).getClientIdsWithDescendants(), ] ) ); diff --git a/packages/block-directory/src/store/test/fixtures/index.js b/packages/block-directory/src/store/test/fixtures/index.js index 29ff9d8f071c8e..aaa18a721baf01 100644 --- a/packages/block-directory/src/store/test/fixtures/index.js +++ b/packages/block-directory/src/store/test/fixtures/index.js @@ -48,3 +48,9 @@ export const blockList = [ innerBlocks: [], }, ]; + +export const blockListIds = blockList.map( ( block ) => block.clientId ); +export const blockListNameMap = blockList.reduce( ( acc, block ) => { + acc[ block.clientId ] = block.name; + return acc; +}, {} ); diff --git a/packages/block-directory/src/store/test/selectors.js b/packages/block-directory/src/store/test/selectors.js index 1c26923618e5db..e9f42f1b5ba3ff 100644 --- a/packages/block-directory/src/store/test/selectors.js +++ b/packages/block-directory/src/store/test/selectors.js @@ -2,7 +2,8 @@ * Internal dependencies */ import { - blockList, + blockListIds, + blockListNameMap, blockTypeInstalled, blockTypeUnused, downloadableBlock, @@ -90,7 +91,10 @@ describe( 'selectors', () => { describe( 'getNewBlockTypes', () => { it( 'should retrieve the block types that are installed and in the post content', () => { getNewBlockTypes.registry = { - select: jest.fn( () => ( { getBlocks: () => blockList } ) ), + select: jest.fn( () => ( { + getBlockName: ( clientId ) => blockListNameMap[ clientId ], + getClientIdsWithDescendants: () => blockListIds, + } ) ), }; const state = { blockManagement: { @@ -107,7 +111,10 @@ describe( 'selectors', () => { it( 'should return an empty array if no blocks are used', () => { getNewBlockTypes.registry = { - select: jest.fn( () => ( { getBlocks: () => [] } ) ), + select: jest.fn( () => ( { + getBlockName: ( clientId ) => blockListNameMap[ clientId ], + getClientIdsWithDescendants: () => [], + } ) ), }; const state = { blockManagement: { @@ -125,7 +132,10 @@ describe( 'selectors', () => { describe( 'getUnusedBlockTypes', () => { it( 'should retrieve the block types that are installed but not used', () => { getUnusedBlockTypes.registry = { - select: jest.fn( () => ( { getBlocks: () => blockList } ) ), + select: jest.fn( () => ( { + getBlockName: ( clientId ) => blockListNameMap[ clientId ], + getClientIdsWithDescendants: () => blockListIds, + } ) ), }; const state = { blockManagement: { @@ -142,7 +152,10 @@ describe( 'selectors', () => { it( 'should return all block types if no blocks are used', () => { getUnusedBlockTypes.registry = { - select: jest.fn( () => ( { getBlocks: () => [] } ) ), + select: jest.fn( () => ( { + getBlockName: ( clientId ) => blockListNameMap[ clientId ], + getClientIdsWithDescendants: () => [], + } ) ), }; const state = { blockManagement: { From a1b2cb0f7a74d5b8be8e9e519492485d3633efcc Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 3 Mar 2025 18:37:25 +0400 Subject: [PATCH 4/4] Remove unused utility method and tests --- .../src/store/utils/has-block-type.js | 24 ----------- .../src/store/utils/test/has-block-type.js | 42 ------------------- 2 files changed, 66 deletions(-) delete mode 100644 packages/block-directory/src/store/utils/has-block-type.js delete mode 100644 packages/block-directory/src/store/utils/test/has-block-type.js diff --git a/packages/block-directory/src/store/utils/has-block-type.js b/packages/block-directory/src/store/utils/has-block-type.js deleted file mode 100644 index 8f83f40591f0ff..00000000000000 --- a/packages/block-directory/src/store/utils/has-block-type.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Check if a block list contains a specific block type. Recursively searches - * through `innerBlocks` if they exist. - * - * @param {Object} blockType A block object to search for. - * @param {Object[]} blocks The list of blocks to look through. - * - * @return {boolean} Whether the blockType is found. - */ -export default function hasBlockType( blockType, blocks = [] ) { - if ( ! blocks.length ) { - return false; - } - if ( blocks.some( ( { name } ) => name === blockType.name ) ) { - return true; - } - for ( let i = 0; i < blocks.length; i++ ) { - if ( hasBlockType( blockType, blocks[ i ].innerBlocks ) ) { - return true; - } - } - - return false; -} diff --git a/packages/block-directory/src/store/utils/test/has-block-type.js b/packages/block-directory/src/store/utils/test/has-block-type.js deleted file mode 100644 index b8104b59d54bf9..00000000000000 --- a/packages/block-directory/src/store/utils/test/has-block-type.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Internal dependencies - */ -import { - blockList, - blockTypeInstalled, - blockTypeUnused, -} from '../../test/fixtures'; -import hasBlockType from '../has-block-type'; - -describe( 'hasBlockType', () => { - it( 'should find the block', () => { - const found = hasBlockType( blockTypeInstalled, blockList ); - expect( found ).toBe( true ); - } ); - - it( 'should not find the unused block', () => { - const found = hasBlockType( blockTypeUnused, blockList ); - expect( found ).toBe( false ); - } ); - - it( 'should find the block in innerBlocks', () => { - const innerBlockList = [ - ...blockList, - { - clientId: 4, - name: 'core/cover', - attributes: {}, - innerBlocks: [ - { - clientId: 5, - name: blockTypeUnused.name, - attributes: {}, - innerBlocks: [], - }, - ], - }, - ]; - const found = hasBlockType( blockTypeUnused, innerBlockList ); - expect( found ).toBe( true ); - } ); -} );