Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .plugin-data
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": "1.0.5",
"version": "1.0.6",
"slug": "blockparty-tabs"
}
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 1.0.6 - 2026-02-11

- Update view and edit script to allow to use nested tabs block (tabs inside tab panel).

## 1.0.5 - 2025-11-10

- Update block icons
Expand Down
4 changes: 2 additions & 2 deletions blockparty-tabs.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Description: Accessible Tabs block for WordPress gutenberg.
* Requires at least: 6.2
* Requires PHP: 8.1
* Version: 1.0.5
* Version: 1.0.6
* Author: Be API Technical team
* Author URI: https://beapi.fr
* License: GPL-2.0-or-later
Expand All @@ -14,7 +14,7 @@

namespace Blockparty\Tabs;

define( 'BLOCKPARTY_TABS_VERSION', '1.0.5' );
define( 'BLOCKPARTY_TABS_VERSION', '1.0.6' );
define( 'BLOCKPARTY_TABS_URL', plugin_dir_url( __FILE__ ) );
define( 'BLOCKPARTY_TABS_DIR', plugin_dir_path( __FILE__ ) );
define( 'BLOCKPARTY_TABS_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "blockparty-tabs",
"version": "1.0.5",
"version": "1.0.6",
"description": "Accessible tabs block for WordPress",
"author": "Be API Technical team",
"license": "GPL-2.0-or-later",
Expand Down
4 changes: 4 additions & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ directory take precedence. For example, `/assets/screenshot-1.png` would win ove

== Changelog ==

= 1.0.6 =

* Update view and edit script to allow to use nested tabs block (tabs inside tab panel).

= 1.0.5 =

* Update block icons
Expand Down
2 changes: 1 addition & 1 deletion src/blockparty-tabs-nav-item/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "blockparty/tabs-nav-item",
"version": "1.0.5",
"version": "1.0.6",
"title": "Tab",
"category": "widgets",
"description": "Accessible tabs block item",
Expand Down
2 changes: 2 additions & 0 deletions src/blockparty-tabs-nav-item/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getBlockType } from '@wordpress/blocks';
import { select } from '@wordpress/data';
import ComposeBlockControls from './ComposeBlockControls';
import getSynchedID from '../blockparty-tabs/GetSynchedID';
import SyncTabsActive from '../blockparty-tabs/SyncTabsActive';

