@ceed/cds 1.24.1-next.3 → 1.25.0

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.
Files changed (63) hide show
  1. package/dist/chunks/rehype-accent-FZRUD7VI.js +39 -0
  2. package/dist/components/CurrencyInput/CurrencyInput.d.ts +1 -1
  3. package/dist/components/CurrencyInput/hooks/use-currency-setting.d.ts +2 -2
  4. package/dist/components/DataTable/components.d.ts +2 -1
  5. package/dist/components/DataTable/styled.d.ts +3 -1
  6. package/dist/components/DataTable/types.d.ts +1 -0
  7. package/dist/components/RadioTileGroup/RadioTileGroup.d.ts +56 -0
  8. package/dist/components/RadioTileGroup/index.d.ts +3 -0
  9. package/dist/components/data-display/DataTable.md +77 -1
  10. package/dist/components/data-display/InfoSign.md +74 -91
  11. package/dist/components/data-display/Typography.md +411 -94
  12. package/dist/components/feedback/CircularProgress.md +257 -0
  13. package/dist/components/feedback/Dialog.md +76 -62
  14. package/dist/components/feedback/Modal.md +430 -138
  15. package/dist/components/feedback/Skeleton.md +280 -0
  16. package/dist/components/feedback/llms.txt +2 -0
  17. package/dist/components/index.d.ts +1 -0
  18. package/dist/components/inputs/Autocomplete.md +356 -107
  19. package/dist/components/inputs/ButtonGroup.md +115 -104
  20. package/dist/components/inputs/CurrencyInput.md +183 -5
  21. package/dist/components/inputs/DatePicker.md +108 -431
  22. package/dist/components/inputs/DateRangePicker.md +131 -492
  23. package/dist/components/inputs/FilterableCheckboxGroup.md +145 -19
  24. package/dist/components/inputs/FormControl.md +361 -0
  25. package/dist/components/inputs/IconButton.md +137 -88
  26. package/dist/components/inputs/Input.md +204 -73
  27. package/dist/components/inputs/MonthPicker.md +95 -422
  28. package/dist/components/inputs/MonthRangePicker.md +89 -466
  29. package/dist/components/inputs/PercentageInput.md +185 -16
  30. package/dist/components/inputs/RadioButton.md +163 -35
  31. package/dist/components/inputs/RadioList.md +241 -0
  32. package/dist/components/inputs/RadioTileGroup.md +507 -0
  33. package/dist/components/inputs/Select.md +222 -326
  34. package/dist/components/inputs/Slider.md +334 -0
  35. package/dist/components/inputs/Switch.md +143 -376
  36. package/dist/components/inputs/Textarea.md +213 -10
  37. package/dist/components/inputs/Uploader/Uploader.md +145 -66
  38. package/dist/components/inputs/llms.txt +4 -0
  39. package/dist/components/navigation/Breadcrumbs.md +57 -308
  40. package/dist/components/navigation/Drawer.md +180 -0
  41. package/dist/components/navigation/Dropdown.md +98 -215
  42. package/dist/components/navigation/IconMenuButton.md +40 -502
  43. package/dist/components/navigation/InsetDrawer.md +281 -650
  44. package/dist/components/navigation/Link.md +31 -348
  45. package/dist/components/navigation/Menu.md +92 -285
  46. package/dist/components/navigation/MenuButton.md +55 -448
  47. package/dist/components/navigation/Pagination.md +47 -338
  48. package/dist/components/navigation/Stepper.md +160 -28
  49. package/dist/components/navigation/Tabs.md +57 -316
  50. package/dist/components/surfaces/Accordions.md +49 -804
  51. package/dist/components/surfaces/Card.md +97 -157
  52. package/dist/components/surfaces/Divider.md +83 -234
  53. package/dist/components/surfaces/Sheet.md +153 -328
  54. package/dist/guides/ThemeProvider.md +89 -0
  55. package/dist/guides/llms.txt +9 -0
  56. package/dist/index.browser.js +224 -0
  57. package/dist/index.browser.js.map +7 -0
  58. package/dist/index.cjs +648 -390
  59. package/dist/index.d.ts +1 -1
  60. package/dist/index.js +563 -361
  61. package/dist/llms.txt +9 -0
  62. package/framer/index.js +1 -163
  63. package/package.json +22 -17
