@ceed/ads 1.35.1 → 1.37.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 (122) hide show
  1. package/README.md +85 -95
  2. package/dist/components/Accordions/Accordions.d.ts +1 -0
  3. package/dist/components/Alert/Alert.d.ts +5 -5
  4. package/dist/components/Autocomplete/Autocomplete.d.ts +2 -2
  5. package/dist/components/Avatar/Avatar.d.ts +7 -17
  6. package/dist/components/Box/Box.d.ts +1 -0
  7. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +6 -5
  8. package/dist/components/Button/Button.d.ts +3 -2
  9. package/dist/components/Calendar/Calendar.d.ts +1 -0
  10. package/dist/components/Card/Card.d.ts +1 -0
  11. package/dist/components/Checkbox/Checkbox.d.ts +1 -0
  12. package/dist/components/Chip/Chip.d.ts +1 -0
  13. package/dist/components/Container/Container.d.ts +6 -1
  14. package/dist/components/DialogActions/DialogActions.d.ts +1 -0
  15. package/dist/components/DialogContent/DialogContent.d.ts +1 -0
  16. package/dist/components/DialogFrame/DialogFrame.d.ts +1 -1
  17. package/dist/components/DialogTitle/DialogTitle.d.ts +1 -0
  18. package/dist/components/Divider/Divider.d.ts +1 -0
  19. package/dist/components/Dropdown/Dropdown.d.ts +28 -1
  20. package/dist/components/FilterMenu/components/MonthRange.d.ts +11 -0
  21. package/dist/components/FilterMenu/types.d.ts +5 -1
  22. package/dist/components/FormControl/FormControl.d.ts +1 -0
  23. package/dist/components/FormHelperText/FormHelperText.d.ts +1 -0
  24. package/dist/components/FormLabel/FormLabel.d.ts +1 -0
  25. package/dist/components/Grid/Grid.d.ts +1 -0
  26. package/dist/components/IconButton/IconButton.d.ts +3 -2
  27. package/dist/components/IconMenuButton/IconMenuButton.d.ts +7 -6
  28. package/dist/components/InfoSign/InfoSign.d.ts +3 -2
  29. package/dist/components/Input/Input.d.ts +8 -22
  30. package/dist/components/InsetDrawer/InsetDrawer.d.ts +1 -0
  31. package/dist/components/Markdown/Markdown.d.ts +9 -24
  32. package/dist/components/Menu/Menu.d.ts +2 -1
  33. package/dist/components/MenuButton/MenuButton.d.ts +10 -8
  34. package/dist/components/Modal/Modal.d.ts +4 -2
  35. package/dist/components/NavigationGroup/NavigationGroup.d.ts +3 -2
  36. package/dist/components/NavigationItem/NavigationItem.d.ts +3 -2
  37. package/dist/components/Navigator/Navigator.d.ts +5 -4
  38. package/dist/components/Pagination/Pagination.d.ts +1 -1
  39. package/dist/components/ProfileMenu/ProfileMenu.d.ts +2 -2
  40. package/dist/components/Radio/Radio.d.ts +1 -0
  41. package/dist/components/RadioList/RadioList.d.ts +3 -2
  42. package/dist/components/Select/Select.d.ts +20 -10
  43. package/dist/components/Sheet/Sheet.d.ts +1 -0
  44. package/dist/components/Stack/Stack.d.ts +1 -0
  45. package/dist/components/Stepper/Stepper.d.ts +2 -1
  46. package/dist/components/Switch/Switch.d.ts +1 -0
  47. package/dist/components/Table/Table.d.ts +7 -5
  48. package/dist/components/Tabs/Tabs.d.ts +1 -0
  49. package/dist/components/Textarea/Textarea.d.ts +8 -20
  50. package/dist/components/ThemeProvider/ThemeProvider.d.ts +4 -2
  51. package/dist/components/Tooltip/Tooltip.d.ts +1 -0
  52. package/dist/components/Typography/Typography.d.ts +1 -0
  53. package/dist/components/Uploader/Uploader.d.ts +18 -17
  54. package/dist/components/data-display/Avatar.md +60 -72
  55. package/dist/components/data-display/Badge.md +197 -181
  56. package/dist/components/data-display/Chip.md +164 -142
  57. package/dist/components/data-display/DataTable.md +843 -338
  58. package/dist/components/data-display/InfoSign.md +1 -3
  59. package/dist/components/data-display/Markdown.md +93 -125
  60. package/dist/components/data-display/Table.md +1453 -1007
  61. package/dist/components/data-display/Typography.md +101 -104
  62. package/dist/components/feedback/Alert.md +80 -86
  63. package/dist/components/feedback/CircularProgress.md +32 -36
  64. package/dist/components/feedback/Dialog.md +25 -17
  65. package/dist/components/feedback/Modal.md +296 -265
  66. package/dist/components/feedback/Skeleton.md +125 -89
  67. package/dist/components/index.d.ts +60 -1
  68. package/dist/components/inputs/Autocomplete.md +191 -95
  69. package/dist/components/inputs/Button.md +83 -83
  70. package/dist/components/inputs/ButtonGroup.md +195 -185
  71. package/dist/components/inputs/Calendar.md +25 -28
  72. package/dist/components/inputs/Checkbox.md +11 -29
  73. package/dist/components/inputs/CurrencyInput.md +4 -4
  74. package/dist/components/inputs/DatePicker.md +229 -110
  75. package/dist/components/inputs/DateRangePicker.md +248 -137
  76. package/dist/components/inputs/FilterMenu.md +138 -8
  77. package/dist/components/inputs/FilterableCheckboxGroup.md +115 -55
  78. package/dist/components/inputs/FormControl.md +75 -69
  79. package/dist/components/inputs/IconButton.md +229 -205
  80. package/dist/components/inputs/Input.md +131 -98
  81. package/dist/components/inputs/MonthPicker.md +186 -84
  82. package/dist/components/inputs/MonthRangePicker.md +73 -49
  83. package/dist/components/inputs/PercentageInput.md +15 -31
  84. package/dist/components/inputs/RadioButton.md +320 -256
  85. package/dist/components/inputs/RadioList.md +66 -50
  86. package/dist/components/inputs/RadioTileGroup.md +287 -170
  87. package/dist/components/inputs/SearchBar.md +82 -60
  88. package/dist/components/inputs/Select.md +181 -115
  89. package/dist/components/inputs/Slider.md +153 -102
  90. package/dist/components/inputs/Switch.md +193 -138
  91. package/dist/components/inputs/Textarea.md +15 -20
  92. package/dist/components/inputs/Uploader/Uploader.md +68 -39
  93. package/dist/components/layout/Box.md +841 -662
  94. package/dist/components/layout/Container.md +3 -11
  95. package/dist/components/layout/Grid.md +480 -394
  96. package/dist/components/layout/Stack.md +739 -566
  97. package/dist/components/navigation/Breadcrumbs.md +182 -116
  98. package/dist/components/navigation/Dropdown.md +732 -391
  99. package/dist/components/navigation/IconMenuButton.md +14 -6
  100. package/dist/components/navigation/InsetDrawer.md +550 -378
  101. package/dist/components/navigation/Link.md +104 -94
  102. package/dist/components/navigation/Menu.md +623 -502
  103. package/dist/components/navigation/MenuButton.md +18 -10
  104. package/dist/components/navigation/NavigationGroup.md +19 -50
  105. package/dist/components/navigation/NavigationItem.md +6 -6
  106. package/dist/components/navigation/Navigator.md +26 -28
  107. package/dist/components/navigation/Pagination.md +86 -75
  108. package/dist/components/navigation/ProfileMenu.md +65 -43
  109. package/dist/components/navigation/Stepper.md +2 -12
  110. package/dist/components/navigation/Tabs.md +209 -183
  111. package/dist/components/surfaces/Accordions.md +89 -172
  112. package/dist/components/surfaces/Card.md +1094 -709
  113. package/dist/components/surfaces/Divider.md +562 -412
  114. package/dist/components/surfaces/Sheet.md +700 -518
  115. package/dist/guides/ThemeProvider.md +65 -40
  116. package/dist/index.browser.js +4 -4
  117. package/dist/index.browser.js.map +4 -4
  118. package/dist/index.cjs +1655 -1548
  119. package/dist/index.d.ts +1 -1
  120. package/dist/index.js +1314 -1199
  121. package/framer/index.js +1 -1
  122. package/package.json +32 -35
