@ceed/cds 1.22.3 → 1.22.5
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/InfoSign.md +74 -91
- package/dist/components/data-display/Typography.md +363 -63
- package/dist/components/feedback/CircularProgress.md +257 -0
- package/dist/components/feedback/Skeleton.md +280 -0
- package/dist/components/feedback/llms.txt +2 -0
- package/dist/components/inputs/ButtonGroup.md +115 -104
- package/dist/components/inputs/CurrencyInput.md +181 -8
- package/dist/components/inputs/DatePicker.md +108 -436
- package/dist/components/inputs/DateRangePicker.md +130 -496
- package/dist/components/inputs/FilterableCheckboxGroup.md +141 -20
- package/dist/components/inputs/FormControl.md +361 -0
- package/dist/components/inputs/IconButton.md +137 -88
- package/dist/components/inputs/Input.md +203 -77
- package/dist/components/inputs/MonthPicker.md +95 -427
- package/dist/components/inputs/MonthRangePicker.md +89 -471
- package/dist/components/inputs/PercentageInput.md +183 -19
- package/dist/components/inputs/RadioButton.md +163 -35
- package/dist/components/inputs/RadioList.md +241 -0
- package/dist/components/inputs/RadioTileGroup.md +146 -62
- package/dist/components/inputs/Select.md +219 -328
- package/dist/components/inputs/Slider.md +334 -0
- package/dist/components/inputs/Switch.md +143 -376
- package/dist/components/inputs/Textarea.md +209 -11
- package/dist/components/inputs/Uploader/Uploader.md +145 -66
- package/dist/components/inputs/llms.txt +3 -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 +152 -327
- package/dist/guides/ThemeProvider.md +89 -0
- package/dist/guides/llms.txt +9 -0
- package/dist/llms.txt +8 -0
- package/package.json +1 -1
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
FilterableCheckboxGroup
|
|
5
|
+
FilterableCheckboxGroup is a multi-select component that combines a search input with a scrollable list of checkboxes. It is designed for scenarios where users need to select multiple items from a potentially large dataset. The built-in search filter narrows down visible options in real time, and virtual scrolling ensures smooth performance even with hundreds of items.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Key capabilities include a "Select all" toggle for bulk selection, automatic sorting (selected items first, then alphabetical), customizable list height, and built-in form integration via `label`, `helperText`, and `required` props. The component accepts options as either simple strings or `{ value, label }` objects.
|
|
8
|
+
|
|
9
|
+
> **Form 구성 시 내장 prop 사용을 권장합니다**
|
|
8
10
|
>
|
|
9
11
|
> 이 컴포넌트는 `label`, `helperText` 등의 Form 요소를 자체적으로 지원합니다.
|
|
10
12
|
> Form을 구성할 때 Typography로 별도의 label이나 helperText를 만드는 대신, 컴포넌트의 내장 prop을 사용하세요.
|
|
@@ -32,7 +34,7 @@ FilterableCheckboxGroup 컴포넌트는 대량의 옵션 중에서 여러 항목
|
|
|
32
34
|
## Usage
|
|
33
35
|
|
|
34
36
|
```tsx
|
|
35
|
-
import { FilterableCheckboxGroup } from '@ceed/
|
|
37
|
+
import { FilterableCheckboxGroup } from '@ceed/cds';
|
|
36
38
|
|
|
37
39
|
function MyComponent() {
|
|
38
40
|
const [selectedValues, setSelectedValues] = useState<string[]>([]);
|
|
@@ -55,11 +57,9 @@ function MyComponent() {
|
|
|
55
57
|
}
|
|
56
58
|
```
|
|
57
59
|
|
|
58
|
-
##
|
|
59
|
-
|
|
60
|
-
### Sizes
|
|
60
|
+
## Sizes
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Three sizes are available: `sm`, `md`, and `lg`. The size affects the search input, checkboxes, and overall spacing.
|
|
63
63
|
|
|
64
64
|
```tsx
|
|
65
65
|
<Stack spacing={3}>
|
|
@@ -69,9 +69,11 @@ function MyComponent() {
|
|
|
69
69
|
</Stack>
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
## Label and Helper Text
|
|
73
|
+
|
|
72
74
|
### With Label
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
Use the `label` prop to describe the purpose of the checkbox group.
|
|
75
77
|
|
|
76
78
|
```tsx
|
|
77
79
|
<FilterableCheckboxGroup
|
|
@@ -83,7 +85,7 @@ function MyComponent() {
|
|
|
83
85
|
|
|
84
86
|
### With Helper Text
|
|
85
87
|
|
|
86
|
-
|
|
88
|
+
Add a `helperText` prop to provide additional guidance below the component.
|
|
87
89
|
|
|
88
90
|
```tsx
|
|
89
91
|
<FilterableCheckboxGroup
|
|
@@ -96,7 +98,7 @@ function MyComponent() {
|
|
|
96
98
|
|
|
97
99
|
### Required Field
|
|
98
100
|
|
|
99
|
-
|
|
101
|
+
Set `required` to append a required indicator to the label.
|
|
100
102
|
|
|
101
103
|
```tsx
|
|
102
104
|
<FilterableCheckboxGroup
|
|
@@ -108,9 +110,9 @@ function MyComponent() {
|
|
|
108
110
|
/>
|
|
109
111
|
```
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
## Custom Max Height
|
|
112
114
|
|
|
113
|
-
|
|
115
|
+
Control the visible height of the options list with the `maxHeight` prop (default: 300px). A smaller height is useful when screen space is limited.
|
|
114
116
|
|
|
115
117
|
```tsx
|
|
116
118
|
<Stack spacing={3}>
|
|
@@ -119,9 +121,9 @@ function MyComponent() {
|
|
|
119
121
|
</Stack>
|
|
120
122
|
```
|
|
121
123
|
|
|
122
|
-
|
|
124
|
+
## Long List with Virtual Scrolling
|
|
123
125
|
|
|
124
|
-
|
|
126
|
+
When the options list is large, the component uses virtual scrolling to render only the visible items. This keeps the UI performant regardless of the total number of options.
|
|
125
127
|
|
|
126
128
|
```tsx
|
|
127
129
|
<FilterableCheckboxGroup
|
|
@@ -134,9 +136,9 @@ function MyComponent() {
|
|
|
134
136
|
/>
|
|
135
137
|
```
|
|
136
138
|
|
|
137
|
-
|
|
139
|
+
## Empty State
|
|
138
140
|
|
|
139
|
-
|
|
141
|
+
When no options are provided, the component displays an empty state. This is useful for dynamically loaded option lists that may start empty.
|
|
140
142
|
|
|
141
143
|
```tsx
|
|
142
144
|
<FilterableCheckboxGroup
|
|
@@ -146,9 +148,9 @@ function MyComponent() {
|
|
|
146
148
|
/>
|
|
147
149
|
```
|
|
148
150
|
|
|
149
|
-
|
|
151
|
+
## Controlled Mode
|
|
150
152
|
|
|
151
|
-
|
|
153
|
+
Pass `value` and `onChange` to manage the selection state externally. This is the recommended approach when the component is part of a form or when the selected values need to be used by other parts of the UI.
|
|
152
154
|
|
|
153
155
|
```tsx
|
|
154
156
|
<Stack spacing={2}>
|
|
@@ -160,9 +162,9 @@ function MyComponent() {
|
|
|
160
162
|
</Stack>
|
|
161
163
|
```
|
|
162
164
|
|
|
163
|
-
|
|
165
|
+
## Sorting Behavior
|
|
164
166
|
|
|
165
|
-
|
|
167
|
+
The component automatically sorts options on initial render: selected items appear first, followed by unselected items in alphabetical order. When new options are added dynamically, they are inserted in the correct sorted position.
|
|
166
168
|
|
|
167
169
|
```tsx
|
|
168
170
|
<Stack spacing={2}>
|
|
@@ -195,3 +197,122 @@ function MyComponent() {
|
|
|
195
197
|
</Typography>
|
|
196
198
|
</Stack>
|
|
197
199
|
```
|
|
200
|
+
|
|
201
|
+
## Disabled State
|
|
202
|
+
|
|
203
|
+
The component supports both full and partial disabling.
|
|
204
|
+
|
|
205
|
+
- **Full disable**: Set `disabled` on the component to disable the search input, "Select all" toggle, and all checkboxes.
|
|
206
|
+
- **Partial disable**: Set `disabled: true` on individual option objects to disable specific items while keeping the rest interactive.
|
|
207
|
+
- **"Select all" behavior**: Disabled options are excluded from the "Select all" toggle. Their selected state is preserved regardless of bulk actions.
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
<Stack spacing={3}>
|
|
211
|
+
<FilterableCheckboxGroup label="Entirely Disabled" placeholder="Search..." helperText="All inputs are disabled" options={defaultOptions.slice(0, 5)} disabled />
|
|
212
|
+
<FilterableCheckboxGroup label="Partially Disabled Options" placeholder="Search..." helperText="Some options are disabled" options={disabledOptions} />
|
|
213
|
+
<Stack spacing={2}>
|
|
214
|
+
<FilterableCheckboxGroup label="Controlled + Partially Disabled" placeholder="Search..." helperText="Disabled options (Banana, Date) maintain their selected state" options={disabledOptions} value={controlledValue} onChange={setControlledValue} />
|
|
215
|
+
<Typography level="body-sm">
|
|
216
|
+
Selected: {controlledValue.length > 0 ? controlledValue.join(', ') : 'None'}
|
|
217
|
+
</Typography>
|
|
218
|
+
<Typography level="body-sm" sx={{
|
|
219
|
+
color: 'text.secondary'
|
|
220
|
+
}}>
|
|
221
|
+
Try "Select all" - it will only affect enabled options (Apple, Cherry, Elderberry)
|
|
222
|
+
</Typography>
|
|
223
|
+
</Stack>
|
|
224
|
+
</Stack>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Common Use Cases
|
|
228
|
+
|
|
229
|
+
### Filter Panel
|
|
230
|
+
|
|
231
|
+
Use FilterableCheckboxGroup in a sidebar or panel to let users filter a data table or list by multiple criteria.
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
import { FilterableCheckboxGroup } from '@ceed/cds';
|
|
235
|
+
|
|
236
|
+
function FilterPanel({ categories, onFilterChange }) {
|
|
237
|
+
const [selected, setSelected] = useState<string[]>([]);
|
|
238
|
+
|
|
239
|
+
const handleChange = (values: string[]) => {
|
|
240
|
+
setSelected(values);
|
|
241
|
+
onFilterChange(values);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<FilterableCheckboxGroup
|
|
246
|
+
label="Categories"
|
|
247
|
+
placeholder="Search categories..."
|
|
248
|
+
options={categories}
|
|
249
|
+
value={selected}
|
|
250
|
+
onChange={handleChange}
|
|
251
|
+
maxHeight={200}
|
|
252
|
+
/>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### User Role Assignment
|
|
258
|
+
|
|
259
|
+
Assign multiple roles to a user from a searchable list.
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
<FilterableCheckboxGroup
|
|
263
|
+
label="Assign Roles"
|
|
264
|
+
placeholder="Search roles..."
|
|
265
|
+
helperText="Select all roles that apply to this user"
|
|
266
|
+
options={[
|
|
267
|
+
{ value: 'admin', label: 'Administrator' },
|
|
268
|
+
{ value: 'editor', label: 'Editor' },
|
|
269
|
+
{ value: 'viewer', label: 'Viewer' },
|
|
270
|
+
{ value: 'moderator', label: 'Moderator' },
|
|
271
|
+
{ value: 'billing', label: 'Billing Manager' },
|
|
272
|
+
]}
|
|
273
|
+
required
|
|
274
|
+
value={selectedRoles}
|
|
275
|
+
onChange={setSelectedRoles}
|
|
276
|
+
/>
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Region / Country Selection
|
|
280
|
+
|
|
281
|
+
When dealing with a large set of options like countries, the search filter and virtual scrolling work together for fast selection.
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
<FilterableCheckboxGroup
|
|
285
|
+
label="Service Regions"
|
|
286
|
+
placeholder="Search countries..."
|
|
287
|
+
options={allCountries.map((c) => ({ value: c.code, label: c.name }))}
|
|
288
|
+
value={selectedCountries}
|
|
289
|
+
onChange={setSelectedCountries}
|
|
290
|
+
/>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Best Practices
|
|
294
|
+
|
|
295
|
+
1. **Provide a meaningful placeholder.** The search input placeholder should hint at the type of content being filtered (e.g., "Search countries..." instead of a generic "Search...").
|
|
296
|
+
|
|
297
|
+
```tsx
|
|
298
|
+
// ✅ Specific and helpful
|
|
299
|
+
<FilterableCheckboxGroup placeholder="Search countries..." />
|
|
300
|
+
|
|
301
|
+
// ❌ Too generic
|
|
302
|
+
<FilterableCheckboxGroup placeholder="Type here..." />
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
2. **Use `maxHeight` to fit the layout.** In tight spaces like sidebars or modals, set a smaller `maxHeight` to prevent the component from dominating the layout.
|
|
306
|
+
|
|
307
|
+
3. **Prefer controlled mode for form integration.** Always pass `value` and `onChange` when the selected values participate in form submission or affect other UI state.
|
|
308
|
+
|
|
309
|
+
4. **Leverage individual `disabled` for permissions.** When some options should be visible but not selectable (e.g., due to user permissions), disable them individually rather than hiding them. This communicates availability without surprising users.
|
|
310
|
+
|
|
311
|
+
5. **Keep option labels concise.** Long labels will be truncated with an ellipsis. If detailed descriptions are needed, consider using a tooltip or a separate detail view.
|
|
312
|
+
|
|
313
|
+
## Accessibility
|
|
314
|
+
|
|
315
|
+
- The search input is labeled by the component's `label` prop, ensuring screen readers announce its purpose.
|
|
316
|
+
- Each checkbox option uses a native `<input type="checkbox">` with an associated `<label>`, enabling click-on-label and proper screen reader announcements.
|
|
317
|
+
- The "Select all" toggle clearly communicates its tri-state behavior (all, none, indeterminate) to assistive technologies.
|
|
318
|
+
- Keyboard navigation is fully supported: Tab to move between the search input and the checkbox list, Space to toggle individual checkboxes, and standard arrow keys to navigate within the list.
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# FormControl
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
FormControl is a wrapper component that provides context to form elements such as Input, Textarea, Select, and more. It manages shared states like `error`, `disabled`, `required`, and `size`, passing them down to its children automatically. Use it with FormLabel and FormHelperText to build accessible, consistent form fields.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<FormControl {...args}>
|
|
9
|
+
<FormLabel>Label</FormLabel>
|
|
10
|
+
<Input placeholder="Enter text…" />
|
|
11
|
+
<FormHelperText>This is helper text.</FormHelperText>
|
|
12
|
+
</FormControl>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Field | Description | Default |
|
|
16
|
+
| ----------- | ----------- | ------- |
|
|
17
|
+
| size | — | — |
|
|
18
|
+
| error | — | — |
|
|
19
|
+
| disabled | — | — |
|
|
20
|
+
| required | — | — |
|
|
21
|
+
| orientation | — | — |
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { FormControl, FormLabel, FormHelperText, Input } from '@ceed/cds';
|
|
27
|
+
|
|
28
|
+
function MyForm() {
|
|
29
|
+
return (
|
|
30
|
+
<FormControl>
|
|
31
|
+
<FormLabel>Username</FormLabel>
|
|
32
|
+
<Input placeholder="Enter username" />
|
|
33
|
+
<FormHelperText>Choose a unique username.</FormHelperText>
|
|
34
|
+
</FormControl>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## With Input
|
|
40
|
+
|
|
41
|
+
The most common pattern — wrapping an Input with a label and helper text.
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
<FormControl>
|
|
45
|
+
<FormLabel>Username</FormLabel>
|
|
46
|
+
<Input placeholder="Enter username" />
|
|
47
|
+
<FormHelperText>Choose a unique username.</FormHelperText>
|
|
48
|
+
</FormControl>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## With Textarea
|
|
52
|
+
|
|
53
|
+
FormControl works equally well with Textarea components.
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
<FormControl>
|
|
57
|
+
<FormLabel>Description</FormLabel>
|
|
58
|
+
<Textarea placeholder="Enter description…" minRows={3} />
|
|
59
|
+
<FormHelperText>Provide a detailed description.</FormHelperText>
|
|
60
|
+
</FormControl>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## With Select
|
|
64
|
+
|
|
65
|
+
Use FormControl with Select for dropdown fields.
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<FormControl>
|
|
69
|
+
<FormLabel>Role</FormLabel>
|
|
70
|
+
<Select placeholder="Select a role" options={roleOptions} />
|
|
71
|
+
<FormHelperText>Select the user role.</FormHelperText>
|
|
72
|
+
</FormControl>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Error State
|
|
76
|
+
|
|
77
|
+
Set `error` on FormControl to propagate the error state to all child components. FormHelperText automatically changes color to indicate the error.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
<FormControl error>
|
|
81
|
+
<FormLabel>Email</FormLabel>
|
|
82
|
+
<Input placeholder="email@example.com" defaultValue="invalid-email" />
|
|
83
|
+
<FormHelperText>Please enter a valid email address.</FormHelperText>
|
|
84
|
+
</FormControl>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<FormControl error>
|
|
89
|
+
<FormLabel>Email</FormLabel>
|
|
90
|
+
<Input placeholder="email@example.com" value="invalid-email" />
|
|
91
|
+
<FormHelperText>Please enter a valid email address.</FormHelperText>
|
|
92
|
+
</FormControl>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Disabled State
|
|
96
|
+
|
|
97
|
+
Set `disabled` to disable all child form elements at once.
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
<FormControl disabled>
|
|
101
|
+
<FormLabel>Name</FormLabel>
|
|
102
|
+
<Input placeholder="Enter name" defaultValue="John Doe" />
|
|
103
|
+
<FormHelperText>This field is disabled.</FormHelperText>
|
|
104
|
+
</FormControl>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
<FormControl disabled>
|
|
109
|
+
<FormLabel>Name</FormLabel>
|
|
110
|
+
<Input placeholder="Enter name" value="John Doe" />
|
|
111
|
+
<FormHelperText>This field is disabled.</FormHelperText>
|
|
112
|
+
</FormControl>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Required Field
|
|
116
|
+
|
|
117
|
+
Set `required` to add an asterisk (\*) to the label and mark the field as required.
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
<FormControl required>
|
|
121
|
+
<FormLabel>Full Name</FormLabel>
|
|
122
|
+
<Input placeholder="Enter your full name" />
|
|
123
|
+
<FormHelperText>This field is required.</FormHelperText>
|
|
124
|
+
</FormControl>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
<FormControl required>
|
|
129
|
+
<FormLabel>Full Name</FormLabel>
|
|
130
|
+
<Input placeholder="Enter your full name" />
|
|
131
|
+
</FormControl>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Sizes
|
|
135
|
+
|
|
136
|
+
FormControl supports `sm`, `md`, and `lg` sizes. The size is inherited by child components.
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
<Stack gap={3}>
|
|
140
|
+
<FormControl size="sm">
|
|
141
|
+
<FormLabel>Small</FormLabel>
|
|
142
|
+
<Input placeholder="Small input" />
|
|
143
|
+
</FormControl>
|
|
144
|
+
<FormControl size="md">
|
|
145
|
+
<FormLabel>Medium</FormLabel>
|
|
146
|
+
<Input placeholder="Medium input" />
|
|
147
|
+
</FormControl>
|
|
148
|
+
<FormControl size="lg">
|
|
149
|
+
<FormLabel>Large</FormLabel>
|
|
150
|
+
<Input placeholder="Large input" />
|
|
151
|
+
</FormControl>
|
|
152
|
+
</Stack>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Horizontal Layout
|
|
156
|
+
|
|
157
|
+
Use `orientation="horizontal"` for inline form controls, such as Switch or Checkbox toggles.
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
<FormControl orientation="horizontal" sx={{
|
|
161
|
+
gap: 2
|
|
162
|
+
}}>
|
|
163
|
+
<FormLabel>Subscribe</FormLabel>
|
|
164
|
+
<Switch />
|
|
165
|
+
</FormControl>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
<FormControl orientation="horizontal" sx={{ gap: 2 }}>
|
|
170
|
+
<FormLabel>Subscribe</FormLabel>
|
|
171
|
+
<Switch />
|
|
172
|
+
</FormControl>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Form Example
|
|
176
|
+
|
|
177
|
+
A complete form demonstrating FormControl with multiple input types.
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
<Stack gap={2} sx={{
|
|
181
|
+
maxWidth: 400
|
|
182
|
+
}}>
|
|
183
|
+
<FormControl required>
|
|
184
|
+
<FormLabel>Name</FormLabel>
|
|
185
|
+
<Input placeholder="Enter your name" />
|
|
186
|
+
</FormControl>
|
|
187
|
+
<FormControl required>
|
|
188
|
+
<FormLabel>Email</FormLabel>
|
|
189
|
+
<Input type="email" placeholder="email@example.com" />
|
|
190
|
+
<FormHelperText>We will never share your email.</FormHelperText>
|
|
191
|
+
</FormControl>
|
|
192
|
+
<FormControl>
|
|
193
|
+
<FormLabel>Role</FormLabel>
|
|
194
|
+
<Select placeholder="Select a role" options={roleOptions} />
|
|
195
|
+
</FormControl>
|
|
196
|
+
<FormControl>
|
|
197
|
+
<FormLabel>Bio</FormLabel>
|
|
198
|
+
<Textarea placeholder="Tell us about yourself…" minRows={3} />
|
|
199
|
+
<FormHelperText>Maximum 500 characters.</FormHelperText>
|
|
200
|
+
</FormControl>
|
|
201
|
+
</Stack>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## FormLabel
|
|
205
|
+
|
|
206
|
+
FormLabel renders a `<label>` element associated with its sibling input. It automatically displays an asterisk when the parent FormControl has `required` set.
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
<FormControl required>
|
|
210
|
+
<FormLabel>Email</FormLabel>
|
|
211
|
+
<Input />
|
|
212
|
+
</FormControl>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## FormHelperText
|
|
216
|
+
|
|
217
|
+
FormHelperText provides supplementary guidance below the input. It automatically switches to the error color when the parent FormControl has `error` set.
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
<FormControl error>
|
|
221
|
+
<FormLabel>Password</FormLabel>
|
|
222
|
+
<Input type="password" />
|
|
223
|
+
<FormHelperText>Password must be at least 8 characters.</FormHelperText>
|
|
224
|
+
</FormControl>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Common Use Cases
|
|
228
|
+
|
|
229
|
+
### Registration Form
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
function RegistrationForm() {
|
|
233
|
+
const [errors, setErrors] = React.useState({});
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<Stack gap={2} component="form">
|
|
237
|
+
<FormControl required error={!!errors.name}>
|
|
238
|
+
<FormLabel>Name</FormLabel>
|
|
239
|
+
<Input placeholder="Enter your name" />
|
|
240
|
+
{errors.name && <FormHelperText>{errors.name}</FormHelperText>}
|
|
241
|
+
</FormControl>
|
|
242
|
+
|
|
243
|
+
<FormControl required error={!!errors.email}>
|
|
244
|
+
<FormLabel>Email</FormLabel>
|
|
245
|
+
<Input type="email" placeholder="email@example.com" />
|
|
246
|
+
{errors.email && <FormHelperText>{errors.email}</FormHelperText>}
|
|
247
|
+
</FormControl>
|
|
248
|
+
|
|
249
|
+
<FormControl required error={!!errors.password}>
|
|
250
|
+
<FormLabel>Password</FormLabel>
|
|
251
|
+
<Input type="password" placeholder="Enter password" />
|
|
252
|
+
<FormHelperText>{errors.password || 'Must be at least 8 characters.'}</FormHelperText>
|
|
253
|
+
</FormControl>
|
|
254
|
+
|
|
255
|
+
<FormControl orientation="horizontal">
|
|
256
|
+
<Checkbox label="I agree to the terms and conditions" />
|
|
257
|
+
</FormControl>
|
|
258
|
+
</Stack>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Settings Form with Mixed Controls
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
function SettingsForm() {
|
|
267
|
+
return (
|
|
268
|
+
<Stack gap={2}>
|
|
269
|
+
<FormControl orientation="horizontal" sx={{ justifyContent: 'space-between' }}>
|
|
270
|
+
<FormLabel>Email notifications</FormLabel>
|
|
271
|
+
<Switch />
|
|
272
|
+
</FormControl>
|
|
273
|
+
|
|
274
|
+
<FormControl>
|
|
275
|
+
<FormLabel>Language</FormLabel>
|
|
276
|
+
<Select
|
|
277
|
+
defaultValue="en"
|
|
278
|
+
options={[
|
|
279
|
+
{ value: 'en', label: 'English' },
|
|
280
|
+
{ value: 'ko', label: 'Korean' },
|
|
281
|
+
]}
|
|
282
|
+
/>
|
|
283
|
+
</FormControl>
|
|
284
|
+
|
|
285
|
+
<FormControl>
|
|
286
|
+
<FormLabel>Notes</FormLabel>
|
|
287
|
+
<Textarea minRows={3} placeholder="Additional notes…" />
|
|
288
|
+
<FormHelperText>Optional</FormHelperText>
|
|
289
|
+
</FormControl>
|
|
290
|
+
</Stack>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
> **Tip: Use built-in form props instead**
|
|
296
|
+
>
|
|
297
|
+
> Input, Select, Textarea, DatePicker 등 Input 계열 컴포넌트는 `label`, `helperText` prop을 자체적으로 지원합니다.
|
|
298
|
+
> 간단한 form 필드에는 각 컴포넌트의 내장 prop을 사용하는 것이 더 간결합니다.
|
|
299
|
+
>
|
|
300
|
+
> ```tsx
|
|
301
|
+
> // ✅ Simpler: built-in props
|
|
302
|
+
> <Input label="Username" helperText="Choose a unique username." />
|
|
303
|
+
>
|
|
304
|
+
> // ⬆️ Equivalent to:
|
|
305
|
+
> <FormControl>
|
|
306
|
+
> <FormLabel>Username</FormLabel>
|
|
307
|
+
> <Input placeholder="Enter username" />
|
|
308
|
+
> <FormHelperText>Choose a unique username.</FormHelperText>
|
|
309
|
+
> </FormControl>
|
|
310
|
+
> ```
|
|
311
|
+
>
|
|
312
|
+
> FormControl is most useful when you need to compose multiple elements, share state across siblings, or use horizontal orientation.
|
|
313
|
+
|
|
314
|
+
## Best Practices
|
|
315
|
+
|
|
316
|
+
1. **Always pair with FormLabel**: Every form field should have a label for accessibility. Use FormLabel inside FormControl.
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
// ✅ Accessible — label is associated with input
|
|
320
|
+
<FormControl>
|
|
321
|
+
<FormLabel>Email</FormLabel>
|
|
322
|
+
<Input />
|
|
323
|
+
</FormControl>
|
|
324
|
+
|
|
325
|
+
// ❌ No label — screen readers cannot identify the input
|
|
326
|
+
<FormControl>
|
|
327
|
+
<Input placeholder="Email" />
|
|
328
|
+
</FormControl>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
2. **Use `error` prop on FormControl, not children**: Set error state on FormControl so all children react consistently.
|
|
332
|
+
|
|
333
|
+
```tsx
|
|
334
|
+
// ✅ Error propagated from FormControl
|
|
335
|
+
<FormControl error>
|
|
336
|
+
<FormLabel>Email</FormLabel>
|
|
337
|
+
<Input />
|
|
338
|
+
<FormHelperText>Invalid email</FormHelperText>
|
|
339
|
+
</FormControl>
|
|
340
|
+
|
|
341
|
+
// ❌ Inconsistent — only Input shows error
|
|
342
|
+
<FormControl>
|
|
343
|
+
<FormLabel>Email</FormLabel>
|
|
344
|
+
<Input error />
|
|
345
|
+
<FormHelperText>Invalid email</FormHelperText>
|
|
346
|
+
</FormControl>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
3. **Use consistent sizing**: Set `size` on FormControl to ensure all children (label, input, helper text) are proportionally sized.
|
|
350
|
+
|
|
351
|
+
4. **Mark required fields**: Use the `required` prop to automatically add visual indicators and `aria-required` attributes.
|
|
352
|
+
|
|
353
|
+
5. **Provide helpful error messages**: When using the error state, always include a FormHelperText explaining what went wrong and how to fix it.
|
|
354
|
+
|
|
355
|
+
## Accessibility
|
|
356
|
+
|
|
357
|
+
- FormControl automatically associates FormLabel with the input using `htmlFor` and `id`.
|
|
358
|
+
- The `required` prop adds `aria-required="true"` to the input and an asterisk to the label.
|
|
359
|
+
- The `error` prop adds `aria-invalid="true"` to the input.
|
|
360
|
+
- FormHelperText is linked to the input via `aria-describedby` so screen readers announce it.
|
|
361
|
+
- Use the `disabled` prop on FormControl to set `aria-disabled` on child elements.
|