@@ -2,7 +2,9 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- The Select component is a form input that allows users to choose one or multiple options from a predefined list. Built on Joy UI's Select, it provides a dropdown interface for selecting values within forms. Select is designed for data input scenarios where users need to pick from a set of options, making it ideal for forms, filters, and configuration settings.
5
+ The Select component is a form input that allows users to choose one or multiple options from a predefined list. Built on Joy UI's Select, it provides a dropdown interface for picking values within forms, filters, and configuration settings.
6
+
7
+ Select accepts options as an array of objects (with `value`, `label`, and optional `secondaryText` / `disabled` fields) or as simple string/number arrays. It wraps the underlying select element with built-in `FormControl`, `FormLabel`, and `FormHelperText` so you can compose complete form fields with a single component.
6
8
 
7
9
  ```tsx
8
10
  <Select options={options} />
@@ -24,16 +26,21 @@ The Select component is a form input that allows users to choose one or multiple
24
26
  | onChange | — | — |
25
27
  | options | — | options |
26
28
 
27
- > ⚠️ **Don't Confuse Select with Dropdown** ⚠️
29
+ > **Don't confuse Select with Dropdown**
30
+ >
31
+ > - **Select**: For form value selection -- the selected value becomes form data.
32
+ > - **Dropdown**: For menus and action lists -- triggers actions or navigation.
28
33
  >
29
- > - **Select**: For form value selection - the selected value becomes form data
30
- > - **Dropdown**: For menus and action lists - triggers actions or navigation
34
+ > Choosing the right component:
31
35
  >
32
- > Choose the right component:
36
+ > - Fewer than 5 options -- consider RadioButton or RadioGroup
37
+ > - More than 20 options -- consider Autocomplete with search
38
+ > - Actions / navigation -- use Dropdown with Menu
39
+
40
+ > **Use built-in form props**
33
41
  >
34
- > - Less than 5 options Consider RadioButton or RadioGroup
35
- > - More than 20 options Consider Autocomplete with search
36
- > - Actions/navigation → Use Dropdown with Menu
42
+ > This component natively supports `label` and `helperText` props.
43
+ > When building forms, use these built-in props instead of manually composing labels and helper text with Typography.
37
44
 
38
45
  ## Usage
39
46
 
@@ -57,19 +64,9 @@ function MyComponent() {
57
64
  }
58
65
  ```
59
66
 
60
- ## Examples
61
-
62
- ### Basic Select
63
-
64
- The default Select with a list of options.
65
-
66
- ```tsx
67
- <Select options={options} />
68
- ```
69
-
70
- ### Variants
67
+ ## Variants
71
68
 
72
- Select supports different visual styles.
69
+ Select supports four visual styles: `outlined` (default), `plain`, `soft`, and `solid`.
73
70
 
74
71
  ```tsx
75
72
  <Stack spacing={4}>
@@ -80,9 +77,16 @@ Select supports different visual styles.
80
77
  </Stack>
81
78
  ```
82
79
 
83
- ### Sizes
80
+ ```tsx
81
+ <Select options={options} defaultValue="dog" />
82
+ <Select options={options} defaultValue="dog" variant="plain" />
83
+ <Select options={options} defaultValue="dog" variant="soft" />
84
+ <Select options={options} defaultValue="dog" variant="solid" />
85
+ ```
86
+
87
+ ## Sizes
84
88
 
85
- Select comes in different sizes.
89
+ Three sizes are available: `sm`, `md` (default), and `lg`.
86
90
 
87
91
  ```tsx
88
92
  <Stack spacing={4}>
@@ -92,9 +96,15 @@ Select comes in different sizes.
92
96
  </Stack>
93
97
  ```
94
98
 
95
- ### Colors
99
+ ```tsx
100
+ <Select options={options} defaultValue="dog" size="sm" />
101
+ <Select options={options} defaultValue="dog" size="md" />
102
+ <Select options={options} defaultValue="dog" size="lg" />
103
+ ```
104
+
105
+ ## Colors
96
106
 
97
- Apply semantic colors to Select.
107
+ Apply semantic colors to communicate intent or state.
98
108
 
