diff --git a/docs/designers-developers/developers/data/data-core-block-editor.md b/docs/designers-developers/developers/data/data-core-block-editor.md index f05788dcf52818..76414491a28182 100644 --- a/docs/designers-developers/developers/data/data-core-block-editor.md +++ b/docs/designers-developers/developers/data/data-core-block-editor.md @@ -440,7 +440,7 @@ _Properties_ - _category_ `string`: Block category that the item is associated with. - _keywords_ `Array`: Keywords that can be searched to find this item. - _isDisabled_ `boolean`: Whether or not the user should be prevented from inserting this item. -- _frecency_ `number`: Hueristic that combines frequency and recency. +- _frecency_ `number`: Heuristic that combines frequency and recency. # **getLastMultiSelectedBlockClientId** diff --git a/packages/block-editor/src/autocompleters/block.js b/packages/block-editor/src/autocompleters/block.js index d7573543284fa7..79cff960e3e71e 100644 --- a/packages/block-editor/src/autocompleters/block.js +++ b/packages/block-editor/src/autocompleters/block.js @@ -15,7 +15,6 @@ import { useMemo } from '@wordpress/element'; */ import { searchBlockItems } from '../components/inserter/search-items'; import useBlockTypesState from '../components/inserter/hooks/use-block-types-state'; -import { includeVariationsInInserterItems } from '../components/inserter/utils'; import BlockIcon from '../components/block-icon'; const SHOWN_BLOCK_TYPES = 9; @@ -98,10 +97,7 @@ function createBlockCompleter() { const options = useMemo( () => - includeVariationsInInserterItems( - filteredItems, - SHOWN_BLOCK_TYPES - ).map( ( blockItem ) => { + filteredItems.map( ( blockItem ) => { const { title, icon, isDisabled } = blockItem; return { key: `block-${ blockItem.id }`, diff --git a/packages/block-editor/src/components/block-types-list/index.js b/packages/block-editor/src/components/block-types-list/index.js index e4e0251d23d5f8..356ed3a7d13d3c 100644 --- a/packages/block-editor/src/components/block-types-list/index.js +++ b/packages/block-editor/src/components/block-types-list/index.js @@ -13,7 +13,6 @@ import { useEffect } from '@wordpress/element'; * Internal dependencies */ import InserterListItem from '../inserter-list-item'; -import { includeVariationsInInserterItems } from '../inserter/utils'; function BlockTypesList( { items = [], @@ -21,14 +20,9 @@ function BlockTypesList( { onHover = () => {}, children, label, - limit, } ) { const composite = useCompositeState(); - const normalizedItems = includeVariationsInInserterItems( items, limit ); - const orderId = normalizedItems.reduce( - ( acc, item ) => acc + '--' + item.id, - '' - ); + const orderId = items.reduce( ( acc, item ) => acc + '--' + item.id, '' ); // This ensures the composite state refreshes when the list order changes. useEffect( () => { @@ -47,7 +41,7 @@ function BlockTypesList( { className="block-editor-block-types-list" aria-label={ label } > - { normalizedItems.map( ( item ) => { + { items.map( ( item ) => { return ( ) } diff --git a/packages/block-editor/src/components/inserter/quick-inserter.js b/packages/block-editor/src/components/inserter/quick-inserter.js index 795e0e4fefa0c1..2ce611558621ce 100644 --- a/packages/block-editor/src/components/inserter/quick-inserter.js +++ b/packages/block-editor/src/components/inserter/quick-inserter.js @@ -80,7 +80,6 @@ function QuickInserterList( { onSelect={ onSelectBlockType } onHover={ onHover } label={ __( 'Blocks' ) } - limit={ SHOWN_BLOCK_TYPES } /> ) } diff --git a/packages/block-editor/src/components/inserter/search-items.js b/packages/block-editor/src/components/inserter/search-items.js index 543ddd273f9243..c442906026bbe7 100644 --- a/packages/block-editor/src/components/inserter/search-items.js +++ b/packages/block-editor/src/components/inserter/search-items.js @@ -1,14 +1,7 @@ /** * External dependencies */ -import { - deburr, - differenceWith, - find, - intersectionWith, - isEmpty, - words, -} from 'lodash'; +import { deburr, differenceWith, find, words } from 'lodash'; // Default search helpers const defaultGetName = ( item ) => item.name || ''; @@ -16,7 +9,6 @@ const defaultGetTitle = ( item ) => item.title; const defaultGetKeywords = ( item ) => item.keywords || []; const defaultGetCategory = ( item ) => item.category; const defaultGetCollection = () => null; -const defaultGetVariations = () => []; /** * Sanitizes the search input string. @@ -92,33 +84,8 @@ export const searchBlockItems = ( ) ), }; - return searchItems( items, searchInput, config ).map( ( item ) => { - if ( isEmpty( item.variations ) ) { - return item; - } - const matchedVariations = item.variations.filter( - ( { title, keywords = [] } ) => { - return ( - intersectionWith( - normalizedSearchTerms, - getNormalizedSearchTerms( title ).concat( keywords ), - ( termToMatch, labelTerm ) => - labelTerm.includes( termToMatch ) - ).length > 0 - ); - } - ); - // When no variations matched, fallback to all variations. - if ( isEmpty( matchedVariations ) ) { - return item; - } - - return { - ...item, - variations: matchedVariations, - }; - } ); + return searchItems( items, searchInput, config ); }; /** @@ -162,7 +129,6 @@ export function getItemSearchRank( item, searchTerm, config = {} ) { getKeywords = defaultGetKeywords, getCategory = defaultGetCategory, getCollection = defaultGetCollection, - getVariations = defaultGetVariations, } = config; const name = getName( item ); @@ -170,7 +136,6 @@ export function getItemSearchRank( item, searchTerm, config = {} ) { const keywords = getKeywords( item ); const category = getCategory( item ); const collection = getCollection( item ); - const variations = getVariations( item ); const normalizedSearchInput = normalizeSearchInput( searchTerm ); const normalizedTitle = normalizeSearchInput( title ); @@ -185,14 +150,9 @@ export function getItemSearchRank( item, searchTerm, config = {} ) { } else if ( normalizedTitle.startsWith( normalizedSearchInput ) ) { rank += 20; } else { - const terms = [ - name, - title, - ...keywords, - category, - collection, - ...variations, - ].join( ' ' ); + const terms = [ name, title, ...keywords, category, collection ].join( + ' ' + ); const normalizedSearchTerms = words( normalizedSearchInput ); const unmatchedTerms = removeMatchingTerms( normalizedSearchTerms, diff --git a/packages/block-editor/src/components/inserter/test/search-items.js b/packages/block-editor/src/components/inserter/test/search-items.js index 3a785eb35e78fa..96402392d36f4d 100644 --- a/packages/block-editor/src/components/inserter/test/search-items.js +++ b/packages/block-editor/src/components/inserter/test/search-items.js @@ -131,75 +131,4 @@ describe( 'searchBlockItems', () => { ) ).toEqual( [ youtubeItem ] ); } ); - - it( 'should match words using also variations and return all matched variations', () => { - const filteredItems = searchBlockItems( - items, - categories, - collections, - 'variation' - ); - - expect( filteredItems ).toHaveLength( 1 ); - expect( filteredItems[ 0 ].variations ).toHaveLength( 3 ); - } ); - - it( 'should match words using also variations and filter out unmatched variations', () => { - const filteredItems = searchBlockItems( - items, - categories, - collections, - 'variations two three' - ); - - expect( filteredItems ).toHaveLength( 1 ); - expect( filteredItems[ 0 ].variations ).toHaveLength( 2 ); - expect( filteredItems[ 0 ].variations[ 0 ].title ).toBe( - 'Variation Two' - ); - expect( filteredItems[ 0 ].variations[ 1 ].title ).toBe( - 'Variation Three' - ); - } ); - - it( 'should search in variation keywords if exist', () => { - const filteredItems = searchBlockItems( - items, - categories, - collections, - 'music' - ); - expect( filteredItems ).toHaveLength( 1 ); - const [ { title, variations } ] = filteredItems; - expect( title ).toBe( 'With Variations' ); - expect( variations[ 0 ].title ).toBe( 'Variation Three' ); - } ); - - it( 'should search in both blocks/variation keywords if exist', () => { - const filteredItems = searchBlockItems( - items, - categories, - collections, - 'random' - ); - expect( filteredItems ).toHaveLength( 2 ); - expect( filteredItems ).toEqual( - expect.arrayContaining( [ - expect.objectContaining( { title: 'Paragraph' } ), - expect.objectContaining( { - title: 'With Variations', - variations: [ - { - name: 'variation-three', - title: 'Variation Three', - keywords: expect.arrayContaining( [ - 'music', - 'random', - ] ), - }, - ], - } ), - ] ) - ); - } ); } ); diff --git a/packages/block-editor/src/components/inserter/test/utils.js b/packages/block-editor/src/components/inserter/test/utils.js deleted file mode 100644 index d09376f55f6b54..00000000000000 --- a/packages/block-editor/src/components/inserter/test/utils.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Internal dependencies - */ -import { - moreItem, - paragraphItem, - someOtherItem, - withVariationsItem, - withSingleVariationItem, - withDefaultVariationItem, -} from './fixtures'; -import { includeVariationsInInserterItems } from '../utils'; - -describe( 'inserter utils', () => { - describe( 'includeVariationsInInserterItems', () => { - it( 'should let a block type be replaced with the default variation', () => { - // The base block type is replaced with the default variation - expect( - includeVariationsInInserterItems( [ withDefaultVariationItem ] ) - ).toEqual( [ - expect.objectContaining( { - id: 'core/block-with-default-variation-special', - } ), - ] ); - // The base block type is supplemented by non-default variations - expect( - includeVariationsInInserterItems( [ withSingleVariationItem ] ) - ).toEqual( [ - expect.objectContaining( { - id: 'core/embed', - } ), - expect.objectContaining( { - id: 'core/embed-youtube', - } ), - ] ); - } ); - it( 'should return items if limit is equal to items length', () => { - const items = [ moreItem, paragraphItem, someOtherItem ]; - const res = includeVariationsInInserterItems( items, 3 ); - expect( res ).toEqual( items ); - } ); - it( 'should slice items if items are more than provided limit', () => { - const items = [ - moreItem, - paragraphItem, - someOtherItem, - withVariationsItem, - ]; - const res = includeVariationsInInserterItems( items, 2 ); - expect( res ).toEqual( [ moreItem, paragraphItem ] ); - } ); - it( 'should fill the items with variations, if limit is set and items are fewer than limit and variations exist', () => { - const items = [ moreItem, paragraphItem, withVariationsItem ]; - const res = includeVariationsInInserterItems( items, 5 ); - expect( res.length ).toEqual( 5 ); - expect( res ).toEqual( [ - ...items, - expect.objectContaining( { - id: 'core/block-with-variations-variation-one', - title: 'Variation One', - } ), - expect.objectContaining( { - id: 'core/block-with-variations-variation-two', - title: 'Variation Two', - } ), - ] ); - } ); - it( 'should return the items, if limit is set and items are fewer than limit and variations do NOT exist', () => { - const items = [ moreItem, paragraphItem, someOtherItem ]; - const res = includeVariationsInInserterItems( items, 4 ); - expect( res.length ).toEqual( 3 ); - expect( res ).toEqual( items ); - } ); - it( 'should return proper result if no limit provided and block variations do NOT exist', () => { - const items = [ moreItem, paragraphItem, someOtherItem ]; - const res = includeVariationsInInserterItems( items ); - expect( res ).toEqual( items ); - } ); - it( 'should return proper result if no limit provided and block variations exist', () => { - const items = [ - moreItem, - paragraphItem, - someOtherItem, - withSingleVariationItem, - ]; - const expected = [ - ...items, - { - ...withSingleVariationItem, - id: 'core/embed-youtube', - title: 'YouTube', - description: 'youtube description', - }, - ]; - const res = includeVariationsInInserterItems( items ); - expect( res ).toEqual( expected ); - } ); - } ); -} ); diff --git a/packages/block-editor/src/components/inserter/utils.js b/packages/block-editor/src/components/inserter/utils.js deleted file mode 100644 index 08f3ca03ae8f2a..00000000000000 --- a/packages/block-editor/src/components/inserter/utils.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Return a function to be used to tranform a block variation to an inserter item - * - * @param {Object} item Denormalized inserter item - * @return {Function} Function to transform a block variation to inserter item - */ -const getItemFromVariation = ( item ) => ( variation ) => ( { - ...item, - id: `${ item.id }-${ variation.name }`, - icon: variation.icon || item.icon, - title: variation.title || item.title, - description: variation.description || item.description, - // If `example` is explicitly undefined for the variation, the preview will not be shown. - example: variation.hasOwnProperty( 'example' ) - ? variation.example - : item.example, - initialAttributes: { - ...item.initialAttributes, - ...variation.attributes, - }, - innerBlocks: variation.innerBlocks, -} ); - -/** - * Normalizes an inserter block types list and includes variations as separate items. - * - * @param {Array} items Denormalized inserter items - * @param {number} [limit=Infinity] Limit returned results by a given threshold. - * @return {Array} Normalized inserter items. - */ -export function includeVariationsInInserterItems( items, limit = Infinity ) { - // Exclude any block type item that is to be replaced by a default - // variation. - const filteredItems = items.filter( - ( { variations = [] } ) => - ! variations.some( ( { isDefault } ) => isDefault ) - ); - - // Fill `variationsToAdd` until there are as many items in total as - // `limit`. - const variationsToAdd = []; - if ( filteredItems.length < limit ) { - // Show all available blocks with variations - for ( const item of items ) { - if ( filteredItems.length + variationsToAdd.length >= limit ) { - break; - } - - const { variations = [] } = item; - if ( variations.length ) { - const variationMapper = getItemFromVariation( item ); - variationsToAdd.push( ...variations.map( variationMapper ) ); - } - } - } - - return [ ...filteredItems, ...variationsToAdd ].slice( 0, limit ); -} diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 4fe021a9cc0966..85c4a17d94bd4e 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1365,6 +1365,30 @@ const canIncludeBlockTypeInInserter = ( state, blockType, rootClientId ) => { return canInsertBlockTypeUnmemoized( state, blockType.name, rootClientId ); }; +/** + * Return a function to be used to tranform a block variation to an inserter item + * + * @param {Object} item Denormalized inserter item + * @return {Function} Function to transform a block variation to inserter item + */ +const getItemFromVariation = ( item ) => ( variation ) => ( { + ...item, + id: `${ item.id }-${ variation.name }`, + icon: variation.icon || item.icon, + title: variation.title || item.title, + description: variation.description || item.description, + // If `example` is explicitly undefined for the variation, the preview will not be shown. + example: variation.hasOwnProperty( 'example' ) + ? variation.example + : item.example, + initialAttributes: { + ...item.initialAttributes, + ...variation.attributes, + }, + innerBlocks: variation.innerBlocks, + keywords: variation.keywords || item.keywords, +} ); + /** * Determines the items that appear in the inserter. Includes both static * items (e.g. a regular block type) and dynamic items (e.g. a reusable block). @@ -1392,7 +1416,7 @@ const canIncludeBlockTypeInInserter = ( state, blockType, rootClientId ) => { * @property {string[]} keywords Keywords that can be searched to find this item. * @property {boolean} isDisabled Whether or not the user should be prevented from inserting * this item. - * @property {number} frecency Hueristic that combines frequency and recency. + * @property {number} frecency Heuristic that combines frequency and recency. */ export const getInserterItems = createSelector( ( state, rootClientId = null ) => { @@ -1502,7 +1526,28 @@ export const getInserterItems = createSelector( ? getReusableBlocks( state ).map( buildReusableBlockInserterItem ) : []; - return [ ...blockTypeInserterItems, ...reusableBlockInserterItems ]; + // Exclude any block type item that is to be replaced by a default + // variation. + const visibleBlockTypeInserterItems = blockTypeInserterItems.filter( + ( { variations = [] } ) => + ! variations.some( ( { isDefault } ) => isDefault ) + ); + + const blockVariations = []; + // Show all available blocks with variations + for ( const item of blockTypeInserterItems ) { + const { variations = [] } = item; + if ( variations.length ) { + const variationMapper = getItemFromVariation( item ); + blockVariations.push( ...variations.map( variationMapper ) ); + } + } + + return [ + ...visibleBlockTypeInserterItems, + ...blockVariations, + ...reusableBlockInserterItems, + ]; }, ( state, rootClientId ) => [ state.blockListSettings[ rootClientId ],