Skip to content

Commit 1c29adb

Browse files
committed
update tabs docs and stories
1 parent baef43b commit 1c29adb

3 files changed

Lines changed: 45 additions & 69 deletions

File tree

packages/circuit-ui/components/Tabs/Tabs.mdx

Lines changed: 21 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ The Tabs component allows users to navigate between different views or sections
1919

2020
### Group related content
2121

22-
Tabs help optimize space by organizing large amounts of content into easily scannable sections, reducing cognitive load for users. They provide simple navigation between related sections that don’t require side-by-side comparison.
22+
Tabs help optimize space by organizing large amounts of content into easily scannable sections, reducing cognitive load for users.
23+
They provide simple navigation between related sections that don’t require side-by-side comparison.
2324

2425
Examples include:
2526

@@ -45,66 +46,40 @@ You can use the Tabs component out of the box to create a tabbed view by providi
4546

4647
### Use subcomponents independently
4748

48-
If you need finer control over your layout, you can use the `Tab`, `TabList`, and `TabPanel` components independently. This gives you flexibility to customize the structure and behavior of your tabs while still relying on the core functionality provided by Tabs.
49+
For finer control over layout, use `TabList` and `TabPanel` independently. Pass your tabs via the `tabs` prop.
50+
`TabList` manages selection state, keyboard navigation, and accessibility attributes internally. Use the `onTabChange` callback to track which panel to show.
4951

50-
⚠️ When doing so, you are responsible for ensuring accessibility and managing state correctly.
52+
- Using `TabList`:
53+
- Provide a `tabs` array with a unique `id` and label for each tab.
54+
- Use `onTabChange` to receive the selected tab's id and toggle panel visibility.
55+
- Using `TabPanel`:
56+
- Assign a unique `id` following the `panel-{id}` convention to match `TabList`'s `aria-controls`.
57+
- Set `aria-labelledby` to `tab-{id}` to reference the corresponding tab.
5158

