@ceed/cds 1.19.0 → 1.19.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.
@@ -2,6 +2,45 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
+ The Tabs component provides a way to organize content into separate views where only one view is visible at a time. Built on Joy UI's Tabs, TabList, Tab, and TabPanel components, it enables users to switch between different sections of related content without leaving the current page. Tabs are ideal for organizing related information that doesn't need to be viewed simultaneously.
6
+
7
+ ```
8
+ <Canvas of={Tabs.Playground} />
9
+ ```
10
+
11
+ | Field | Description | Default |
12
+ | ------- | ----------- | --------- |
13
+ | color | — | "primary" |
14
+ | size | — | "md" |
15
+ | variant | — | "plain" |
16
+
17
+ ## Usage
18
+
19
+ ```tsx
20
+ import { Tabs, TabList, Tab, TabPanel } from '@ceed/cds';
21
+
22
+ function MyComponent() {
23
+ return (
24
+ <Tabs defaultValue={0}>
25
+ <TabList>
26
+ <Tab>First Tab</Tab>
27
+ <Tab>Second Tab</Tab>
28
+ <Tab>Third Tab</Tab>
29
+ </TabList>
30
+ <TabPanel value={0}>Content for the first tab</TabPanel>
31
+ <TabPanel value={1}>Content for the second tab</TabPanel>
32
+ <TabPanel value={2}>Content for the third tab</TabPanel>
33
+ </Tabs>
34
+ );
35
+ }
36
+ ```
37
+
38
+ ## Examples
39
+
40
+ ### Basic Tabs
41
+
42
+ Default tab navigation with horizontal layout.
43
+
5
44
  ```tsx
6
45
  <Tabs {...args}>
7
46
  <TabList>
@@ -21,14 +60,434 @@
21
60
  </Tabs>
22
61
  ```
23
62
 
24
- | Field | Description | Default |
25
- | ------- | ----------- | --------- |
26
- | color | — | "primary" |
27
- | size | — | "md" |
28
- | variant | — | "plain" |
63
+ ### Variants
29
64
 
30
- ## Usage
65
+ Tabs support different visual styles through variants.
66
+
67
+ ```
68
+ <Canvas of={Tabs.Variants} />
69
+ ```
70
+
71
+ ### Sizes
72
+
73
+ Tabs come in different sizes to fit various use cases.
74
+
75
+ ```
76
+ <Canvas of={Tabs.Sizes} />
77
+ ```
78
+
79
+ ### Colors
80
+
81
+ Apply semantic colors to tabs.
82
+
83
+ ```
84
+ <Canvas of={Tabs.Colors} />
85
+ ```
86
+
87
+ ### With Decorators
88
+
89
+ Add icons or other elements to tab labels.
90
+
91
+ ```tsx
92
+ <Tabs {...args}>
93
+ <TabList>
94
+ <Tab startDecorator={<Call />} color={color} variant={variant}>
95
+ Tab1
96
+ </Tab>
97
+ <Tab endDecorator={<Sms />} color={color} variant={variant}>
98
+ Tab2
99
+ </Tab>
100
+ <Tab startDecorator={<Email />} endDecorator={<MoreVert />} color={color} variant={variant}>
101
+ Tab3
102
+ </Tab>
103
+ </TabList>
104
+ <TabPanel value={0}>Tab list1</TabPanel>
105
+ <TabPanel value={1}>Tab list2</TabPanel>
106
+ <TabPanel value={2}>Tab list3</TabPanel>
107
+ </Tabs>
108
+ ```
109
+
110
+ ### Vertical Orientation
111
+
112
+ Tabs can be arranged vertically for sidebar-style navigation.
113
+
114
+ ```
115
+ <Canvas of={Tabs.Vertical} />
116
+ ```
117
+
118
+ ### Disabled Tabs
119
+
120
+ Individual tabs can be disabled while keeping others active.
121
+
122
+ ```
123
+ <Canvas of={Tabs.Disabled} />
124
+ ```
125
+
126
+ ### Controlled Tabs
127
+
128
+ Programmatically control the active tab.
129
+
130
+ ```
131
+ <Canvas of={Tabs.Controlled} />
132
+ ```
133
+
134
+ ## When to Use
135
+
136
+ ### ✅ Good Use Cases
137
+
138
+ - **Related content sections**: When content can be logically grouped and users don't need to see all sections at once
139
+ - **Settings pages**: Organizing different categories of settings (Profile, Security, Notifications)
140
+ - **Dashboard views**: Switching between different data views or time periods
141
+ - **Product details**: Showing Description, Specifications, Reviews as separate tabs
142
+ - **Form sections**: Breaking long forms into logical steps (not for strict wizard flows)
143
+ - **Code examples**: Showing code in different languages or frameworks
144
+
145
+ ### ❌ When Not to Use
146
+
147
+ - **Primary navigation**: Use a navigation menu or sidebar for main app navigation
148
+ - **Sequential workflows**: Use Stepper for multi-step processes that must be completed in order
149
+ - **Comparing content**: If users need to see multiple sections simultaneously, use accordions or show all content
150
+ - **Very few items**: If there are only 2 options, consider using a toggle or radio buttons
151
+ - **Many items**: More than 5-6 tabs become hard to navigate; consider a dropdown or nested navigation
152
+ - **Mobile layouts**: Consider alternative patterns like accordions or stacked sections on small screens
153
+
154
+ ## Common Use Cases
155
+
156
+ ### Settings Page
157
+
158
+ ```tsx
159
+ function SettingsPage() {
160
+ return (
161
+ <Tabs defaultValue={0}>
162
+ <TabList>
163
+ <Tab startDecorator={<PersonIcon />}>Profile</Tab>
164
+ <Tab startDecorator={<SecurityIcon />}>Security</Tab>
165
+ <Tab startDecorator={<NotificationsIcon />}>Notifications</Tab>
166
+ <Tab startDecorator={<PaletteIcon />}>Appearance</Tab>
167
+ </TabList>
168
+ <TabPanel value={0}>
169
+ <ProfileSettings />
170
+ </TabPanel>
171
+ <TabPanel value={1}>
172
+ <SecuritySettings />
173
+ </TabPanel>
174
+ <TabPanel value={2}>
175
+ <NotificationSettings />
176
+ </TabPanel>
177
+ <TabPanel value={3}>
178
+ <AppearanceSettings />
179
+ </TabPanel>
180
+ </Tabs>
181
+ );
182
+ }
183
+ ```
184
+
185
+ ### Product Detail Page
31
186
 