99
109
  ```tsx
100
110
  <Stack spacing={4}>
@@ -106,9 +116,17 @@ Apply semantic colors to Select.
106
116
  </Stack>
107
117
  ```
108
118
 
109
- ### With Decorators
119
+ ```tsx
120
+ <Select options={options} defaultValue="dog" color="primary" />
121
+ <Select options={options} defaultValue="dog" color="neutral" />
122
+ <Select options={options} defaultValue="dog" color="success" />
123
+ <Select options={options} defaultValue="dog" color="danger" />
124
+ <Select options={options} defaultValue="dog" color="warning" />
125
+ ```
110
126
 
111
- Add icons or badges to the Select.
127
+ ## Decorators
128
+
129
+ Use `startDecorator` and `endDecorator` to add icons, badges, or other elements beside the select trigger.
112
130
 
113
131
  ```tsx
114
132
  <Select placeholder="Select a pet…" startDecorator={<FavoriteBorder />} endDecorator={<Chip size="sm" color="danger" variant="soft">
@@ -118,9 +136,24 @@ width: 240
118
136
  }} options={options} />
119
137
  ```
120
138
 
121
- ### With Label
139
+ ```tsx
140
+ import FavoriteBorder from '@mui/icons-material/FavoriteBorder';
141
+ import { Chip } from '@ceed/cds';
122
142
 
123
- Select with an associated label.
143
+ <Select
144
+ placeholder="Select a pet..."
145
+ startDecorator={<FavoriteBorder />}
146
+ endDecorator={
147
+ <Chip size="sm" color="danger" variant="soft">+5</Chip>
148
+ }
149
+ options={options}
150
+ sx={{ width: 240 }}
151
+ />
152
+ ```
153
+
154
+ ## Label and Helper Text
155
+
156
+ The `label` prop renders a form label above the select. The `helperText` prop renders descriptive text below it. Both are built into the component so you do not need to compose them separately.
124
157
 
125
158
  ```tsx
126
159
  <>
@@ -128,19 +161,26 @@ Select with an associated label.
128
161
  </>
129
162
  ```
130
163
 
131
- ### With Helper Text
132
-
133
- Add helper text for additional context.
134
-
135
164
  ```tsx
136
165
  <>
137
166
  <Select label="Select" helperText="I'm helper text" defaultValue="dog" options={options} />
138
167
  </>
139
168
  ```
140
169
 
141
- ### Error State
170
+ ```tsx
171
+ <Select label="Pet" options={options} defaultValue="dog" />
142
172
 
143
- Show validation errors.
173
+ <Select
174
+ label="Pet"
175
+ helperText="Choose your favorite animal"
176
+ options={options}
177
+ defaultValue="dog"
178
+ />
179
+ ```
180
+
181
+ ## Error State
182
+
183
+ Set `error` to `true` to visually indicate a validation error. Combine with `helperText` to display an error message.
144
184
 
145
185
  ```tsx
146
186
  <>
@@ -148,9 +188,18 @@ Show validation errors.
148
188
  </>
149
189
  ```
150
190
 
151
- ### Form Control
191
+ ```tsx
192
+ <Select
193
+ label="Pet"
194
+ error
195
+ helperText="This field is required"
196
+ options={options}
197
+ />
198
+ ```
199
+
200
+ ## Form Control
152
201
 
153
- Complete form integration with label and helper text.
202
+ Combining `label`, `helperText`, and `error` produces a complete form field. Here is a normal field next to one with an error.
154
203
 
155
204
  ```tsx
156
205
  <>
@@ -159,9 +208,9 @@ Complete form integration with label and helper text.
159
208
  </>
160
209
  ```
161
210
 
162
- ### Required Field
211
+ ## Required Field
163
212
 
164
- Mark the Select as required.
213
+ Set `required` to mark the field as required. An asterisk is added to the label automatically.
165
214
 
166
215
  ```tsx
167
216
  <Select
@@ -172,9 +221,18 @@ Mark the Select as required.
172
221
  />
173
222
  ```
174
223
 
