Skip to content

Commit

Permalink
Storybook: Add TabbedSidebar stories and improve docs (#68118)
Browse files Browse the repository at this point in the history
* Add stories and improve TabbedSidebar docs

* Refactor TabbedSidebar story

* Make TabbedSidebar story props controllable

Co-authored-by: Sukhendu2002 <[email protected]>
Co-authored-by: t-hamano <[email protected]>
  • Loading branch information
3 people authored Jan 3, 2025
1 parent 67cc9d5 commit 0f2a584
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 13 deletions.
37 changes: 24 additions & 13 deletions packages/block-editor/src/components/tabbed-sidebar/README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
# Tabbed Panel
# TabbedSidebar

The `TabbedPanel` component is used to create the secondary panels in the editor.
The `TabbedSidebar` component is used to create secondary panels in the editor with tabbed navigation.

## Development guidelines

This acts as a wrapper for the `Tabs` component, but adding conventions that can be shared between all secondary panels, for example:
This acts as a wrapper for the `Tabs` component, adding conventions that can be shared between all secondary panels, including:

- A close button
- Tabs that fill the panel
- Custom scollbars
- Custom scrollbars

### Usage

Renders a block alignment toolbar with alignments options.

```jsx
import { TabbedSidebar } from '@wordpress/components';
import { TabbedSidebar } from '@wordpress/block-editor';

const MyTabbedSidebar = () => (
<TabbedSidebar
tabs={ [
{
name: 'slug-1',
title: _x( 'Title 1', 'context' ),
panel: <PanelContents>,
panel: <PanelContents />,
panelRef: useRef('an-optional-ref'),
},
{
Expand All @@ -35,6 +33,8 @@ const MyTabbedSidebar = () => (
onClose={ onClickCloseButton }
onSelect={ onSelectTab }
defaultTabId="slug-1"
selectedTab="slug-1"
closeButtonLabel="Close sidebar"
ref={ tabsRef }
/>
);
Expand All @@ -47,30 +47,41 @@ const MyTabbedSidebar = () => (
- **Type:** `String`
- **Default:** `undefined`

This is passed to the `Tabs` component so it can handle the tab to select by default when it component renders.
The ID of the tab to be selected by default when the component renders.

### `onClose`

- **Type:** `Function`

The function that is called when the close button is clicked.
Function called when the close button is clicked.

### `onSelect`

- **Type:** `Function`

This is passed to the `Tabs` component - it will be called when a tab has been selected. It is passed the selected tab's ID as an argument.
Function called when a tab is selected. Receives the selected tab's ID as an argument.

### `selectedTab`

- **Type:** `String`
- **Default:** `undefined`

This is passed to the `Tabs` component - it will display this tab as selected.
The ID of the currently selected tab.

### `tabs`

- **Type:** `Array`
- **Default:** `undefined`

An array of tabs which will be rendered as `TabList` and `TabPanel` components.
Array of tab objects. Each tab should have:

- `name` (string): Unique identifier for the tab
- `title` (string): Display title for the tab
- `panel` (React.Node): Content to display in the tab panel
- `panelRef` (React.Ref, optional): Reference to the tab panel element

#### `closeButtonLabel`

- **Type:** `String`

Accessibility label for the close button.
38 changes: 38 additions & 0 deletions packages/block-editor/src/components/tabbed-sidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,44 @@ import { unlock } from '../../lock-unlock';

const { Tabs } = unlock( componentsPrivateApis );

/**
* A component that creates a tabbed sidebar with a close button.
*
* @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/tabbed-sidebar/README.md
*
* @example
* ```jsx
* function MyTabbedSidebar() {
* return (
* <TabbedSidebar
* tabs={ [
* {
* name: 'tab1',
* title: 'Settings',
* panel: <PanelContents />,
* }
* ] }
* onClose={ () => {} }
* onSelect={ () => {} }
* defaultTabId="tab1"
* selectedTab="tab1"
* closeButtonLabel="Close sidebar"
* />
* );
* }
* ```
*
* @param {Object} props Component props.
* @param {string} [props.defaultTabId] The ID of the tab to be selected by default when the component renders.
* @param {Function} props.onClose Function called when the close button is clicked.
* @param {Function} props.onSelect Function called when a tab is selected. Receives the selected tab's ID as an argument.
* @param {string} props.selectedTab The ID of the currently selected tab.
* @param {Array} props.tabs Array of tab objects. Each tab should have: name (string), title (string),
* panel (React.Node), and optionally panelRef (React.Ref).
* @param {string} props.closeButtonLabel Accessibility label for the close button.
* @param {Object} ref Forward ref to the tabs list element.
* @return {Element} The tabbed sidebar component.
*/
function TabbedSidebar(
{ defaultTabId, onClose, onSelect, selectedTab, tabs, closeButtonLabel },
ref
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* WordPress dependencies
*/
import { useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import TabbedSidebar from '../';

const meta = {
title: 'BlockEditor/TabbedSidebar',
component: TabbedSidebar,
tags: [ 'status-private' ],
parameters: {
docs: {
canvas: { sourceState: 'shown' },
description: {
component:
'A component that creates a tabbed sidebar with a close button.',
},
},
},
argTypes: {
defaultTabId: {
control: { type: null },
table: {
type: { summary: 'string' },
},
description:
'The ID of the tab to be selected by default when the component renders.',
},
onClose: {
action: 'onClose',
control: { type: null },
table: {
type: { summary: 'function' },
},
description: 'Function called when the close button is clicked.',
},
onSelect: {
action: 'onSelect',
control: { type: null },
table: {
type: { summary: 'function' },
},
description:
"Function called when a tab is selected. Receives the selected tab's ID as an argument.",
},
selectedTab: {
control: { type: null },
table: {
type: { summary: 'string' },
},
description: 'The ID of the currently selected tab.',
},
tabs: {
control: { type: 'array' },
table: {
type: { summary: 'array' },
},
description:
'Array of tab objects. Each tab should have: name (string), title (string), panel (React.Node), and optionally panelRef (React.Ref).',
},
closeButtonLabel: {
control: { type: 'text' },
table: {
type: { summary: 'string' },
},
description: 'Accessibility label for the close button.',
},
},
};

export default meta;

const DEMO_TABS = [
{ name: 'tab1', title: 'Settings' },
{ name: 'tab2', title: 'Styles' },
{ name: 'tab3', title: 'Advanced' },
];

export const Default = {
render: function Template( { onSelect, onClose, ...args } ) {
const [ selectedTab, setSelectedTab ] = useState();

return (
<TabbedSidebar
{ ...args }
selectedTab={ selectedTab }
onSelect={ ( ...changeArgs ) => {
onSelect( ...changeArgs );
setSelectedTab( ...changeArgs );
} }
onClose={ onClose }
/>
);
},
args: {
tabs: DEMO_TABS,
defaultTabId: 'tab1',
closeButtonLabel: 'Close Sidebar',
},
};

1 comment on commit 0f2a584

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in 0f2a584.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/12596397323
📝 Reported issues:

Please sign in to comment.