@@ -52,9 +52,9 @@ When the input has a value, hovering over the component reveals a clear (✕) bu
52
52
 
53
53
  ```tsx
54
54
  <Stack alignItems="flex-start" gap={2}>
55
- <Typography level="body-sm">Hover over the input to reveal the clear (✕) button.</Typography>
56
- <SearchBar value={value} onChange={setValue} />
57
- </Stack>
55
+ <Typography level="body-sm">Hover over the input to reveal the clear (✕) button.</Typography>
56
+ <SearchBar value={value} onChange={setValue} />
57
+ </Stack>
58
58
  ```
59
59
 
60
60
  ## Placeholder
@@ -63,19 +63,25 @@ Each `SearchBarOption` can include a `placeholder` field that is shown in the te
63
63
 
64
64
  ```tsx
65
65
  <Stack alignItems="flex-start" gap={3}>
66
- <Stack alignItems="flex-start" gap={1}>
67
- <Typography level="body-xs" fontWeight="md">
68
- No placeholder prop — uses the active option's placeholder
69
- </Typography>
70
- <SearchBar showSelect options={SAMPLE_OPTIONS} value={value} onChange={setValue} />
66
+ <Stack alignItems="flex-start" gap={1}>
67
+ <Typography level="body-xs" fontWeight="md">
68
+ No placeholder prop — uses the active option's placeholder
69
+ </Typography>
70
+ <SearchBar showSelect options={SAMPLE_OPTIONS} value={value} onChange={setValue} />
71
+ </Stack>
72
+ <Stack alignItems="flex-start" gap={1}>
73
+ <Typography level="body-xs" fontWeight="md">
74
+ placeholder="Search by keyword" — overrides option-level placeholder
75
+ </Typography>
76
+ <SearchBar
77
+ showSelect
78
+ options={SAMPLE_OPTIONS}
79
+ placeholder="Search by keyword"
80
+ value={value}
81
+ onChange={setValue}
82
+ />
83
+ </Stack>
71
84
  </Stack>
72
- <Stack alignItems="flex-start" gap={1}>
73
- <Typography level="body-xs" fontWeight="md">
74
- placeholder="Search by keyword" — overrides option-level placeholder
75
- </Typography>
76
- <SearchBar showSelect options={SAMPLE_OPTIONS} placeholder="Search by keyword" value={value} onChange={setValue} />
77
- </Stack>
78
- </Stack>
79
85
  ```