175
- ### Multiple Selection
224
+ ```tsx
225
+ <Select
226
+ label="Pet"
227
+ helperText="This field is required"
228
+ required
229
+ options={options}
230
+ />
231
+ ```
232
+
233
+ ## Multiple Selection
176
234
 
177
- Allow selecting multiple options.
235
+ Set `multiple` to allow selecting more than one option. The value becomes an array.
178
236
 
179
237
  ```tsx
180
238
  <Select
@@ -186,9 +244,18 @@ Allow selecting multiple options.
186
244
  />
187
245
  ```
188
246
 
189
- ### Controlled Select
247
+ ```tsx
248
+ <Select
249
+ label="Pets"
250
+ multiple
251
+ defaultValue={['dog']}
252
+ options={options}
253
+ />
254
+ ```
255
+
256
+ ## Controlled Select
190
257
 
191
- Programmatically control the selected value.
258
+ Use the `value` and `onChange` props for controlled behavior when you need to synchronize the selected value with other state.
192
259
 
193
260
  ```tsx
194
261
  <div>
@@ -207,9 +274,20 @@ Programmatically control the selected value.
207
274
  </div>
208
275
  ```
209
276
 
210
- ### Disabled Options
277
+ ```tsx
278
+ const [value, setValue] = useState('dog');
279
+
280
+ <Select
281
+ label="Pet"
282
+ value={value}
283
+ onChange={(event, newValue) => setValue(newValue)}
284
+ options={options}
285
+ />
286
+ ```
287
+
288
+ ## Disabled Options
211
289
 
212
- Individual options can be disabled.
290
+ Individual options can be disabled by setting `disabled: true` on the option object.
213
291
 
214
292
  ```tsx
215
293
  <Select
@@ -222,9 +300,20 @@ Individual options can be disabled.
222
300
  />
223
301
  ```
224
302
 
225
- ### Options with Secondary Text
303
+ ```tsx
304
+ const options = [
305
+ { value: 'dog', label: 'Dog', disabled: true },
306
+ { value: 'cat', label: 'Cat' },
307
+ { value: 'fish', label: 'Fish', disabled: true },
308
+ { value: 'bird', label: 'Bird' },
309
+ ];
310
+
311
+ <Select label="Pet" options={options} defaultValue="dog" />
312
+ ```
313
+
314
+ ## Options with Secondary Text
226
315
 
227
- Display additional information for each option.
316
+ Add a `secondaryText` field to display supplementary information below each option label. The secondary text size scales proportionally with the component `size`.
228
317
 
229
318
  ```tsx
230
319
  <Stack spacing={4} direction="row" alignItems="flex-start">
@@ -240,58 +329,76 @@ Display additional information for each option.
240
329
  </Stack>
241
330
  ```
242
331
 
243
- ## When to Use
332
+ ```tsx
333
+ const userOptions = [
334
+ { value: 'emily', label: 'Emily Carter', secondaryText: '(415) 555-0198' },
335
+ { value: 'daniel', label: 'Daniel Kim', secondaryText: '(212) 555-0421' },
336
+ { value: 'sophia', label: 'Sophia Martinez', secondaryText: '(646) 555-0734' },
337
+ ];
244
338
 
245
- ### Good Use Cases
339
+ <Select placeholder="Select a contact" options={userOptions} />
340
+ ```
246
341
 
247
- - **Forms**: When users need to select a value that will be submitted as form data
248
- - **Filters**: Selecting filter criteria for lists or search results
249
- - **Settings**: Configuration options where user needs to pick one value
250
- - **Data entry**: Standardized input where free text isn't appropriate
251
- - **5-20 options**: Ideal number of choices for a Select
252
- - **Single source of truth**: When the selected value should be clearly visible
342
+ ## Numeric Values
253
343
 
254
- ### When Not to Use
344
+ Select supports both string and numeric option values. When using numeric values, TypeScript infers the correct type automatically.
255
345
 
