diff --git a/packages/block-editor/src/components/tabbed-sidebar/README.md b/packages/block-editor/src/components/tabbed-sidebar/README.md index 42001dbfc79cb..35c44730ffc15 100644 --- a/packages/block-editor/src/components/tabbed-sidebar/README.md +++ b/packages/block-editor/src/components/tabbed-sidebar/README.md @@ -1,21 +1,19 @@ -# 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 = () => ( ( { name: 'slug-1', title: _x( 'Title 1', 'context' ), - panel: , + panel: , panelRef: useRef('an-optional-ref'), }, { @@ -35,6 +33,8 @@ const MyTabbedSidebar = () => ( onClose={ onClickCloseButton } onSelect={ onSelectTab } defaultTabId="slug-1" + selectedTab="slug-1" + closeButtonLabel="Close sidebar" ref={ tabsRef } /> ); @@ -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. \ No newline at end of file diff --git a/packages/block-editor/src/components/tabbed-sidebar/index.js b/packages/block-editor/src/components/tabbed-sidebar/index.js index c9ff6bbf6555f..f142f538cfe8f 100644 --- a/packages/block-editor/src/components/tabbed-sidebar/index.js +++ b/packages/block-editor/src/components/tabbed-sidebar/index.js @@ -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 ( + * , + * } + * ] } + * 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 diff --git a/packages/block-editor/src/components/tabbed-sidebar/stories/index.story.js b/packages/block-editor/src/components/tabbed-sidebar/stories/index.story.js new file mode 100644 index 0000000000000..49825be19b90c --- /dev/null +++ b/packages/block-editor/src/components/tabbed-sidebar/stories/index.story.js @@ -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 ( + { + onSelect( ...changeArgs ); + setSelectedTab( ...changeArgs ); + } } + onClose={ onClose } + /> + ); + }, + args: { + tabs: DEMO_TABS, + defaultTabId: 'tab1', + closeButtonLabel: 'Close Sidebar', + }, +};