52-
- Using Tab:
53-
- Provide a unique `id` for each tab.
54-
- Add an `aria-controls` attribute that points to the id of its corresponding panel.
55-
- Implement keyboard navigation (e.g., arrow keys, Home/End) to support keyboard users, or use the `useTabState` helper [hook](#usetabstate) to manage state and interactions. This is not necessary for [tab-like navigation](#tab-like-navigation).
56-
- Using TabPanel:
57-
- Assign a unique id to each panel.
58-
- Ensure this id matches the `aria-controls` value of its corresponding tab.
59-
- Set `aria-labelledby` to reference the id of the associated tab.
60-
61-
<Story of={Stories.ControlledState} />
59+
<Story of={Stories.WithTabPanel} />
6260

6361
#### useTabState
6462

65-
Use this hook to manage tab state and handle user interactions. The hook accepts an array of tab IDs and an optional initially selected index, and returns:
66-
- `selectedId` : the id of the currently selected tab.
67-
- `onTabKeyDown` : an `onKeyDown` handler for your Tab component that manages arrow-key navigation and updates selectedId.
68-
- `onTabClick` : an `onClick` handler for your Tab component that updates selectedId when a tab is clicked.
63+
If you need even finer control and want to build a fully custom tab implementation, `useTabState` is available as a standalone hook.
64+
It accepts an array of tab IDs and an optional initially selected index, and returns `selectedId`, an `onTabKeyDown` handler for arrow-key navigation, and an `onTabClick` handler for click selection.
6965

7066
```tsx
71-
import {useTabState} from '@sumup-oss/circuit-ui';
72-
73-
const {
74-
selectedId,
75-
onTabKeyDown,
76-
onTabClick,
77-
} = useTabState(
78-
['tab-1', 'tab-2', 'tab-3'],
79-
2,
80-
);
67+
import { useTabState } from "@sumup-oss/circuit-ui";
68+
69+
const { selectedId, onTabKeyDown, onTabClick } = useTabState(
70+
["tab-1", "tab-2", "tab-3"],
71+
2
72+
);
8173
```
8274

8375
### Tab-like navigation
8476

85-
If you need to implement a tab-like navigation rather than true tab semantics, you can control the rendering behavior of the TabList and Tab components to achieve this.
86-
- Set the `as` prop of Tablist to `"navigation"` to render a native `<nav>` containing an element with the role `list` to wrap its children.
87-
- Set the `as` prop of Tab component to `"listitem"` to wrap your tab in an element with role `listitem`.
77+
If you need to implement a tab-like navigation rather than true tab semantics, set the `as` prop of `TabList` to `"navigation"`. This renders a native `<nav>` containing an element with the role `list` to wrap its children.
8878

89-
The components would render styled to look like tabs but without any tab ARIA roles. This ensures a correctly structured and accessible navigation pattern that visually matches the tab design.
90-
91-
```tsx
92-
<TabList as="navigation">
93-
<Tab as="listitem" href="/home">Home</Tab>
94-
<Tab as="listitem" href="/about">About</Tab>
95-
<Tab as="listitem" href="/contact">Contact</Tab>
96-
</TabList>
97-
```
79+
The component renders styled to look like tabs but without any tab ARIA roles. This ensures a correctly structured and accessible navigation pattern that visually matches the tab design.
9880

9981
<Story of={TabListStories.Navigation} />
10082

101-
### With links
102-
103-
Tabs render as buttons by default. In navigation contexts, you can pass a `href` prop to render them as links.
104-
Avoid using external links, as this can create an inconsistent user experience across tabs. Instead, place the external link outside the Tabs component to maintain predictable behavior.
105-
106-
<Story of={Stories.Links} />
107-
10883
### Stretched
10984

11085
Set the `stretched` prop to true to make all tabs expand and share the available space evenly.

packages/circuit-ui/components/Tabs/Tabs.stories.tsx

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
* limitations under the License.
1414
*/
1515

16+
import { useState } from 'react';
17+
1618
import { ArrowLeft, ExternalLink } from '@sumup-oss/icons';
1719

1820
import { Body } from '../Body/index.js';
@@ -24,7 +26,6 @@ import type { TabsProps } from './Tabs.js';
2426
import { Tabs } from './Tabs.js';
2527
import { TabList } from './components/TabList/TabList.js';
2628
import { TabPanel } from './components/TabPanel/TabPanel.js';
27-
import { Tab } from './components/Tab/Tab.js';
2829
import { action } from 'storybook/actions';
2930

3031
export default {
@@ -120,28 +121,30 @@ Stretched.args = {
120121
stretched: true,
121122
};
122123

123-
export const Links = () => (
124-
<TabList>
125-
<Tab selected>Home</Tab>
126-
<Tab href="#posts" target="">
127-
Posts
128-
</Tab>
129-
<Tab href="#reviews">Reviews</Tab>
130-
</TabList>
131-
);
124+
export const WithTabPanel = () => {
125+
const items = tabs.slice(0, 3);
126+
const [selectedId, setSelectedId] = useState(items[0].id);
132127

133-
Links.parameters = {
134-
controls: { hideNoControlsWarning: true },
128+
return (
129+
<div>
130+
<TabList
131+
tabs={items.map(({ id, tab }) => ({ id, tab }))}
132+
onTabChange={setSelectedId}
133+
/>
134+
{items.map(({ id, panel }) => (
135+
<TabPanel
136+
key={id}
137+
id={`panel-${id}`}
138+
aria-labelledby={`tab-${id}`}
139+
hidden={selectedId !== id}
140+
>
141+
{panel}
142+
</TabPanel>
143+
))}
144+
</div>
145+
);
135146
};
136147

137-
export const WithTabChangeCallback = () => (
138-
<Tabs
139-
items={tabs.slice(0, 3)}
140-
onTabChange={action('Changed to tab with id:')}
141-
/>
142-
);
143-
144-
WithTabChangeCallback.storyName = 'With TabChange';
145-
WithTabChangeCallback.parameters = {
148+
WithTabPanel.parameters = {
146149
controls: { hideNoControlsWarning: true },
147150
};

packages/circuit-ui/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,6 @@ export type {
124124

125125
export { Tabs } from './components/Tabs/Tabs.js';
126126
export type { TabsProps } from './components/Tabs/Tabs.js';
127-
export { Tab } from './components/Tabs/components/Tab/Tab.js';
128-
export type { TabProps } from './components/Tabs/components/Tab/Tab.js';
129127
export { TabPanel } from './components/Tabs/components/TabPanel/TabPanel.js';
130128
export type { TabPanelProps } from './components/Tabs/components/TabPanel/TabPanel.js';
131129
export { TabList } from './components/Tabs/components/TabList/TabList.js';

0 commit comments

Comments
 (0)