Refactor TabPanels to compound Tab children pattern#15
Conversation
Co-authored-by: OmgRod <89850217+OmgRod@users.noreply.github.com> Agent-Logs-Url: https://github.com/OmgRod/MiniWiki/sessions/bede1e37-f2b4-4f66-875b-e24030741638
There was a problem hiding this comment.
Pull request overview
Refactors the documentation tab component API to work reliably in MDX by moving from a tabs array prop (with JSX values) to a compound-component pattern (<TabPanels><Tab ... /></TabPanels>), enabling rich MD/HTML tab content.
Changes:
- Replaced
tabsarray prop usage withTabPanels+Tabcompound children API. - Registered
Tabin the MDX component map so it can be used directly in MDX docs pages. - Updated the tabs documentation page (props + examples) to the new API.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| components/Tabs.js | Implements compound TabPanels/Tab API and index-based active tab state. |
| components/mdx-components.js | Exposes Tab to MDX by importing/exporting it in the MDX components map. |
| content/guides/components/tabs.mdx | Updates docs and examples to use the new compound API. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export function TabPanels({ children }) { | ||
| const tabs = React.Children.toArray(children).filter( | ||
| (child) => React.isValidElement(child) && child.props.title != null, | ||
| ); | ||
| const [activeIndex, setActiveIndex] = useState(0); | ||
|
|
There was a problem hiding this comment.
TabPanels derives tabs from children every render, but activeIndex is initialized once and can become out-of-range if the set/order of children changes (e.g., conditional rendering). That can leave no tab selected and show an empty panel. Consider syncing/clamping activeIndex whenever the computed tabs.length changes (or when the active tab is removed).
| {tabs.map((tab, index) => ( | ||
| <button | ||
| key={tab.id} | ||
| key={tab.props.title} |
There was a problem hiding this comment.
key={tab.props.title} is not guaranteed to be unique/stable (duplicate titles will cause React key collisions and incorrect reuse). Prefer using the element’s existing key (from React.Children.toArray) or another unique identifier rather than the title text.
| key={tab.props.title} | |
| key={tab.key ?? index} |
| @@ -1,8 +1,14 @@ | |||
| import { useState } from 'react'; | |||
| import React, { useState } from 'react'; | |||
There was a problem hiding this comment.
This file now uses a default React import mainly for React.Children/React.isValidElement, but other components in this repo typically import named APIs from react (e.g., components/Accordion.js:1). To stay consistent and enable tree-shaking, consider importing Children/isValidElement/useState as named imports instead of import React, ....
| <div className="my-6 overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900"> | ||
| <div className="flex flex-wrap gap-1 border-b border-slate-200 p-2 dark:border-slate-800"> | ||
| {tabs.map((tab) => ( | ||
| {tabs.map((tab, index) => ( | ||
| <button | ||
| key={tab.id} | ||
| key={tab.props.title} | ||
| type="button" | ||
| onClick={() => setActive(tab.id)} | ||
| onClick={() => setActiveIndex(index)} | ||
| className={`rounded-md px-3 py-1.5 text-sm transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 ${ | ||
| tab.id === active | ||
| index === activeIndex | ||
| ? 'bg-blue-600 text-white' | ||
| : 'text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-800' | ||
| }`} | ||
| > | ||
| {tab.label} | ||
| {tab.props.title} | ||
| </button> | ||
| ))} |
There was a problem hiding this comment.
The tab UI is rendered as a set of buttons without tab semantics. For accessibility, consider adding role="tablist" on the container, role="tab" + aria-selected on each button, and role="tabpanel" on the content region (with proper id/aria-controls wiring) so screen readers can interpret this as a tab interface.
TabPanelswas non-functional in docs because MDX can't serialize JavaScript object arrays with JSX values. The component also only supported plain-string tab content.Changes
components/Tabs.js— Replaced thetabsarray prop with aTabPanels+Tabcompound component pattern.Tabacceptstitle(button label) andchildren(any React/MD content).TabPanelscollects children viaReact.Children.toArray, filters to elements with atitleprop, and tracks active tab by index.components/mdx-components.js— AddedTabto named imports from./Tabsand registered it inmdxComponents.content/guides/components/tabs.mdx— Updated docs, props tables, and live examples to the new API.New usage:
Original prompt
📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.