80
86
 
81
87
  ## onSearch
@@ -84,32 +90,46 @@ Each `SearchBarOption` can include a `placeholder` field that is shown in the te
84
90
 
85
91
  ```tsx
86
92
  <Stack alignItems="flex-start" gap={2}>
87
- <SearchBar showSelect options={SAMPLE_OPTIONS} value={value} onChange={setValue} onSearch={setLastSearch} />
88
- <Typography level="body-sm">value: "{value}"</Typography>
89
- {lastSearch && <Typography level="body-sm">
90
- onSearch: [{lastSearch.selectValue}] "{lastSearch.inputValue}"
91
- </Typography>}
92
- </Stack>
93
+ <SearchBar
94
+ showSelect
95
+ options={SAMPLE_OPTIONS}
96
+ value={value}
97
+ onChange={setValue}
98
+ onSearch={setLastSearch}
99
+ />
100
+ <Typography level="body-sm">value: "{value}"</Typography>
101
+ {lastSearch && (
102
+ <Typography level="body-sm">
103
+ onSearch: [{lastSearch.selectValue}] "{lastSearch.inputValue}"
104
+ </Typography>
105
+ )}
106
+ </Stack>
93
107
  ```
94
108
 
95
109
  When `showSelect` is `false`, `selectValue` is omitted from the payload entirely — not `undefined` as a key, but absent.
96
110
 
97
111
  ```tsx
98
112
  <Stack alignItems="flex-start" gap={2}>
99
- <Typography level="body-sm">
100
- When <code>showSelect</code> is <code>false</code>, <code>selectValue</code> is omitted from the{' '}
101
- <code>onSearch</code> payload.
102
- </Typography>
103
- <SearchBar value={value} onChange={setValue} onSearch={setLastSearch} />
104
- {lastSearch && <Stack alignItems="flex-start" gap={0.5}>
105
- <Typography level="body-sm">inputValue: "{lastSearch.inputValue}"</Typography>
106
- <Typography level="body-sm" sx={{
107
- color: lastSearch.selectValue === undefined ? 'success.500' : 'danger.500'
108
- }}>
109
- selectValue: {lastSearch.selectValue === undefined ? 'undefined ✅' : `"${lastSearch.selectValue}" ❌`}
110
- </Typography>
111
- </Stack>}
112
- </Stack>
113
+ <Typography level="body-sm">
114
+ When <code>showSelect</code> is <code>false</code>, <code>selectValue</code> is omitted from
115
+ the <code>onSearch</code> payload.
116
+ </Typography>
117
+ <SearchBar value={value} onChange={setValue} onSearch={setLastSearch} />
118
+ {lastSearch && (
119
+ <Stack alignItems="flex-start" gap={0.5}>
120
+ <Typography level="body-sm">inputValue: "{lastSearch.inputValue}"</Typography>
121
+ <Typography
122
+ level="body-sm"
123
+ sx={{
124
+ color: lastSearch.selectValue === undefined ? "success.500" : "danger.500"
125
+ }}
126
+ >
127
+ selectValue:{" "}
128
+ {lastSearch.selectValue === undefined ? "undefined ✅" : `"${lastSearch.selectValue}" ❌`}
129
+ </Typography>
130
+ </Stack>
131
+ )}
132
+ </Stack>
113
133
  ```