32
187
  ```tsx
33
- import { Tabs } from '@ceed/cds';
188
+ function ProductDetails({ product }) {
189
+ return (
190
+ <Tabs defaultValue={0}>
191
+ <TabList>
192
+ <Tab>Description</Tab>
193
+ <Tab>Specifications</Tab>
194
+ <Tab>Reviews ({product.reviewCount})</Tab>
195
+ <Tab>Q&A</Tab>
196
+ </TabList>
197
+ <TabPanel value={0}>
198
+ <Typography>{product.description}</Typography>
199
+ </TabPanel>
200
+ <TabPanel value={1}>
201
+ <SpecificationTable specs={product.specifications} />
202
+ </TabPanel>
203
+ <TabPanel value={2}>
204
+ <ReviewList reviews={product.reviews} />
205
+ </TabPanel>
206
+ <TabPanel value={3}>
207
+ <QASection productId={product.id} />
208
+ </TabPanel>
209
+ </Tabs>
210
+ );
211
+ }
34
212
  ```
213
+
214
+ ### Dashboard with Time Filters
215
+
216
+ ```tsx
217
+ function DashboardTabs() {
218
+ const [timeRange, setTimeRange] = useState('week');
219
+
220
+ return (
221
+ <Tabs value={timeRange} onChange={(e, val) => setTimeRange(val as string)}>
222
+ <TabList>
223
+ <Tab value="day">Today</Tab>
224
+ <Tab value="week">This Week</Tab>
225
+ <Tab value="month">This Month</Tab>
226
+ <Tab value="year">This Year</Tab>
227
+ </TabList>
228
+ <TabPanel value="day">
229
+ <DailyStats />
230
+ </TabPanel>
231
+ <TabPanel value="week">
232
+ <WeeklyStats />
233
+ </TabPanel>
234
+ <TabPanel value="month">
235
+ <MonthlyStats />
236
+ </TabPanel>
237
+ <TabPanel value="year">
238
+ <YearlyStats />
239
+ </TabPanel>
240
+ </Tabs>
241
+ );
242
+ }
243
+ ```
244
+
245
+ ### Code Examples
246
+
247
+ ```tsx
248
+ function CodeExamples() {
249
+ return (
250
+ <Tabs defaultValue="react">
251
+ <TabList>
252
+ <Tab value="react">React</Tab>
253
+ <Tab value="vue">Vue</Tab>
254
+ <Tab value="angular">Angular</Tab>
255
+ </TabList>
256
+ <TabPanel value="react">
257
+ <CodeBlock language="jsx">{reactCode}</CodeBlock>
258
+ </TabPanel>
259
+ <TabPanel value="vue">
260
+ <CodeBlock language="vue">{vueCode}</CodeBlock>
261
+ </TabPanel>
262
+ <TabPanel value="angular">
263
+ <CodeBlock language="typescript">{angularCode}</CodeBlock>
264
+ </TabPanel>
265
+ </Tabs>
266
+ );
267
+ }
268
+ ```
269
+
270
+ ### Vertical Sidebar Navigation
271
+
272
+ ```tsx
273
+ function SidebarTabs() {
274
+ return (
275
+ <Box sx={{ display: 'flex' }}>
276
+ <Tabs orientation="vertical" defaultValue={0} sx={{ minWidth: 200 }}>
277
+ <TabList>
278
+ <Tab startDecorator={<DashboardIcon />}>Dashboard</Tab>
279
+ <Tab startDecorator={<AnalyticsIcon />}>Analytics</Tab>
280
+ <Tab startDecorator={<ReportsIcon />}>Reports</Tab>
281
+ <Tab startDecorator={<SettingsIcon />}>Settings</Tab>
282
+ </TabList>
283
+ </Tabs>
284
+ <Box sx={{ flex: 1, p: 2 }}>
285
+ <TabPanel value={0}>Dashboard content</TabPanel>
286
+ <TabPanel value={1}>Analytics content</TabPanel>
287
+ <TabPanel value={2}>Reports content</TabPanel>
288
+ <TabPanel value={3}>Settings content</TabPanel>
289
+ </Box>
290
+ </Box>
291
+ );
292
+ }
293
+ ```
294
+
295
+ ## Component Structure
296
+
297
+ Tabs uses a composition pattern with multiple sub-components:
298
+
299
+ ```tsx
300
+ <Tabs> {/* Container - manages state */}
301
+ <TabList> {/* Tab button container */}
302
+ <Tab>Tab 1</Tab> {/* Individual tab buttons */}
303
+ <Tab>Tab 2</Tab>
304
+ </TabList>
305
+ <TabPanel value={0}> {/* Content panels */}
306
+ Content 1
307
+ </TabPanel>
308
+ <TabPanel value={1}>
309
+ Content 2
310
+ </TabPanel>
311
+ </Tabs>
312
+ ```
313
+
314
+ ## Props and Customization
315
+
316
+ ### Tabs Props
317
+
318
+ | Prop | Type | Default | Description |
319
+ | -------------- | ---------------------------- | -------------- | ----------------------------------- |
320
+ | `defaultValue` | `string \| number` | - | Default selected tab (uncontrolled) |
321
+ | `value` | `string \| number` | - | Selected tab (controlled) |
322
+ | `onChange` | `function` | - | Callback when tab changes |
323
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Tab layout direction |
324
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of all child components |
325
+
326
+ ### Tab Props
327
+
328
+ | Prop | Type | Default | Description |
329
+ | ---------------- | -------------------------------------------------------------- | --------- | ---------------------------------- |
330
+ | `value` | `string \| number` | - | Tab identifier (defaults to index) |
331
+ | `variant` | `'plain' \| 'outlined' \| 'soft' \| 'solid'` | `'plain'` | Visual style |
332
+ | `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | - | Color scheme |
333
+ | `disabled` | `boolean` | `false` | Disable the tab |
334
+ | `startDecorator` | `ReactNode` | - | Element before tab label |
335
+ | `endDecorator` | `ReactNode` | - | Element after tab label |
336
+
337
+ ### TabPanel Props
338
+
339
+ | Prop | Type | Default | Description |
340
+ | ------------- | ------------------ | ------- | ----------------------------- |
341
+ | `value` | `string \| number` | - | Matching tab value |
342
+ | `keepMounted` | `boolean` | `false` | Keep panel in DOM when hidden |
343
+
344
+ ### Custom Styling
345
+
346
+ ```tsx
347
+ <Tabs
348
+ sx={{
349
+ '--Tabs-gap': '8px',
350
+ }}
351
+ >
352
+ <TabList
353
+ sx={{
354
+ borderBottom: '1px solid',
355
+ borderColor: 'divider',
356
+ }}
357
+ >
358
+ <Tab
359
+ sx={{
360
+ '&.Mui-selected': {
361
+ borderBottom: '2px solid',
362
+ borderColor: 'primary.500',
363
+ },
364
+ }}
365
+ >
366
+ Custom Tab
367
+ </Tab>
368
+ </TabList>
369
+ </Tabs>
370
+ ```
371
+
372
+ ## Accessibility
373
+
374
+ Tabs components follow WAI-ARIA Tabs pattern for comprehensive accessibility:
375
+
376
+ ### ARIA Attributes
377
+
378
+ - `role="tablist"` on TabList
379
+ - `role="tab"` on each Tab
380
+ - `role="tabpanel"` on each TabPanel
381
+ - `aria-selected` indicates the active tab
382
+ - `aria-controls` links tabs to their panels
383
+ - `aria-labelledby` links panels to their tabs
384
+
385
+ ### Keyboard Navigation
386
+
387
+ - **Tab**: Move focus to the tab list, then to the active panel
388
+ - **Arrow Left/Right**: Move between tabs (horizontal orientation)
389
+ - **Arrow Up/Down**: Move between tabs (vertical orientation)
390
+ - **Home**: Move to first tab
391
+ - **End**: Move to last tab
392
+ - **Enter/Space**: Activate the focused tab
393
+
394
+ ### Screen Reader Support
395
+
396
+ ```tsx
397
+ <Tabs aria-label="Account settings tabs">
398
+ <TabList>
399
+ <Tab>Profile</Tab>
400
+ <Tab>Security</Tab>
401
+ </TabList>
402
+ <TabPanel value={0}>
403
+ <h2 id="profile-heading">Profile Settings</h2>
404
+ {/* Content */}
405
+ </TabPanel>
406
+ </Tabs>
407
+ ```
408
+
409
+ ## Best Practices
410
+
411
+ ### ✅ Do
412
+
413
+ 1. **Use clear, concise labels**: Tab labels should clearly describe the content
414
+
415
+ ```tsx
416
+ // ✅ Good: Clear labels
417
+ <Tab>Account Settings</Tab>
418
+ <Tab>Notifications</Tab>
419
+
420
+ // ❌ Bad: Vague labels
421
+ <Tab>Tab 1</Tab>
422
+ <Tab>Other</Tab>
423
+ ```
424
+
425
+ 2. **Keep tab count manageable**: Limit to 5-6 tabs maximum
426
+
427
+ 3. **Order tabs logically**: Place most important or frequently used tabs first
428
+
429
+ 4. **Use consistent styling**: All tabs should have the same visual weight
430
+
431
+ 5. **Show active state clearly**: Users should always know which tab is selected
432
+
433
+ ### ❌ Don't
434
+
435
+ 1. **Don't hide critical information**: Important content shouldn't be buried in secondary tabs
436
+
437
+ 2. **Don't use for sequential processes**: Use Stepper for ordered workflows
438
+
439
+ ```tsx
440
+ // ❌ Bad: Sequential steps as tabs
441
+ <Tab>Step 1: Personal Info</Tab>
442
+ <Tab>Step 2: Payment</Tab>
443
+ <Tab>Step 3: Confirm</Tab>
444
+
445
+ // ✅ Good: Use Stepper instead
446
+ <Stepper activeStep={activeStep}>
447
+ <Step>Personal Info</Step>
448
+ <Step>Payment</Step>
449
+ <Step>Confirm</Step>
450
+ </Stepper>
451
+ ```
452
+
453
+ 3. **Don't nest tabs within tabs**: Creates confusing navigation
454
+
455
+ 4. **Don't use inconsistent tab counts**: If tabs are dynamic, consider alternative UI patterns
456
+
457
+ ## Performance Considerations
458
+
459
+ ### Lazy Loading Tab Content
460
+
461
+ By default, inactive tab panels are not rendered. For complex content, this provides automatic lazy loading:
462
+
463
+ ```tsx
464
+ <TabPanel value={0}>
465
+ {/* Only rendered when this tab is active */}
466
+ <HeavyComponent />
467
+ </TabPanel>
468
+ ```
469
+
470
+ ### Keep Mounted
471
+
472
+ Use `keepMounted` when tab content needs to maintain state:
473
+
474
+ ```tsx
475
+ <TabPanel value={0} keepMounted>
476
+ {/* Stays in DOM, maintains form state */}
477
+ <FormWithState />
478
+ </TabPanel>
479
+ ```
480
+
481
+ ### Avoid Heavy Re-renders
482
+
483
+ Memoize tab content when it depends on complex calculations:
484
+
485
+ ```tsx
486
+ const tabContent = useMemo(() => (
487
+ <ExpensiveComponent data={data} />
488
+ ), [data]);
489
+
490
+ <TabPanel value={0}>{tabContent}</TabPanel>
491
+ ```
492
+
493
+ Tabs provide an intuitive way to organize related content while keeping the interface clean and navigable. Use them thoughtfully to enhance content discoverability without overwhelming users.