@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
|
+
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.
|
|
6
|
+
|
|
5
7
|
```tsx
|
|
6
8
|
<Select options={options} />
|
|
7
9
|
```
|
|
@@ -22,8 +24,53 @@
|
|
|
22
24
|
| onChange | — | — |
|
|
23
25
|
| options | — | options |
|
|
24
26
|
|
|
27
|
+
> ⚠️ **Don't Confuse Select with Dropdown** ⚠️
|
|
28
|
+
>
|
|
29
|
+
> - **Select**: For form value selection - the selected value becomes form data
|
|
30
|
+
> - **Dropdown**: For menus and action lists - triggers actions or navigation
|
|
31
|
+
>
|
|
32
|
+
> Choose the right component:
|
|
33
|
+
>
|
|
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
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { Select } from '@ceed/ads';
|
|
42
|
+
|
|
43
|
+
const options = [
|
|
44
|
+
{ value: 'dog', label: 'Dog' },
|
|
45
|
+
{ value: 'cat', label: 'Cat' },
|
|
46
|
+
{ value: 'fish', label: 'Fish' },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
function MyComponent() {
|
|
50
|
+
return (
|
|
51
|
+
<Select
|
|
52
|
+
label="Choose a pet"
|
|
53
|
+
options={options}
|
|
54
|
+
defaultValue="dog"
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
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
|
+
|
|
25
70
|
### Variants
|
|
26
71
|
|
|
72
|
+
Select supports different visual styles.
|
|
73
|
+
|
|
27
74
|
```tsx
|
|
28
75
|
<Stack spacing={4}>
|
|
29
76
|
<Select defaultValue="dog" options={options} />
|
|
@@ -35,6 +82,8 @@
|
|
|
35
82
|
|
|
36
83
|
### Sizes
|
|
37
84
|
|
|
85
|
+
Select comes in different sizes.
|
|
86
|
+
|
|
38
87
|
```tsx
|
|
39
88
|
<Stack spacing={4}>
|
|
40
89
|
<Select defaultValue="dog" size="sm" options={options} />
|
|
@@ -45,6 +94,8 @@
|
|
|
45
94
|
|
|
46
95
|
### Colors
|
|
47
96
|
|
|
97
|
+
Apply semantic colors to Select.
|
|
98
|
+
|
|
48
99
|
```tsx
|
|
49
100
|
<Stack spacing={4}>
|
|
50
101
|
<Select defaultValue="dog" color="primary" options={options} />
|
|
@@ -54,3 +105,552 @@
|
|
|
54
105
|
<Select defaultValue="dog" color="warning" options={options} />
|
|
55
106
|
</Stack>
|
|
56
107
|
```
|
|
108
|
+
|
|
109
|
+
### With Decorators
|
|
110
|
+
|
|
111
|
+
Add icons or badges to the Select.
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
<Select placeholder="Select a pet…" startDecorator={<FavoriteBorder />} endDecorator={<Chip size="sm" color="danger" variant="soft">
|
|
115
|
+
+5
|
|
116
|
+
</Chip>} sx={{
|
|
117
|
+
width: 240
|
|
118
|
+
}} options={options} />
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### With Label
|
|
122
|
+
|
|
123
|
+
Select with an associated label.
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
<>
|
|
127
|
+
<Select label="Label" defaultValue="dog" options={options} />
|
|
128
|
+
</>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### With Helper Text
|
|
132
|
+
|
|
133
|
+
Add helper text for additional context.
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
<>
|
|
137
|
+
<Select label="Select" helperText="I'm helper text" defaultValue="dog" options={options} />
|
|
138
|
+
</>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Error State
|
|
142
|
+
|
|
143
|
+
Show validation errors.
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
<>
|
|
147
|
+
<Select label="label" error defaultValue="dog" options={options} />
|
|
148
|
+
</>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Form Control
|
|
152
|
+
|
|
153
|
+
Complete form integration with label and helper text.
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
<>
|
|
157
|
+
<Select label="Label" helperText="I'm helper text" defaultValue="dog" options={options} />
|
|
158
|
+
<Select label="Label" helperText="I'm helper text" defaultValue="dog" error options={options} />
|
|
159
|
+
</>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Required Field
|
|
163
|
+
|
|
164
|
+
Mark the Select as required.
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
<Select
|
|
168
|
+
options={options}
|
|
169
|
+
label="Label"
|
|
170
|
+
helperText="I'm helper text"
|
|
171
|
+
required
|
|
172
|
+
/>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Multiple Selection
|
|
176
|
+
|
|
177
|
+
Allow selecting multiple options.
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
<Select
|
|
181
|
+
options={options}
|
|
182
|
+
label="Label"
|
|
183
|
+
helperText="I'm helper text"
|
|
184
|
+
defaultValue={['dog']}
|
|
185
|
+
multiple
|
|
186
|
+
/>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Controlled Select
|
|
190
|
+
|
|
191
|
+
Programmatically control the selected value.
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
<div>
|
|
195
|
+
<Select {...args} options={options} value={value} onChange={(event, newValue) => {
|
|
196
|
+
setValue(newValue);
|
|
197
|
+
args.onChange?.(event, newValue);
|
|
198
|
+
}} />
|
|
199
|
+
<Select {...args} options={['dog', 'cat', 'fish', 'bird'] as string[]} value={value} onChange={(event, newValue) => {
|
|
200
|
+
setValue(newValue);
|
|
201
|
+
args.onChange?.(event, newValue);
|
|
202
|
+
}} />
|
|
203
|
+
<Select {...args} options={['dog', 'cat', 'fish', 'bird'] as string[]} value={[value]} onChange={(event, newValue) => {
|
|
204
|
+
setValue(newValue);
|
|
205
|
+
args.onChange?.(event, newValue);
|
|
206
|
+
}} multiple />
|
|
207
|
+
</div>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Disabled Options
|
|
211
|
+
|
|
212
|
+
Individual options can be disabled.
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
<Select
|
|
216
|
+
options={options.map((option, idx) => ({
|
|
217
|
+
...option,
|
|
218
|
+
disabled: idx % 2 === 0
|
|
219
|
+
}))}
|
|
220
|
+
label="Disabled Options"
|
|
221
|
+
defaultValue="dog"
|
|
222
|
+
/>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Options with Secondary Text
|
|
226
|
+
|
|
227
|
+
Display additional information for each option.
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
<Stack spacing={4} direction="row" alignItems="flex-start">
|
|
231
|
+
{sizes.map(size => <Stack key={size} spacing={1}>
|
|
232
|
+
<span style={{
|
|
233
|
+
color: '#6366f1',
|
|
234
|
+
fontSize: 12
|
|
235
|
+
}}>{size}</span>
|
|
236
|
+
<Select placeholder="Placeholder" options={optionsWithSecondaryText} sx={{
|
|
237
|
+
minWidth: 200
|
|
238
|
+
}} size={size} />
|
|
239
|
+
</Stack>)}
|
|
240
|
+
</Stack>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## When to Use
|
|
244
|
+
|
|
245
|
+
### ✅ Good Use Cases
|
|
246
|
+
|
|
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
|
|
253
|
+
|
|
254
|
+
### ❌ When Not to Use
|
|
255
|
+
|
|
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
|
|
261
|
+
|
|
262
|
+
## Common Use Cases
|
|
263
|
+
|
|
264
|
+
### Form with Select
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
function UserForm() {
|
|
268
|
+
const [country, setCountry] = useState('');
|
|
269
|
+
const [role, setRole] = useState('');
|
|
270
|
+
|
|
271
|
+
return (
|
|
272
|
+
<form onSubmit={handleSubmit}>
|
|
273
|
+
<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
|
+
<Select
|
|
284
|
+
label="Role"
|
|
285
|
+
required
|
|
286
|
+
value={role}
|
|
287
|
+
onChange={(e, val) => setRole(val)}
|
|
288
|
+
options={[
|
|
289
|
+
{ value: 'admin', label: 'Administrator' },
|
|
290
|
+
{ value: 'editor', label: 'Editor' },
|
|
291
|
+
{ value: 'viewer', label: 'Viewer' },
|
|
292
|
+
]}
|
|
293
|
+
/>
|
|
294
|
+
|
|
295
|
+
<Button type="submit">Submit</Button>
|
|
296
|
+
</Stack>
|
|
297
|
+
</form>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Filter Select
|
|
303
|
+
|
|
304
|
+
```tsx
|
|
305
|
+
function ProductFilter({ onFilterChange }) {
|
|
306
|
+
const [category, setCategory] = useState('all');
|
|
307
|
+
const [sortBy, setSortBy] = useState('newest');
|
|
308
|
+
|
|
309
|
+
useEffect(() => {
|
|
310
|
+
onFilterChange({ category, sortBy });
|
|
311
|
+
}, [category, sortBy]);
|
|
312
|
+
|
|
313
|
+
return (
|
|
314
|
+
<Stack direction="row" spacing={2}>
|
|
315
|
+
<Select
|
|
316
|
+
label="Category"
|
|
317
|
+
size="sm"
|
|
318
|
+
value={category}
|
|
319
|
+
onChange={(e, val) => setCategory(val)}
|
|
320
|
+
options={[
|
|
321
|
+
{ value: 'all', label: 'All Categories' },
|
|
322
|
+
{ value: 'electronics', label: 'Electronics' },
|
|
323
|
+
{ value: 'clothing', label: 'Clothing' },
|
|
324
|
+
{ value: 'home', label: 'Home & Garden' },
|
|
325
|
+
]}
|
|
326
|
+
/>
|
|
327
|
+
|
|
328
|
+
<Select
|
|
329
|
+
label="Sort By"
|
|
330
|
+
size="sm"
|
|
331
|
+
value={sortBy}
|
|
332
|
+
onChange={(e, val) => setSortBy(val)}
|
|
333
|
+
options={[
|
|
334
|
+
{ value: 'newest', label: 'Newest First' },
|
|
335
|
+
{ value: 'price-low', label: 'Price: Low to High' },
|
|
336
|
+
{ value: 'price-high', label: 'Price: High to Low' },
|
|
337
|
+
{ value: 'popular', label: 'Most Popular' },
|
|
338
|
+
]}
|
|
339
|
+
/>
|
|
340
|
+
</Stack>
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
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
|
+
### Dependent Selects
|
|
373
|
+
|
|
374
|
+
```tsx
|
|
375
|
+
function LocationSelect() {
|
|
376
|
+
const [country, setCountry] = useState('');
|
|
377
|
+
const [city, setCity] = useState('');
|
|
378
|
+
|
|
379
|
+
const cities = useMemo(() => {
|
|
380
|
+
if (!country) return [];
|
|
381
|
+
return getCitiesByCountry(country);
|
|
382
|
+
}, [country]);
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
<Stack spacing={2}>
|
|
386
|
+
<Select
|
|
387
|
+
label="Country"
|
|
388
|
+
value={country}
|
|
389
|
+
onChange={(e, val) => {
|
|
390
|
+
setCountry(val);
|
|
391
|
+
setCity(''); // Reset city when country changes
|
|
392
|
+
}}
|
|
393
|
+
options={countries}
|
|
394
|
+
/>
|
|
395
|
+
|
|
396
|
+
<Select
|
|
397
|
+
label="City"
|
|
398
|
+
value={city}
|
|
399
|
+
onChange={(e, val) => setCity(val)}
|
|
400
|
+
options={cities}
|
|
401
|
+
disabled={!country}
|
|
402
|
+
placeholder={country ? 'Select city' : 'Select country first'}
|
|
403
|
+
/>
|
|
404
|
+
</Stack>
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
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
|
+
## Best Practices
|
|
541
|
+
|
|
542
|
+
### ✅ Do
|
|
543
|
+
|
|
544
|
+
1. **Use clear labels**: Labels should clearly describe what the user is selecting
|
|
545
|
+
|
|
546
|
+
```tsx
|
|
547
|
+
// ✅ Good: Clear, specific label
|
|
548
|
+
<Select label="Preferred contact method" options={contactMethods} />
|
|
549
|
+
|
|
550
|
+
// ❌ Bad: Vague label
|
|
551
|
+
<Select label="Select" options={contactMethods} />
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
2. **Provide meaningful option labels**: Options should be easy to understand
|
|
555
|
+
|
|
556
|
+
```tsx
|
|
557
|
+
// ✅ Good: Descriptive options
|
|
558
|
+
{ value: 'express', label: 'Express Shipping (1-2 days)' }
|
|
559
|
+
|
|
560
|
+
// ❌ Bad: Cryptic options
|
|
561
|
+
{ value: 'exp', label: 'EXP' }
|
|
562
|
+
```
|
|
563
|
+
|
|
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
|
|
573
|
+
|
|
574
|
+
```tsx
|
|
575
|
+
<Select
|
|
576
|
+
label="Timezone"
|
|
577
|
+
helperText="Select the timezone for your reports"
|
|
578
|
+
options={timezones}
|
|
579
|
+
/>
|
|
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
|
+
|
|
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..." />}
|
|
625
|
+
/>
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### Controlled vs Uncontrolled
|
|
629
|
+
|
|
630
|
+
Use controlled mode only when necessary:
|
|
631
|
+
|
|
632
|
+
```tsx
|
|
633
|
+
// Uncontrolled - simpler, better performance
|
|
634
|
+
<Select defaultValue="option1" options={options} />
|
|
635
|
+
|
|
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} />
|
|
639
|
+
```
|
|
640
|
+
|
|
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
|
+
```
|
|
655
|
+
|
|
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.
|