-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BlockSwitcher: Defer transform calculations until the Dropdown is open #57892
BlockSwitcher: Defer transform calculations until the Dropdown is open #57892
Conversation
Size Change: +146 B (0%) Total Size: 1.7 MB
ℹ️ View Unchanged
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love the improvement. Thank you, Nik.
I think another small improvement would be to stop passing around the blocks
value. This will prevent re-rendering BlockSwitcher
on every keystroke.
Yes, it would be great if you could move the blocks selector too :) |
I can't move it down because we need to check the validity of blocks to decide if we can even render the component.
I updated not to pass the blocks, but I don't believe this makes any difference since the BlockSwitcher component needs the blocks too and they will trigger a rerender on every keystroke. Do I miss something? |
@@ -57,10 +57,7 @@ test.describe( 'Toolbar roving tabindex', () => { | |||
// ensures list block toolbar uses roving tabindex | |||
await editor.insertBlock( { name: 'core/list' } ); | |||
await page.keyboard.type( 'List' ); | |||
await ToolbarRovingTabindexUtils.testBlockToolbarKeyboardNavigation( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes no sense to test list as a simple block since it has inner blocks for a while. The only reason this was passing is because List item
has no transformation currently. testGroupKeyboardNavigation
is handling tests for container blocks.
e4ff6af
to
aab7c63
Compare
This is what I had in mind. Example
diff --git packages/block-editor/src/components/block-switcher/index.js packages/block-editor/src/components/block-switcher/index.js
index fd341f09596..4a6644d7866 100644
--- packages/block-editor/src/components/block-switcher/index.js
+++ packages/block-editor/src/components/block-switcher/index.js
@@ -163,59 +163,63 @@ function BlockSwitcherDropdownMenuContents( {
export const BlockSwitcher = ( { clientIds } ) => {
const blockInformation = useBlockDisplayInformation( clientIds?.[ 0 ] );
- const { canRemove, hasBlockStyles, icon, blocks, invalidBlocks } =
- useSelect(
- ( select ) => {
- const {
- getBlockRootClientId,
- getBlocksByClientId,
- canRemoveBlocks,
- } = select( blockEditorStore );
- const { getBlockStyles, getBlockType } = select( blocksStore );
- const _blocks = getBlocksByClientId( clientIds );
- if (
- ! _blocks.length ||
- _blocks.some( ( block ) => ! block )
- ) {
- return { invalidBlocks: true };
- }
- const rootClientId = getBlockRootClientId( clientIds );
- const [ { name: firstBlockName } ] = _blocks;
- const _isSingleBlockSelected = _blocks.length === 1;
- let _icon;
- if ( _isSingleBlockSelected ) {
- _icon = blockInformation?.icon; // Take into account active block variations.
- } else {
- const isSelectionOfSameType =
- new Set( _blocks.map( ( { name } ) => name ) ).size ===
- 1;
- // When selection consists of blocks of multiple types, display an
- // appropriate icon to communicate the non-uniformity.
- _icon = isSelectionOfSameType
- ? getBlockType( firstBlockName )?.icon
- : copy;
- }
- return {
- canRemove: canRemoveBlocks( clientIds, rootClientId ),
- hasBlockStyles:
- _isSingleBlockSelected &&
- !! getBlockStyles( firstBlockName )?.length,
- icon: _icon,
- blocks: _blocks,
- };
- },
- [ clientIds, blockInformation?.icon ]
- );
+ const {
+ canRemove,
+ hasBlockStyles,
+ icon,
+ invalidBlocks,
+ isReusable,
+ isTemplate,
+ } = useSelect(
+ ( select ) => {
+ const {
+ getBlockRootClientId,
+ getBlocksByClientId,
+ canRemoveBlocks,
+ } = select( blockEditorStore );
+ const { getBlockStyles, getBlockType } = select( blocksStore );
+ const _blocks = getBlocksByClientId( clientIds );
+ if ( ! _blocks.length || _blocks.some( ( block ) => ! block ) ) {
+ return { invalidBlocks: true };
+ }
+ const rootClientId = getBlockRootClientId( clientIds );
+ const [ { name: firstBlockName } ] = _blocks;
+ const _isSingleBlockSelected = _blocks.length === 1;
+ let _icon;
+ if ( _isSingleBlockSelected ) {
+ _icon = blockInformation?.icon; // Take into account active block variations.
+ } else {
+ const isSelectionOfSameType =
+ new Set( _blocks.map( ( { name } ) => name ) ).size === 1;
+ // When selection consists of blocks of multiple types, display an
+ // appropriate icon to communicate the non-uniformity.
+ _icon = isSelectionOfSameType
+ ? getBlockType( firstBlockName )?.icon
+ : copy;
+ }
+ return {
+ canRemove: canRemoveBlocks( clientIds, rootClientId ),
+ hasBlockStyles:
+ _isSingleBlockSelected &&
+ !! getBlockStyles( firstBlockName )?.length,
+ icon: _icon,
+ isReusable:
+ _isSingleBlockSelected && isReusableBlock( _blocks[ 0 ] ),
+ isTemplate:
+ _isSingleBlockSelected && isTemplatePart( _blocks[ 0 ] ),
+ };
+ },
+ [ clientIds, blockInformation?.icon ]
+ );
const blockTitle = useBlockDisplayTitle( {
clientId: clientIds?.[ 0 ],
maximumLength: 35,
} );
+
if ( invalidBlocks ) {
return null;
}
- const isSingleBlock = blocks.length === 1;
- const isReusable = isSingleBlock && isReusableBlock( blocks[ 0 ] );
- const isTemplate = isSingleBlock && isTemplatePart( blocks[ 0 ] );
+
const hideDropdown = ! hasBlockStyles && ! canRemove;
if ( hideDropdown ) {
return (
@@ -238,6 +242,8 @@ export const BlockSwitcher = ( { clientIds } ) => {
</ToolbarGroup>
);
}
+
+ const isSingleBlock = clientIds.length === 1;
const blockSwitcherLabel = isSingleBlock
? blockTitle
: __( 'Multiple blocks selected' );
@@ -248,9 +254,9 @@ export const BlockSwitcher = ( { clientIds } ) => {
_n(
'Change type of %d block',
'Change type of %d blocks',
- blocks.length
+ clientIds.length
),
- blocks.length
+ clientIds.length
);
return (
<ToolbarGroup> |
Okay, I got it now. I updated per your suggestion. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, Nik.
The changes look great and test well for me. The feature also has a good e2e test coverage.
Please consider that as reported at #51445 this button should not get a HTML This button is also used to communicate the block type. In fac, its accessible name is the block type. By making the button not focusable, this information is not available to keyboard users and screen reader users. Additionally:
|
Sorry for the confusion, @afercia. I initially thought that the dropdown would always be displayed after this PR, which would have removed the need to disabling the button. Your concerns are valid but not necessarily a blocker for this PR. Since it mainly contains performance improvement. |
Btw, created a follow-up for |
What?
This PR refactors the BlockSwitcher component so as to defer the expensive transform calculations. This should have some small improvements at least in some metrics like
block select, type with top toolbar
etc..Why?
Currently when we render this component we calculate all the available transforms(either block or pattern) in order to decide whether to render a Dropdown menu or a simple disabled button. This is happening even if the user never clicks the button and also happens on every keystroke because we need the block information to determine the eligible transforms.
How?
With the refactor, there is a quite rare use case where a block might not have anything to show in the dropdown. In this case we show a message that there is no available transform and in core we can replicate I think only for
List item
block. I think that's an acceptable trade-off for the performance gain we have.Additionally we still render a disabled button when a single block is selected with no styles that cannot be removed.
Notes
I refactored the tests too as most of them didn't seem to add much value.
Testing Instructions
List
block and selecting aList item
block.Screenshots or screencast