114
134
 
115
135
  ## Width
@@ -118,31 +138,33 @@ SearchBar sizes to its content by default. Pass any `Box` width prop to constrai
118
138
 
119
139
  ```tsx
120
140
  <Stack alignItems="flex-start" gap={3}>
121
- <Stack alignItems="flex-start" gap={1}>
122
- <Typography level="body-xs" fontWeight="md">
123
- width="100%" — stretches to parent width
124
- </Typography>
125
- <Box sx={{
126
- border: '1px dashed',
127
- borderColor: 'neutral.300',
128
- padding: 1
129
- }}>
130
- <SearchBar width="100%" value={value} onChange={setValue} />
131
- </Box>
132
- </Stack>
133
- <Stack alignItems="flex-start" gap={1}>
134
- <Typography level="body-xs" fontWeight="md">
135
- width={400} — fixed 400px
136
- </Typography>
137
- <SearchBar width={400} value={value} onChange={setValue} />
138
- </Stack>
139
- <Stack alignItems="flex-start" gap={1}>
140
- <Typography level="body-xs" fontWeight="md">
141
- No width prop — sizes to content (inline-flex default)
142
- </Typography>
143
- <SearchBar value={value} onChange={setValue} />
144
- </Stack>
145
- </Stack>
141
+ <Stack alignItems="flex-start" gap={1}>
142
+ <Typography level="body-xs" fontWeight="md">
143
+ width="100%" — stretches to parent width
144
+ </Typography>
145
+ <Box
146
+ sx={{
147
+ border: "1px dashed",
148
+ borderColor: "neutral.300",
149
+ padding: 1
150
+ }}
151
+ >
152
+ <SearchBar width="100%" value={value} onChange={setValue} />
153
+ </Box>
154
+ </Stack>
155
+ <Stack alignItems="flex-start" gap={1}>
156
+ <Typography level="body-xs" fontWeight="md">
157
+ width={400} fixed 400px
158
+ </Typography>
159
+ <SearchBar width={400} value={value} onChange={setValue} />
160
+ </Stack>
161
+ <Stack alignItems="flex-start" gap={1}>
162
+ <Typography level="body-xs" fontWeight="md">
163
+ No width prop — sizes to content (inline-flex default)
164
+ </Typography>
165
+ <SearchBar value={value} onChange={setValue} />
166
+ </Stack>
167
+ </Stack>
146
168
  ```
147
169
 
148
170
  ## Props and Customization
@@ -54,13 +54,7 @@ const options = [
54
54
  ];
55
55
 
56
56
  function MyComponent() {
57
- return (
58
- <Select
59
- label="Choose a pet"
60
- options={options}
61
- defaultValue="dog"
62
- />
63
- );
57
+ return <Select label="Choose a pet" options={options} defaultValue="dog" />;
64
58
  }
65
59
  ```
66
60
 
@@ -70,11 +64,11 @@ Select supports four visual styles: `outlined` (default), `plain`, `soft`, and `
70
64
 
71
65
  ```tsx
72
66
  <Stack spacing={4}>
73
- <Select defaultValue="dog" options={options} />
74
- <Select defaultValue="dog" variant="plain" options={options} />
75
- <Select defaultValue="dog" variant="soft" options={options} />
76
- <Select defaultValue="dog" variant="solid" options={options} />
77
- </Stack>
67
+ <Select defaultValue="dog" options={options} />
68
+ <Select defaultValue="dog" variant="plain" options={options} />
69
+ <Select defaultValue="dog" variant="soft" options={options} />
70
+ <Select defaultValue="dog" variant="solid" options={options} />
71
+ </Stack>
78
72
  ```
79
73
 
80
74
  ```tsx
@@ -90,10 +84,10 @@ Three sizes are available: `sm`, `md` (default), and `lg`.
90
84
 
91
85
  ```tsx
92
86
  <Stack spacing={4}>