256
- - **Actions/navigation**: Use Dropdown with Menu instead
257
- - **2-4 options visible at once**: Consider RadioGroup for better visibility
258
- - **20+ options**: Use Autocomplete with search functionality
259
- - **Complex selections**: Consider a different pattern if selections involve complex logic
260
- - **Toggle between states**: Use Switch or Checkbox instead
346
+ ```tsx
347
+ <Stack spacing={4}>
348
+ <Select defaultValue={1} options={numericOptions} />
349
+ <Select defaultValue={1} variant="plain" options={numericOptions} />
350
+ <Select defaultValue={1} variant="soft" options={numericOptions} />
351
+ <Select defaultValue={1} variant="solid" options={numericOptions} />
352
+ </Stack>
353
+ ```
354
+
355
+ ```tsx
356
+ const numericOptions = [
357
+ { value: 1, label: 'Option 1' },
358
+ { value: 2, label: 'Option 2' },
359
+ { value: 3, label: 'Option 3' },
360
+ ];
361
+
362
+ <Select options={numericOptions} defaultValue={1} />
363
+ ```
261
364
 
262
365
  ## Common Use Cases
263
366
 
264
- ### Form with Select
367
+ ### Form with Validation
265
368
 
266
369
  ```tsx
370
+ import { useState } from 'react';
371
+ import { Select, Button, Stack } from '@ceed/cds';
372
+
267
373
  function UserForm() {
268
- const [country, setCountry] = useState('');
269
- const [role, setRole] = useState('');
374
+ const [role, setRole] = useState<string | null>(null);
375
+ const [submitted, setSubmitted] = useState(false);
376
+
377
+ const handleSubmit = (e: React.FormEvent) => {
378
+ e.preventDefault();
379
+ setSubmitted(true);
380
+ if (role) {
381
+ // submit form
382
+ }
383
+ };
270
384
 
271
385
  return (
272
386
  <form onSubmit={handleSubmit}>
273
387
  <Stack spacing={2}>
274
- <Select
275
- label="Country"
276
- required
277
- value={country}
278
- onChange={(e, val) => setCountry(val)}
279
- options={countries}
280
- placeholder="Select your country"
281
- />
282
-
283
388
  <Select
284
389
  label="Role"
285
390
  required
286
391
  value={role}
287
392
  onChange={(e, val) => setRole(val)}
393
+ error={submitted && !role}
394
+ helperText={submitted && !role ? 'Please select a role' : undefined}
288
395
  options={[
289
396
  { value: 'admin', label: 'Administrator' },
290
397
  { value: 'editor', label: 'Editor' },
291
398
  { value: 'viewer', label: 'Viewer' },
292
399
  ]}
400
+ placeholder="Select a role..."
293
401
  />
294
-
295
402
  <Button type="submit">Submit</Button>
296
403
  </Stack>
297
404
  </form>
@@ -299,9 +406,11 @@ function UserForm() {
299
406
  }
300
407
  ```
301
408
 
302
- ### Filter Select
409
+ ### Filter Bar
303
410
 
304
411
  ```tsx
412
+ import { Select, Stack } from '@ceed/cds';
413
+
305
414
  function ProductFilter({ onFilterChange }) {
306
415
  const [category, setCategory] = useState('all');
307
416
  const [sortBy, setSortBy] = useState('newest');
@@ -321,10 +430,8 @@ function ProductFilter({ onFilterChange }) {
321
430
  { value: 'all', label: 'All Categories' },
322
431
  { value: 'electronics', label: 'Electronics' },
323
432
  { value: 'clothing', label: 'Clothing' },
324
- { value: 'home', label: 'Home & Garden' },
325
433
  ]}
326
434
  />
327
-
328
435
  <Select
329
436
  label="Sort By"
330
437
  size="sm"
@@ -334,7 +441,6 @@ function ProductFilter({ onFilterChange }) {
334
441
  { value: 'newest', label: 'Newest First' },
335
442
  { value: 'price-low', label: 'Price: Low to High' },
336
443
  { value: 'price-high', label: 'Price: High to Low' },
337
- { value: 'popular', label: 'Most Popular' },
338
444
  ]}
339
445
  />
340
446
  </Stack>
@@ -342,39 +448,15 @@ function ProductFilter({ onFilterChange }) {
342
448
  }
343
449
  ```
344
450
 
