Skip to content

Commit

Permalink
Storybook: Add TabbedSidebar stories and improve docs (WordPress#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 <sukhendu2002@git.wordpress.org>
Co-authored-by: t-hamano <wildworks@git.wordpress.org>
  • Loading branch information
3 people authored and bph committed Jan 8, 2025
1 parent 55a06ba commit e3ceb20
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',
},
};

0 comments on commit e3ceb20

Please sign in to comment.