93
- <Select defaultValue="dog" size="sm" options={options} />
94
- <Select defaultValue="dog" size="md" options={options} />
95
- <Select defaultValue="dog" size="lg" options={options} />
96
- </Stack>
87
+ <Select defaultValue="dog" size="sm" options={options} />
88
+ <Select defaultValue="dog" size="md" options={options} />
89
+ <Select defaultValue="dog" size="lg" options={options} />
90
+ </Stack>
97
91
  ```
98
92
 
99
93
  ```tsx
@@ -108,12 +102,12 @@ Apply semantic colors to communicate intent or state.
108
102
 
109
103
  ```tsx
110
104
  <Stack spacing={4}>
111
- <Select defaultValue="dog" color="primary" options={options} />
112
- <Select defaultValue="dog" color="neutral" options={options} />
113
- <Select defaultValue="dog" color="success" options={options} />
114
- <Select defaultValue="dog" color="danger" options={options} />
115
- <Select defaultValue="dog" color="warning" options={options} />
116
- </Stack>
105
+ <Select defaultValue="dog" color="primary" options={options} />
106
+ <Select defaultValue="dog" color="neutral" options={options} />
107
+ <Select defaultValue="dog" color="success" options={options} />
108
+ <Select defaultValue="dog" color="danger" options={options} />
109
+ <Select defaultValue="dog" color="warning" options={options} />
110
+ </Stack>
117
111
  ```
118
112
 
119
113
  ```tsx
@@ -129,11 +123,19 @@ Apply semantic colors to communicate intent or state.
129
123
  Use `startDecorator` and `endDecorator` to add icons, badges, or other elements beside the select trigger.
130
124
 
131
125
  ```tsx
132
- <Select placeholder="Select a pet…" startDecorator={<FavoriteBorder />} endDecorator={<Chip size="sm" color="danger" variant="soft">
133
- +5
134
- </Chip>} sx={{
135
- width: 240
136
- }} options={options} />
126
+ <Select
127
+ placeholder="Select a pet…"
128
+ startDecorator={<FavoriteBorder />}
129
+ endDecorator={
130
+ <Chip size="sm" color="danger" variant="soft">
131
+ +5
132
+ </Chip>
133
+ }
134
+ sx={{
135
+ width: 240
136
+ }}
137
+ options={options}
138
+ />
137
139
  ```
138
140
 
139
141
  ```tsx
@@ -144,11 +146,13 @@ import { Chip } from '@ceed/ads';
144
146
  placeholder="Select a pet..."
145
147
  startDecorator={<FavoriteBorder />}
146
148
  endDecorator={
147
- <Chip size="sm" color="danger" variant="soft">+5</Chip>
149
+ <Chip size="sm" color="danger" variant="soft">
150
+ +5
151
+ </Chip>
148
152
  }
149
153
  options={options}
150
154
  sx={{ width: 240 }}
151
- />
155
+ />;
152
156
  ```
153
157
 
154
158
  ## Label and Helper Text
@@ -157,14 +161,14 @@ The `label` prop renders a form label above the select. The `helperText` prop re
157
161
 
158
162
  ```tsx
159
163
  <>
160
- <Select label="Label" defaultValue="dog" options={options} />
161
- </>
164
+ <Select label="Label" defaultValue="dog" options={options} />
165
+ </>
162
166
  ```
163
167
 
164
168
  ```tsx
165
169
  <>
166
- <Select label="Select" helperText="I'm helper text" defaultValue="dog" options={options} />
167
- </>
170
+ <Select label="Select" helperText="I'm helper text" defaultValue="dog" options={options} />
171
+ </>
168
172
  ```
169
173
 
170
174
  ```tsx
@@ -184,17 +188,12 @@ Set `error` to `true` to visually indicate a validation error. Combine with `hel
184
188
 
185
189
  ```tsx
186
190
  <>
187
- <Select label="label" error defaultValue="dog" options={options} />
188
- </>
191
+ <Select label="label" error defaultValue="dog" options={options} />
192
+ </>
189
193
  ```
190
194
 
191
195
  ```tsx
192
- <Select
193
- label="Pet"
194
- error
195
- helperText="This field is required"
196
- options={options}
197
- />
196
+ <Select label="Pet" error helperText="This field is required" options={options} />
198
197
  ```
199
198
 
200
199
  ## Form Control
@@ -203,9 +202,61 @@ Combining `label`, `helperText`, and `error` produces a complete form field. Her
203
202
 
204
203
  ```tsx
205
204
  <>
