From 813574291506cfd5a90a3cc031f09d3e2d7b2a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= Date: Mon, 8 Dec 2025 16:05:28 +0100 Subject: [PATCH 1/8] Adds h1-h6 as block variations for the heading block: - Heading level shown in sidebar inspector variation picker - Level-specific icons in the sidebar picker - Proper variation detection via isActive callbacks The variations use scope ['block', 'transform'] to appear in the sidebar picker and transforms, while keeping the inserter clean with just the base "Heading" block. --- .../block-variation-transforms/index.js | 34 ++++++++++++++----- .../block-variation-transforms/style.scss | 4 +-- .../block-library/src/heading/variations.js | 31 +++++++++++------ 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/packages/block-editor/src/components/block-variation-transforms/index.js b/packages/block-editor/src/components/block-variation-transforms/index.js index 327b94286bfcbf..43d2e5069288d3 100644 --- a/packages/block-editor/src/components/block-variation-transforms/index.js +++ b/packages/block-editor/src/components/block-variation-transforms/index.js @@ -12,6 +12,14 @@ import { } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; +import { + headingLevel1, + headingLevel2, + headingLevel3, + headingLevel4, + headingLevel5, + headingLevel6, +} from '@wordpress/icons'; /** * Internal dependencies @@ -229,17 +237,25 @@ function __experimentalBlockVariationTransforms( { blockClientId } ) { ( v ) => v.name !== 'stretchy-paragraph' ); } else if ( blockName === 'core/heading' ) { - if ( - activeBlockVariation?.name === 'stretchy-heading' || - unfilteredVariations.every( ( v ) => - [ 'heading', 'stretchy-heading' ].includes( v.name ) - ) - ) { + // Hide variations picker when stretchy-heading is active. + if ( activeBlockVariation?.name === 'stretchy-heading' ) { return []; } - return unfilteredVariations.filter( - ( v ) => v.name !== 'stretchy-heading' - ); + // Filter out stretchy-heading, showing only h1-h6 with level-specific icons. + const HEADING_LEVEL_ICONS = { + h1: headingLevel1, + h2: headingLevel2, + h3: headingLevel3, + h4: headingLevel4, + h5: headingLevel5, + h6: headingLevel6, + }; + return unfilteredVariations + .filter( ( v ) => v.name !== 'stretchy-heading' ) + .map( ( v ) => ( { + ...v, + icon: HEADING_LEVEL_ICONS[ v.name ] || v.icon, + } ) ); } return unfilteredVariations; }, [ activeBlockVariation?.name, blockName, unfilteredVariations ] ); diff --git a/packages/block-editor/src/components/block-variation-transforms/style.scss b/packages/block-editor/src/components/block-variation-transforms/style.scss index faddedc52858fa..e8ab1811e2f6ce 100644 --- a/packages/block-editor/src/components/block-variation-transforms/style.scss +++ b/packages/block-editor/src/components/block-variation-transforms/style.scss @@ -6,10 +6,10 @@ padding: 0 $grid-unit-20 $grid-unit-20 52px; &:where(fieldset) { - // Reset `fieldset` browser defaults. + // Reset `fieldset` browser defaults, keeping intentional padding. border: 0; - padding: 0; margin: 0; + min-inline-size: 0; } } diff --git a/packages/block-library/src/heading/variations.js b/packages/block-library/src/heading/variations.js index 158adaa41e6692..2915ce4138802b 100644 --- a/packages/block-library/src/heading/variations.js +++ b/packages/block-library/src/heading/variations.js @@ -1,22 +1,33 @@ /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { Path, SVG } from '@wordpress/primitives'; import { heading } from '@wordpress/icons'; +const defined = ( x ) => x !== undefined && x !== null; + +const HEADING_DESCRIPTION = __( + 'Introduce new sections and organize content to help visitors (and search engines) understand the structure of your content.' +); + const variations = [ - { - name: 'heading', - title: __( 'Heading' ), - description: __( - 'Introduce new sections and organize content to help visitors (and search engines) understand the structure of your content.' + ...[ 1, 2, 3, 4, 5, 6 ].map( ( level ) => ( { + name: `h${ level }`, + title: sprintf( + /* translators: %d: heading level e.g: "1", "2", "3" */ + __( 'Heading %d' ), + level ), - isDefault: true, - scope: [ 'inserter', 'transform' ], - attributes: { fitText: undefined }, + description: HEADING_DESCRIPTION, icon: heading, - }, + attributes: { level }, + scope: [ 'block', 'transform' ], + keywords: [ `h${ level }` ], + isActive: ( blockAttributes ) => + ! defined( blockAttributes.fitText ) && + blockAttributes.level === level, + } ) ), // There is a hardcoded workaround in packages/block-editor/src/store/selectors.js // to make Stretchy variations appear as the last of their sections in the inserter. { From 25d5818121f23bd1967eae557545d30c1bb3b622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= Date: Mon, 8 Dec 2025 16:08:08 +0100 Subject: [PATCH 2/8] Editor: Make block-scope variations searchable via slash commands Extends the inserter to include block-scope variations in search results, enabling slash commands like `/h3` to find variations that appear in the sidebar variation picker. Variations with only 'block' scope (not 'inserter') are marked as `isSearchOnly` and filtered from the inserter browse view, but remain searchable. --- .../components/inserter/block-types-tab.js | 5 +++++ packages/block-editor/src/store/selectors.js | 22 ++++++++++++++++++- .../block-library/src/heading/variations.js | 13 ++++------- 3 files changed, 30 insertions(+), 10 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 d37a6ca5694b09..c20eca95bb49f8 100644 --- a/packages/block-editor/src/components/inserter/block-types-tab.js +++ b/packages/block-editor/src/components/inserter/block-types-tab.js @@ -186,6 +186,11 @@ export function BlockTypesTab( continue; } + // Skip search-only items from browse view (they're still searchable). + if ( item.isSearchOnly ) { + continue; + } + if ( item.isAllowedInCurrentRoot ) { itemsForCurrentRoot.push( item ); } else { diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 733f3418f98d42..e89618b9c4921f 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2078,6 +2078,8 @@ const getItemFromVariation = ( state, item ) => ( variation ) => { innerBlocks: variation.innerBlocks, keywords: variation.keywords || item.keywords, frecency: calculateFrecency( time, count ), + // Pass through search-only flag for block-scope variations. + isSearchOnly: variation.isSearchOnly, }; }; @@ -2151,6 +2153,24 @@ const buildBlockTypeItem = blockType.name, 'inserter' ); + const blockVariations = getBlockVariations( blockType.name, 'block' ); + // Combine inserter and block variations. Block-scope variations without + // inserter scope are searchable via slash commands but hidden from browse. + const inserterVariationNames = new Set( + inserterVariations.map( ( variation ) => variation.name ) + ); + const allVariations = [ + ...inserterVariations, + ...blockVariations + .filter( + ( variation ) => + ! inserterVariationNames.has( variation.name ) + ) + .map( ( variation ) => ( { + ...variation, + isSearchOnly: true, + } ) ), + ]; return { ...blockItemBase, initialAttributes: {}, @@ -2159,7 +2179,7 @@ const buildBlockTypeItem = keywords: blockType.keywords, parent: blockType.parent, ancestor: blockType.ancestor, - variations: inserterVariations, + variations: allVariations, example: blockType.example, utility: 1, // Deprecated. }; diff --git a/packages/block-library/src/heading/variations.js b/packages/block-library/src/heading/variations.js index 2915ce4138802b..300dcc3d458c34 100644 --- a/packages/block-library/src/heading/variations.js +++ b/packages/block-library/src/heading/variations.js @@ -5,12 +5,6 @@ import { __, sprintf } from '@wordpress/i18n'; import { Path, SVG } from '@wordpress/primitives'; import { heading } from '@wordpress/icons'; -const defined = ( x ) => x !== undefined && x !== null; - -const HEADING_DESCRIPTION = __( - 'Introduce new sections and organize content to help visitors (and search engines) understand the structure of your content.' -); - const variations = [ ...[ 1, 2, 3, 4, 5, 6 ].map( ( level ) => ( { name: `h${ level }`, @@ -19,14 +13,15 @@ const variations = [ __( 'Heading %d' ), level ), - description: HEADING_DESCRIPTION, + description: __( + 'Introduce new sections and organize content to help visitors (and search engines) understand the structure of your content.' + ), icon: heading, attributes: { level }, scope: [ 'block', 'transform' ], keywords: [ `h${ level }` ], isActive: ( blockAttributes ) => - ! defined( blockAttributes.fitText ) && - blockAttributes.level === level, + ! blockAttributes.fitText && blockAttributes.level === level, } ) ), // There is a hardcoded workaround in packages/block-editor/src/store/selectors.js // to make Stretchy variations appear as the last of their sections in the inserter. From 1cd67562b60800a6d5a31034409e332265966b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= Date: Mon, 8 Dec 2025 17:11:17 +0100 Subject: [PATCH 3/8] Consolidate handling of levels through varations Remove level dropdown UI --- .../block-variation-transforms/index.js | 27 +++---------------- packages/block-library/src/heading/edit.js | 11 +------- .../block-library/src/heading/variations.js | 20 ++++++++++++-- 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/packages/block-editor/src/components/block-variation-transforms/index.js b/packages/block-editor/src/components/block-variation-transforms/index.js index 43d2e5069288d3..0519a9b1f98c26 100644 --- a/packages/block-editor/src/components/block-variation-transforms/index.js +++ b/packages/block-editor/src/components/block-variation-transforms/index.js @@ -12,14 +12,6 @@ import { } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; -import { - headingLevel1, - headingLevel2, - headingLevel3, - headingLevel4, - headingLevel5, - headingLevel6, -} from '@wordpress/icons'; /** * Internal dependencies @@ -241,21 +233,10 @@ function __experimentalBlockVariationTransforms( { blockClientId } ) { if ( activeBlockVariation?.name === 'stretchy-heading' ) { return []; } - // Filter out stretchy-heading, showing only h1-h6 with level-specific icons. - const HEADING_LEVEL_ICONS = { - h1: headingLevel1, - h2: headingLevel2, - h3: headingLevel3, - h4: headingLevel4, - h5: headingLevel5, - h6: headingLevel6, - }; - return unfilteredVariations - .filter( ( v ) => v.name !== 'stretchy-heading' ) - .map( ( v ) => ( { - ...v, - icon: HEADING_LEVEL_ICONS[ v.name ] || v.icon, - } ) ); + // Filter out stretchy-heading. + return unfilteredVariations.filter( + ( variation ) => variation.name !== 'stretchy-heading' + ); } return unfilteredVariations; }, [ activeBlockVariation?.name, blockName, unfilteredVariations ] ); diff --git a/packages/block-library/src/heading/edit.js b/packages/block-library/src/heading/edit.js index ea58a75d51cda5..636e200c425b92 100644 --- a/packages/block-library/src/heading/edit.js +++ b/packages/block-library/src/heading/edit.js @@ -15,7 +15,6 @@ import { RichText, useBlockProps, store as blockEditorStore, - HeadingLevelDropdown, useBlockEditingMode, } from '@wordpress/block-editor'; @@ -32,8 +31,7 @@ function HeadingEdit( { style, clientId, } ) { - const { textAlign, content, level, levelOptions, placeholder, anchor } = - attributes; + const { textAlign, content, level, placeholder, anchor } = attributes; const tagName = 'h' + level; const blockProps = useBlockProps( { className: clsx( { @@ -94,13 +92,6 @@ function HeadingEdit( { <> { blockEditingMode === 'default' && ( - - setAttributes( { level: newLevel } ) - } - /> { diff --git a/packages/block-library/src/heading/variations.js b/packages/block-library/src/heading/variations.js index 300dcc3d458c34..2a842f9cad2c62 100644 --- a/packages/block-library/src/heading/variations.js +++ b/packages/block-library/src/heading/variations.js @@ -3,7 +3,23 @@ */ import { __, sprintf } from '@wordpress/i18n'; import { Path, SVG } from '@wordpress/primitives'; -import { heading } from '@wordpress/icons'; +import { + headingLevel1, + headingLevel2, + headingLevel3, + headingLevel4, + headingLevel5, + headingLevel6, +} from '@wordpress/icons'; + +const LEVEL_ICONS = [ + headingLevel1, + headingLevel2, + headingLevel3, + headingLevel4, + headingLevel5, + headingLevel6, +]; const variations = [ ...[ 1, 2, 3, 4, 5, 6 ].map( ( level ) => ( { @@ -16,7 +32,7 @@ const variations = [ description: __( 'Introduce new sections and organize content to help visitors (and search engines) understand the structure of your content.' ), - icon: heading, + icon: LEVEL_ICONS[ level - 1 ], attributes: { level }, scope: [ 'block', 'transform' ], keywords: [ `h${ level }` ], From fb935724f921f63a20bdfbf0d94ce5b58e473b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= Date: Mon, 8 Dec 2025 18:38:09 +0100 Subject: [PATCH 4/8] Fix test --- test/e2e/specs/editor/blocks/heading.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/specs/editor/blocks/heading.spec.js b/test/e2e/specs/editor/blocks/heading.spec.js index b27c0fa52e4e6b..7ffbf6490f0809 100644 --- a/test/e2e/specs/editor/blocks/heading.spec.js +++ b/test/e2e/specs/editor/blocks/heading.spec.js @@ -365,8 +365,8 @@ test.describe( 'Heading', () => { await expect( headingListViewItem, - 'should show default block name if the content is empty' - ).toHaveText( 'Heading' ); + 'should show variation name if the content is empty' + ).toHaveText( 'Heading 2' ); await editor.canvas .getByRole( 'document', { From e6a60747ffee378f41bb5635f6ea70e3159de804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= Date: Mon, 8 Dec 2025 20:29:53 +0100 Subject: [PATCH 5/8] Support filtering of levelOptions for back compat --- packages/block-library/src/heading/index.js | 25 +++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/heading/index.js b/packages/block-library/src/heading/index.js index 3e5c7bf23972e1..7dde61dd7e068c 100644 --- a/packages/block-library/src/heading/index.js +++ b/packages/block-library/src/heading/index.js @@ -3,7 +3,11 @@ */ import { heading as icon } from '@wordpress/icons'; import { __, sprintf } from '@wordpress/i18n'; -import { privateApis as blocksPrivateApis } from '@wordpress/blocks'; +import { + privateApis as blocksPrivateApis, + getBlockType, + unregisterBlockVariation, +} from '@wordpress/blocks'; /** * Internal dependencies @@ -86,4 +90,21 @@ if ( window.__experimentalContentOnlyInspectorFields ) { }; } -export const init = () => initBlock( { name, metadata, settings } ); +export const init = () => { + const block = initBlock( { name, metadata, settings } ); + + // Unregister heading level variations based on `levelOptions` attribute. + // This is for backwards compatibility, as extenders can now unregister the + // variation directly: `wp.blocks.unregisterBlockVariation( 'core/heading', 'h1' )`. + const levelOptions = + getBlockType( name )?.attributes?.levelOptions?.default; + if ( levelOptions ) { + [ 1, 2, 3, 4, 5, 6 ].forEach( ( level ) => { + if ( ! levelOptions.includes( level ) ) { + unregisterBlockVariation( name, `h${ level }` ); + } + } ); + } + + return block; +}; From f079360cf25b7bf1a6822bd62dd7cbb9c7e5254e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= Date: Thu, 11 Dec 2025 15:00:17 +0100 Subject: [PATCH 6/8] Fix e2e tests --- packages/block-editor/src/store/selectors.js | 10 +++++++++- .../editor/various/autocomplete-and-mentions.spec.js | 4 +++- .../e2e/specs/editor/various/content-only-lock.spec.js | 2 +- .../various/keep-styles-on-block-transforms.spec.js | 2 +- test/e2e/specs/editor/various/list-view.spec.js | 6 +++--- test/e2e/specs/editor/various/rich-text.spec.js | 8 ++++++-- .../editor/various/toolbar-roving-tabindex.spec.js | 8 +++++--- test/e2e/specs/widgets/customizing-widgets.spec.js | 2 +- 8 files changed, 29 insertions(+), 13 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index e89618b9c4921f..223eeb748670bf 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2302,7 +2302,15 @@ export const getInserterItems = createRegistrySelector( ( select ) => ( accumulator, item ) => { const { variations = [] } = item; // Exclude any block type item that is to be replaced by a default variation. - if ( ! variations.some( ( { isDefault } ) => isDefault ) ) { + // Only consider non-search-only variations ("inserter" scope) for this check. + // "block" scope variations marked as `isSearchOnly` should not cause the base + // block to be excluded. + if ( + ! variations.some( + ( variation ) => + ! variation.isSearchOnly && variation.isDefault + ) + ) { accumulator.push( item ); } if ( variations.length ) { diff --git a/test/e2e/specs/editor/various/autocomplete-and-mentions.spec.js b/test/e2e/specs/editor/various/autocomplete-and-mentions.spec.js index 7143f3dfc96b7b..1557c92c160deb 100644 --- a/test/e2e/specs/editor/various/autocomplete-and-mentions.spec.js +++ b/test/e2e/specs/editor/various/autocomplete-and-mentions.spec.js @@ -569,9 +569,11 @@ test.describe( 'Autocomplete (@firefox, @webkit)', () => { page.locator( `role=option[name="Heading"i]` ) ).toBeVisible(); // Get the assertive live region screen reader announcement. + // Heading variations (h1-h6) are now searchable, so searching for "heading" + // returns: Heading, Heading 1-6, and Stretchy Heading = 8 results. await expect( page.getByText( - '3 results found, use up and down arrow keys to navigate.' + '8 results found, use up and down arrow keys to navigate.' ) ).toBeVisible(); } ); diff --git a/test/e2e/specs/editor/various/content-only-lock.spec.js b/test/e2e/specs/editor/various/content-only-lock.spec.js index 10852700ba5e6d..2138a4c0e402a0 100644 --- a/test/e2e/specs/editor/various/content-only-lock.spec.js +++ b/test/e2e/specs/editor/various/content-only-lock.spec.js @@ -120,7 +120,7 @@ test.describe( 'Content-only lock', () => { await editor.clickBlockOptionsMenuItem( 'Delete' ); // Select an outside block await editor.canvas - .locator( 'role=document[name="Block: Heading"i]' ) + .locator( 'role=document[name="Block: Heading 2"i]' ) .click(); // Select a locked nested paragraph block again await pageUtils.pressKeys( 'ArrowUp' ); diff --git a/test/e2e/specs/editor/various/keep-styles-on-block-transforms.spec.js b/test/e2e/specs/editor/various/keep-styles-on-block-transforms.spec.js index 074db6e1bac685..60b68dfdbd834b 100644 --- a/test/e2e/specs/editor/various/keep-styles-on-block-transforms.spec.js +++ b/test/e2e/specs/editor/various/keep-styles-on-block-transforms.spec.js @@ -20,7 +20,7 @@ test.describe( 'Keep styles on block transforms', () => { await page.click( 'role=button[name="Text"i]' ); await page.click( 'role=option[name="Luminous vivid orange"i]' ); - await page.click( 'role=button[name="Heading"i]' ); + await page.click( 'role=button[name="Heading 2"i]' ); await page.click( 'role=menuitem[name="Paragraph"i]' ); await expect.poll( editor.getBlocks ).toMatchObject( [ diff --git a/test/e2e/specs/editor/various/list-view.spec.js b/test/e2e/specs/editor/various/list-view.spec.js index 4f83a3713ddd36..c9335edb46f0de 100644 --- a/test/e2e/specs/editor/various/list-view.spec.js +++ b/test/e2e/specs/editor/various/list-view.spec.js @@ -58,7 +58,7 @@ test.describe( 'List View', () => { exact: true, } ); const headingBlockItem = listView.getByRole( 'gridcell', { - name: 'Heading', + name: 'Heading 2', exact: true, } ); @@ -1090,7 +1090,7 @@ test.describe( 'List View', () => { await page.keyboard.press( 'ArrowRight' ); // Move focus and select the Heading block. await listView - .getByRole( 'gridcell', { name: 'Heading', exact: true } ) + .getByRole( 'gridcell', { name: 'Heading 2', exact: true } ) .dblclick(); // Select both inner blocks in the column. await page.keyboard.press( 'Shift+ArrowDown' ); @@ -1188,7 +1188,7 @@ test.describe( 'List View', () => { } ); // Click on the Heading block to select it. await listView - .getByRole( 'gridcell', { name: 'Heading', exact: true } ) + .getByRole( 'gridcell', { name: 'Heading 2', exact: true } ) .click(); await listView .getByRole( 'gridcell', { name: 'File' } ) diff --git a/test/e2e/specs/editor/various/rich-text.spec.js b/test/e2e/specs/editor/various/rich-text.spec.js index b38fae9dafaf78..d9dc2d0475ab7d 100644 --- a/test/e2e/specs/editor/various/rich-text.spec.js +++ b/test/e2e/specs/editor/various/rich-text.spec.js @@ -19,8 +19,12 @@ test.describe( 'RichText (@firefox, @webkit)', () => { // // See: https://github.com/WordPress/gutenberg/issues/3091 await editor.insertBlock( { name: 'core/heading' } ); - await editor.clickBlockToolbarButton( 'Change level' ); - await page.locator( 'role=menuitemradio[name="Heading 3"]' ).click(); + + // Open the block inspector sidebar and use variations to change level. + await editor.openDocumentSettingsSidebar(); + await page + .getByRole( 'button', { name: 'Heading 3', exact: true } ) + .click(); expect( await editor.getBlocks() ).toMatchObject( [ { diff --git a/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js b/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js index 73680e841e7ea7..6274419443d2f0 100644 --- a/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js +++ b/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js @@ -46,12 +46,14 @@ test.describe( 'Toolbar roving tabindex', () => { await page.keyboard.type( 'Heading' ); await ToolbarRovingTabindexUtils.testBlockToolbarKeyboardNavigation( 'Block: Heading', - 'Heading' + 'Heading 2' + ); + await ToolbarRovingTabindexUtils.wrapCurrentBlockWithGroup( + 'Heading 2' ); - await ToolbarRovingTabindexUtils.wrapCurrentBlockWithGroup( 'Heading' ); await ToolbarRovingTabindexUtils.testGroupKeyboardNavigation( 'Block: Heading', - 'Heading' + 'Heading 2' ); // ensures list block toolbar uses roving tabindex diff --git a/test/e2e/specs/widgets/customizing-widgets.spec.js b/test/e2e/specs/widgets/customizing-widgets.spec.js index cc73ad73886b9c..ed0f4b4a95748b 100644 --- a/test/e2e/specs/widgets/customizing-widgets.spec.js +++ b/test/e2e/specs/widgets/customizing-widgets.spec.js @@ -286,7 +286,7 @@ test.describe( 'Widgets Customizer', () => { await editHeadingWidget.click(); const headingBlock = page.locator( - 'role=document[name="Block: Heading"i] >> text="First Heading"' + 'role=document[name="Block: Heading 2"i] >> text="First Heading"' ); await expect( headingBlock ).toBeFocused(); } ); From a6ab7af9b693acdb53f5a55fa17201d3bc2cf67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= Date: Thu, 11 Dec 2025 17:26:02 +0100 Subject: [PATCH 7/8] Simplify logic, don't carry over isDefault for search-only variations --- packages/block-editor/src/store/selectors.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 223eeb748670bf..8fe3b0ce101b2d 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2169,6 +2169,9 @@ const buildBlockTypeItem = .map( ( variation ) => ( { ...variation, isSearchOnly: true, + // Block-scope `isDefault` is for the placeholder picker, + // not for the inserter, so don't carry it over. + isDefault: false, } ) ), ]; return { @@ -2302,15 +2305,7 @@ export const getInserterItems = createRegistrySelector( ( select ) => ( accumulator, item ) => { const { variations = [] } = item; // Exclude any block type item that is to be replaced by a default variation. - // Only consider non-search-only variations ("inserter" scope) for this check. - // "block" scope variations marked as `isSearchOnly` should not cause the base - // block to be excluded. - if ( - ! variations.some( - ( variation ) => - ! variation.isSearchOnly && variation.isDefault - ) - ) { + if ( ! variations.some( ( { isDefault } ) => isDefault ) ) { accumulator.push( item ); } if ( variations.length ) { From 82b367368cd42e663fb3c92bd2ba01b279c72ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= Date: Thu, 11 Dec 2025 17:33:12 +0100 Subject: [PATCH 8/8] More test fixes --- test/e2e/specs/editor/plugins/block-variations.spec.js | 4 +++- .../editor/various/autocomplete-and-mentions.spec.js | 8 ++------ .../various/keep-styles-on-block-transforms.spec.js | 5 ++++- .../specs/editor/various/toolbar-roving-tabindex.spec.js | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/test/e2e/specs/editor/plugins/block-variations.spec.js b/test/e2e/specs/editor/plugins/block-variations.spec.js index ec7987f0c352d9..42ea508a6f9c8b 100644 --- a/test/e2e/specs/editor/plugins/block-variations.spec.js +++ b/test/e2e/specs/editor/plugins/block-variations.spec.js @@ -112,7 +112,9 @@ test.describe( 'Block variations', () => { .locator( 'role=button[name="Add default block"i]' ) .click(); await page.keyboard.type( '/Columns' ); - await page.getByRole( 'option', { name: 'Columns' } ).click(); + await page + .getByRole( 'option', { name: 'Columns', exact: true } ) + .click(); await editor.canvas .getByRole( 'list', { name: 'Block variations' } ) diff --git a/test/e2e/specs/editor/various/autocomplete-and-mentions.spec.js b/test/e2e/specs/editor/various/autocomplete-and-mentions.spec.js index 1557c92c160deb..fe5c644357a724 100644 --- a/test/e2e/specs/editor/various/autocomplete-and-mentions.spec.js +++ b/test/e2e/specs/editor/various/autocomplete-and-mentions.spec.js @@ -566,15 +566,11 @@ test.describe( 'Autocomplete (@firefox, @webkit)', () => { await page.keyboard.type( 'heading' ); await expect( - page.locator( `role=option[name="Heading"i]` ) + page.getByRole( 'option', { name: 'Heading', exact: true } ) ).toBeVisible(); // Get the assertive live region screen reader announcement. - // Heading variations (h1-h6) are now searchable, so searching for "heading" - // returns: Heading, Heading 1-6, and Stretchy Heading = 8 results. await expect( - page.getByText( - '8 results found, use up and down arrow keys to navigate.' - ) + page.getByText( 'use up and down arrow keys to navigate.' ) ).toBeVisible(); } ); } ); diff --git a/test/e2e/specs/editor/various/keep-styles-on-block-transforms.spec.js b/test/e2e/specs/editor/various/keep-styles-on-block-transforms.spec.js index 60b68dfdbd834b..76c131449d7235 100644 --- a/test/e2e/specs/editor/various/keep-styles-on-block-transforms.spec.js +++ b/test/e2e/specs/editor/various/keep-styles-on-block-transforms.spec.js @@ -20,7 +20,10 @@ test.describe( 'Keep styles on block transforms', () => { await page.click( 'role=button[name="Text"i]' ); await page.click( 'role=option[name="Luminous vivid orange"i]' ); - await page.click( 'role=button[name="Heading 2"i]' ); + await page + .getByRole( 'toolbar', { name: 'Block tools' } ) + .getByRole( 'button', { name: 'Heading 2' } ) + .click(); await page.click( 'role=menuitem[name="Paragraph"i]' ); await expect.poll( editor.getBlocks ).toMatchObject( [ diff --git a/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js b/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js index 6274419443d2f0..20ab3bcb024e6d 100644 --- a/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js +++ b/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js @@ -45,14 +45,14 @@ test.describe( 'Toolbar roving tabindex', () => { await editor.insertBlock( { name: 'core/heading' } ); await page.keyboard.type( 'Heading' ); await ToolbarRovingTabindexUtils.testBlockToolbarKeyboardNavigation( - 'Block: Heading', + 'Block: Heading 2', 'Heading 2' ); await ToolbarRovingTabindexUtils.wrapCurrentBlockWithGroup( 'Heading 2' ); await ToolbarRovingTabindexUtils.testGroupKeyboardNavigation( - 'Block: Heading', + 'Block: Heading 2', 'Heading 2' );