@ceed/ads 1.23.2 → 1.23.4

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