206
- <Select label="Label" helperText="I'm helper text" defaultValue="dog" options={options} />
207
- <Select label="Label" helperText="I'm helper text" defaultValue="dog" error options={options} />
208
- </>
205
+ <Select label="Label" helperText="I'm helper text" defaultValue="dog" options={options} />
206
+ <Select label="Label" helperText="I'm helper text" defaultValue="dog" error options={options} />
207
+ </>
208
+ ```
209
+
210
+ ## Slot Props and Deterministic Ids
211
+
212
+ The `slotProps` prop targets both Joy Select's own slots (`button`, `listbox`, `root`, …) and the wrapper slots that `Select` composes around it (`formControl`, `formLabel`, `formHelperText`). Pass `id` (or `slotProps.formControl.id`) to pin the field's ids deterministically — `FormControl` derives the button `id` and the label `id` (`${id}-label`) from it, which keeps `byLabelText` / `byRole('combobox', { name })` queries stable in tests.
213
+
214
+ ```tsx
215
+ <Stack
216
+ spacing={3}
217
+ sx={{
218
+ minWidth: 240
219
+ }}
220
+ >
221
+ {/* id seeds deterministic FormControl ids: button#pet, label#pet-label (testable via byLabelText/byRole) */}
222
+ <Select
223
+ id="pet"
224
+ label="Pet"
225
+ defaultValue="cat"
226
+ options={options}
227
+ slotProps={{
228
+ formLabel: {
229
+ sx: {
230
+ color: "primary.500"
231
+ }
232
+ },
233
+ formHelperText: {
234
+ sx: {
235
+ fontStyle: "italic"
236
+ }
237
+ },
238
+ button: {
239
+ "aria-describedby": "pet-extra"
240
+ }
241
+ }}
242
+ helperText="Wired via slotProps"
243
+ />
244
+ </Stack>
245
+ ```
246
+
247
+ ```tsx
248
+ <Select
249
+ id="pet"
250
+ label="Pet"
251
+ helperText="Wired via slotProps"
252
+ options={options}
253
+ slotProps={{
254
+ formLabel: { sx: { color: 'primary.500' } },
255
+ formHelperText: { sx: { fontStyle: 'italic' } },
256
+ button: { 'aria-describedby': 'pet-extra' },
257
+ }}
258
+ />
259
+ // → renders button#pet and label#pet-label, connected via aria-labelledby
209
260
  ```
210
261
 
211
262
  ## Required Field
@@ -222,12 +273,7 @@ Set `required` to mark the field as required. An asterisk is added to the label
222
273
  ```
223
274
 
224
275
  ```tsx
225
- <Select
226
- label="Pet"
227
- helperText="This field is required"
228
- required
229
- options={options}
230
- />
276
+ <Select label="Pet" helperText="This field is required" required options={options} />
231
277
  ```
232
278
 
233
279
  ## Multiple Selection
@@ -245,12 +291,7 @@ Set `multiple` to allow selecting more than one option. The value becomes an arr
245
291
  ```
246
292
 
247
293
  ```tsx
248
- <Select
249
- label="Pets"
250
- multiple
251
- defaultValue={['dog']}
252
- options={options}
253
- />
294
+ <Select label="Pets" multiple defaultValue={['dog']} options={options} />
254
295
  ```
255
296
 
256
297
  ## Controlled Select
@@ -259,30 +300,41 @@ Use the `value` and `onChange` props for controlled behavior when you need to sy
259
300
 
260
301
  ```tsx
261
302
  <div>
262
- <Select {...args} options={options} value={value} onChange={(event, newValue) => {
263
- setValue(newValue);
264
- args.onChange?.(event, newValue);
265
- }} />
266
- <Select {...args} options={['dog', 'cat', 'fish', 'bird'] as string[]} value={value} onChange={(event, newValue) => {
267
- setValue(newValue);
268
- args.onChange?.(event, newValue);
269
- }} />
270
- <Select {...args} options={['dog', 'cat', 'fish', 'bird'] as string[]} value={[value]} onChange={(event, newValue) => {
271
- setValue(newValue);
272
- args.onChange?.(event, newValue);
273
- }} multiple />
274
- </div>
303
+ <Select
304
+ {...args}
305
+ options={options}
306
+ value={value}
307
+ onChange={(event, newValue) => {
308
+ setValue(newValue);
309
+ args.onChange?.(event, newValue);
310
+ }}
311
+ />
312
+ <Select
313
+ {...args}
314
+ options={["dog", "cat", "fish", "bird"] as string[]}
315
+ value={value}
316
+ onChange={(event, newValue) => {
317
+ setValue(newValue);
318
+ args.onChange?.(event, newValue);
319
+ }}
320
+ />
321
+ <Select
322
+ {...args}
323
+ options={["dog", "cat", "fish", "bird"] as string[]}
324
+ value={[value]}
325
+ onChange={(event, newValue) => {
326
+ setValue(newValue);
327
+ args.onChange?.(event, newValue);
328
+ }}
329
+ multiple
330
+ />
331
+ </div>
275
332
  ```
276
333
 
277
334
  ```tsx