345
- ### Multiple Select with Tags
346
-
347
- ```tsx
348
- function TagSelect() {
349
- const [selectedTags, setSelectedTags] = useState([]);
350
-
351
- const tagOptions = [
352
- { value: 'urgent', label: 'Urgent' },
353
- { value: 'feature', label: 'Feature' },
354
- { value: 'bug', label: 'Bug' },
355
- { value: 'enhancement', label: 'Enhancement' },
356
- { value: 'documentation', label: 'Documentation' },
357
- ];
358
-
359
- return (
360
- <Select
361
- label="Tags"
362
- multiple
363
- value={selectedTags}
364
- onChange={(e, val) => setSelectedTags(val)}
365
- options={tagOptions}
366
- placeholder="Select tags..."
367
- />
368
- );
369
- }
370
- ```
371
-
372
451
  ### Dependent Selects
373
452
 
374
453
  ```tsx
454
+ import { useState, useMemo } from 'react';
455
+ import { Select, Stack } from '@ceed/cds';
456
+
375
457
  function LocationSelect() {
376
- const [country, setCountry] = useState('');
377
- const [city, setCity] = useState('');
458
+ const [country, setCountry] = useState<string | null>(null);
459
+ const [city, setCity] = useState<string | null>(null);
378
460
 
379
461
  const cities = useMemo(() => {
380
462
  if (!country) return [];
@@ -388,269 +470,83 @@ function LocationSelect() {
388
470
  value={country}
389
471
  onChange={(e, val) => {
390
472
  setCountry(val);
391
- setCity(''); // Reset city when country changes
473
+ setCity(null);
392
474
  }}
393
475
  options={countries}
476
+ placeholder="Select a country"
394
477
  />
395
-
396
478
  <Select
397
479
  label="City"
398
480
  value={city}
399
481
  onChange={(e, val) => setCity(val)}
400
482
  options={cities}
401
483
  disabled={!country}
402
- placeholder={country ? 'Select city' : 'Select country first'}
484
+ placeholder={country ? 'Select a city' : 'Select a country first'}
403
485
  />
404
486
  </Stack>
405
487
  );
406
488
  }
407
489
  ```
408
490
 
