@ceed/ads 1.20.0 → 1.20.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/components/data-display/Markdown.md +832 -0
- package/dist/components/feedback/Dialog.md +605 -3
- package/dist/components/feedback/Modal.md +656 -24
- package/dist/components/feedback/llms.txt +1 -1
- package/dist/components/inputs/Autocomplete.md +734 -2
- package/dist/components/inputs/Calendar.md +655 -1
- package/dist/components/inputs/DatePicker.md +699 -3
- package/dist/components/inputs/DateRangePicker.md +815 -1
- package/dist/components/inputs/MonthPicker.md +626 -4
- package/dist/components/inputs/MonthRangePicker.md +682 -4
- package/dist/components/inputs/Select.md +600 -0
- package/dist/components/layout/Container.md +507 -0
- package/dist/components/navigation/Breadcrumbs.md +582 -0
- package/dist/components/navigation/IconMenuButton.md +693 -0
- package/dist/components/navigation/InsetDrawer.md +1150 -3
- package/dist/components/navigation/Link.md +526 -0
- package/dist/components/navigation/MenuButton.md +632 -0
- package/dist/components/navigation/NavigationGroup.md +401 -1
- package/dist/components/navigation/NavigationItem.md +311 -0
- package/dist/components/navigation/Navigator.md +373 -0
- package/dist/components/navigation/Pagination.md +521 -0
- package/dist/components/navigation/ProfileMenu.md +605 -0
- package/dist/components/navigation/Tabs.md +609 -7
- package/dist/components/surfaces/Accordions.md +947 -3
- package/dist/llms.txt +1 -1
- package/package.json +1 -1
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
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.
|
|
6
|
+
|
|
5
7
|
```tsx
|
|
6
8
|
<Accordions {...args} />
|
|
7
9
|
```
|
|
@@ -10,7 +12,55 @@
|
|
|
10
12
|
| ----- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
11
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> }] |
|
|
12
14
|
|
|
13
|
-
|
|
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
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { Accordions } from '@ceed/ads';
|
|
28
|
+
|
|
29
|
+
function FAQSection() {
|
|
30
|
+
return (
|
|
31
|
+
<Accordions
|
|
32
|
+
items={[
|
|
33
|
+
{
|
|
34
|
+
summary: 'What is your return policy?',
|
|
35
|
+
details: <p>We offer a 30-day return policy for all unused items.</p>,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
summary: 'How long does shipping take?',
|
|
39
|
+
details: <p>Standard shipping takes 3-5 business days.</p>,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
summary: 'Do you ship internationally?',
|
|
43
|
+
details: <p>Yes, we ship to over 50 countries worldwide.</p>,
|
|
44
|
+
},
|
|
45
|
+
]}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Examples
|
|
52
|
+
|
|
53
|
+
### Playground
|
|
54
|
+
|
|
55
|
+
Interactive example with all controls.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<Accordions {...args} />
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Basics
|
|
62
|
+
|
|
63
|
+
Basic accordion with multiple collapsible sections.
|
|
14
64
|
|
|
15
65
|
```tsx
|
|
16
66
|
<Accordions
|
|
@@ -27,10 +77,10 @@
|
|
|
27
77
|
/>
|
|
28
78
|
```
|
|
29
79
|
|
|
30
|
-
## Customization
|
|
31
|
-
|
|
32
80
|
### Sizes
|
|
33
81
|
|
|
82
|
+
Available size options: `sm`, `md`, `lg`.
|
|
83
|
+
|
|
34
84
|
```tsx
|
|
35
85
|
<>
|
|
36
86
|
<Accordions {...args} size="sm" />
|
|
@@ -41,6 +91,8 @@
|
|
|
41
91
|
|
|
42
92
|
### Variants
|
|
43
93
|
|
|
94
|
+
Style variants: `plain`, `outlined`, `soft`, `solid`.
|
|
95
|
+
|
|
44
96
|
```tsx
|
|
45
97
|
<>
|
|
46
98
|
<div>
|
|
@@ -64,6 +116,8 @@
|
|
|
64
116
|
|
|
65
117
|
### Colors
|
|
66
118
|
|
|
119
|
+
Color options: `primary`, `neutral`, `danger`, `success`, `warning`.
|
|
120
|
+
|
|
67
121
|
```tsx
|
|
68
122
|
<>
|
|
69
123
|
<div>
|
|
@@ -91,6 +145,896 @@
|
|
|
91
145
|
|
|
92
146
|
### Removing Divider
|
|
93
147
|
|
|
148
|
+
Use `disableDivider` to remove the divider lines between accordion items.
|
|
149
|
+
|
|
94
150
|
```tsx
|
|
95
151
|
<Accordions {...args} disableDivider />
|
|
96
152
|
```
|
|
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
|
+
|
|
175
|
+
## Common Use Cases
|
|
176
|
+
|
|
177
|
+
### FAQ Section
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
function FAQAccordion() {
|
|
181
|
+
const faqs = [
|
|
182
|
+
{
|
|
183
|
+
summary: 'What payment methods do you accept?',
|
|
184
|
+
details: (
|
|
185
|
+
<Typography>
|
|
186
|
+
We accept Visa, MasterCard, American Express, and PayPal.
|
|
187
|
+
All payments are processed securely through our payment gateway.
|
|
188
|
+
</Typography>
|
|
189
|
+
),
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
summary: 'How can I track my order?',
|
|
193
|
+
details: (
|
|
194
|
+
<Typography>
|
|
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.
|
|
198
|
+
</Typography>
|
|
199
|
+
),
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
summary: 'What is your refund policy?',
|
|
203
|
+
details: (
|
|
204
|
+
<Stack gap={1}>
|
|
205
|
+
<Typography>Our refund policy includes:</Typography>
|
|
206
|
+
<ul>
|
|
207
|
+
<li>30-day money-back guarantee</li>
|
|
208
|
+
<li>Full refund for unused items</li>
|
|
209
|
+
<li>Refunds processed within 5-7 business days</li>
|
|
210
|
+
</ul>
|
|
211
|
+
</Stack>
|
|
212
|
+
),
|
|
213
|
+
},
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<Box sx={{ maxWidth: 600 }}>
|
|
218
|
+
<Typography level="h2" sx={{ mb: 2 }}>
|
|
219
|
+
Frequently Asked Questions
|
|
220
|
+
</Typography>
|
|
221
|
+
<Accordions items={faqs} variant="outlined" />
|
|
222
|
+
</Box>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Settings Panel
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
function SettingsAccordion({ settings, onSettingChange }) {
|
|
231
|
+
const settingsItems = [
|
|
232
|
+
{
|
|
233
|
+
summary: 'Account Settings',
|
|
234
|
+
details: (
|
|
235
|
+
<Stack gap={2}>
|
|
236
|
+
<FormControl>
|
|
237
|
+
<FormLabel>Display Name</FormLabel>
|
|
238
|
+
<Input
|
|
239
|
+
value={settings.displayName}
|
|
240
|
+
onChange={(e) => onSettingChange('displayName', e.target.value)}
|
|
241
|
+
/>
|
|
242
|
+
</FormControl>
|
|
243
|
+
<FormControl>
|
|
244
|
+
<FormLabel>Email</FormLabel>
|
|
245
|
+
<Input
|
|
246
|
+
type="email"
|
|
247
|
+
value={settings.email}
|
|
248
|
+
onChange={(e) => onSettingChange('email', e.target.value)}
|
|
249
|
+
/>
|
|
250
|
+
</FormControl>
|
|
251
|
+
</Stack>
|
|
252
|
+
),
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
summary: 'Notification Preferences',
|
|
256
|
+
details: (
|
|
257
|
+
<Stack gap={1}>
|
|
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
|
+
/>
|
|
294
|
+
</Stack>
|
|
295
|
+
),
|
|
296
|
+
},
|
|
297
|
+
];
|
|
298
|
+
|
|
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
|
+
);
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Product Details
|
|
311
|
+
|
|
312
|
+
```tsx
|
|
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
|
+
```
|
|
389
|
+
|
|
390
|
+
### Navigation Menu
|
|
391
|
+
|
|
392
|
+
```tsx
|
|
393
|
+
function NavigationAccordion() {
|
|
394
|
+
const menuItems = [
|
|
395
|
+
{
|
|
396
|
+
summary: 'Products',
|
|
397
|
+
details: (
|
|
398
|
+
<List size="sm">
|
|
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>
|
|
431
|
+
</List>
|
|
432
|
+
),
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
summary: 'Resources',
|
|
436
|
+
details: (
|
|
437
|
+
<List size="sm">
|
|
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>
|
|
453
|
+
</List>
|
|
454
|
+
),
|
|
455
|
+
},
|
|
456
|
+
];
|
|
457
|
+
|
|
458
|
+
return (
|
|
459
|
+
<Box sx={{ width: 250 }}>
|
|
460
|
+
<Accordions items={menuItems} variant="plain" size="sm" disableDivider />
|
|
461
|
+
</Box>
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Help Documentation
|
|
467
|
+
|
|
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
|
+
```
|
|
546
|
+
|
|
547
|
+
### Collapsible Form Sections
|
|
548
|
+
|
|
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
|
+
});
|
|
556
|
+
|
|
557
|
+
const updateField = (section, field, value) => {
|
|
558
|
+
setFormData((prev) => ({
|
|
559
|
+
...prev,
|
|
560
|
+
[section]: { ...prev[section], [field]: value },
|
|
561
|
+
}));
|
|
562
|
+
};
|
|
563
|
+
|
|
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
|
+
```
|
|
779
|
+
|
|
780
|
+
## Accessibility
|
|
781
|
+
|
|
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.
|