278
335
  const [value, setValue] = useState('dog');
279
336
 
280
- <Select
281
- label="Pet"
282
- value={value}
283
- onChange={(event, newValue) => setValue(newValue)}
284
- options={options}
285
- />
337
+ <Select label="Pet" value={value} onChange={(event, newValue) => setValue(newValue)} options={options} />;
286
338
  ```
287
339
 
288
340
  ## Disabled Options
@@ -308,7 +360,7 @@ const options = [
308
360
  { value: 'bird', label: 'Bird' },
309
361
  ];
310
362
 
311
- <Select label="Pet" options={options} defaultValue="dog" />
363
+ <Select label="Pet" options={options} defaultValue="dog" />;
312
364
  ```
313
365
 
314
366
  ## Options with Secondary Text
@@ -317,16 +369,27 @@ Add a `secondaryText` field to display supplementary information below each opti
317
369
 
318
370
  ```tsx
319
371
  <Stack spacing={4} direction="row" alignItems="flex-start">
320
- {sizes.map(size => <Stack key={size} spacing={1}>
321
- <span style={{
322
- color: '#6366f1',
323
- fontSize: 12
324
- }}>{size}</span>
325
- <Select placeholder="Placeholder" options={optionsWithSecondaryText} sx={{
326
- minWidth: 200
327
- }} size={size} />
328
- </Stack>)}
329
- </Stack>
372
+ {sizes.map((size) => (
373
+ <Stack key={size} spacing={1}>
374
+ <span
375
+ style={{
376
+ color: "#6366f1",
377
+ fontSize: 12
378
+ }}
379
+ >
380
+ {size}
381
+ </span>
382
+ <Select
383
+ placeholder="Placeholder"
384
+ options={optionsWithSecondaryText}
385
+ sx={{
386
+ minWidth: 200
387
+ }}
388
+ size={size}
389
+ />
390
+ </Stack>
391
+ ))}
392
+ </Stack>
330
393
  ```
331
394
 
332
395
  ```tsx
@@ -336,7 +399,7 @@ const userOptions = [
336
399
  { value: 'sophia', label: 'Sophia Martinez', secondaryText: '(646) 555-0734' },
337
400
  ];
338
401
 
339
- <Select placeholder="Select a contact" options={userOptions} />
402
+ <Select placeholder="Select a contact" options={userOptions} />;
340
403
  ```
341
404
 
342
405
  ## Numeric Values
@@ -345,11 +408,11 @@ Select supports both string and numeric option values. When using numeric values
345
408
 
346
409
  ```tsx
347
410
  <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>
411
+ <Select defaultValue={1} options={numericOptions} />
412
+ <Select defaultValue={1} variant="plain" options={numericOptions} />
413
+ <Select defaultValue={1} variant="soft" options={numericOptions} />
414
+ <Select defaultValue={1} variant="solid" options={numericOptions} />
415
+ </Stack>
353
416
  ```
354
417
 
355
418
  ```tsx
@@ -359,7 +422,7 @@ const numericOptions = [
359
422
  { value: 3, label: 'Option 3' },
360
423
  ];
361
424
 
362
- <Select options={numericOptions} defaultValue={1} />
425
+ <Select options={numericOptions} defaultValue={1} />;
363
426
  ```
364
427
 
365
428
  ## Form with Validation
