From eaee07522e493fdbc7725df7c6f2aa2d3a38b475 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Wed, 17 Feb 2021 08:46:12 +0200 Subject: [PATCH] Prioritize core blocks in the inserter (#28945) * prioritze core blocks in the inserter context * prioritize core blocks in the selector --- .../components/inserter/block-types-tab.js | 12 +--- packages/block-editor/src/store/selectors.js | 22 +++++-- .../block-editor/src/store/test/selectors.js | 64 +++++++++++++++++++ 3 files changed, 84 insertions(+), 14 deletions(-) diff --git a/packages/block-editor/src/components/inserter/block-types-tab.js b/packages/block-editor/src/components/inserter/block-types-tab.js index 94b76b02ac694f..00b51a4ed2165d 100644 --- a/packages/block-editor/src/components/inserter/block-types-tab.js +++ b/packages/block-editor/src/components/inserter/block-types-tab.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { map, findIndex, flow, sortBy, groupBy, orderBy } from 'lodash'; +import { map, flow, groupBy, orderBy } from 'lodash'; /** * WordPress dependencies @@ -43,22 +43,14 @@ export function BlockTypesTab( { }, [ items ] ); const itemsPerCategory = useMemo( () => { - const getCategoryIndex = ( item ) => { - return findIndex( - categories, - ( category ) => category.slug === item.category - ); - }; - return flow( ( itemList ) => itemList.filter( ( item ) => item.category && item.category !== 'reusable' ), - ( itemList ) => sortBy( itemList, getCategoryIndex ), ( itemList ) => groupBy( itemList, 'category' ) )( items ); - }, [ items, categories ] ); + }, [ items ] ); const itemsPerCollection = useMemo( () => { // Create a new Object to avoid mutating collection. diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index ae383dff603312..8dcac3f9cfb8c4 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1600,12 +1600,26 @@ export const getInserterItems = createSelector( blockVariations.push( ...variations.map( variationMapper ) ); } } - - return [ + // Prioritize core blocks's display in inserter. + const prioritizeCoreBlocks = ( a, b ) => { + const coreBlockNamePrefix = 'core/'; + const firstIsCoreBlock = a.name.startsWith( coreBlockNamePrefix ); + const secondIsCoreBlock = b.name.startsWith( coreBlockNamePrefix ); + if ( firstIsCoreBlock && secondIsCoreBlock ) { + return 0; + } + return firstIsCoreBlock && ! secondIsCoreBlock ? -1 : 1; + }; + // Ensure core blocks are prioritized in the returned results, + // because third party blocks can be registered earlier than + // the core blocks (usually by using the `init` action), + // thus affecting the display order. + // We don't sort reusable blocks as they are handled differently. + const sortedBlockTypes = [ ...visibleBlockTypeInserterItems, ...blockVariations, - ...reusableBlockInserterItems, - ]; + ].sort( prioritizeCoreBlocks ); + return [ ...sortedBlockTypes, ...reusableBlockInserterItems ]; }, ( state, rootClientId ) => [ state.blockListSettings[ rootClientId ], diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js index 77f6ba4df1987e..6ea84f777df21e 100644 --- a/packages/block-editor/src/store/test/selectors.js +++ b/packages/block-editor/src/store/test/selectors.js @@ -3423,3 +3423,67 @@ describe( '__experimentalGetParsedReusableBlock', () => { ); } ); } ); + +describe( 'getInserterItems with core blocks prioritization', () => { + // This test is in a seperate `describe` because all other tests register + // some test `core` blocks and interfere with the purpose of the specific test. + // This tests the functionality to ensure core blocks are prioritized in the + // returned results, because third party blocks can be registered earlier than + // the core blocks (usually by using the `init` action), thus affecting the display order. + beforeEach( () => { + registerBlockType( 'plugin/block-a', { + save() {}, + category: 'text', + title: 'Plugin Block A', + icon: 'test', + } ); + registerBlockType( 'another-plugin/block-b', { + save() {}, + category: 'text', + title: 'Another Plugin Block B', + icon: 'test', + } ); + registerBlockType( 'core/block', { + save() {}, + category: 'text', + title: 'Core Block A', + } ); + registerBlockType( 'core/test-block-a', { + save: ( props ) => props.attributes.text, + category: 'design', + title: 'Core Block B', + icon: 'test', + keywords: [ 'testing' ], + } ); + } ); + afterEach( () => { + [ + 'plugin/block-a', + 'another-plugin/block-b', + 'core/block', + 'core/test-block-a', + ].forEach( unregisterBlockType ); + } ); + it( 'should prioritize core blocks by sorting them at the top of the returned list', () => { + const state = { + blocks: { + byClientId: {}, + attributes: {}, + order: {}, + parents: {}, + cache: {}, + }, + settings: {}, + preferences: {}, + blockListSettings: {}, + }; + const items = getInserterItems( state ); + const expectedResult = [ + 'core/block', + 'core/test-block-a', + 'plugin/block-a', + 'another-plugin/block-b', + ]; + expect( items.map( ( { name } ) => name ) ).toEqual( expectedResult ); + } ); +} );