export default function Edit( {
attributes,
Expand Down Expand Up @@ -47,6 +48,7 @@ export default function Edit( {

return (
<>
<SyncTabsActive clientId={ clientId } />
<ComposeBlockControls
clientId={ clientId }
index={ index }
Expand Down
2 changes: 1 addition & 1 deletion src/blockparty-tabs-nav/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "blockparty/tabs-nav",
"version": "1.0.5",
"version": "1.0.6",
"title": "Tabs list",
"category": "widgets",
"description": "Accessible tabs block item",
Expand Down
2 changes: 1 addition & 1 deletion src/blockparty-tabs-panel-item/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "blockparty/tabs-panel-item",
"version": "1.0.5",
"version": "1.0.6",
"title": "Panel",
"category": "widgets",
"icon": "layout",
Expand Down
2 changes: 2 additions & 0 deletions src/blockparty-tabs-panel-item/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import classnames from 'classnames';
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
import { select } from '@wordpress/data';
import getSynchedID from '../blockparty-tabs/GetSynchedID';
import SyncTabsActive from '../blockparty-tabs/SyncTabsActive';

export default function Edit( {
setAttributes,
Expand Down Expand Up @@ -46,6 +47,7 @@ export default function Edit( {

return (
<div { ...blockProps }>
<SyncTabsActive clientId={ clientId } />
<InnerBlocks
allowedBlocks={ allowedBlocks }
templateLock={ false }
Expand Down
2 changes: 1 addition & 1 deletion src/blockparty-tabs-panels/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "blockparty/tabs-panels",
"version": "1.0.5",
"version": "1.0.6",
"title": "Panels",
"category": "widgets",
"description": "Accessible tabs block item",
Expand Down
105 changes: 105 additions & 0 deletions src/blockparty-tabs/SyncTabsActive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**

Check failure on line 1 in src/blockparty-tabs/SyncTabsActive.js

View workflow job for this annotation

GitHub Actions / Lint JS

Expected JSDoc block lines to be aligned

Check failure on line 1 in src/blockparty-tabs/SyncTabsActive.js

View workflow job for this annotation

GitHub Actions / Lint JS

Delete `·`
* Syncs the active tab state with the block selection in the editor.

Check failure on line 2 in src/blockparty-tabs/SyncTabsActive.js

View workflow job for this annotation

GitHub Actions / Lint JS

Expected JSDoc block to be aligned
*
* When this tab item (nav-item or panel-item) is the one selected or is the
* closest tab item to the current block selection, updates the parent
* blockparty/tabs block's tabsActive attribute to this item's index.
*
* This direct link (the clicked block updates its own tabs block) ensures
* correct behavior with nested tabs: only the tabs block that owns the
* selected tab is updated.
*
* @since 1.0.6
*
* @param {Object} props Component props.
* @param {string} props.clientId This block's clientId (tabs-nav-item or tabs-panel-item).
* @return {null} Renders nothing.
*/
import { useEffect } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';

/** Block names that represent a single tab (nav link or panel). */
const TAB_ITEM_NAMES = [
'blockparty/tabs-nav-item',
'blockparty/tabs-panel-item',
];

export default function SyncTabsActive( { clientId } ) {
const { selectedBlockClientId, closestTabItemId, tabsBlockId, myIndex } =
useSelect(
( select ) => {
const {
getBlockName,
getBlockIndex,
getSelectedBlockClientId,
getBlockParents,
getBlockRootClientId,
} = select( 'core/block-editor' );
const selected = getSelectedBlockClientId();
if ( ! selected ) {
return {
selectedBlockClientId: null,
closestTabItemId: null,
tabsBlockId: null,
myIndex: 0,
};
}
// Find the tab item closest to the selection: the selected block if it
// is a tab item, otherwise the first tab item in the ancestor chain.
let closest = null;
if (
TAB_ITEM_NAMES.indexOf( getBlockName( selected ) ) !== -1
) {
closest = selected;
} else {
const parents = getBlockParents( selected );
for ( let i = 0; i < parents.length; i += 1 ) {
if (
TAB_ITEM_NAMES.indexOf(
getBlockName( parents[ i ] )
) !== -1
) {
closest = parents[ i ];
break;
}
}
Comment thread
MarieComet marked this conversation as resolved.
}
// Parent chain: this block → tabs-nav or tabs-panels → blockparty/tabs.
const directParent = getBlockRootClientId( clientId );
const tabsId = directParent
? getBlockRootClientId( directParent )
: null;
return {
selectedBlockClientId: selected,
closestTabItemId: closest,
tabsBlockId: tabsId,
myIndex: getBlockIndex( clientId ),
};
},
[ clientId ]
);

const { updateBlockAttributes } = useDispatch( 'core/block-editor' );

// When this block is the active tab (closest to selection or selected), sync tabsActive.
useEffect( () => {
if ( ! selectedBlockClientId || ! tabsBlockId ) {
return;
}
const isActiveTab =
closestTabItemId === clientId || selectedBlockClientId === clientId;
if ( ! isActiveTab ) {
return;
}
updateBlockAttributes( tabsBlockId, { tabsActive: myIndex } );
}, [
selectedBlockClientId,
closestTabItemId,
clientId,
tabsBlockId,
myIndex,
updateBlockAttributes,
] );

return null;
}
44 changes: 0 additions & 44 deletions src/blockparty-tabs/TabsFocus.js

This file was deleted.

2 changes: 1 addition & 1 deletion src/blockparty-tabs/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "blockparty/tabs",
"version": "1.0.5",
"version": "1.0.6",
"title": "Tabs",
"category": "widgets",
"description": "Accessible tabs block",
Expand Down
2 changes: 0 additions & 2 deletions src/blockparty-tabs/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
} from '@wordpress/block-editor';
import { PanelBody, PanelRow, TextControl } from '@wordpress/components';
import { select } from '@wordpress/data';
import TabsFocus from './TabsFocus';
import {
heading,
justifyRight,
Expand Down Expand Up @@ -119,7 +118,6 @@ export default function Edit( { attributes, setAttributes, clientId } ) {
</PanelRow>
</PanelBody>
</InspectorControls>
<TabsFocus clientId={ clientId } setAttributes={ setAttributes } />
<div { ...innerBlocksProps } />
</>
);
Expand Down
13 changes: 6 additions & 7 deletions src/blockparty-tabs/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,17 @@ class TabsAutomatic {
this.firstTab = null;
this.lastTab = null;

this.tabs = Array.from(
this.tablistNode.querySelectorAll( '[role=tab]' )
this.tabnav = this.tablistNode.querySelector(
'.wp-block-blockparty-tabs-nav'
);
this.tabs = Array.from( this.tabnav.querySelectorAll( 'a[role=tab]' ) );
this.tabpanelsNode = this.tabnav.nextElementSibling;

this.tabpanels = [];

for ( let i = 0; i < this.tabs.length; i += 1 ) {
const tab = this.tabs[ i ];
const tabpanel = this.tablistNode
.closest( '.wp-block-blockparty-tabs' )
.querySelectorAll( '.wp-block-blockparty-tabs-panels > *' )[
i
];
const tabpanel = this.tabpanelsNode.children[ i ];

tab.tabIndex = -1;
tab.setAttribute( 'aria-selected', 'false' );
Expand Down
Loading