diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 7988bfe1d7bc53..b449cd539f91aa 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -895,8 +895,8 @@ Single tab within a tabs block. ([Source](https://github.com/WordPress/gutenberg
- **Experimental:** true
- **Category:** design
- **Parent:** core/tabs
-- **Supports:** ~~html~~, ~~inserter~~, ~~reusable~~
-- **Attributes:** title
+- **Supports:** anchor, ~~html~~, ~~reusable~~
+- **Attributes:** isActive, label
## Table
@@ -925,8 +925,7 @@ Organize content into tabs. ([Source](https://github.com/WordPress/gutenberg/tre
- **Experimental:** true
- **Category:** design
- **Allowed Blocks:** core/tab
-- **Supports:** align (full, wide), color (text, ~~background~~), interactivity, layout (default, ~~allowJustification~~, ~~allowSwitching~~), shadow, spacing (margin, padding), ~~html~~
-- **Attributes:** activeTab
+- **Supports:** align (full, wide), color (text, ~~background~~), interactivity, spacing (margin, padding), ~~html~~
## Tag Cloud
diff --git a/packages/block-library/src/editor.scss b/packages/block-library/src/editor.scss
index c43137c632b73f..3f88c3f45575bf 100644
--- a/packages/block-library/src/editor.scss
+++ b/packages/block-library/src/editor.scss
@@ -44,6 +44,7 @@
@import "./social-links/editor.scss";
@import "./spacer/editor.scss";
@import "./table/editor.scss";
+@import "./tabs/editor.scss";
@import "./template-part/editor.scss";
@import "./text-columns/editor.scss";
@import "./video/editor.scss";
diff --git a/packages/block-library/src/tab/block.json b/packages/block-library/src/tab/block.json
index fdabf177c2697b..ce258113e1b2c5 100644
--- a/packages/block-library/src/tab/block.json
+++ b/packages/block-library/src/tab/block.json
@@ -8,16 +8,19 @@
"textdomain": "default",
"__experimental": true,
"attributes": {
- "title": {
+ "isActive": {
+ "type": "boolean",
+ "default": false
+ },
+ "label": {
"type": "string"
}
},
"parent": [ "core/tabs" ],
"supports": {
+ "anchor": true,
"html": false,
- "inserter": false,
"reusable": false
},
- "usesContext": [ "tabs/activeTab" ],
"style": "wp-block-tab"
}
diff --git a/packages/block-library/src/tab/edit.js b/packages/block-library/src/tab/edit.js
index 27757e0dc2af4c..ac8ac7b5982e28 100644
--- a/packages/block-library/src/tab/edit.js
+++ b/packages/block-library/src/tab/edit.js
@@ -5,15 +5,25 @@ import {
InnerBlocks,
useBlockProps,
useInnerBlocksProps,
+ store as blockEditorStore,
} from '@wordpress/block-editor';
+import { useSelect } from '@wordpress/data';
+
+export default function Edit( { attributes, clientId } ) {
+ const hasChildBlocks = useSelect(
+ ( select ) =>
+ select( blockEditorStore ).getBlockOrder( clientId ).length > 0,
+ [ clientId ]
+ );
+ const { isActive } = attributes;
-export default function Edit( { clientId, context } ) {
- const isActive = context[ 'tabs/activeTab' ] === clientId;
const blockProps = useBlockProps( {
className: isActive ? 'is-active' : '',
} );
const innerBlocksProps = useInnerBlocksProps( blockProps, {
- renderAppender: InnerBlocks.ButtonBlockAppender,
+ renderAppender: hasChildBlocks
+ ? undefined
+ : InnerBlocks.ButtonBlockAppender,
} );
return
;
}
diff --git a/packages/block-library/src/tab/save.js b/packages/block-library/src/tab/save.js
index b78ba5bb38bddf..000acdcd4a6055 100644
--- a/packages/block-library/src/tab/save.js
+++ b/packages/block-library/src/tab/save.js
@@ -3,14 +3,10 @@
*/
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
-export default function save( { attributes } ) {
- const { title } = attributes;
-
+export default function save() {
return (
);
}
diff --git a/packages/block-library/src/tab/style.scss b/packages/block-library/src/tab/style.scss
index a2051c5861ef6f..e96d32fb7e6bd3 100644
--- a/packages/block-library/src/tab/style.scss
+++ b/packages/block-library/src/tab/style.scss
@@ -1,8 +1,13 @@
.wp-block-tab {
display: none;
+ padding: 1em 0;
&.is-active {
display: block;
}
+
+ > *:first-child {
+ margin-top: 0;
+ }
}
diff --git a/packages/block-library/src/tabs/block.json b/packages/block-library/src/tabs/block.json
index 49e2b2e551c933..a421044e6d0dba 100644
--- a/packages/block-library/src/tabs/block.json
+++ b/packages/block-library/src/tabs/block.json
@@ -8,15 +8,7 @@
"textdomain": "default",
"__experimental": true,
"allowedBlocks": [ "core/tab" ],
- "attributes": {
- "activeTab": {
- "type": "string",
- "default": ""
- }
- },
- "providesContext": {
- "tabs/activeTab": "activeTab"
- },
+ "attributes": {},
"supports": {
"align": [ "wide", "full" ],
"color": {
@@ -25,16 +17,11 @@
},
"html": false,
"interactivity": true,
- "layout": {
- "default": { "type": "flex", "orientation": "horizontal" },
- "allowSwitching": false,
- "allowJustification": false
- },
- "shadow": true,
"spacing": {
"margin": true,
"padding": true
}
},
+ "editorStyle": "wp-block-tabs-editor",
"style": "wp-block-tabs"
}
diff --git a/packages/block-library/src/tabs/edit.js b/packages/block-library/src/tabs/edit.js
index 2bc9d781be1954..ff81724f94bf45 100644
--- a/packages/block-library/src/tabs/edit.js
+++ b/packages/block-library/src/tabs/edit.js
@@ -7,18 +7,18 @@ import clsx from 'clsx';
* WordPress dependencies
*/
import {
- InnerBlocks,
useBlockProps,
useInnerBlocksProps,
store as blockEditorStore,
RichText,
} from '@wordpress/block-editor';
import { useDispatch, useSelect } from '@wordpress/data';
-import { useEffect } from '@wordpress/element';
+import { useCallback, useEffect } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
const TABS_TEMPLATE = [
- [ 'core/tab', { title: 'Tab 1' } ],
- [ 'core/tab', { title: 'Tab 2' } ],
+ [ 'core/tab', { label: 'Tab 1' } ],
+ [ 'core/tab', { label: 'Tab 2' } ],
];
const ALLOWED_FORMATS = [
@@ -34,49 +34,96 @@ const ALLOWED_FORMATS = [
'core/text-color',
];
-export default function Edit( { attributes, clientId, setAttributes } ) {
- const { activeTab } = attributes;
- const innerBlocks = useSelect(
- ( select ) => select( blockEditorStore ).getBlocks( clientId ),
- [ clientId ]
- );
+export default function Edit( { clientId } ) {
+ const { innerTabs, selectedTabClientId } = useSelect(
+ ( select ) => {
+ const {
+ getBlocks,
+ getSelectedBlockClientId,
+ hasSelectedInnerBlock,
+ } = select( blockEditorStore );
+ const innerBlocks = getBlocks( clientId );
+ const selectedBlockClientId = getSelectedBlockClientId();
+ let selectedTabId = null;
- const blockProps = useBlockProps();
+ // Find the first tab that is selected or has selected inner blocks so we can set it as active.
+ for ( const block of innerBlocks ) {
+ if (
+ block.clientId === selectedBlockClientId ||
+ hasSelectedInnerBlock( block.clientId, true )
+ ) {
+ selectedTabId = block.clientId;
+ break;
+ }
+ }
- const innerBlockProps = useInnerBlocksProps(
- {
- className: 'wp-block-tabs__content',
+ return {
+ innerTabs: innerBlocks,
+ selectedTabClientId: selectedTabId,
+ };
},
- {
- renderAppender: InnerBlocks.ButtonBlockAppender,
- template: TABS_TEMPLATE,
- }
+ [ clientId ]
);
const { __unstableMarkNextChangeAsNotPersistent, updateBlockAttributes } =
useDispatch( blockEditorStore );
- const setActiveTab = ( tabId ) => {
- __unstableMarkNextChangeAsNotPersistent();
- setAttributes( { activeTab: tabId } );
- };
+ const setActiveTab = useCallback(
+ ( activeTabClientId ) => {
+ // Set each inner tab's `isActive` attribute.
+ innerTabs.forEach( ( block ) => {
+ __unstableMarkNextChangeAsNotPersistent();
+ updateBlockAttributes( block.clientId, {
+ isActive: block.clientId === activeTabClientId,
+ } );
+ } );
+ },
+ [
+ innerTabs,
+ updateBlockAttributes,
+ __unstableMarkNextChangeAsNotPersistent,
+ ]
+ );
useEffect( () => {
- // Initialize the first tab as active when the component mounts.
- if ( innerBlocks.length ) {
- setActiveTab( innerBlocks[ 0 ].clientId );
+ if ( innerTabs?.length ) {
+ // Set the first tab as active when the editor is loaded
+ setActiveTab( innerTabs[ 0 ].clientId );
}
- }, [] ); // eslint-disable-line react-hooks/exhaustive-deps -- only run effect once when component mounts.
+ }, [] ); // eslint-disable-line react-hooks/exhaustive-deps -- set first tab as active when the editor is loaded.
- // if ( ! innerBlocks || innerBlocks.length === 0 ) {
- // return null;
- // }
+ useEffect( () => {
+ const hasActiveTab =
+ innerTabs &&
+ innerTabs.some( ( block ) => block.attributes.isActive );
+
+ if ( selectedTabClientId ) {
+ // If an inner tab block is selected, or its inner blocks are selected, it becomes the active tab.
+ setActiveTab( selectedTabClientId );
+ } else if ( ! hasActiveTab && innerTabs?.length ) {
+ // Otherwise, if there's no active tab, default to the first inner tab.
+ setActiveTab( innerTabs[ 0 ].clientId );
+ }
+ }, [ innerTabs, selectedTabClientId, setActiveTab ] );
+
+ const blockProps = useBlockProps();
+ const innerBlockProps = useInnerBlocksProps(
+ {
+ className: 'wp-block-tabs__content',
+ },
+ {
+ __experimentalCaptureToolbars: true,
+ clientId,
+ orientation: 'horizontal',
+ template: TABS_TEMPLATE,
+ }
+ );
return (
- { innerBlocks.map( ( block ) => {
- const isActive = block.clientId === activeTab;
+ { innerTabs.map( ( block ) => {
+ const isActive = block.attributes.isActive;
const tabIndex = isActive ? '0' : '-1';
// TODO: Add unique ids and aria attributes for accessibility.
@@ -98,12 +145,13 @@ export default function Edit( { attributes, clientId, setAttributes } ) {
updateBlockAttributes( block.clientId, {
- title: value,
+ label: value,
} )
}
+ placeholder={ __( 'Add label…' ) }
+ value={ block.attributes.label }
/>
diff --git a/packages/block-library/src/tabs/editor.scss b/packages/block-library/src/tabs/editor.scss
new file mode 100644
index 00000000000000..eb39135ff40b59
--- /dev/null
+++ b/packages/block-library/src/tabs/editor.scss
@@ -0,0 +1,5 @@
+.wp-block-tabs {
+ &__content > .block-list-appender {
+ top: -30px;
+ }
+}