Skip to main content
MediumFrontendExtend

Extend an Accordion with Controlled Mode and Exclusive Opening

You have a working Accordion built using the compound component pattern. It renders panels that open and close when their trigger is clicked. The existing implementation is uncontrolled — it manages its own open/close s...

What you will practice

TypeScriptReactReact PatternsState ManagementCompound ComponentsControlled ComponentsContext

Requirements

  • The existing uncontrolled behavior (click trigger → panel opens/closes) must not be broken.
  • When `exclusive={true}` is passed to `Accordion`, clicking a trigger closes any currently open panel before opening the new one. Clicking the already-open panel's trigger still closes it.
  • When `activePanel` (string | null) and `onPanelChange` (function) are both provided, the Accordion reads its open/close state from `activePanel` and calls `onPanelChange(id)` or `onPanelChange(null)` on toggle — it must not call `setActivePanel` internally.
  • `Accordion.Panel` must accept a `disabled?: boolean` prop. When `disabled` is true, the panel's trigger must have a `disabled` HTML attribute and clicking it must not open the panel or call any callback.
  • The `data-testid` attributes on existing elements must not be changed.

Starter files

App.tsxEditable starter

What the judge checks

  • Runs in the react environment with the react-testing-library runner.
  • Uses a 10000ms judge budget.
  • Behavior rules include: Uncontrolled Toggle Works, Exclusive Mode Closes Others, Controlled Mode Syncs Externally, Disabled Panel Cannot Open.

Constraints

  • Do not modify `data-testid` attributes on any existing element.
  • The compound component API (`Accordion`, `Accordion.Panel`, `Accordion.Trigger`, `Accordion.Content`) must remain intact.
  • Controlled mode only activates when both `activePanel` and `onPanelChange` are provided — not just one of them.

Example behavior

Input
<Accordion exclusive>
  <Accordion.Panel id="a">...</Accordion.Panel>
  <Accordion.Panel id="b">...</Accordion.Panel>
</Accordion>
Output
Opening panel 'b' while 'a' is open → panel 'a' closes, panel 'b' opens.

Exclusive mode: at most one panel is open at any time.

Input
const [open, setOpen] = useState('a');
<Accordion activePanel={open} onPanelChange={setOpen}>
Output
Clicking panel 'b' trigger → setOpen('b') is called → Accordion re-renders with panel 'b' open.

Controlled mode: the Accordion reflects external state and delegates changes upward.

Follow-up

The current implementation only allows one panel open at a time in exclusive mode. How would you add a `multi` prop that allows any number of panels to be open simultaneously?