@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.
- package/dist/chunks/rehype-accent-FZRUD7VI.js +39 -0
- package/dist/components/CurrencyInput/CurrencyInput.d.ts +1 -1
- package/dist/components/CurrencyInput/hooks/use-currency-setting.d.ts +2 -2
- package/dist/components/DataTable/components.d.ts +2 -1
- package/dist/components/DataTable/styled.d.ts +3 -1
- package/dist/components/DataTable/types.d.ts +1 -0
- package/dist/components/RadioTileGroup/RadioTileGroup.d.ts +56 -0
- package/dist/components/RadioTileGroup/index.d.ts +3 -0
- package/dist/components/data-display/DataTable.md +77 -1
- package/dist/components/data-display/InfoSign.md +74 -91
- package/dist/components/data-display/Typography.md +411 -94
- package/dist/components/feedback/CircularProgress.md +257 -0
- package/dist/components/feedback/Dialog.md +76 -62
- package/dist/components/feedback/Modal.md +430 -138
- package/dist/components/feedback/Skeleton.md +280 -0
- package/dist/components/feedback/llms.txt +2 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/inputs/Autocomplete.md +356 -107
- package/dist/components/inputs/ButtonGroup.md +115 -104
- package/dist/components/inputs/CurrencyInput.md +183 -5
- package/dist/components/inputs/DatePicker.md +108 -431
- package/dist/components/inputs/DateRangePicker.md +131 -492
- package/dist/components/inputs/FilterableCheckboxGroup.md +145 -19
- package/dist/components/inputs/FormControl.md +361 -0
- package/dist/components/inputs/IconButton.md +137 -88
- package/dist/components/inputs/Input.md +204 -73
- package/dist/components/inputs/MonthPicker.md +95 -422
- package/dist/components/inputs/MonthRangePicker.md +89 -466
- package/dist/components/inputs/PercentageInput.md +185 -16
- package/dist/components/inputs/RadioButton.md +163 -35
- package/dist/components/inputs/RadioList.md +241 -0
- package/dist/components/inputs/RadioTileGroup.md +507 -0
- package/dist/components/inputs/Select.md +222 -326
- package/dist/components/inputs/Slider.md +334 -0
- package/dist/components/inputs/Switch.md +143 -376
- package/dist/components/inputs/Textarea.md +213 -10
- package/dist/components/inputs/Uploader/Uploader.md +145 -66
- package/dist/components/inputs/llms.txt +4 -0
- package/dist/components/navigation/Breadcrumbs.md +57 -308
- package/dist/components/navigation/Drawer.md +180 -0
- package/dist/components/navigation/Dropdown.md +98 -215
- package/dist/components/navigation/IconMenuButton.md +40 -502
- package/dist/components/navigation/InsetDrawer.md +281 -650
- package/dist/components/navigation/Link.md +31 -348
- package/dist/components/navigation/Menu.md +92 -285
- package/dist/components/navigation/MenuButton.md +55 -448
- package/dist/components/navigation/Pagination.md +47 -338
- package/dist/components/navigation/Stepper.md +160 -28
- package/dist/components/navigation/Tabs.md +57 -316
- package/dist/components/surfaces/Accordions.md +49 -804
- package/dist/components/surfaces/Card.md +97 -157
- package/dist/components/surfaces/Divider.md +83 -234
- package/dist/components/surfaces/Sheet.md +153 -328
- package/dist/guides/ThemeProvider.md +89 -0
- package/dist/guides/llms.txt +9 -0
- package/dist/index.browser.js +224 -0
- package/dist/index.browser.js.map +7 -0
- package/dist/index.cjs +648 -390
- package/dist/index.d.ts +1 -1
- package/dist/index.js +563 -361
- package/dist/llms.txt +9 -0
- package/framer/index.js +1 -163
- 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
|
|
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
|
-
>
|
|
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
|
-
>
|
|
30
|
-
> - **Dropdown**: For menus and action lists - triggers actions or navigation
|
|
34
|
+
> Choosing the right component:
|
|
31
35
|
>
|
|
32
|
-
>
|
|
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
|
-
>
|
|
35
|
-
> -
|
|
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
|
-
##
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
139
|
+
```tsx
|
|
140
|
+
import FavoriteBorder from '@mui/icons-material/FavoriteBorder';
|
|
141
|
+
import { Chip } from '@ceed/cds';
|
|
122
142
|
|
|
123
|
-
Select
|
|
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
|
-
|
|
170
|
+
```tsx
|
|
171
|
+
<Select label="Pet" options={options} defaultValue="dog" />
|
|
142
172
|
|
|
143
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
211
|
+
## Required Field
|
|
163
212
|
|
|
164
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
247
|
+
```tsx
|
|
248
|
+
<Select
|
|
249
|
+
label="Pets"
|
|
250
|
+
multiple
|
|
251
|
+
defaultValue={['dog']}
|
|
252
|
+
options={options}
|
|
253
|
+
/>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Controlled Select
|
|
190
257
|
|
|
191
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
339
|
+
<Select placeholder="Select a contact" options={userOptions} />
|
|
340
|
+
```
|
|
246
341
|
|
|
247
|
-
|
|
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
|
-
|
|
344
|
+
Select supports both string and numeric option values. When using numeric values, TypeScript infers the correct type automatically.
|
|
255
345
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
|
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 [
|
|
269
|
-
const [
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
|
496
|
+
// ✅ Good
|
|
548
497
|
<Select label="Preferred contact method" options={contactMethods} />
|
|
549
498
|
|
|
550
|
-
// ❌ Bad
|
|
499
|
+
// ❌ Bad
|
|
551
500
|
<Select label="Select" options={contactMethods} />
|
|
552
501
|
```
|
|
553
502
|
|
|
554
|
-
2. **Provide meaningful option
|
|
503
|
+
2. **Provide meaningful option text.** Each option label should be immediately understandable.
|
|
555
504
|
|
|
556
505
|
```tsx
|
|
557
|
-
// ✅ Good
|
|
506
|
+
// ✅ Good
|
|
558
507
|
{ value: 'express', label: 'Express Shipping (1-2 days)' }
|
|
559
508
|
|
|
560
|
-
// ❌ Bad
|
|
509
|
+
// ❌ Bad
|
|
561
510
|
{ value: 'exp', label: 'EXP' }
|
|
562
511
|
```
|
|
563
512
|
|
|
564
|
-
3. **
|
|
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="
|
|
577
|
-
|
|
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
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
634
|
-
<Select
|
|
536
|
+
// ✅ Good
|
|
537
|
+
<Select label="Timezone" helperText="Used for report scheduling" options={timezones} />
|
|
635
538
|
|
|
636
|
-
//
|
|
637
|
-
|
|
638
|
-
<
|
|
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
|
-
|
|
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
|
-
|
|
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.
|