409
- ### With Custom Option Rendering
410
-
411
- ```tsx
412
- function UserSelect() {
413
- const userOptions = [
414
- { value: 'user1', label: 'John Doe', secondaryText: 'john@example.com' },
415
- { value: 'user2', label: 'Jane Smith', secondaryText: 'jane@example.com' },
416
- { value: 'user3', label: 'Bob Johnson', secondaryText: 'bob@example.com' },
417
- ];
418
-
419
- return (
420
- <Select
421
- label="Assign to"
422
- options={userOptions}
423
- placeholder="Select a user..."
424
- />
425
- );
426
- }
427
- ```
428
-
429
- ## Props and Customization
430
-
431
- ### Key Props
432
-
433
- | Prop | Type | Default | Description |
434
- | -------------- | -------------------------------------------------------------- | ------------ | ------------------------------- |
435
- | `options` | `Option[]` | `[]` | Array of options to display |
436
- | `value` | `T \| T[]` | - | Selected value(s) (controlled) |
437
- | `defaultValue` | `T \| T[]` | - | Default value (uncontrolled) |
438
- | `onChange` | `function` | - | Callback when selection changes |
439
- | `multiple` | `boolean` | `false` | Allow multiple selections |
440
- | `label` | `string` | - | Label text above the Select |
441
- | `helperText` | `string` | - | Helper text below the Select |
442
- | `error` | `boolean` | `false` | Show error state |
443
- | `required` | `boolean` | `false` | Mark as required |
444
- | `disabled` | `boolean` | `false` | Disable the Select |
445
- | `placeholder` | `string` | - | Placeholder text |
446
- | `variant` | `'plain' \| 'outlined' \| 'soft' \| 'solid'` | `'outlined'` | Visual style |
447
- | `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | - | Color scheme |
448
- | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Component size |
449
-
450
- ### Option Type
451
-
452
- ```tsx
453
- interface Option {
454
- value: string | number;
455
- label: string;
456
- secondaryText?: string;
457
- disabled?: boolean;
458
- }
459
- ```
460
-
461
- ### Numeric Values
462
-
463
- Select supports both string and numeric values:
464
-
465
- ```tsx
466
- <Stack spacing={4}>
467
- <Select defaultValue={1} options={numericOptions} />
468
- <Select defaultValue={1} variant="plain" options={numericOptions} />
469
- <Select defaultValue={1} variant="soft" options={numericOptions} />
470
- <Select defaultValue={1} variant="solid" options={numericOptions} />
471
- </Stack>
472
- ```
473
-
474
- ```tsx
475
- const numericOptions = [
476
- { value: 1, label: 'Option 1' },
477
- { value: 2, label: 'Option 2' },
478
- { value: 3, label: 'Option 3' },
479
- ];
480
-
481
- <Select options={numericOptions} defaultValue={1} />
482
- ```
483
-
484
- ### Custom Styling
485
-
486
- ```tsx
487
- <Select
488
- options={options}
489
- sx={{
490
- minWidth: 200,
491
- '& .MuiSelect-button': {
492
- borderRadius: 'xl',
493
- },
494
- }}
495
- />
496
- ```
497
-
498
- ## Accessibility
499
-
500
- Select components follow accessibility best practices:
501
-
502
- ### ARIA Attributes
503
-
504
- - Uses native listbox role with proper ARIA attributes
505
- - `aria-labelledby` connects to the label
506
- - `aria-describedby` connects to helper text
507
- - `aria-required` when required prop is true
508
- - `aria-invalid` when error prop is true
509
-
510
- ### Keyboard Navigation
511
-
512
- - **Tab**: Focus the Select
513
- - **Enter/Space**: Open the dropdown
514
- - **Arrow Up/Down**: Navigate through options
515
- - **Home/End**: Jump to first/last option
516
- - **Enter**: Select the focused option
517
- - **Escape**: Close the dropdown
518
-
519
- ### Screen Reader Support
520
-
521
- ```tsx
522
- <Select
523
- label="Country"
524
- helperText="Select your country of residence"
525
- options={countries}
526
- aria-label="Country selection"
527
- />
528
- ```
529
-
530
- ### Form Association
531
-
532
- ```tsx
533
- <FormControl required error={hasError}>
534
- <FormLabel>Country</FormLabel>
535
- <Select options={countries} />
536
- <FormHelperText>{hasError ? 'Please select a country' : 'Required field'}</FormHelperText>
537
- </FormControl>
538
- ```
539
-
540
491
  ## Best Practices
541
492
 
542
- ### Do
543
-
544
- 1. **Use clear labels**: Labels should clearly describe what the user is selecting
493
+ 1. **Use clear, specific labels.** The label should describe exactly what the user is choosing.
545
494
 
546
495
  ```tsx
547
- // ✅ Good: Clear, specific label
496
+ // ✅ Good
548
497
  <Select label="Preferred contact method" options={contactMethods} />
549
498
 
550
- // ❌ Bad: Vague label
499
+ // ❌ Bad
551
500
  <Select label="Select" options={contactMethods} />
552
501
  ```
553
502
 
554
- 2. **Provide meaningful option labels**: Options should be easy to understand
503
+ 2. **Provide meaningful option text.** Each option label should be immediately understandable.
555
504
 
556
505
  ```tsx
557
- // ✅ Good: Descriptive options
506
+ // ✅ Good
558
507
  { value: 'express', label: 'Express Shipping (1-2 days)' }
559
508
 
560
- // ❌ Bad: Cryptic options
509
+ // ❌ Bad
561
510
  { value: 'exp', label: 'EXP' }
562
511
  ```
563
512
 
564
- 3. **Order options logically**: Alphabetically, by frequency, or by importance
565
-
566
- 4. **Include a placeholder**: Help users understand what to select
567
-
568
- ```tsx
569
- <Select placeholder="Select a country..." options={countries} />
570
- ```
571
-
572
- 5. **Use helper text**: Provide additional context when needed
513
+ 3. **Do not use Select for actions.** Select is for data input. For action menus (Edit, Delete, etc.), use Dropdown with Menu.
573
514
 
574
515
  ```tsx
516
+ // ✅ Good: Select for data
575
517
  <Select
576
- label="Timezone"
577
- helperText="Select the timezone for your reports"
578
- options={timezones}
518
+ label="Country"
519
+ options={countries}
579
520
  />
