@ceed/cds 1.29.0 → 1.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Overview.md +5 -5
- package/dist/components/data-display/Avatar.md +110 -69
- package/dist/components/data-display/Badge.md +91 -39
- package/dist/components/data-display/Chip.md +49 -20
- package/dist/components/data-display/DataTable.md +93 -0
- package/dist/components/data-display/InfoSign.md +12 -0
- package/dist/components/data-display/Table.md +72 -55
- package/dist/components/data-display/Tooltip.md +40 -40
- package/dist/components/data-display/Typography.md +53 -70
- package/dist/components/feedback/Alert.md +88 -72
- package/dist/components/feedback/CircularProgress.md +17 -0
- package/dist/components/feedback/Skeleton.md +17 -0
- package/dist/components/inputs/Button.md +94 -68
- package/dist/components/inputs/ButtonGroup.md +17 -0
- package/dist/components/inputs/Calendar.md +118 -457
- package/dist/components/inputs/Checkbox.md +185 -430
- package/dist/components/inputs/CurrencyInput.md +22 -0
- package/dist/components/inputs/DatePicker.md +36 -0
- package/dist/components/inputs/DateRangePicker.md +26 -0
- package/dist/components/inputs/FilterableCheckboxGroup.md +20 -3
- package/dist/components/inputs/FormControl.md +32 -2
- package/dist/components/inputs/IconButton.md +18 -0
- package/dist/components/inputs/Input.md +198 -136
- package/dist/components/inputs/MonthPicker.md +25 -0
- package/dist/components/inputs/MonthRangePicker.md +23 -0
- package/dist/components/inputs/PercentageInput.md +25 -0
- package/dist/components/inputs/RadioButton.md +23 -0
- package/dist/components/inputs/RadioList.md +20 -1
- package/dist/components/inputs/RadioTileGroup.md +37 -3
- package/dist/components/inputs/Select.md +56 -0
- package/dist/components/inputs/Slider.md +23 -0
- package/dist/components/inputs/Switch.md +20 -0
- package/dist/components/inputs/Textarea.md +32 -8
- package/dist/components/inputs/Uploader/Uploader.md +21 -0
- package/dist/components/layout/Box.md +52 -41
- package/dist/components/layout/Grid.md +87 -81
- package/dist/components/layout/Stack.md +88 -68
- package/dist/components/navigation/Breadcrumbs.md +57 -46
- package/dist/components/navigation/Drawer.md +17 -0
- package/dist/components/navigation/Dropdown.md +13 -0
- package/dist/components/navigation/IconMenuButton.md +17 -0
- package/dist/components/navigation/InsetDrawer.md +130 -292
- package/dist/components/navigation/Link.md +78 -0
- package/dist/components/navigation/Menu.md +17 -0
- package/dist/components/navigation/MenuButton.md +18 -0
- package/dist/components/navigation/Pagination.md +13 -0
- package/dist/components/navigation/Stepper.md +15 -0
- package/dist/components/navigation/Tabs.md +27 -0
- package/dist/components/surfaces/Accordions.md +804 -49
- package/dist/components/surfaces/Card.md +173 -97
- package/dist/components/surfaces/Divider.md +246 -82
- package/dist/components/surfaces/Sheet.md +15 -0
- package/package.json +1 -1
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
Accordions is a component that displays a vertically stacked list of collapsible sections
|
|
6
|
-
|
|
7
|
-
Accordions are ideal for FAQ pages, settings panels, product detail sections, and any content that benefits from being grouped into expandable categories. The component supports multiple variants, colors, and sizes for flexible visual customization.
|
|
5
|
+
Accordions is a component that displays a vertically stacked list of collapsible sections, each with a header that users can click to expand or collapse the content below. It efficiently organizes content by allowing users to focus on one section at a time while keeping other sections collapsed, reducing visual clutter and improving content navigation. Accordions are commonly used for FAQs, settings panels, navigation menus, and any content that benefits from progressive disclosure.
|
|
8
6
|
|
|
9
7
|
```tsx
|
|
10
8
|
<Accordions {...args} />
|
|
@@ -14,6 +12,15 @@ Accordions are ideal for FAQ pages, settings panels, product detail sections, an
|
|
|
14
12
|
| ----- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
15
13
|
| items | Array of accordion summaries and details | \[\{ summary: 'First header', details: \<h3>Content of the first accordion.\</h3> }, \{ summary: 'Second header', details: \<h3>Content of the second accordion.\</h3> }, \{ summary: 'Third header', details: \<h3>Content of the third accordion.\</h3> }] |
|
|
16
14
|
|
|
15
|
+
> ⚠️ **Usage Warning** ⚠️
|
|
16
|
+
>
|
|
17
|
+
> Consider these guidelines when using Accordions:
|
|
18
|
+
>
|
|
19
|
+
> - **Don't hide critical content**: Important information should be visible by default
|
|
20
|
+
> - **Limit nesting**: Avoid nesting accordions within accordions
|
|
21
|
+
> - **Keep headers concise**: Users should understand content without expanding
|
|
22
|
+
> - **Consider Tabs**: Use Tabs for content that users need to compare side-by-side
|
|
23
|
+
|
|
17
24
|
## Usage
|
|
18
25
|
|
|
19
26
|
```tsx
|
|
@@ -41,9 +48,19 @@ function FAQSection() {
|
|
|
41
48
|
}
|
|
42
49
|
```
|
|
43
50
|
|
|
44
|
-
##
|
|
51
|
+
## Examples
|
|
52
|
+
|
|
53
|
+
### Playground
|
|
54
|
+
|
|
55
|
+
Interactive example with all controls.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<Accordions {...args} />
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Basics
|
|
45
62
|
|
|
46
|
-
|
|
63
|
+
Basic accordion with multiple collapsible sections.
|
|
47
64
|
|
|
48
65
|
```tsx
|
|
49
66
|
<Accordions
|
|
@@ -60,9 +77,9 @@ A basic accordion with multiple collapsible sections.
|
|
|
60
77
|
/>
|
|
61
78
|
```
|
|
62
79
|
|
|
63
|
-
|
|
80
|
+
### Sizes
|
|
64
81
|
|
|
65
|
-
Available
|
|
82
|
+
Available size options: `sm`, `md`, `lg`.
|
|
66
83
|
|
|
67
84
|
```tsx
|
|
68
85
|
<>
|
|
@@ -72,9 +89,9 @@ Available in three sizes: `sm`, `md`, and `lg`. Use smaller sizes for sidebars o
|
|
|
72
89
|
</>
|
|
73
90
|
```
|
|
74
91
|
|
|
75
|
-
|
|
92
|
+
### Variants
|
|
76
93
|
|
|
77
|
-
|
|
94
|
+
Style variants: `plain`, `outlined`, `soft`, `solid`.
|
|
78
95
|
|
|
79
96
|
```tsx
|
|
80
97
|
<>
|
|
@@ -97,9 +114,9 @@ Four style variants are available: `plain`, `outlined`, `soft`, and `solid`. Cho
|
|
|
97
114
|
</>
|
|
98
115
|
```
|
|
99
116
|
|
|
100
|
-
|
|
117
|
+
### Colors
|
|
101
118
|
|
|
102
|
-
|
|
119
|
+
Color options: `primary`, `neutral`, `danger`, `success`, `warning`.
|
|
103
120
|
|
|
104
121
|
```tsx
|
|
105
122
|
<>
|
|
@@ -126,21 +143,40 @@ Apply semantic colors to match the accordion's purpose: `primary`, `neutral`, `d
|
|
|
126
143
|
</>
|
|
127
144
|
```
|
|
128
145
|
|
|
129
|
-
|
|
146
|
+
### Removing Divider
|
|
130
147
|
|
|
131
|
-
Use
|
|
148
|
+
Use `disableDivider` to remove the divider lines between accordion items.
|
|
132
149
|
|
|
133
150
|
```tsx
|
|
134
151
|
<Accordions {...args} disableDivider />
|
|
135
152
|
```
|
|
136
153
|
|
|
154
|
+
## When to Use
|
|
155
|
+
|
|
156
|
+
### ✅ Good Use Cases
|
|
157
|
+
|
|
158
|
+
- **FAQs**: Frequently asked questions with expandable answers
|
|
159
|
+
- **Settings panels**: Grouped settings that can be expanded individually
|
|
160
|
+
- **Product details**: Specifications, reviews, shipping info sections
|
|
161
|
+
- **Documentation**: Collapsible sections for detailed explanations
|
|
162
|
+
- **Navigation menus**: Expandable category menus
|
|
163
|
+
- **Form sections**: Long forms grouped into logical sections
|
|
164
|
+
- **Help content**: Step-by-step instructions or troubleshooting guides
|
|
165
|
+
|
|
166
|
+
### ❌ When Not to Use
|
|
167
|
+
|
|
168
|
+
- **Critical information**: Don't hide must-read content in accordions
|
|
169
|
+
- **Short content**: Use a list or cards if content is minimal
|
|
170
|
+
- **Comparison content**: Use Tabs when users need to compare sections
|
|
171
|
+
- **Primary navigation**: Use Navigator or Tabs for main navigation
|
|
172
|
+
- **Single item**: Don't use accordion for a single collapsible section
|
|
173
|
+
- **Deeply nested content**: Avoid nesting accordions within accordions
|
|
174
|
+
|
|
137
175
|
## Common Use Cases
|
|
138
176
|
|
|
139
177
|
### FAQ Section
|
|
140
178
|
|
|
141
179
|
```tsx
|
|
142
|
-
import { Accordions, Typography, Stack, Box } from '@ceed/cds';
|
|
143
|
-
|
|
144
180
|
function FAQAccordion() {
|
|
145
181
|
const faqs = [
|
|
146
182
|
{
|
|
@@ -148,7 +184,7 @@ function FAQAccordion() {
|
|
|
148
184
|
details: (
|
|
149
185
|
<Typography>
|
|
150
186
|
We accept Visa, MasterCard, American Express, and PayPal.
|
|
151
|
-
All payments are processed securely.
|
|
187
|
+
All payments are processed securely through our payment gateway.
|
|
152
188
|
</Typography>
|
|
153
189
|
),
|
|
154
190
|
},
|
|
@@ -156,8 +192,9 @@ function FAQAccordion() {
|
|
|
156
192
|
summary: 'How can I track my order?',
|
|
157
193
|
details: (
|
|
158
194
|
<Typography>
|
|
159
|
-
Once your order ships, you
|
|
160
|
-
number
|
|
195
|
+
Once your order ships, you'll receive an email with a tracking
|
|
196
|
+
number. You can use this number on our website or the carrier's
|
|
197
|
+
site to track your package.
|
|
161
198
|
</Typography>
|
|
162
199
|
),
|
|
163
200
|
},
|
|
@@ -190,9 +227,7 @@ function FAQAccordion() {
|
|
|
190
227
|
### Settings Panel
|
|
191
228
|
|
|
192
229
|
```tsx
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
function SettingsPanel() {
|
|
230
|
+
function SettingsAccordion({ settings, onSettingChange }) {
|
|
196
231
|
const settingsItems = [
|
|
197
232
|
{
|
|
198
233
|
summary: 'Account Settings',
|
|
@@ -200,11 +235,18 @@ function SettingsPanel() {
|
|
|
200
235
|
<Stack gap={2}>
|
|
201
236
|
<FormControl>
|
|
202
237
|
<FormLabel>Display Name</FormLabel>
|
|
203
|
-
<Input
|
|
238
|
+
<Input
|
|
239
|
+
value={settings.displayName}
|
|
240
|
+
onChange={(e) => onSettingChange('displayName', e.target.value)}
|
|
241
|
+
/>
|
|
204
242
|
</FormControl>
|
|
205
243
|
<FormControl>
|
|
206
244
|
<FormLabel>Email</FormLabel>
|
|
207
|
-
<Input
|
|
245
|
+
<Input
|
|
246
|
+
type="email"
|
|
247
|
+
value={settings.email}
|
|
248
|
+
onChange={(e) => onSettingChange('email', e.target.value)}
|
|
249
|
+
/>
|
|
208
250
|
</FormControl>
|
|
209
251
|
</Stack>
|
|
210
252
|
),
|
|
@@ -213,32 +255,179 @@ function SettingsPanel() {
|
|
|
213
255
|
summary: 'Notification Preferences',
|
|
214
256
|
details: (
|
|
215
257
|
<Stack gap={1}>
|
|
216
|
-
<Checkbox
|
|
217
|
-
|
|
218
|
-
|
|
258
|
+
<Checkbox
|
|
259
|
+
label="Email notifications"
|
|
260
|
+
checked={settings.emailNotifications}
|
|
261
|
+
onChange={(e) =>
|
|
262
|
+
onSettingChange('emailNotifications', e.target.checked)
|
|
263
|
+
}
|
|
264
|
+
/>
|
|
265
|
+
<Checkbox
|
|
266
|
+
label="Push notifications"
|
|
267
|
+
checked={settings.pushNotifications}
|
|
268
|
+
onChange={(e) =>
|
|
269
|
+
onSettingChange('pushNotifications', e.target.checked)
|
|
270
|
+
}
|
|
271
|
+
/>
|
|
272
|
+
<Checkbox
|
|
273
|
+
label="Weekly digest"
|
|
274
|
+
checked={settings.weeklyDigest}
|
|
275
|
+
onChange={(e) => onSettingChange('weeklyDigest', e.target.checked)}
|
|
276
|
+
/>
|
|
277
|
+
</Stack>
|
|
278
|
+
),
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
summary: 'Privacy Settings',
|
|
282
|
+
details: (
|
|
283
|
+
<Stack gap={1}>
|
|
284
|
+
<Checkbox
|
|
285
|
+
label="Make profile public"
|
|
286
|
+
checked={settings.publicProfile}
|
|
287
|
+
onChange={(e) => onSettingChange('publicProfile', e.target.checked)}
|
|
288
|
+
/>
|
|
289
|
+
<Checkbox
|
|
290
|
+
label="Show activity status"
|
|
291
|
+
checked={settings.showActivity}
|
|
292
|
+
onChange={(e) => onSettingChange('showActivity', e.target.checked)}
|
|
293
|
+
/>
|
|
219
294
|
</Stack>
|
|
220
295
|
),
|
|
221
296
|
},
|
|
222
297
|
];
|
|
223
298
|
|
|
224
|
-
return
|
|
299
|
+
return (
|
|
300
|
+
<Box sx={{ maxWidth: 500 }}>
|
|
301
|
+
<Typography level="h3" sx={{ mb: 2 }}>
|
|
302
|
+
Settings
|
|
303
|
+
</Typography>
|
|
304
|
+
<Accordions items={settingsItems} variant="soft" color="neutral" />
|
|
305
|
+
</Box>
|
|
306
|
+
);
|
|
225
307
|
}
|
|
226
308
|
```
|
|
227
309
|
|
|
228
|
-
###
|
|
310
|
+
### Product Details
|
|
229
311
|
|
|
230
312
|
```tsx
|
|
231
|
-
|
|
313
|
+
function ProductAccordion({ product }) {
|
|
314
|
+
const productDetails = [
|
|
315
|
+
{
|
|
316
|
+
summary: 'Description',
|
|
317
|
+
details: (
|
|
318
|
+
<Typography>{product.description}</Typography>
|
|
319
|
+
),
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
summary: 'Specifications',
|
|
323
|
+
details: (
|
|
324
|
+
<Table>
|
|
325
|
+
<tbody>
|
|
326
|
+
{Object.entries(product.specs).map(([key, value]) => (
|
|
327
|
+
<tr key={key}>
|
|
328
|
+
<td>
|
|
329
|
+
<Typography level="body-sm" fontWeight="md">
|
|
330
|
+
{key}
|
|
331
|
+
</Typography>
|
|
332
|
+
</td>
|
|
333
|
+
<td>
|
|
334
|
+
<Typography level="body-sm">{value}</Typography>
|
|
335
|
+
</td>
|
|
336
|
+
</tr>
|
|
337
|
+
))}
|
|
338
|
+
</tbody>
|
|
339
|
+
</Table>
|
|
340
|
+
),
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
summary: 'Shipping & Returns',
|
|
344
|
+
details: (
|
|
345
|
+
<Stack gap={2}>
|
|
346
|
+
<Box>
|
|
347
|
+
<Typography level="title-sm">Shipping</Typography>
|
|
348
|
+
<Typography level="body-sm">
|
|
349
|
+
Free shipping on orders over $50. Standard delivery 3-5 days.
|
|
350
|
+
</Typography>
|
|
351
|
+
</Box>
|
|
352
|
+
<Box>
|
|
353
|
+
<Typography level="title-sm">Returns</Typography>
|
|
354
|
+
<Typography level="body-sm">
|
|
355
|
+
30-day return policy. Items must be unused with original tags.
|
|
356
|
+
</Typography>
|
|
357
|
+
</Box>
|
|
358
|
+
</Stack>
|
|
359
|
+
),
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
summary: `Reviews (${product.reviewCount})`,
|
|
363
|
+
details: (
|
|
364
|
+
<Stack gap={2}>
|
|
365
|
+
{product.reviews.slice(0, 3).map((review) => (
|
|
366
|
+
<Card key={review.id} variant="outlined">
|
|
367
|
+
<CardContent>
|
|
368
|
+
<Stack direction="row" justifyContent="space-between">
|
|
369
|
+
<Typography level="title-sm">{review.author}</Typography>
|
|
370
|
+
<Typography level="body-xs" color="neutral">
|
|
371
|
+
{review.date}
|
|
372
|
+
</Typography>
|
|
373
|
+
</Stack>
|
|
374
|
+
<Typography level="body-sm">{review.content}</Typography>
|
|
375
|
+
</CardContent>
|
|
376
|
+
</Card>
|
|
377
|
+
))}
|
|
378
|
+
<Button variant="plain" size="sm">
|
|
379
|
+
View all reviews
|
|
380
|
+
</Button>
|
|
381
|
+
</Stack>
|
|
382
|
+
),
|
|
383
|
+
},
|
|
384
|
+
];
|
|
385
|
+
|
|
386
|
+
return <Accordions items={productDetails} variant="plain" />;
|
|
387
|
+
}
|
|
388
|
+
```
|
|
232
389
|
|
|
233
|
-
|
|
390
|
+
### Navigation Menu
|
|
391
|
+
|
|
392
|
+
```tsx
|
|
393
|
+
function NavigationAccordion() {
|
|
234
394
|
const menuItems = [
|
|
235
395
|
{
|
|
236
396
|
summary: 'Products',
|
|
237
397
|
details: (
|
|
238
398
|
<List size="sm">
|
|
239
|
-
<ListItem
|
|
240
|
-
|
|
241
|
-
|
|
399
|
+
<ListItem>
|
|
400
|
+
<ListItemButton onClick={() => navigate('/products/electronics')}>
|
|
401
|
+
Electronics
|
|
402
|
+
</ListItemButton>
|
|
403
|
+
</ListItem>
|
|
404
|
+
<ListItem>
|
|
405
|
+
<ListItemButton onClick={() => navigate('/products/clothing')}>
|
|
406
|
+
Clothing
|
|
407
|
+
</ListItemButton>
|
|
408
|
+
</ListItem>
|
|
409
|
+
<ListItem>
|
|
410
|
+
<ListItemButton onClick={() => navigate('/products/home')}>
|
|
411
|
+
Home & Garden
|
|
412
|
+
</ListItemButton>
|
|
413
|
+
</ListItem>
|
|
414
|
+
</List>
|
|
415
|
+
),
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
summary: 'Services',
|
|
419
|
+
details: (
|
|
420
|
+
<List size="sm">
|
|
421
|
+
<ListItem>
|
|
422
|
+
<ListItemButton onClick={() => navigate('/services/consulting')}>
|
|
423
|
+
Consulting
|
|
424
|
+
</ListItemButton>
|
|
425
|
+
</ListItem>
|
|
426
|
+
<ListItem>
|
|
427
|
+
<ListItemButton onClick={() => navigate('/services/support')}>
|
|
428
|
+
Support
|
|
429
|
+
</ListItemButton>
|
|
430
|
+
</ListItem>
|
|
242
431
|
</List>
|
|
243
432
|
),
|
|
244
433
|
},
|
|
@@ -246,8 +435,21 @@ function SidebarMenu() {
|
|
|
246
435
|
summary: 'Resources',
|
|
247
436
|
details: (
|
|
248
437
|
<List size="sm">
|
|
249
|
-
<ListItem
|
|
250
|
-
|
|
438
|
+
<ListItem>
|
|
439
|
+
<ListItemButton onClick={() => navigate('/docs')}>
|
|
440
|
+
Documentation
|
|
441
|
+
</ListItemButton>
|
|
442
|
+
</ListItem>
|
|
443
|
+
<ListItem>
|
|
444
|
+
<ListItemButton onClick={() => navigate('/blog')}>
|
|
445
|
+
Blog
|
|
446
|
+
</ListItemButton>
|
|
447
|
+
</ListItem>
|
|
448
|
+
<ListItem>
|
|
449
|
+
<ListItemButton onClick={() => navigate('/tutorials')}>
|
|
450
|
+
Tutorials
|
|
451
|
+
</ListItemButton>
|
|
452
|
+
</ListItem>
|
|
251
453
|
</List>
|
|
252
454
|
),
|
|
253
455
|
},
|
|
@@ -261,25 +463,578 @@ function SidebarMenu() {
|
|
|
261
463
|
}
|
|
262
464
|
```
|
|
263
465
|
|
|
264
|
-
|
|
466
|
+
### Help Documentation
|
|
265
467
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
468
|
+
```tsx
|
|
469
|
+
function HelpAccordion() {
|
|
470
|
+
const helpTopics = [
|
|
471
|
+
{
|
|
472
|
+
summary: 'Getting Started',
|
|
473
|
+
details: (
|
|
474
|
+
<Stack gap={2}>
|
|
475
|
+
<Typography level="title-sm">Step 1: Create an Account</Typography>
|
|
476
|
+
<Typography level="body-sm">
|
|
477
|
+
Click the "Sign Up" button and fill in your details.
|
|
478
|
+
</Typography>
|
|
479
|
+
<Typography level="title-sm">Step 2: Verify Email</Typography>
|
|
480
|
+
<Typography level="body-sm">
|
|
481
|
+
Check your inbox for a verification email and click the link.
|
|
482
|
+
</Typography>
|
|
483
|
+
<Typography level="title-sm">Step 3: Complete Profile</Typography>
|
|
484
|
+
<Typography level="body-sm">
|
|
485
|
+
Add your profile picture and bio to personalize your account.
|
|
486
|
+
</Typography>
|
|
487
|
+
</Stack>
|
|
488
|
+
),
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
summary: 'Troubleshooting',
|
|
492
|
+
details: (
|
|
493
|
+
<Stack gap={2}>
|
|
494
|
+
<Box>
|
|
495
|
+
<Typography level="title-sm" color="danger">
|
|
496
|
+
Can't log in?
|
|
497
|
+
</Typography>
|
|
498
|
+
<Typography level="body-sm">
|
|
499
|
+
Try resetting your password or clearing your browser cache.
|
|
500
|
+
</Typography>
|
|
501
|
+
</Box>
|
|
502
|
+
<Box>
|
|
503
|
+
<Typography level="title-sm" color="danger">
|
|
504
|
+
Page not loading?
|
|
505
|
+
</Typography>
|
|
506
|
+
<Typography level="body-sm">
|
|
507
|
+
Check your internet connection and try refreshing the page.
|
|
508
|
+
</Typography>
|
|
509
|
+
</Box>
|
|
510
|
+
</Stack>
|
|
511
|
+
),
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
summary: 'Contact Support',
|
|
515
|
+
details: (
|
|
516
|
+
<Stack gap={2}>
|
|
517
|
+
<Typography level="body-sm">
|
|
518
|
+
Need more help? Reach out to our support team:
|
|
519
|
+
</Typography>
|
|
520
|
+
<Stack gap={1}>
|
|
521
|
+
<Typography level="body-sm">
|
|
522
|
+
📧 Email: support@example.com
|
|
523
|
+
</Typography>
|
|
524
|
+
<Typography level="body-sm">
|
|
525
|
+
💬 Live Chat: Available 9am-5pm EST
|
|
526
|
+
</Typography>
|
|
527
|
+
<Typography level="body-sm">
|
|
528
|
+
📞 Phone: 1-800-EXAMPLE
|
|
529
|
+
</Typography>
|
|
530
|
+
</Stack>
|
|
531
|
+
</Stack>
|
|
532
|
+
),
|
|
533
|
+
},
|
|
534
|
+
];
|
|
535
|
+
|
|
536
|
+
return (
|
|
537
|
+
<Box sx={{ maxWidth: 600 }}>
|
|
538
|
+
<Typography level="h2" sx={{ mb: 2 }}>
|
|
539
|
+
Help Center
|
|
540
|
+
</Typography>
|
|
541
|
+
<Accordions items={helpTopics} variant="outlined" color="primary" />
|
|
542
|
+
</Box>
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
```
|
|
269
546
|
|
|
270
|
-
|
|
547
|
+
### Collapsible Form Sections
|
|
271
548
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
549
|
+
```tsx
|
|
550
|
+
function MultiSectionForm({ onSubmit }) {
|
|
551
|
+
const [formData, setFormData] = useState({
|
|
552
|
+
personal: { firstName: '', lastName: '', email: '' },
|
|
553
|
+
address: { street: '', city: '', zipCode: '' },
|
|
554
|
+
payment: { cardNumber: '', expiry: '', cvv: '' },
|
|
555
|
+
});
|
|
275
556
|
|
|
276
|
-
|
|
557
|
+
const updateField = (section, field, value) => {
|
|
558
|
+
setFormData((prev) => ({
|
|
559
|
+
...prev,
|
|
560
|
+
[section]: { ...prev[section], [field]: value },
|
|
561
|
+
}));
|
|
562
|
+
};
|
|
277
563
|
|
|
278
|
-
|
|
564
|
+
const formSections = [
|
|
565
|
+
{
|
|
566
|
+
summary: 'Personal Information',
|
|
567
|
+
details: (
|
|
568
|
+
<Stack gap={2}>
|
|
569
|
+
<Stack direction="row" gap={2}>
|
|
570
|
+
<FormControl sx={{ flex: 1 }}>
|
|
571
|
+
<FormLabel>First Name</FormLabel>
|
|
572
|
+
<Input
|
|
573
|
+
value={formData.personal.firstName}
|
|
574
|
+
onChange={(e) =>
|
|
575
|
+
updateField('personal', 'firstName', e.target.value)
|
|
576
|
+
}
|
|
577
|
+
/>
|
|
578
|
+
</FormControl>
|
|
579
|
+
<FormControl sx={{ flex: 1 }}>
|
|
580
|
+
<FormLabel>Last Name</FormLabel>
|
|
581
|
+
<Input
|
|
582
|
+
value={formData.personal.lastName}
|
|
583
|
+
onChange={(e) =>
|
|
584
|
+
updateField('personal', 'lastName', e.target.value)
|
|
585
|
+
}
|
|
586
|
+
/>
|
|
587
|
+
</FormControl>
|
|
588
|
+
</Stack>
|
|
589
|
+
<FormControl>
|
|
590
|
+
<FormLabel>Email</FormLabel>
|
|
591
|
+
<Input
|
|
592
|
+
type="email"
|
|
593
|
+
value={formData.personal.email}
|
|
594
|
+
onChange={(e) =>
|
|
595
|
+
updateField('personal', 'email', e.target.value)
|
|
596
|
+
}
|
|
597
|
+
/>
|
|
598
|
+
</FormControl>
|
|
599
|
+
</Stack>
|
|
600
|
+
),
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
summary: 'Shipping Address',
|
|
604
|
+
details: (
|
|
605
|
+
<Stack gap={2}>
|
|
606
|
+
<FormControl>
|
|
607
|
+
<FormLabel>Street Address</FormLabel>
|
|
608
|
+
<Input
|
|
609
|
+
value={formData.address.street}
|
|
610
|
+
onChange={(e) =>
|
|
611
|
+
updateField('address', 'street', e.target.value)
|
|
612
|
+
}
|
|
613
|
+
/>
|
|
614
|
+
</FormControl>
|
|
615
|
+
<Stack direction="row" gap={2}>
|
|
616
|
+
<FormControl sx={{ flex: 2 }}>
|
|
617
|
+
<FormLabel>City</FormLabel>
|
|
618
|
+
<Input
|
|
619
|
+
value={formData.address.city}
|
|
620
|
+
onChange={(e) =>
|
|
621
|
+
updateField('address', 'city', e.target.value)
|
|
622
|
+
}
|
|
623
|
+
/>
|
|
624
|
+
</FormControl>
|
|
625
|
+
<FormControl sx={{ flex: 1 }}>
|
|
626
|
+
<FormLabel>ZIP Code</FormLabel>
|
|
627
|
+
<Input
|
|
628
|
+
value={formData.address.zipCode}
|
|
629
|
+
onChange={(e) =>
|
|
630
|
+
updateField('address', 'zipCode', e.target.value)
|
|
631
|
+
}
|
|
632
|
+
/>
|
|
633
|
+
</FormControl>
|
|
634
|
+
</Stack>
|
|
635
|
+
</Stack>
|
|
636
|
+
),
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
summary: 'Payment Details',
|
|
640
|
+
details: (
|
|
641
|
+
<Stack gap={2}>
|
|
642
|
+
<FormControl>
|
|
643
|
+
<FormLabel>Card Number</FormLabel>
|
|
644
|
+
<Input
|
|
645
|
+
placeholder="1234 5678 9012 3456"
|
|
646
|
+
value={formData.payment.cardNumber}
|
|
647
|
+
onChange={(e) =>
|
|
648
|
+
updateField('payment', 'cardNumber', e.target.value)
|
|
649
|
+
}
|
|
650
|
+
/>
|
|
651
|
+
</FormControl>
|
|
652
|
+
<Stack direction="row" gap={2}>
|
|
653
|
+
<FormControl sx={{ flex: 1 }}>
|
|
654
|
+
<FormLabel>Expiry Date</FormLabel>
|
|
655
|
+
<Input
|
|
656
|
+
placeholder="MM/YY"
|
|
657
|
+
value={formData.payment.expiry}
|
|
658
|
+
onChange={(e) =>
|
|
659
|
+
updateField('payment', 'expiry', e.target.value)
|
|
660
|
+
}
|
|
661
|
+
/>
|
|
662
|
+
</FormControl>
|
|
663
|
+
<FormControl sx={{ flex: 1 }}>
|
|
664
|
+
<FormLabel>CVV</FormLabel>
|
|
665
|
+
<Input
|
|
666
|
+
type="password"
|
|
667
|
+
placeholder="123"
|
|
668
|
+
value={formData.payment.cvv}
|
|
669
|
+
onChange={(e) =>
|
|
670
|
+
updateField('payment', 'cvv', e.target.value)
|
|
671
|
+
}
|
|
672
|
+
/>
|
|
673
|
+
</FormControl>
|
|
674
|
+
</Stack>
|
|
675
|
+
</Stack>
|
|
676
|
+
),
|
|
677
|
+
},
|
|
678
|
+
];
|
|
679
|
+
|
|
680
|
+
return (
|
|
681
|
+
<Box sx={{ maxWidth: 500 }}>
|
|
682
|
+
<Accordions items={formSections} variant="outlined" />
|
|
683
|
+
<Button sx={{ mt: 2 }} fullWidth onClick={() => onSubmit(formData)}>
|
|
684
|
+
Submit Order
|
|
685
|
+
</Button>
|
|
686
|
+
</Box>
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
## Props and Customization
|
|
692
|
+
|
|
693
|
+
### Key Props
|
|
694
|
+
|
|
695
|
+
| Prop | Type | Default | Description |
|
|
696
|
+
| ---------------- | -------------------------------------------------------------- | ----------- | ------------------------------------------------- |
|
|
697
|
+
| `items` | `Array<{ summary: string; details: ReactNode }>` | - | Array of accordion items with headers and content |
|
|
698
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the accordion |
|
|
699
|
+
| `variant` | `'plain' \| 'outlined' \| 'soft' \| 'solid'` | `'plain'` | Visual style variant |
|
|
700
|
+
| `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | `'neutral'` | Color theme |
|
|
701
|
+
| `disableDivider` | `boolean` | `false` | Remove divider lines between items |
|
|
702
|
+
|
|
703
|
+
### Items Structure
|
|
704
|
+
|
|
705
|
+
```tsx
|
|
706
|
+
interface AccordionItem {
|
|
707
|
+
summary: string; // Header text displayed when collapsed
|
|
708
|
+
details: ReactNode; // Content shown when expanded
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Example items array
|
|
712
|
+
const items = [
|
|
713
|
+
{
|
|
714
|
+
summary: 'Section Title',
|
|
715
|
+
details: <Typography>Section content...</Typography>,
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
summary: 'Another Section',
|
|
719
|
+
details: (
|
|
720
|
+
<Stack gap={2}>
|
|
721
|
+
<Typography>Multiple elements...</Typography>
|
|
722
|
+
<Button>Action</Button>
|
|
723
|
+
</Stack>
|
|
724
|
+
),
|
|
725
|
+
},
|
|
726
|
+
];
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
### Variant Options
|
|
730
|
+
|
|
731
|
+
```tsx
|
|
732
|
+
// Plain - minimal styling, no background
|
|
733
|
+
<Accordions items={items} variant="plain" />
|
|
734
|
+
|
|
735
|
+
// Outlined - bordered container
|
|
736
|
+
<Accordions items={items} variant="outlined" />
|
|
737
|
+
|
|
738
|
+
// Soft - subtle background color
|
|
739
|
+
<Accordions items={items} variant="soft" />
|
|
740
|
+
|
|
741
|
+
// Solid - full background color
|
|
742
|
+
<Accordions items={items} variant="solid" />
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
### Size Options
|
|
746
|
+
|
|
747
|
+
```tsx
|
|
748
|
+
// Small - compact for sidebars/menus
|
|
749
|
+
<Accordions items={items} size="sm" />
|
|
750
|
+
|
|
751
|
+
// Medium - default size
|
|
752
|
+
<Accordions items={items} size="md" />
|
|
753
|
+
|
|
754
|
+
// Large - spacious for main content
|
|
755
|
+
<Accordions items={items} size="lg" />
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
### Color Options
|
|
759
|
+
|
|
760
|
+
```tsx
|
|
761
|
+
// Primary - brand color emphasis
|
|
762
|
+
<Accordions items={items} color="primary" />
|
|
763
|
+
|
|
764
|
+
// Neutral - subtle, default
|
|
765
|
+
<Accordions items={items} color="neutral" />
|
|
766
|
+
|
|
767
|
+
// Status colors
|
|
768
|
+
<Accordions items={items} color="success" />
|
|
769
|
+
<Accordions items={items} color="warning" />
|
|
770
|
+
<Accordions items={items} color="danger" />
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
### Without Dividers
|
|
774
|
+
|
|
775
|
+
```tsx
|
|
776
|
+
// Clean look without divider lines
|
|
777
|
+
<Accordions items={items} disableDivider />
|
|
778
|
+
```
|
|
279
779
|
|
|
280
780
|
## Accessibility
|
|
281
781
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
782
|
+
Accordions includes built-in accessibility features:
|
|
783
|
+
|
|
784
|
+
### ARIA Attributes
|
|
785
|
+
|
|
786
|
+
- Accordion headers have `role="button"` with `aria-expanded`
|
|
787
|
+
- Content regions have `role="region"` with `aria-labelledby`
|
|
788
|
+
- Proper focus management between sections
|
|
789
|
+
|
|
790
|
+
### Keyboard Navigation
|
|
791
|
+
|
|
792
|
+
- **Tab**: Move focus between accordion headers
|
|
793
|
+
- **Enter/Space**: Expand or collapse focused accordion
|
|
794
|
+
- **Arrow Down**: Move focus to next accordion header
|
|
795
|
+
- **Arrow Up**: Move focus to previous accordion header
|
|
796
|
+
- **Home**: Move focus to first accordion header
|
|
797
|
+
- **End**: Move focus to last accordion header
|
|
798
|
+
|
|
799
|
+
### Screen Reader Support
|
|
800
|
+
|
|
801
|
+
```tsx
|
|
802
|
+
// Headers announce: "Section Title, collapsed/expanded, button"
|
|
803
|
+
<Accordions
|
|
804
|
+
items={[
|
|
805
|
+
{
|
|
806
|
+
summary: 'Shipping Information', // Announced as header
|
|
807
|
+
details: <Typography>Content...</Typography>,
|
|
808
|
+
},
|
|
809
|
+
]}
|
|
810
|
+
/>
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
### Focus Visibility
|
|
814
|
+
|
|
815
|
+
- Clear focus indicators on headers
|
|
816
|
+
- Focus remains on header after expand/collapse
|
|
817
|
+
- Tab navigation works within expanded content
|
|
818
|
+
|
|
819
|
+
## Best Practices
|
|
820
|
+
|
|
821
|
+
### ✅ Do
|
|
822
|
+
|
|
823
|
+
1. **Use descriptive headers**: Make content clear without expanding
|
|
824
|
+
|
|
825
|
+
```tsx
|
|
826
|
+
// ✅ Good: Descriptive headers
|
|
827
|
+
<Accordions
|
|
828
|
+
items={[
|
|
829
|
+
{ summary: 'Return Policy (30 days)', details: '...' },
|
|
830
|
+
{ summary: 'Shipping Options & Costs', details: '...' },
|
|
831
|
+
]}
|
|
832
|
+
/>
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
2. **Group related content**: Keep sections logically organized
|
|
836
|
+
|
|
837
|
+
```tsx
|
|
838
|
+
// ✅ Good: Logical grouping
|
|
839
|
+
<Accordions
|
|
840
|
+
items={[
|
|
841
|
+
{ summary: 'Account Settings', details: accountSettings },
|
|
842
|
+
{ summary: 'Notification Settings', details: notificationSettings },
|
|
843
|
+
{ summary: 'Privacy Settings', details: privacySettings },
|
|
844
|
+
]}
|
|
845
|
+
/>
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
3. **Keep content scannable**: Use lists and headings inside accordions
|
|
849
|
+
|
|
850
|
+
```tsx
|
|
851
|
+
// ✅ Good: Scannable content
|
|
852
|
+
<Accordions
|
|
853
|
+
items={[
|
|
854
|
+
{
|
|
855
|
+
summary: 'Features',
|
|
856
|
+
details: (
|
|
857
|
+
<ul>
|
|
858
|
+
<li>Feature one</li>
|
|
859
|
+
<li>Feature two</li>
|
|
860
|
+
<li>Feature three</li>
|
|
861
|
+
</ul>
|
|
862
|
+
),
|
|
863
|
+
},
|
|
864
|
+
]}
|
|
865
|
+
/>
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
4. **Match variant to context**: Use appropriate styling
|
|
869
|
+
|
|
870
|
+
```tsx
|
|
871
|
+
// ✅ Good: Outlined for forms, plain for navigation
|
|
872
|
+
<Accordions items={formSections} variant="outlined" />
|
|
873
|
+
<Accordions items={menuItems} variant="plain" size="sm" />
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
### ❌ Don't
|
|
877
|
+
|
|
878
|
+
1. **Don't hide critical information**: Important content should be visible
|
|
879
|
+
|
|
880
|
+
```tsx
|
|
881
|
+
// ❌ Bad: Hiding important warnings
|
|
882
|
+
<Accordions
|
|
883
|
+
items={[
|
|
884
|
+
{ summary: 'Important Safety Information', details: criticalWarning },
|
|
885
|
+
]}
|
|
886
|
+
/>
|
|
887
|
+
|
|
888
|
+
// ✅ Good: Show warnings prominently
|
|
889
|
+
<Alert color="warning">{criticalWarning}</Alert>
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
2. **Don't nest accordions**: Avoid complex hierarchies
|
|
893
|
+
|
|
894
|
+
```tsx
|
|
895
|
+
// ❌ Bad: Nested accordions
|
|
896
|
+
<Accordions
|
|
897
|
+
items={[
|
|
898
|
+
{
|
|
899
|
+
summary: 'Parent',
|
|
900
|
+
details: (
|
|
901
|
+
<Accordions items={[{ summary: 'Child', details: '...' }]} />
|
|
902
|
+
),
|
|
903
|
+
},
|
|
904
|
+
]}
|
|
905
|
+
/>
|
|
906
|
+
|
|
907
|
+
// ✅ Good: Flat structure or use alternative
|
|
908
|
+
<Accordions items={flattenedItems} />
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
3. **Don't use for single items**: Unnecessary complexity
|
|
912
|
+
|
|
913
|
+
```tsx
|
|
914
|
+
// ❌ Bad: Single accordion item
|
|
915
|
+
<Accordions items={[{ summary: 'Details', details: content }]} />
|
|
916
|
+
|
|
917
|
+
// ✅ Good: Use simple disclosure or always show content
|
|
918
|
+
<Card>
|
|
919
|
+
<CardContent>{content}</CardContent>
|
|
920
|
+
</Card>
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
4. **Don't overload with too many sections**: Keep it manageable
|
|
924
|
+
|
|
925
|
+
```tsx
|
|
926
|
+
// ❌ Bad: Too many accordion items (15+)
|
|
927
|
+
<Accordions items={arrayOf15Items} />
|
|
928
|
+
|
|
929
|
+
// ✅ Good: Group or paginate if needed
|
|
930
|
+
<Accordions items={arrayOf5To7Items} />
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
## Performance Considerations
|
|
934
|
+
|
|
935
|
+
### Lazy Load Heavy Content
|
|
936
|
+
|
|
937
|
+
For accordions with heavy content, render only when expanded:
|
|
938
|
+
|
|
939
|
+
```tsx
|
|
940
|
+
function LazyAccordionContent({ isExpanded, children }) {
|
|
941
|
+
const [hasBeenExpanded, setHasBeenExpanded] = useState(false);
|
|
942
|
+
|
|
943
|
+
useEffect(() => {
|
|
944
|
+
if (isExpanded) setHasBeenExpanded(true);
|
|
945
|
+
}, [isExpanded]);
|
|
946
|
+
|
|
947
|
+
// Render placeholder until first expansion
|
|
948
|
+
if (!hasBeenExpanded) {
|
|
949
|
+
return <Typography color="neutral">Loading...</Typography>;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
return children;
|
|
953
|
+
}
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
### Memoize Items Array
|
|
957
|
+
|
|
958
|
+
Prevent unnecessary re-renders by memoizing items:
|
|
959
|
+
|
|
960
|
+
```tsx
|
|
961
|
+
const accordionItems = useMemo(
|
|
962
|
+
() => [
|
|
963
|
+
{
|
|
964
|
+
summary: 'Section 1',
|
|
965
|
+
details: <ExpensiveComponent data={data1} />,
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
summary: 'Section 2',
|
|
969
|
+
details: <ExpensiveComponent data={data2} />,
|
|
970
|
+
},
|
|
971
|
+
],
|
|
972
|
+
[data1, data2]
|
|
973
|
+
);
|
|
974
|
+
|
|
975
|
+
<Accordions items={accordionItems} />
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
### Avoid Inline Functions in Items
|
|
979
|
+
|
|
980
|
+
```tsx
|
|
981
|
+
// ❌ Bad: Creates new objects on every render
|
|
982
|
+
<Accordions
|
|
983
|
+
items={data.map((item) => ({
|
|
984
|
+
summary: item.title,
|
|
985
|
+
details: <Content data={item} />,
|
|
986
|
+
}))}
|
|
987
|
+
/>
|
|
988
|
+
|
|
989
|
+
// ✅ Good: Memoized transformation
|
|
990
|
+
const items = useMemo(
|
|
991
|
+
() =>
|
|
992
|
+
data.map((item) => ({
|
|
993
|
+
summary: item.title,
|
|
994
|
+
details: <Content data={item} />,
|
|
995
|
+
})),
|
|
996
|
+
[data]
|
|
997
|
+
);
|
|
998
|
+
<Accordions items={items} />
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
### Virtualize Long Lists
|
|
1002
|
+
|
|
1003
|
+
For many accordion items, consider virtualization:
|
|
1004
|
+
|
|
1005
|
+
```tsx
|
|
1006
|
+
// For very long lists (20+ items), consider:
|
|
1007
|
+
// 1. Pagination with limited items per page
|
|
1008
|
+
// 2. Search/filter to reduce visible items
|
|
1009
|
+
// 3. Grouping into categories
|
|
1010
|
+
|
|
1011
|
+
function FilteredAccordions({ allItems }) {
|
|
1012
|
+
const [search, setSearch] = useState('');
|
|
1013
|
+
|
|
1014
|
+
const filteredItems = useMemo(
|
|
1015
|
+
() =>
|
|
1016
|
+
allItems.filter((item) =>
|
|
1017
|
+
item.summary.toLowerCase().includes(search.toLowerCase())
|
|
1018
|
+
),
|
|
1019
|
+
[allItems, search]
|
|
1020
|
+
);
|
|
1021
|
+
|
|
1022
|
+
return (
|
|
1023
|
+
<Stack gap={2}>
|
|
1024
|
+
<Input
|
|
1025
|
+
placeholder="Search..."
|
|
1026
|
+
value={search}
|
|
1027
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
1028
|
+
/>
|
|
1029
|
+
<Accordions items={filteredItems.slice(0, 10)} />
|
|
1030
|
+
{filteredItems.length > 10 && (
|
|
1031
|
+
<Typography level="body-sm" color="neutral">
|
|
1032
|
+
Showing 10 of {filteredItems.length} results
|
|
1033
|
+
</Typography>
|
|
1034
|
+
)}
|
|
1035
|
+
</Stack>
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
Accordions provides an efficient way to organize and present collapsible content sections. Use descriptive headers, keep content scannable, and choose appropriate variants based on your context. For critical information, consider displaying it prominently rather than hiding it within an accordion.
|