Skip to content

Conversation

@akdetrick
Copy link
Collaborator

@akdetrick akdetrick commented Dec 9, 2025

Closes NDS-2059

Introduces "headless" accordion layout components. Engineers may pass arbitrary JSX to the summary (trigger) and the content to create any style of accordion-like UI.


Screenshot 2025-12-09 at 4 43 09 PM Screenshot 2025-12-09 at 4 43 37 PM Screenshot 2025-12-09 at 4 52 04 PM

@akdetrick akdetrick changed the title Add Accordion, AccordionSet components Add Accordion, AccordionSet components 🪗 Dec 9, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces two new "headless" layout components (Accordion and AccordionSet) to the Narmi Design System, providing flexible accordion UI patterns where consumers control the presentation through render props. The components support both controlled and uncontrolled modes, with AccordionSet offering exclusive (one-at-a-time) or native (multiple-open) behaviors.

Key Changes

  • Added Accordion component with renderSummary render prop for customizable triggers and content
  • Added AccordionSet wrapper component to coordinate multiple accordions with exclusive or native behavior modes
  • Implemented accessibility features including ARIA attributes (aria-expanded, aria-controls, aria-hidden) and proper button semantics

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/Accordion/index.tsx Core accordion component with controlled/uncontrolled state management and accessibility attributes
src/Accordion/index.scss CSS transitions using max-height and interpolate-size for smooth expand/collapse animations
src/Accordion/index.stories.tsx Comprehensive Storybook examples demonstrating custom styling, focusable content, and controlled mode
src/AccordionSet/index.tsx Wrapper component managing multiple accordions with exclusive or native behavior modes
src/AccordionSet/index.stories.tsx Storybook examples showing exclusive/native modes and custom styling patterns
src/index.ts Added TypeScript exports for Accordion and AccordionSet components
src/index.js Added JavaScript exports for Accordion and AccordionSet components
src/index.scss Imported Accordion SCSS for component styling

Comment on lines 44 to 62
// In native mode, let each accordion manage its own state
const shouldControlAccordion = behavior === "exclusive";
const isThisAccordionOpen = openAccordionIndex === index;

const handleToggle = (newIsOpen: boolean) => {
if (shouldControlAccordion) {
setOpenAccordionIndex(newIsOpen ? index : null);
}
// Fire callback even if uncontrolled in an AccordionSet
child.props.onUserToggle?.(newIsOpen);
};

return React.cloneElement(child, {
key: index,
...(shouldControlAccordion && {
isOpen: isThisAccordionOpen,
onUserToggle: handleToggle,
}),
});
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] In "native" mode, the AccordionSet doesn't add any behavior beyond wrapping accordions in a div. The handleToggle function is only passed to children in exclusive mode (line 58-61). In native mode, each Accordion already has its own uncontrolled state management. Consider documenting when and why to use AccordionSet with "native" mode, or if it provides semantic/accessibility benefits beyond a plain div wrapper.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine.

Comment on lines 56 to 61
return React.cloneElement(child, {
key: index,
...(shouldControlAccordion && {
isOpen: isThisAccordionOpen,
onUserToggle: handleToggle,
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not familiar with cloneElement but it seems like a smart use case. Does this link all of the child instances of onUserToggle so that exclusive mode opening one of the children will close all others?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly - cloneElement is basically a way to intercept a react child and modify its props.

AccordionSet can use this to manage "exclusive" mode, where only one child opens at a time

Copy link
Contributor

Copilot AI commented Dec 9, 2025

@akdetrick I've opened a new pull request, #1944, to work on those changes. Once the pull request is ready, I'll request review from you.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions
Copy link

github-actions bot commented Dec 9, 2025

✅ No visual changes detected

View Chromatic Build

📘 Storybook preview

View Storybook preview


This PR is posted and updated automatically by the Chromatic action

Copilot AI and others added 5 commits December 9, 2025 23:14
Co-authored-by: akdetrick <231252+akdetrick@users.noreply.github.com>
[WIP] Update Accordion components to use useId for labelledby attribute
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions
Copy link

github-actions bot commented Dec 9, 2025

✅ No visual changes detected

View Chromatic Build

📘 Storybook preview

View Storybook preview


This PR is posted and updated automatically by the Chromatic action

@github-actions
Copy link

github-actions bot commented Dec 9, 2025

✅ No visual changes detected

View Chromatic Build

📘 Storybook preview

View Storybook preview


This PR is posted and updated automatically by the Chromatic action

@github-actions
Copy link

github-actions bot commented Dec 9, 2025

✅ No visual changes detected

View Chromatic Build

📘 Storybook preview

View Storybook preview


This PR is posted and updated automatically by the Chromatic action

@github-actions
Copy link

github-actions bot commented Dec 9, 2025

✅ No visual changes detected

View Chromatic Build

📘 Storybook preview

View Storybook preview


This PR is posted and updated automatically by the Chromatic action

Copy link
Contributor

@graphicsforge graphicsforge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes a lot of sense to me, we have a lot of added customizability with the renderSummary prop getting the isOpen state, and the exclusive AccordionSet allows us to manage relationships between multiple Accordions.

@akdetrick akdetrick merged commit ffeeb93 into main Dec 11, 2025
10 of 11 checks passed
@akdetrick akdetrick deleted the feat/Accordion branch December 11, 2025 21:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants