diff --git a/packages/block-directory/src/store/selectors.js b/packages/block-directory/src/store/selectors.js index f7e541dd14ed82..f40c6dc1209a54 100644 --- a/packages/block-directory/src/store/selectors.js +++ b/packages/block-directory/src/store/selectors.js @@ -4,10 +4,7 @@ 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 = []; /** * Returns true if application is requesting for downloadable blocks. @@ -30,7 +27,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; } /** @@ -56,16 +53,33 @@ 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; + } - 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(), ] ) ); @@ -81,16 +95,33 @@ 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; + } - 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: { 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 ); - } ); -} );