@@ -546,25 +609,27 @@ function LocationSelect() {
546
609
 
547
610
  ### Key Props
548
611
 
549
- | Prop | Type | Default | Description |
550
- | ---------------- | ---------------------------------------------------------------- | ----------------- | ---------------------------------------------------- |
551
- | `options` | `OptionType[]` | `[]` | Array of options (objects or primitives) |
552
- | `value` | `InferOptionValue<OptionType> \| InferOptionValue<OptionType>[]` | - | Selected value(s) for controlled mode |
553
- | `defaultValue` | `InferOptionValue<OptionType> \| InferOptionValue<OptionType>[]` | - | Initial value for uncontrolled mode |
554
- | `onChange` | `(event: { target: { name?, value } }, newValue) => void` | - | Callback when selection changes |
555
- | `multiple` | `boolean` | `false` | Allow multiple selections (value becomes an array) |
556
- | `label` | `string` | - | Form label displayed above the select |
557
- | `helperText` | `string` | - | Helper text displayed below the select |
558
- | `error` | `boolean` | `false` | Applies danger color to indicate validation error |
559
- | `required` | `boolean` | `false` | Marks the field as required (adds asterisk to label) |
560
- | `disabled` | `boolean` | `false` | Disables the select |
561
- | `placeholder` | `string` | `'Choose one...'` | Placeholder text when no value is selected |
562
- | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Select size |
563
- | `variant` | `'outlined' \| 'plain' \| 'solid' \| 'soft'` | `'outlined'` | Visual style variant |
564
- | `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | `'neutral'` | Color scheme |
565
- | `startDecorator` | `ReactNode` | - | Content rendered before the select trigger text |
566
- | `endDecorator` | `ReactNode` | - | Content rendered after the select trigger text |
567
- | `sx` | `SxProps` | - | Custom styles using the MUI system |
612
+ | Prop | Type | Default | Description |
613
+ | ---------------- | ----------------------------------------------------------------------- | ----------------- | -------------------------------------------------------------- |
614
+ | `options` | `OptionType[]` | `[]` | Array of options (objects or primitives) |
615
+ | `value` | `InferOptionValue<OptionType> \| InferOptionValue<OptionType>[]` | - | Selected value(s) for controlled mode |
616
+ | `defaultValue` | `InferOptionValue<OptionType> \| InferOptionValue<OptionType>[]` | - | Initial value for uncontrolled mode |
617
+ | `onChange` | `(event: { target: { name?, value } }, newValue) => void` | - | Callback when selection changes |
618
+ | `multiple` | `boolean` | `false` | Allow multiple selections (value becomes an array) |
619
+ | `label` | `string` | - | Form label displayed above the select |
620
+ | `helperText` | `string` | - | Helper text displayed below the select |
621
+ | `error` | `boolean` | `false` | Applies danger color to indicate validation error |
622
+ | `required` | `boolean` | `false` | Marks the field as required (adds asterisk to label) |
623
+ | `disabled` | `boolean` | `false` | Disables the select |
624
+ | `placeholder` | `string` | `'Choose one...'` | Placeholder text when no value is selected |
625
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Select size |
626
+ | `variant` | `'outlined' \| 'plain' \| 'solid' \| 'soft'` | `'outlined'` | Visual style variant |
627
+ | `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | `'neutral'` | Color scheme |
628
+ | `startDecorator` | `ReactNode` | - | Content rendered before the select trigger text |
629
+ | `endDecorator` | `ReactNode` | - | Content rendered after the select trigger text |
630
+ | `id` | `string` | - | Seeds deterministic ids: button `id`, label `${id}-label` |
631
+ | `slotProps` | `{ formControl?, formLabel?, formHelperText?, button?, listbox?, ... }` | - | Props forwarded to composed wrapper slots and Joy Select slots |
632
+ | `sx` | `SxProps` | - | Custom styles using the MUI system |
568
633
 
569
634
  ### Option Type
570
635
 
@@ -588,19 +653,20 @@ const options = [
588
653
  { value: 2, label: 'Option 2' },
589
654
  ] as const;
590
655
 
591
- <Select<typeof options[number], false>
656
+ <Select<(typeof options)[number], false>
592
657
  options={options}
593
658
  onChange={(e, val) => {
594
659
  // val is typed as number
595
660
  }}
596
- />
661
+ />;
597
662
  ```
598
663
 
599
664
  > **Note**: Select also accepts all Joy UI Select props.
600
665
 
601
666
  ## Accessibility
602
667
 
603
- - **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.
668
+ - **Label association**: When you use the `label` prop, the component automatically connects the label to the select element via `aria-labelledby` (the internal `FormControl` propagates a `labelId` to both the label and the button slot). You do not need to wire `useId` or a custom `FormLabel` externally. Always provide a label for screen reader users.
669
+ - **Deterministic ids for tests**: Pass `id` (or `slotProps.formControl.id`) to fix the generated ids, so `byLabelText` / `byRole('combobox', { name })` resolve to a stable accessible name.
604
670
  - **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.
605
671
  - **Error announcement**: When `error` is set, `aria-invalid` is applied. Pair it with a descriptive `helperText` so assistive technology can announce the error reason.
606
672
  - **Required state**: The `required` prop adds `aria-required` and a visible asterisk to the label, communicating the requirement both visually and programmatically.