580
- ```
581
-
582
- ### ❌ Don't
583
-
584
- 1. **Don't use for actions**: Select is for data, not navigation or actions
585
-
586
- ```tsx
587
- // ❌ Bad: Using Select for actions
588
- <Select options={[
589
- { value: 'edit', label: 'Edit' },
590
- { value: 'delete', label: 'Delete' },
591
- ]} />
592
-
593
- // ✅ Good: Use Dropdown for actions
594
- <Dropdown>
595
- <MenuButton>Actions</MenuButton>
596
- <Menu>
597
- <MenuItem onClick={handleEdit}>Edit</MenuItem>
598
- <MenuItem onClick={handleDelete}>Delete</MenuItem>
599
- </Menu>
600
- </Dropdown>
601
- ```
602
-
603
- 2. **Don't disable without explanation**: If options are unavailable, explain why
604
-
605
- 3. **Don't use with very few options**: Consider RadioGroup for 2-4 options
606
-
607
- 4. **Don't use with many options without search**: Use Autocomplete for 20+ options
608
-
609
- ## Performance Considerations
610
521
 
611
- ### Large Option Lists
612
-
613
- For lists with many options, consider:
614
-
615
- 1. **Pagination or virtual scrolling**: For very large lists
616
- 2. **Autocomplete**: When users need to search through options
617
- 3. **Lazy loading**: Load options on demand
618
-
619
- ```tsx
620
- // For many options, use Autocomplete instead
621
- <Autocomplete
622
- options={largeOptionList}
623
- getOptionLabel={(option) => option.label}
624
- renderInput={(params) => <Input {...params} label="Search..." />}
522
+ // Bad: Select for actions
523
+ <Select
524
+ options={[
525
+ { value: 'edit', label: 'Edit' },
526
+ { value: 'delete', label: 'Delete' },
527
+ ]}
625
528
  />
626
529
  ```
627
530
 
628
- ### Controlled vs Uncontrolled
531
+ 4. **Choose the right component for the option count.** For 2--4 visible choices, use RadioGroup. For 20+ options that need searching, use Autocomplete. Select works best with roughly 5--20 options.
629
532
 
630
- Use controlled mode only when necessary:
533
+ 5. **Use the built-in `label` and `helperText` props** instead of manually wrapping Select with FormControl, FormLabel, and FormHelperText. The component handles the composition and accessibility wiring internally.
631
534
 
632
535
  ```tsx
633
- // Uncontrolled - simpler, better performance
634
- <Select defaultValue="option1" options={options} />
536
+ // Good
537
+ <Select label="Timezone" helperText="Used for report scheduling" options={timezones} />
635
538
 
636
- // Controlled - when you need to sync with other state
637
- const [value, setValue] = useState('option1');
638
- <Select value={value} onChange={(e, val) => setValue(val)} options={options} />
539
+ // Bad
540
+ <FormControl>
541
+ <FormLabel>Timezone</FormLabel>
542
+ <Select options={timezones} />
543
+ <FormHelperText>Used for report scheduling</FormHelperText>
544
+ </FormControl>
639
545
  ```
640
546
 
641
- ### Memoize Options
642
-
643
- When options are computed, memoize them:
644
-
645
- ```tsx
646
- const options = useMemo(() =>
647
- data.map(item => ({
648
- value: item.id,
649
- label: item.name,
650
- })),
651
- [data]);
652
-
653
- <Select options={options} />
654
- ```
547
+ ## Accessibility
655
548
 
656
- Select is a fundamental form component that provides a clean interface for choosing from predefined options. Use it appropriately to create intuitive forms and ensure users can easily make selections.
549
+ - **Label association**: When you use the `label` prop, the component automatically connects the label to the select element via `aria-labelledby`. Always provide a label for screen reader users.
550
+ - **Keyboard navigation**: The select can be opened with Enter or Space, navigated with Arrow Up/Down and Home/End, confirmed with Enter, and dismissed with Escape.
551
+ - **Error announcement**: When `error` is set, `aria-invalid` is applied. Pair it with a descriptive `helperText` so assistive technology can announce the error reason.
552
+ - **Required state**: The `required` prop adds `aria-required` and a visible asterisk to the label, communicating the requirement both visually and programmatically.