From da69c158522ac397b09aa16d1b6c0f8518626cf7 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Fri, 31 Jul 2026 13:10:48 +0530 Subject: [PATCH 1/6] Add placeholder instead of tabs label --- packages/block-library/CHANGELOG.md | 1 + packages/block-library/src/tab-list/edit.js | 3 +- .../block-library/src/tab-panel/README.md | 1 + .../block-library/src/tab-panel/block.json | 3 + packages/block-library/src/tab-panels/edit.js | 6 +- .../block-library/src/tabs/use-tab-actions.js | 5 +- test/e2e/specs/editor/blocks/tabs.spec.js | 90 +++++++++++++++++++ 7 files changed, 101 insertions(+), 8 deletions(-) diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index 18f0a8183c15ea..c8f8d9101e67a9 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -8,6 +8,7 @@ - Playlist: Shorten the track toolbar button label from "Add track" to "Add". - Gallery: Rename the dynamic variation's "Convert to images" action to "Detach", and confirm it in a dialog explaining that the gallery will keep its current images but stop updating automatically ([#80727](https://github.com/WordPress/gutenberg/pull/80727)). - Page List: Rename the "Edit" action to "Detach", and confirm it in a dialog explaining that the list will keep its current pages but stop adding new ones automatically, matching the Gallery block ([#80847](https://github.com/WordPress/gutenberg/pull/80847)). +- Tabs: Start new tabs with an empty label showing a "Tab title" placeholder instead of a generic "Tab", and add a `placeholder` attribute to Tab Panel so patterns and templates can customize the prompt ([#](https://github.com/WordPress/gutenberg/pull/)). ### Bug Fixes diff --git a/packages/block-library/src/tab-list/edit.js b/packages/block-library/src/tab-list/edit.js index 7557e85d04798c..ceaa92f0633729 100644 --- a/packages/block-library/src/tab-list/edit.js +++ b/packages/block-library/src/tab-list/edit.js @@ -83,6 +83,7 @@ function Edit( { () => tabPanels.map( ( tab ) => ( { label: tab.attributes.label || '', + placeholder: tab.attributes.placeholder || __( 'Tab title' ), clientId: tab.clientId, } ) ), [ tabPanels ] @@ -224,7 +225,7 @@ function Edit( { handleLabelChange( index, newLabel ) diff --git a/packages/block-library/src/tab-panel/README.md b/packages/block-library/src/tab-panel/README.md index 7b47e5de1bc693..672fbd92b6f745 100644 --- a/packages/block-library/src/tab-panel/README.md +++ b/packages/block-library/src/tab-panel/README.md @@ -20,6 +20,7 @@ _Defined via the [`attributes`](https://developer.wordpress.org/block-editor/ref | Attribute | [Type](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#type-validation) | [Default](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#default-value) | Description | |-----------|------|---------|-------------| | `label` | `string` | `""` | — | +| `placeholder` | `string` | — | — | ## Supports diff --git a/packages/block-library/src/tab-panel/block.json b/packages/block-library/src/tab-panel/block.json index 71eb4fa3428ca3..4753a8a51437b5 100644 --- a/packages/block-library/src/tab-panel/block.json +++ b/packages/block-library/src/tab-panel/block.json @@ -10,6 +10,9 @@ "label": { "type": "string", "default": "" + }, + "placeholder": { + "type": "string" } }, "parent": [ "core/tab-panels" ], diff --git a/packages/block-library/src/tab-panels/edit.js b/packages/block-library/src/tab-panels/edit.js index 13d3acd511446d..b9c8af09f94620 100644 --- a/packages/block-library/src/tab-panels/edit.js +++ b/packages/block-library/src/tab-panels/edit.js @@ -7,7 +7,6 @@ import { store as blockEditorStore, } from '@wordpress/block-editor'; import { useSelect } from '@wordpress/data'; -import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -19,10 +18,7 @@ import TabToolbarControls from '../tabs/tab-toolbar-controls'; * inner blocks are empty). templateLock is false, so this is never applied to * existing blocks that already have tab panels saved. */ -const TAB_PANELS_TEMPLATE = [ - [ 'core/tab-panel', { label: __( 'Tab' ) } ], - [ 'core/tab-panel', { label: __( 'Tab' ) } ], -]; +const TAB_PANELS_TEMPLATE = [ [ 'core/tab-panel' ], [ 'core/tab-panel' ] ]; export default function Edit( { clientId } ) { const blockProps = useBlockProps(); diff --git a/packages/block-library/src/tabs/use-tab-actions.js b/packages/block-library/src/tabs/use-tab-actions.js index 48042bbc435425..3800115f5eb300 100644 --- a/packages/block-library/src/tabs/use-tab-actions.js +++ b/packages/block-library/src/tabs/use-tab-actions.js @@ -1,7 +1,6 @@ /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; import { createBlock } from '@wordpress/blocks'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { useDispatch, useRegistry } from '@wordpress/data'; @@ -58,8 +57,10 @@ export default function useTabActions( tabsClientId ) { } const newIndex = atIndex ?? tabPanelBlocks.length; + + // The label is left empty so the new tab prompts for a title. insertBlock( - createBlock( 'core/tab-panel', { label: __( 'Tab' ) } ), + createBlock( 'core/tab-panel' ), newIndex, tabPanelsClientId, false diff --git a/test/e2e/specs/editor/blocks/tabs.spec.js b/test/e2e/specs/editor/blocks/tabs.spec.js index 48ee010574a013..b1dc06227faf1f 100644 --- a/test/e2e/specs/editor/blocks/tabs.spec.js +++ b/test/e2e/specs/editor/blocks/tabs.spec.js @@ -279,6 +279,96 @@ test.describe( 'Tabs', () => { } ); } ); + test.describe( 'Tab labels', () => { + const placeholders = ( editor, text ) => + editor.canvas.locator( + `[role="tab"] [data-rich-text-placeholder="${ text }"]` + ); + + const panelAttribute = async ( editor, attribute ) => + ( await editor.getBlocks() )[ 0 ].innerBlocks[ 1 ].innerBlocks.map( + ( panel ) => panel.attributes[ attribute ] + ); + + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'start empty and prompt for a title', async ( { editor } ) => { + await editor.insertBlock( { name: 'core/tabs' } ); + + await expect( editor.canvas.getByRole( 'tab' ) ).toHaveCount( 2 ); + await expect + .poll( () => panelAttribute( editor, 'label' ) ) + .toEqual( [ '', '' ] ); + await expect( placeholders( editor, 'Tab title' ) ).toHaveCount( + 2 + ); + } ); + + test( "use the tab panel's placeholder attribute when set", async ( { + editor, + } ) => { + await editor.insertBlock( { + name: 'core/tabs', + innerBlocks: [ + { name: 'core/tab-list' }, + { + name: 'core/tab-panels', + innerBlocks: [ + { + name: 'core/tab-panel', + attributes: { placeholder: 'Section name' }, + }, + ], + }, + ], + } ); + + await expect( placeholders( editor, 'Section name' ) ).toHaveCount( + 1 + ); + await expect( placeholders( editor, 'Tab title' ) ).toHaveCount( + 0 + ); + } ); + + test( 'stay empty on tabs added from the toolbar', async ( { + editor, + } ) => { + await editor.insertBlock( { + name: 'core/tabs', + innerBlocks: [ + { name: 'core/tab-list' }, + { + name: 'core/tab-panels', + innerBlocks: [ + { + name: 'core/tab-panel', + attributes: { label: 'Overview' }, + }, + ], + }, + ], + } ); + + await editor.canvas + .getByRole( 'tab', { name: 'Overview' } ) + .click(); + await editor.clickBlockToolbarButton( 'Add tab' ); + + await expect( editor.canvas.getByRole( 'tab' ) ).toHaveCount( 2 ); + await expect + .poll( () => panelAttribute( editor, 'label' ) ) + .toEqual( [ 'Overview', '' ] ); + + // Only the new, untitled tab prompts for a title. + await expect( placeholders( editor, 'Tab title' ) ).toHaveCount( + 1 + ); + } ); + } ); + // TODO: Add a `Frontend functionality` describe block for front-end // interaction tests (e.g. switching tabs on the published post). } ); From 96b3bb39c1ba7f2712821c5635286dc7dbecf3cc Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Fri, 31 Jul 2026 14:03:24 +0530 Subject: [PATCH 2/6] Update block docs and add changelog entry --- docs/reference-guides/core-blocks/README.md | 2 +- packages/block-library/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference-guides/core-blocks/README.md b/docs/reference-guides/core-blocks/README.md index f48357d4137395..0859ddd3442ecb 100644 --- a/docs/reference-guides/core-blocks/README.md +++ b/docs/reference-guides/core-blocks/README.md @@ -1014,7 +1014,7 @@ Content for a tab in a tabbed interface. - **Category:** [design](https://developer.wordpress.org/block-editor/reference-guides/core-blocks/core-blocks-design/) - **Parent:** core/tab-panels - **Supports:** anchor, color (background, text), layout, spacing (blockGap, padding), typography (fontSize), ~~html~~, ~~visibility~~ -- **Attributes:** label +- **Attributes:** label, placeholder ## Tab Panels diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index c8f8d9101e67a9..6a1277bec008a0 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -8,7 +8,7 @@ - Playlist: Shorten the track toolbar button label from "Add track" to "Add". - Gallery: Rename the dynamic variation's "Convert to images" action to "Detach", and confirm it in a dialog explaining that the gallery will keep its current images but stop updating automatically ([#80727](https://github.com/WordPress/gutenberg/pull/80727)). - Page List: Rename the "Edit" action to "Detach", and confirm it in a dialog explaining that the list will keep its current pages but stop adding new ones automatically, matching the Gallery block ([#80847](https://github.com/WordPress/gutenberg/pull/80847)). -- Tabs: Start new tabs with an empty label showing a "Tab title" placeholder instead of a generic "Tab", and add a `placeholder` attribute to Tab Panel so patterns and templates can customize the prompt ([#](https://github.com/WordPress/gutenberg/pull/)). +- Tabs: Start new tabs with an empty label showing a "Tab title" placeholder instead of a generic "Tab", and add a `placeholder` attribute to Tab Panel so patterns and templates can customize the prompt ([#81009](https://github.com/WordPress/gutenberg/pull/81009)). ### Bug Fixes From 6cdc4cea06253e5c91ef684b61f13d70f83b6972 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Mon, 3 Aug 2026 10:15:28 +0530 Subject: [PATCH 3/6] Updated tests and asserts placeholder check to existing tests --- test/e2e/specs/editor/blocks/tabs.spec.js | 102 +++------------------- 1 file changed, 11 insertions(+), 91 deletions(-) diff --git a/test/e2e/specs/editor/blocks/tabs.spec.js b/test/e2e/specs/editor/blocks/tabs.spec.js index b1dc06227faf1f..9741adb3b601f1 100644 --- a/test/e2e/specs/editor/blocks/tabs.spec.js +++ b/test/e2e/specs/editor/blocks/tabs.spec.js @@ -26,7 +26,10 @@ test.describe( 'Tabs', () => { }, { name: 'core/tab-panel', - attributes: { label: 'Tab 2' }, + attributes: { + label: 'Tab 2', + placeholder: 'Section name', + }, innerBlocks: [ { name: 'core/paragraph', @@ -162,6 +165,9 @@ test.describe( 'Tabs', () => { await expect( newTab.locator( '[contenteditable="true"]' ) ).toBeFocused(); + await expect( + newTab.locator( '[data-rich-text-placeholder]' ) + ).toHaveAttribute( 'data-rich-text-placeholder', 'Tab title' ); // The new tab's panel is the active one and is visible. // Use `exact: true` to avoid matching the parent 'Block: Tab Panels' name. @@ -192,6 +198,10 @@ test.describe( 'Tabs', () => { // Empty the tab label, then delete again to remove the tab. await pageUtils.pressKeys( 'primary+a' ); await page.keyboard.press( 'Delete' ); + await expect( + tab2.locator( '[data-rich-text-placeholder]' ) + ).toHaveAttribute( 'data-rich-text-placeholder', 'Section name' ); + await page.keyboard.press( 'Backspace' ); const tabs = editor.canvas.getByRole( 'tab' ); @@ -279,96 +289,6 @@ test.describe( 'Tabs', () => { } ); } ); - test.describe( 'Tab labels', () => { - const placeholders = ( editor, text ) => - editor.canvas.locator( - `[role="tab"] [data-rich-text-placeholder="${ text }"]` - ); - - const panelAttribute = async ( editor, attribute ) => - ( await editor.getBlocks() )[ 0 ].innerBlocks[ 1 ].innerBlocks.map( - ( panel ) => panel.attributes[ attribute ] - ); - - test.beforeEach( async ( { admin } ) => { - await admin.createNewPost(); - } ); - - test( 'start empty and prompt for a title', async ( { editor } ) => { - await editor.insertBlock( { name: 'core/tabs' } ); - - await expect( editor.canvas.getByRole( 'tab' ) ).toHaveCount( 2 ); - await expect - .poll( () => panelAttribute( editor, 'label' ) ) - .toEqual( [ '', '' ] ); - await expect( placeholders( editor, 'Tab title' ) ).toHaveCount( - 2 - ); - } ); - - test( "use the tab panel's placeholder attribute when set", async ( { - editor, - } ) => { - await editor.insertBlock( { - name: 'core/tabs', - innerBlocks: [ - { name: 'core/tab-list' }, - { - name: 'core/tab-panels', - innerBlocks: [ - { - name: 'core/tab-panel', - attributes: { placeholder: 'Section name' }, - }, - ], - }, - ], - } ); - - await expect( placeholders( editor, 'Section name' ) ).toHaveCount( - 1 - ); - await expect( placeholders( editor, 'Tab title' ) ).toHaveCount( - 0 - ); - } ); - - test( 'stay empty on tabs added from the toolbar', async ( { - editor, - } ) => { - await editor.insertBlock( { - name: 'core/tabs', - innerBlocks: [ - { name: 'core/tab-list' }, - { - name: 'core/tab-panels', - innerBlocks: [ - { - name: 'core/tab-panel', - attributes: { label: 'Overview' }, - }, - ], - }, - ], - } ); - - await editor.canvas - .getByRole( 'tab', { name: 'Overview' } ) - .click(); - await editor.clickBlockToolbarButton( 'Add tab' ); - - await expect( editor.canvas.getByRole( 'tab' ) ).toHaveCount( 2 ); - await expect - .poll( () => panelAttribute( editor, 'label' ) ) - .toEqual( [ 'Overview', '' ] ); - - // Only the new, untitled tab prompts for a title. - await expect( placeholders( editor, 'Tab title' ) ).toHaveCount( - 1 - ); - } ); - } ); - // TODO: Add a `Frontend functionality` describe block for front-end // interaction tests (e.g. switching tabs on the published post). } ); From b7d69227eda2cfeded0f962d10d8ebcddd986b11 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Tue, 4 Aug 2026 08:34:42 +0530 Subject: [PATCH 4/6] Remove placeholder attribute --- docs/reference-guides/core-blocks/README.md | 2 +- packages/block-library/CHANGELOG.md | 2 +- packages/block-library/src/tab-list/edit.js | 3 +-- packages/block-library/src/tab-panel/README.md | 1 - packages/block-library/src/tab-panel/block.json | 3 --- packages/block-library/src/tabs/use-tab-actions.js | 1 - test/e2e/specs/editor/blocks/tabs.spec.js | 8 +------- 7 files changed, 4 insertions(+), 16 deletions(-) diff --git a/docs/reference-guides/core-blocks/README.md b/docs/reference-guides/core-blocks/README.md index 0859ddd3442ecb..f48357d4137395 100644 --- a/docs/reference-guides/core-blocks/README.md +++ b/docs/reference-guides/core-blocks/README.md @@ -1014,7 +1014,7 @@ Content for a tab in a tabbed interface. - **Category:** [design](https://developer.wordpress.org/block-editor/reference-guides/core-blocks/core-blocks-design/) - **Parent:** core/tab-panels - **Supports:** anchor, color (background, text), layout, spacing (blockGap, padding), typography (fontSize), ~~html~~, ~~visibility~~ -- **Attributes:** label, placeholder +- **Attributes:** label ## Tab Panels diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index 30b2a4fa49408c..f4c451cb9aa805 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -8,7 +8,7 @@ - Playlist: Expose the parent "Add track" toolbar control to selected Playlist Track child blocks via block toolbar sharing ([#80368](https://github.com/WordPress/gutenberg/pull/80368)). - Gallery: Rename the dynamic variation's "Convert to images" action to "Detach", and confirm it in a dialog explaining that the gallery will keep its current images but stop updating automatically ([#80727](https://github.com/WordPress/gutenberg/pull/80727)). - Page List: Rename the "Edit" action to "Detach", and confirm it in a dialog explaining that the list will keep its current pages but stop adding new ones automatically, matching the Gallery block ([#80847](https://github.com/WordPress/gutenberg/pull/80847)). -- Tabs: Start new tabs with an empty label showing a "Tab title" placeholder instead of a generic "Tab", and add a `placeholder` attribute to Tab Panel so patterns and templates can customize the prompt ([#81009](https://github.com/WordPress/gutenberg/pull/81009)). +- Tabs: Start new tabs with an empty label showing a "Tab title" placeholder instead of a generic "Tab" ([#81009](https://github.com/WordPress/gutenberg/pull/81009)). ### Bug Fixes diff --git a/packages/block-library/src/tab-list/edit.js b/packages/block-library/src/tab-list/edit.js index ceaa92f0633729..7557e85d04798c 100644 --- a/packages/block-library/src/tab-list/edit.js +++ b/packages/block-library/src/tab-list/edit.js @@ -83,7 +83,6 @@ function Edit( { () => tabPanels.map( ( tab ) => ( { label: tab.attributes.label || '', - placeholder: tab.attributes.placeholder || __( 'Tab title' ), clientId: tab.clientId, } ) ), [ tabPanels ] @@ -225,7 +224,7 @@ function Edit( { handleLabelChange( index, newLabel ) diff --git a/packages/block-library/src/tab-panel/README.md b/packages/block-library/src/tab-panel/README.md index 672fbd92b6f745..7b47e5de1bc693 100644 --- a/packages/block-library/src/tab-panel/README.md +++ b/packages/block-library/src/tab-panel/README.md @@ -20,7 +20,6 @@ _Defined via the [`attributes`](https://developer.wordpress.org/block-editor/ref | Attribute | [Type](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#type-validation) | [Default](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#default-value) | Description | |-----------|------|---------|-------------| | `label` | `string` | `""` | — | -| `placeholder` | `string` | — | — | ## Supports diff --git a/packages/block-library/src/tab-panel/block.json b/packages/block-library/src/tab-panel/block.json index 4753a8a51437b5..71eb4fa3428ca3 100644 --- a/packages/block-library/src/tab-panel/block.json +++ b/packages/block-library/src/tab-panel/block.json @@ -10,9 +10,6 @@ "label": { "type": "string", "default": "" - }, - "placeholder": { - "type": "string" } }, "parent": [ "core/tab-panels" ], diff --git a/packages/block-library/src/tabs/use-tab-actions.js b/packages/block-library/src/tabs/use-tab-actions.js index 3800115f5eb300..906cbeff812b7c 100644 --- a/packages/block-library/src/tabs/use-tab-actions.js +++ b/packages/block-library/src/tabs/use-tab-actions.js @@ -58,7 +58,6 @@ export default function useTabActions( tabsClientId ) { const newIndex = atIndex ?? tabPanelBlocks.length; - // The label is left empty so the new tab prompts for a title. insertBlock( createBlock( 'core/tab-panel' ), newIndex, diff --git a/test/e2e/specs/editor/blocks/tabs.spec.js b/test/e2e/specs/editor/blocks/tabs.spec.js index 9741adb3b601f1..929ac32da20922 100644 --- a/test/e2e/specs/editor/blocks/tabs.spec.js +++ b/test/e2e/specs/editor/blocks/tabs.spec.js @@ -26,10 +26,7 @@ test.describe( 'Tabs', () => { }, { name: 'core/tab-panel', - attributes: { - label: 'Tab 2', - placeholder: 'Section name', - }, + attributes: { label: 'Tab 2' }, innerBlocks: [ { name: 'core/paragraph', @@ -198,9 +195,6 @@ test.describe( 'Tabs', () => { // Empty the tab label, then delete again to remove the tab. await pageUtils.pressKeys( 'primary+a' ); await page.keyboard.press( 'Delete' ); - await expect( - tab2.locator( '[data-rich-text-placeholder]' ) - ).toHaveAttribute( 'data-rich-text-placeholder', 'Section name' ); await page.keyboard.press( 'Backspace' ); From 9a419348f8dc40c2629e7e54c85a69de0aad8fd1 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Tue, 4 Aug 2026 08:35:57 +0530 Subject: [PATCH 5/6] Remove extra spacing --- test/e2e/specs/editor/blocks/tabs.spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/e2e/specs/editor/blocks/tabs.spec.js b/test/e2e/specs/editor/blocks/tabs.spec.js index 929ac32da20922..c75ebffa7d399b 100644 --- a/test/e2e/specs/editor/blocks/tabs.spec.js +++ b/test/e2e/specs/editor/blocks/tabs.spec.js @@ -195,7 +195,6 @@ test.describe( 'Tabs', () => { // Empty the tab label, then delete again to remove the tab. await pageUtils.pressKeys( 'primary+a' ); await page.keyboard.press( 'Delete' ); - await page.keyboard.press( 'Backspace' ); const tabs = editor.canvas.getByRole( 'tab' ); From ba6614c2bf57d27111e67a334fb74c3991d8d5b2 Mon Sep 17 00:00:00 2001 From: hbhalodia Date: Tue, 4 Aug 2026 10:56:26 +0530 Subject: [PATCH 6/6] Remove extra line --- packages/block-library/src/tabs/use-tab-actions.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block-library/src/tabs/use-tab-actions.js b/packages/block-library/src/tabs/use-tab-actions.js index 906cbeff812b7c..40c273aaee110c 100644 --- a/packages/block-library/src/tabs/use-tab-actions.js +++ b/packages/block-library/src/tabs/use-tab-actions.js @@ -57,7 +57,6 @@ export default function useTabActions( tabsClientId ) { } const newIndex = atIndex ?? tabPanelBlocks.length; - insertBlock( createBlock( 'core/tab-panel' ), newIndex,