Skip to content

Commit fbe6874

Browse files
authored
feat: Accordion EDS 2.0 (#4912)
1 parent 6448306 commit fbe6874

9 files changed

Lines changed: 728 additions & 0 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import figma from '@figma/code-connect'
2+
import { Accordion } from '.'
3+
4+
figma.connect(
5+
Accordion,
6+
'https://www.figma.com/design/dz0XQdc5j7AAtjXr1gTfVR/EDS-Core-Components?node-id=6600-15092&m=dev',
7+
{
8+
props: {
9+
defaultOpen: figma.enum('Open', { On: true, Off: false }),
10+
title: figma.string('Title'),
11+
content: figma.string('Content'),
12+
},
13+
example: ({ defaultOpen, title, content }) => (
14+
<Accordion>
15+
<Accordion.Item defaultOpen={defaultOpen}>
16+
<Accordion.Header>{title}</Accordion.Header>
17+
<Accordion.Panel>{content}</Accordion.Panel>
18+
</Accordion.Item>
19+
</Accordion>
20+
),
21+
},
22+
)
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import { useState } from 'react'
2+
import type { Meta, StoryFn, StoryObj } from '@storybook/react-vite'
3+
import { Accordion } from '.'
4+
import type { AccordionProps } from '.'
5+
6+
const meta: Meta<typeof Accordion> = {
7+
title: 'EDS 2.0 (beta)/Surface/Accordion',
8+
component: Accordion,
9+
tags: ['beta'],
10+
parameters: {
11+
docs: {
12+
description: {
13+
component: `
14+
⚠️ **Beta Component** - This component is under active development.
15+
16+
Built on the native \`<details>\`/\`<summary>\` elements for free accessibility,
17+
keyboard support and CSS-only open/close animation.
18+
19+
\`\`\`tsx
20+
import { Accordion } from '@equinor/eds-core-react/next'
21+
22+
<Accordion>
23+
<Accordion.Item>
24+
<Accordion.Header>Title</Accordion.Header>
25+
<Accordion.Panel>Content</Accordion.Panel>
26+
</Accordion.Item>
27+
</Accordion>
28+
\`\`\`
29+
`,
30+
},
31+
},
32+
},
33+
argTypes: {
34+
exclusive: {
35+
control: 'boolean',
36+
description:
37+
'When true, only one item can be open at a time (native HTML group via the `name` attribute).',
38+
},
39+
},
40+
args: {
41+
exclusive: false,
42+
},
43+
}
44+
45+
export default meta
46+
47+
export const Introduction: StoryFn<AccordionProps> = (args) => (
48+
<Accordion {...args}>
49+
<Accordion.Item defaultOpen>
50+
<Accordion.Header>First section</Accordion.Header>
51+
<Accordion.Panel>
52+
Accordions help manage information density while keeping pages
53+
scannable.
54+
</Accordion.Panel>
55+
</Accordion.Item>
56+
<Accordion.Item>
57+
<Accordion.Header>Second section</Accordion.Header>
58+
<Accordion.Panel>
59+
Each section is a native &lt;details&gt; element, so keyboard and screen
60+
reader behaviour comes for free.
61+
</Accordion.Panel>
62+
</Accordion.Item>
63+
<Accordion.Item>
64+
<Accordion.Header>Third section</Accordion.Header>
65+
<Accordion.Panel>
66+
Set <code>exclusive</code> on the root to only allow one open at a time.
67+
</Accordion.Panel>
68+
</Accordion.Item>
69+
</Accordion>
70+
)
71+
72+
export const Exclusive: StoryObj<AccordionProps> = {
73+
args: { exclusive: true },
74+
render: (args) => (
75+
<Accordion {...args}>
76+
<Accordion.Item>
77+
<Accordion.Header>Only one open at a time</Accordion.Header>
78+
<Accordion.Panel>
79+
Opening another section closes this one automatically.
80+
</Accordion.Panel>
81+
</Accordion.Item>
82+
<Accordion.Item>
83+
<Accordion.Header>Try me</Accordion.Header>
84+
<Accordion.Panel>
85+
The exclusive grouping uses the native HTML <code>name</code>{' '}
86+
attribute on <code>&lt;details&gt;</code>.
87+
</Accordion.Panel>
88+
</Accordion.Item>
89+
<Accordion.Item>
90+
<Accordion.Header>And me</Accordion.Header>
91+
<Accordion.Panel>No JS state machine needed.</Accordion.Panel>
92+
</Accordion.Item>
93+
</Accordion>
94+
),
95+
}
96+
97+
export const Controlled: StoryFn = () => {
98+
const [open, setOpen] = useState(true)
99+
return (
100+
<Accordion>
101+
<Accordion.Item open={open} onOpenChange={setOpen}>
102+
<Accordion.Header>Controlled item</Accordion.Header>
103+
<Accordion.Panel>
104+
Open state is held by the parent component. Currently:{' '}
105+
<strong>{open ? 'open' : 'closed'}</strong>.
106+
</Accordion.Panel>
107+
</Accordion.Item>
108+
</Accordion>
109+
)
110+
}
111+
112+
const SchemePreview = () => (
113+
<Accordion>
114+
<Accordion.Item defaultOpen>
115+
<Accordion.Header>Open by default</Accordion.Header>
116+
<Accordion.Panel>
117+
Title is held at <code>--eds-color-text-accent-subtle</code> when open.
118+
Content uses <code>--eds-color-text-subtle</code>.
119+
</Accordion.Panel>
120+
</Accordion.Item>
121+
<Accordion.Item>
122+
<Accordion.Header>Closed</Accordion.Header>
123+
<Accordion.Panel>Hidden until opened.</Accordion.Panel>
124+
</Accordion.Item>
125+
<Accordion.Item>
126+
<Accordion.Header>Hover or focus me</Accordion.Header>
127+
<Accordion.Panel>
128+
Hover bg <code>--eds-color-bg-fill-muted-default</code>; focus outline{' '}
129+
<code>--eds-color-border-focus</code>.
130+
</Accordion.Panel>
131+
</Accordion.Item>
132+
</Accordion>
133+
)
134+
135+
const DensityPreview = () => (
136+
<Accordion>
137+
<Accordion.Item defaultOpen>
138+
<Accordion.Header>First section</Accordion.Header>
139+
<Accordion.Panel>
140+
Spacing and sizes pick up the ancestor <code>data-density</code> value.
141+
</Accordion.Panel>
142+
</Accordion.Item>
143+
<Accordion.Item>
144+
<Accordion.Header>Second section</Accordion.Header>
145+
<Accordion.Panel>Content hidden until opened.</Accordion.Panel>
146+
</Accordion.Item>
147+
</Accordion>
148+
)
149+
150+
/**
151+
* Accordion respects the `data-density` attribute on an ancestor element so
152+
* spacing and sizing tokens stay coordinated with sibling components.
153+
*/
154+
export const Density: StoryFn = () => (
155+
<div style={{ display: 'flex', flexDirection: 'column', gap: 32 }}>
156+
<div data-density="spacious">
157+
<h3 style={{ margin: '0 0 12px' }}>Spacious (default)</h3>
158+
<DensityPreview />
159+
</div>
160+
<div data-density="comfortable">
161+
<h3 style={{ margin: '0 0 12px' }}>Comfortable</h3>
162+
<DensityPreview />
163+
</div>
164+
</div>
165+
)
166+
167+
/**
168+
* Side-by-side preview of the Accordion in both color schemes — used to
169+
* verify dark theme since Storybook's own dark-mode toggle is unreliable.
170+
*/
171+
export const ColorSchemes: StoryFn = () => (
172+
<div style={{ display: 'flex', gap: 32, alignItems: 'flex-start' }}>
173+
<div
174+
data-color-scheme="light"
175+
style={{
176+
flex: 1,
177+
padding: 24,
178+
background: 'var(--eds-color-bg-canvas)',
179+
color: 'var(--eds-color-text-strong)',
180+
}}
181+
>
182+
<h3 style={{ marginTop: 0 }}>Light</h3>
183+
<SchemePreview />
184+
</div>
185+
<div
186+
data-color-scheme="dark"
187+
style={{
188+
flex: 1,
189+
padding: 24,
190+
background: 'var(--eds-color-bg-canvas)',
191+
color: 'var(--eds-color-text-strong)',
192+
}}
193+
>
194+
<h3 style={{ marginTop: 0 }}>Dark</h3>
195+
<SchemePreview />
196+
</div>
197+
</div>
198+
)

0 commit comments

Comments
 (0)