Skip to content

Commit

Permalink
Add new API to allow inserter items to be prioritised (#50510)
Browse files Browse the repository at this point in the history
* Proposing a way to sort items in the block inspector based on allowed blocks

* Add inserterPriority API to inner blocks

* Sort inserter based on inserterPriority prop from block list settings

* Use new inserterPriority API in Nav block

* Correct comment

Co-authored-by: Andrei Draganescu <[email protected]>

* Remove redundant prop

* Avoid stale inserterPriority

* Make sorting function stable

* Renaming

* Remove redundant comment

* remove spacer

* Set prioritisedBlocks as empty array when no blockListSettings are found.

There are instances in the main inserter search results where the rootClientId is undefined, such as when searching for a block. This means there are no blockListSettings, which ends up in no `proritisedBlocks` property being returned and it crashses the app.

* proritise -> prioritize for consistency

* Update packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js

Co-authored-by: Alex Lende <[email protected]>

* Renaming constant to match updated name

* Add prioritizedKInnerBlocks to the inner-blocks README

* lint fix

* update comment

* update comment

* pass the correct props to useNestedSettingsUpdate

* Use stable ref

Co-authored-by: George Mamadashvili <[email protected]>

* Register the test Plugin for e2e tests

* Register block with prioritzedInserterBlocks set

* Add initial test scaffold

* Tidy up scaffolded test

* Add test for new API

It fails :(

* Try removing sort from helper

Why is this even here? It shouldn’t transfer results like this.

* Fix test

* Add test to check does not override allowedBlocks when conflicted

* Add additional assertion for retaining of correct number of results

* Ensure tests reflect target of Quick Inserter

* sort allowed blocks on the tests that consume getAllBlockInserterItemTitles

* Improve e2e test comment

* Update packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js

* Update packages/block-editor/src/components/inner-blocks/README.md

---------

Co-authored-by: Dave Smith <[email protected]>
Co-authored-by: Andrei Draganescu <[email protected]>
Co-authored-by: Jerry Jones <[email protected]>
Co-authored-by: Alex Lende <[email protected]>
Co-authored-by: George Mamadashvili <[email protected]>
Co-authored-by: MaggieCabrera <[email protected]>
  • Loading branch information
7 people authored May 15, 2023
1 parent 541b0dd commit adefb89
Show file tree
Hide file tree
Showing 16 changed files with 328 additions and 51 deletions.
5 changes: 5 additions & 0 deletions packages/block-editor/src/components/inner-blocks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,8 @@ For example, a button block, deeply nested in several levels of block `X` that u

- **Type:** `Function`
- **Default:** - `undefined`. The placeholder is an optional function that can be passed in to be a rendered component placed in front of the appender. This can be used to represent an example state prior to any blocks being placed. See the Social Links for an implementation example.

### `prioritizedInserterBlocks`

- **Type:** `Array`
- **Default:** - `undefined`. Determines which block types should be shown in the block inserter. For example, when inserting a block within the Navigation block we specify `core/navigation-link` and `core/navigation-link/page` as these are the most commonly used inner blocks. `prioritizedInserterBlocks` takes an array of the form {blockName}/{variationName}, where {variationName} is optional.
2 changes: 2 additions & 0 deletions packages/block-editor/src/components/inner-blocks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ function UncontrolledInnerBlocks( props ) {
const {
clientId,
allowedBlocks,
prioritizedInserterBlocks,
__experimentalDefaultBlock,
__experimentalDirectInsert,
template,
Expand All @@ -62,6 +63,7 @@ function UncontrolledInnerBlocks( props ) {
useNestedSettingsUpdate(
clientId,
allowedBlocks,
prioritizedInserterBlocks,
__experimentalDefaultBlock,
__experimentalDirectInsert,
templateLock,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,13 @@ function UncontrolledInnerBlocks( props ) {
const {
clientId,
allowedBlocks,
prioritizedInserterBlocks,
__experimentalDefaultBlock,
__experimentalDirectInsert,
template,
templateLock,
templateInsertUpdatesSelection,
__experimentalCaptureToolbars: captureToolbars,
orientation,
renderAppender,
renderFooterAppender,
Expand All @@ -95,7 +99,17 @@ function UncontrolledInnerBlocks( props ) {

const context = useBlockContext( clientId );

useNestedSettingsUpdate( clientId, allowedBlocks, templateLock );
useNestedSettingsUpdate(
clientId,
allowedBlocks,
prioritizedInserterBlocks,
__experimentalDefaultBlock,
__experimentalDirectInsert,
templateLock,
captureToolbars,
orientation,
layout
);

useInnerBlockTemplateSync(
clientId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const pendingSettingsUpdates = new WeakMap();
* @param {string} clientId The client ID of the block to update.
* @param {string[]} allowedBlocks An array of block names which are permitted
* in inner blocks.
* @param {string[]} prioritizedInserterBlocks Block names and/or block variations to be prioritized in the inserter, in the format {blockName}/{variationName}.
* @param {?WPDirectInsertBlock} __experimentalDefaultBlock The default block to insert: [ blockName, { blockAttributes } ].
* @param {?Function|boolean} __experimentalDirectInsert If a default block should be inserted directly by the
* appender.
Expand All @@ -40,6 +41,7 @@ const pendingSettingsUpdates = new WeakMap();
export default function useNestedSettingsUpdate(
clientId,
allowedBlocks,
prioritizedInserterBlocks,
__experimentalDefaultBlock,
__experimentalDirectInsert,
templateLock,
Expand All @@ -64,13 +66,27 @@ export default function useNestedSettingsUpdate(
[ clientId ]
);

// Memoize as inner blocks implementors often pass a new array on every
// render.
const _allowedBlocks = useMemo( () => allowedBlocks, allowedBlocks );
// Memoize allowedBlocks and prioritisedInnerBlocks based on the contents
// of the arrays. Implementors often pass a new array on every render,
// and the contents of the arrays are just strings, so the entire array
// can be passed as dependencies.

const _allowedBlocks = useMemo(
() => allowedBlocks,
// eslint-disable-next-line react-hooks/exhaustive-deps
allowedBlocks
);

const _prioritizedInserterBlocks = useMemo(
() => prioritizedInserterBlocks,
// eslint-disable-next-line react-hooks/exhaustive-deps
prioritizedInserterBlocks
);

useLayoutEffect( () => {
const newSettings = {
allowedBlocks: _allowedBlocks,
prioritizedInserterBlocks: _prioritizedInserterBlocks,
templateLock:
templateLock === undefined || parentLock === 'contentOnly'
? parentLock
Expand Down Expand Up @@ -130,6 +146,7 @@ export default function useNestedSettingsUpdate(
clientId,
blockListSettings,
_allowedBlocks,
_prioritizedInserterBlocks,
__experimentalDefaultBlock,
__experimentalDirectInsert,
templateLock,
Expand Down
10 changes: 1 addition & 9 deletions packages/block-editor/src/components/inserter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ class PrivateInserter extends Component {
prioritizePatterns,
onSelectOrClose,
selectBlockOnInsert,
orderInitialBlockItems,
} = this.props;

if ( isQuick ) {
Expand All @@ -174,7 +173,6 @@ class PrivateInserter extends Component {
isAppender={ isAppender }
prioritizePatterns={ prioritizePatterns }
selectBlockOnInsert={ selectBlockOnInsert }
orderInitialBlockItems={ orderInitialBlockItems }
/>
);
}
Expand Down Expand Up @@ -426,13 +424,7 @@ export const ComposedPrivateInserter = compose( [
] )( PrivateInserter );

const Inserter = forwardRef( ( props, ref ) => {
return (
<ComposedPrivateInserter
ref={ ref }
{ ...props }
orderInitialBlockItems={ undefined }
/>
);
return <ComposedPrivateInserter ref={ ref } { ...props } />;
} );

export default Inserter;
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export default function QuickInserter( {
isAppender,
prioritizePatterns,
selectBlockOnInsert,
orderInitialBlockItems,
} ) {
const [ filterValue, setFilterValue ] = useState( '' );
const [ destinationRootClientId, onInsertBlocks ] = useInsertionPoint( {
Expand Down Expand Up @@ -125,7 +124,6 @@ export default function QuickInserter( {
isDraggable={ false }
prioritizePatterns={ prioritizePatterns }
selectBlockOnInsert={ selectBlockOnInsert }
orderInitialBlockItems={ orderInitialBlockItems }
/>
</div>

Expand Down
55 changes: 49 additions & 6 deletions packages/block-editor/src/components/inserter/search-results.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { __, _n, sprintf } from '@wordpress/i18n';
import { VisuallyHidden } from '@wordpress/components';
import { useDebounce, useAsyncList } from '@wordpress/compose';
import { speak } from '@wordpress/a11y';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
Expand All @@ -21,6 +22,7 @@ import useBlockTypesState from './hooks/use-block-types-state';
import { searchBlockItems, searchItems } from './search-items';
import InserterListbox from '../inserter-listbox';
import { orderBy } from '../../utils/sorting';
import { store as blockEditorStore } from '../../store';

const INITIAL_INSERTER_RESULTS = 9;
/**
Expand All @@ -31,6 +33,24 @@ const INITIAL_INSERTER_RESULTS = 9;
*/
const EMPTY_ARRAY = [];

const orderInitialBlockItems = ( items, priority ) => {
if ( ! priority ) {
return items;
}

items.sort( ( { id: aName }, { id: bName } ) => {
// Sort block items according to `priority`.
let aIndex = priority.indexOf( aName );
let bIndex = priority.indexOf( bName );
// All other block items should come after that.
if ( aIndex < 0 ) aIndex = priority.length;
if ( bIndex < 0 ) bIndex = priority.length;
return aIndex - bIndex;
} );

return items;
};

function InserterSearchResults( {
filterValue,
onSelect,
Expand All @@ -46,10 +66,22 @@ function InserterSearchResults( {
shouldFocusBlock = true,
prioritizePatterns,
selectBlockOnInsert,
orderInitialBlockItems,
} ) {
const debouncedSpeak = useDebounce( speak, 500 );

const { prioritizedBlocks } = useSelect(
( select ) => {
const blockListSettings =
select( blockEditorStore ).getBlockListSettings( rootClientId );

return {
prioritizedBlocks:
blockListSettings?.prioritizedInserterBlocks || EMPTY_ARRAY,
};
},
[ rootClientId ]
);

const [ destinationRootClientId, onInsertBlocks ] = useInsertionPoint( {
onSelect,
rootClientId,
Expand Down Expand Up @@ -89,10 +121,16 @@ function InserterSearchResults( {
if ( maxBlockTypesToShow === 0 ) {
return [];
}

let orderedItems = orderBy( blockTypes, 'frecency', 'desc' );
if ( ! filterValue && orderInitialBlockItems ) {
orderedItems = orderInitialBlockItems( orderedItems );

if ( ! filterValue && prioritizedBlocks.length ) {
orderedItems = orderInitialBlockItems(
orderedItems,
prioritizedBlocks
);
}

const results = searchBlockItems(
orderedItems,
blockTypeCategories,
Expand All @@ -108,8 +146,8 @@ function InserterSearchResults( {
blockTypes,
blockTypeCategories,
blockTypeCollections,
maxBlockTypes,
orderInitialBlockItems,
maxBlockTypesToShow,
prioritizedBlocks,
] );

// Announce search results on change.
Expand All @@ -124,7 +162,12 @@ function InserterSearchResults( {
count
);
debouncedSpeak( resultsFoundMessage );
}, [ filterValue, debouncedSpeak ] );
}, [
filterValue,
debouncedSpeak,
filteredBlockTypes,
filteredBlockPatterns,
] );

const currentShownBlockTypes = useAsyncList( filteredBlockTypes, {
step: INITIAL_INSERTER_RESULTS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@
import { useInstanceId } from '@wordpress/compose';
import { speak } from '@wordpress/a11y';
import { useSelect } from '@wordpress/data';
import {
forwardRef,
useState,
useEffect,
useCallback,
} from '@wordpress/element';
import { forwardRef, useState, useEffect } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';

/**
Expand All @@ -19,11 +14,6 @@ import { store as blockEditorStore } from '../../store';
import useBlockDisplayTitle from '../block-title/use-block-display-title';
import { ComposedPrivateInserter as PrivateInserter } from '../inserter';

const prioritizedInserterBlocks = [
'core/navigation-link/page',
'core/navigation-link',
];

export const Appender = forwardRef(
( { nestingLevel, blockCount, clientId, ...props }, ref ) => {
const [ insertedBlock, setInsertedBlock ] = useState( null );
Expand Down Expand Up @@ -68,19 +58,6 @@ export const Appender = forwardRef(
);
}, [ insertedBlockTitle ] );

const orderInitialBlockItems = useCallback( ( items ) => {
items.sort( ( { id: aName }, { id: bName } ) => {
// Sort block items according to `prioritizedInserterBlocks`.
let aIndex = prioritizedInserterBlocks.indexOf( aName );
let bIndex = prioritizedInserterBlocks.indexOf( bName );
// All other block items should come after that.
if ( aIndex < 0 ) aIndex = prioritizedInserterBlocks.length;
if ( bIndex < 0 ) bIndex = prioritizedInserterBlocks.length;
return aIndex - bIndex;
} );
return items;
}, [] );

if ( hideInserter ) {
return null;
}
Expand Down Expand Up @@ -110,7 +87,6 @@ export const Appender = forwardRef(
setInsertedBlock( maybeInsertedBlock );
}
} }
orderInitialBlockItems={ orderInitialBlockItems }
/>
<div
className="offcanvas-editor-appender__description"
Expand Down
5 changes: 5 additions & 0 deletions packages/block-library/src/navigation/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ export const ALLOWED_BLOCKS = [
'core/navigation-submenu',
'core/loginout',
];

export const PRIORITIZED_INSERTER_BLOCKS = [
'core/navigation-link/page',
'core/navigation-link',
];
7 changes: 6 additions & 1 deletion packages/block-library/src/navigation/edit/inner-blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import { useMemo } from '@wordpress/element';
* Internal dependencies
*/
import PlaceholderPreview from './placeholder/placeholder-preview';
import { DEFAULT_BLOCK, ALLOWED_BLOCKS } from '../constants';
import {
DEFAULT_BLOCK,
ALLOWED_BLOCKS,
PRIORITIZED_INSERTER_BLOCKS,
} from '../constants';

export default function NavigationInnerBlocks( {
clientId,
Expand Down Expand Up @@ -93,6 +97,7 @@ export default function NavigationInnerBlocks( {
onInput,
onChange,
allowedBlocks: ALLOWED_BLOCKS,
prioritizedInserterBlocks: PRIORITIZED_INSERTER_BLOCKS,
__experimentalDefaultBlock: DEFAULT_BLOCK,
__experimentalDirectInsert: shouldDirectInsert,
orientation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ export async function getAllBlockInserterItemTitles() {
return inserterItem.innerText;
} );
} );
return [ ...new Set( inserterItemTitles ) ].sort();
return [ ...new Set( inserterItemTitles ) ];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
/**
* Plugin Name: Gutenberg Test InnerBlocks Prioritized Inserter Blocks
* Plugin URI: https://github.com/WordPress/gutenberg
* Author: Gutenberg Team
*
* @package gutenberg-test-inner-blocks-prioritized-inserter-blocks
*/

/**
* Registers a custom script for the plugin.
*/
function enqueue_inner_blocks_prioritized_inserter_blocks_script() {
wp_enqueue_script(
'gutenberg-test-inner-blocks-prioritized-inserter-blocks',
plugins_url( 'inner-blocks-prioritized-inserter-blocks/index.js', __FILE__ ),
array(
'wp-blocks',
'wp-block-editor',
'wp-element',
'wp-i18n',
),
filemtime( plugin_dir_path( __FILE__ ) . 'inner-blocks-prioritized-inserter-blocks/index.js' ),
true
);
}

add_action( 'init', 'enqueue_inner_blocks_prioritized_inserter_blocks_script' );
Loading

0 comments on commit adefb89

Please sign in to comment.