Build an accordion
easyUI Components+160 XP
Render three FAQ-style panels, each with a clickable header and hidden body content.
Clicking a header's panel open toggles it — but opening one panel must close whichever other panel was open, since only one can be expanded at a time.
Examples
// Initial render — nothing expanded.
<Accordion />
// → three headers, no body visible.
// Click panel 1's header
// → body-1 appears.
// Click panel 2's header
// → body-1 disappears, body-2 appears.Opening another panel implicitly closes the one that was open.
// Panel body is absent from the DOM while closed,
// not just hidden with CSS:
{openId === panel.id ? (
<div data-testid={`accordion-body-${panel.id}`}>
{panel.body}
</div>
) : null}The grader counts `[data-testid^="accordion-body-"]` nodes, so hiding with `display: none` fails.
What it has to do
- Each panel header is a `<button data-testid="accordion-header-ID">`.
- Each panel body renders only while open, as `<div data-testid="accordion-body-ID">` — absent from the DOM while closed, not just hidden.
- All panels start closed.
- Opening a panel closes any other panel that was open — at most one body is ever present at once.
- Clicking the header of the currently-open panel closes it, leaving none open.
Notes
- Model open state as a single `openId` rather than an array of booleans — the "only one open" invariant falls out, and toggling becomes `openId === id ? null : id`.
- A production-grade accordion should also expose `aria-expanded` on each header and link the header to its body with `aria-controls` / `id`, so screen readers announce the state change.
- Clicking the open panel's own header closes it (returns to zero-open) — don't skip the toggle-off path.
Stuck? The react course covers the ideas this problem is built on.
import { useState } from "react"; const PANELS = [ { id: 1, title: "What is this?", body: "A frequently-asked-questions accordion." }, { id: 2, title: "How do I use it?", body: "Click a question to reveal its answer." }, { id: 3, title: "Can more than one be open?", body: "No — opening one should close any other." }, ]; export default function App() { const [openId, setOpenId] = useState(null); // TODO: clicking a header should toggle its panel, and opening one // panel should close whichever other one was open. return ( <div style={{ fontFamily: "sans-serif", padding: 16 }}> {PANELS.map((panel) => ( <div key={panel.id} style={{ borderBottom: "1px solid #ddd" }}> <button data-testid={`accordion-header-${panel.id}`} style={{ width: "100%", textAlign: "left", padding: 12, fontWeight: "bold" }} > {panel.title} </button> <div data-testid={`accordion-body-${panel.id}`} style={{ padding: 12 }}> {panel.body} </div> </div> ))} </div> ); }
- 1.Each panel header is a `<button data-testid="accordion-header-ID">`.
- 2.Each panel body renders only while open, as `<div data-testid="accordion-body-ID">` — absent from the DOM while closed, not just hidden.
- 3.All panels start closed.
- 4.Opening a panel closes any other panel that was open — at most one body is ever present at once.
- 5.Clicking the header of the currently-open panel closes it, leaving none open.
5 automated assertions run against your rendered component. The exact assertion names are hidden so you build to the behaviour, not to the test.
5 tests · +160 XP
Discussion
Sign in to postSign in to join the conversation.Sign in
No comments yet. Be the first to say something.