Repository navigation
feat: add Tabs & subcomponents - #3289
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 4 Skipped Deployments
|
🦋 Changeset detectedLatest commit: 6906416 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ec762d0. Configure here.
laurenashpole
left a comment
There was a problem hiding this comment.
LGTM! All my comments include the word "could" because they're nitpicky personal preference things you can do or not if you want.
| parameters: { | ||
| a11y: { | ||
| context: '.sui-${componentName}', | ||
| context: '[data-ui="${componentName}"]', |
There was a problem hiding this comment.
There's a lot going on in this repo, things get easy to miss ;)
| return context | ||
| } | ||
|
|
||
| function getTabId(tabsRootId: string, value: string): string { |
There was a problem hiding this comment.
Could combine these into a getId that also accepts a suffix.
There was a problem hiding this comment.
I kind of like having two simple functions instead of one function with two control flows personally, and also I can't seem to come up with a sensible name for the merged function. But it's a loose opinion so if this happens to change in the future I won’t object ;)

Description
This PR introduces the new Tabs component and its subcomponents (
Tabs.List,Tabs.Tab,Tabs.Panel). This is a complete rewrite of the existing Tabs components and is a breaking change — therefore, there are no codemods included with this work.The root Tabs component sets up a React Context provider; this provider takes
valueandonValueChangeprops from the consumer.valuesets the active tab, whileonValueChangeis an updater function for thevalue. Typically, these will be provided viauseStatevariables and setters, but the consumer is free to use any methodology they wish.The
Tabs.Listcomponent is mainly responsible for establishing the correct accessibility tree and the layout for the enclosed tabs.Tabs.Listrequires eitheraria-labeloraria-labelledbyfrom the consumer to correctly label the tab list.Tabs.Tabtakes the following:value: a string representing the ID of the tab, which will be repeated for its matchingTab.Panel; this is thevalueto use in the rootTabscomponent for denoting the active tablabel: a string to use as the Tab’s text labelicon: an optional icontone: an optionalToneFinally,
Tab.Paneltakes avaluewhich is the samevaluestring as its matchingTabs.Tab.When a tab is clicked or activated via keyboard (see below), the provided
onValueChangefunction will fire with that tab’svalueas the argument for the function.As a whole, Tabs and its subcomponents align with the recommended WAI-ARIA Tabs example (manual activation branch). As such,
Tabs.Listhas anonKeyDownlistener, which handles the keyboard interaction recommendations in the WAI-ARIA example:Tabs.List, to the currently active tabTab.PanelOn the last point above: the active
Tab.Panelhas a defaulttabindexof0to enable focusing when there is no focusable content. Tab panels with focusable content should unset the panel’stabindexso that the panel’s first focusable component receives focus instead. This is an end user concern, so we should be sure to document this.What to review
Testing
All functionality should be tested (some of it in a Storybook story which is excluded from the Storybook UI but is run via Vitest).
Note
Medium Risk
Introduces a breaking rewrite of Tabs as a new public, accessibility-sensitive API; keyboard and ARIA behavior need correct consumer usage (e.g. panel tabindex when content is focusable).
Overview
Adds a new
TabsAPI to@sanity/ui(Tabs,Tabs.List,Tabs.Tab,Tabs.Panel) with controlledvalue/onValueChange, styling intabs.css, and exports wired through the package index and changeset (patch).Tabs.Listrequiresaria-labeloraria-labelledby, implements WAI-ARIA tablist behavior (arrow/Home/End focus on tabs; selection via click), and panels show/hide withdisplayplus default paneltabIndex={0}for keyboard exit from the tab strip. Tabs support optional icons and tones.Storybook adds Tabs stories (default, tones/icons, Vitest-only interaction coverage). depth-testing, inp-testing, and mount-testing routes gain tab demos; ui3/ui4 still use separate
Tab/TabList/TabPanelimports while ui5 uses the new compoundTabs.Also turns off
react/jsx-no-constructed-context-valuesin oxlint (React Compiler), updatescreate-ui.jsStorybook a11y context to[data-ui="…"], and bumps the generatedVERSION.Reviewed by Cursor Bugbot for commit 6906416. Bugbot is set up for automated code reviews on this repo. Configure here.