@workday/canvas-kit-docs 14.2.25 → 14.2.27
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/es6/lib/stackblitzFiles/packageJSONFile.js +5 -5
- package/dist/es6/lib/stackblitzFiles/packageJSONFile.ts +5 -5
- package/dist/es6/mdx/accessibility/examples/AriaLiveRegions/CommentBoxWithCharLimit.d.ts +2 -0
- package/dist/es6/mdx/accessibility/examples/AriaLiveRegions/CommentBoxWithCharLimit.d.ts.map +1 -0
- package/dist/es6/mdx/accessibility/examples/AriaLiveRegions/CommentBoxWithCharLimit.js +31 -0
- package/dist/mdx/accessibility/AriaLiveRegions.mdx +14 -15
- package/dist/mdx/react/form-field/FormField.mdx +96 -44
- package/dist/mdx/react/form-field/examples/Caution.tsx +6 -4
- package/dist/mdx/react/form-field/examples/Error.tsx +1 -1
- package/dist/mdx/react/form-field/examples/GroupedInputs.tsx +2 -2
- package/dist/mdx/react/form-field/examples/HiddenLabel.tsx +24 -9
- package/dist/mdx/react/text-area/TextArea.mdx +41 -15
- package/dist/mdx/react/text-input/TextInput.mdx +40 -14
- package/dist/mdx/react/text-input/examples/Icons.tsx +23 -19
- package/package.json +6 -6
- package/dist/mdx/react/text-area/examples/Caution.tsx +0 -21
- package/dist/mdx/react/text-area/examples/Error.tsx +0 -21
- package/dist/mdx/react/text-input/examples/Caution.tsx +0 -21
- package/dist/mdx/react/text-input/examples/Error.tsx +0 -21
- /package/dist/mdx/accessibility/{AccessibilityTesting.mdx → TestingTableWithFormFields.mdx} +0 -0
|
@@ -18,11 +18,11 @@ export const packageJSONFile = `{
|
|
|
18
18
|
"@emotion/react": "11.11.4",
|
|
19
19
|
"@types/react": "18.2.60",
|
|
20
20
|
"@types/react-dom": "18.2.19",
|
|
21
|
-
"@workday/canvas-kit-labs-react": "14.2.
|
|
22
|
-
"@workday/canvas-kit-preview-react": "14.2.
|
|
23
|
-
"@workday/canvas-kit-react": "14.2.
|
|
24
|
-
"@workday/canvas-kit-react-fonts": "^14.2.
|
|
25
|
-
"@workday/canvas-kit-styling": "14.2.
|
|
21
|
+
"@workday/canvas-kit-labs-react": "14.2.27",
|
|
22
|
+
"@workday/canvas-kit-preview-react": "14.2.27",
|
|
23
|
+
"@workday/canvas-kit-react": "14.2.27",
|
|
24
|
+
"@workday/canvas-kit-react-fonts": "^14.2.27",
|
|
25
|
+
"@workday/canvas-kit-styling": "14.2.27",
|
|
26
26
|
"@workday/canvas-system-icons-web": "3.0.36",
|
|
27
27
|
"@workday/canvas-tokens-web": "3.1.2"
|
|
28
28
|
},
|
|
@@ -18,11 +18,11 @@ export const packageJSONFile = `{
|
|
|
18
18
|
"@emotion/react": "11.11.4",
|
|
19
19
|
"@types/react": "18.2.60",
|
|
20
20
|
"@types/react-dom": "18.2.19",
|
|
21
|
-
"@workday/canvas-kit-labs-react": "14.2.
|
|
22
|
-
"@workday/canvas-kit-preview-react": "14.2.
|
|
23
|
-
"@workday/canvas-kit-react": "14.2.
|
|
24
|
-
"@workday/canvas-kit-react-fonts": "^14.2.
|
|
25
|
-
"@workday/canvas-kit-styling": "14.2.
|
|
21
|
+
"@workday/canvas-kit-labs-react": "14.2.27",
|
|
22
|
+
"@workday/canvas-kit-preview-react": "14.2.27",
|
|
23
|
+
"@workday/canvas-kit-react": "14.2.27",
|
|
24
|
+
"@workday/canvas-kit-react-fonts": "^14.2.27",
|
|
25
|
+
"@workday/canvas-kit-styling": "14.2.27",
|
|
26
26
|
"@workday/canvas-system-icons-web": "3.0.36",
|
|
27
27
|
"@workday/canvas-tokens-web": "3.1.2"
|
|
28
28
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommentBoxWithCharLimit.d.ts","sourceRoot":"","sources":["../../../../../../mdx/accessibility/examples/AriaLiveRegions/CommentBoxWithCharLimit.tsx"],"names":[],"mappings":"AAQA,eAAO,MAAM,uBAAuB,+CA2CnC,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FormField } from '@workday/canvas-kit-react/form-field';
|
|
4
|
+
import { TextArea } from '@workday/canvas-kit-react/text-area';
|
|
5
|
+
import { AriaLiveRegion, AccessibleHide } from '@workday/canvas-kit-react/common';
|
|
6
|
+
const MAX_CHARACTERS = 200;
|
|
7
|
+
const DEBOUNCE_DELAY = 2000; // 2 seconds after user stops typing
|
|
8
|
+
export const CommentBoxWithCharLimit = () => {
|
|
9
|
+
const [value, setValue] = React.useState('');
|
|
10
|
+
const [liveUpdateStr, setLiveUpdateStr] = React.useState('');
|
|
11
|
+
const hintTextStr = `${value.length} of ${MAX_CHARACTERS} characters`;
|
|
12
|
+
const handleChange = (event) => {
|
|
13
|
+
setValue(event.target.value);
|
|
14
|
+
};
|
|
15
|
+
const handleBlur = () => {
|
|
16
|
+
setLiveUpdateStr('');
|
|
17
|
+
};
|
|
18
|
+
React.useEffect(() => {
|
|
19
|
+
// Immediately announce when limit is reached (bypass debounce)
|
|
20
|
+
if (value.length === MAX_CHARACTERS) {
|
|
21
|
+
setLiveUpdateStr(`Character limit reached. ${value.length} of ${MAX_CHARACTERS} characters`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Otherwise, debounce the updates
|
|
25
|
+
const timer = setTimeout(() => {
|
|
26
|
+
setLiveUpdateStr(`${value.length} of ${MAX_CHARACTERS} characters`);
|
|
27
|
+
}, DEBOUNCE_DELAY);
|
|
28
|
+
return () => clearTimeout(timer);
|
|
29
|
+
}, [value.length]);
|
|
30
|
+
return (_jsxs(FormField, { children: [_jsx(FormField.Label, { children: "Comments" }), _jsxs(FormField.Field, { children: [_jsx(FormField.Input, { as: TextArea, onChange: handleChange, onBlur: handleBlur, value: value, maxLength: MAX_CHARACTERS }), _jsx(FormField.Hint, { children: hintTextStr }), _jsx(AriaLiveRegion, { as: AccessibleHide, children: liveUpdateStr })] })] }));
|
|
31
|
+
};
|
|
@@ -3,7 +3,7 @@ import {AriaLiveRegion} from '@workday/canvas-kit-react/common';
|
|
|
3
3
|
import FilterListWithLiveStatus from './examples/AriaLiveRegions/FilterListWithLiveStatus';
|
|
4
4
|
import VisibleLiveRegion from './examples/AriaLiveRegions/VisibleLiveRegion';
|
|
5
5
|
import HiddenLiveRegion from './examples/AriaLiveRegions/HiddenLiveRegion';
|
|
6
|
-
import
|
|
6
|
+
import CommentBoxWithCharLimit from './examples/AriaLiveRegions/CommentBoxWithCharLimit';
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
# ARIA Live Regions
|
|
@@ -60,21 +60,20 @@ describe how many items in the list are shown.
|
|
|
60
60
|
|
|
61
61
|
<ExampleCodeBlock code={FilterListWithLiveStatus} />
|
|
62
62
|
|
|
63
|
-
##
|
|
63
|
+
## Debouncing an `AriaLiveRegion`: `TextArea` with character limit
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
Using a live region to announce the character count of a text area can help screen reader users
|
|
66
|
+
track their progress. However, announcing on every keystroke would be extremely disruptive—imagine
|
|
67
|
+
hearing "5 of 200 characters... 6 of 200 characters... 7 of 200 characters" for each letter typed!
|
|
68
|
+
In this example, we've implemented debouncing to wait 2 seconds after the user stops typing before
|
|
69
|
+
announcing the count.
|
|
68
70
|
|
|
69
|
-
**Note:**
|
|
70
|
-
errors to screen reader users can be a nice experience for simple forms with a very limited number
|
|
71
|
-
of error conditions. As forms increase in complexity, live regions on each error message can become
|
|
72
|
-
increasingly distracting and disruptive to the experience, especially if users are trying to first
|
|
73
|
-
understand the information that is required of them to complete the task.
|
|
71
|
+
**Note:** Turn on a screen reader for this experience.
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
- Used the `as={AccessibleHide}` prop to hide the live region from view with CSS
|
|
74
|
+
- The live region will only update when a 2 second timer expires after the last keystroke
|
|
75
|
+
- If users have reached the maximum number of characters, the live region will update immediately to
|
|
76
|
+
inform users that they have reached the limit
|
|
77
|
+
- The live region will be cleared on blur events when users leave the field
|
|
79
78
|
|
|
80
|
-
<ExampleCodeBlock code={
|
|
79
|
+
<ExampleCodeBlock code={CommentBoxWithCharLimit} />
|
|
@@ -28,43 +28,6 @@ by passing in `TextInput`, `Select`, `RadioGroup` and other form elements to `Fo
|
|
|
28
28
|
yarn add @workday/canvas-kit-react
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
## Accessibility
|
|
32
|
-
|
|
33
|
-
The `FormField` adds a `for` attribute to the `FormField.Label` (`<label>` element) element that
|
|
34
|
-
matches the `id` attribute of the `FormField.Input` which is usually a `input` element. This both
|
|
35
|
-
labels the input for screen readers and other assitive technology as well as will focus on the input
|
|
36
|
-
when the user clicks on the label. If your form field input component is more complicated, the
|
|
37
|
-
`FormField` will also add an `id` to the `FormField.Label` and an `aria-labelledby` to the
|
|
38
|
-
`FormField.Input` component. You can then forward the `aria-labelledby` to whatever elements you
|
|
39
|
-
need for the proper accessibility.
|
|
40
|
-
|
|
41
|
-
For example, the DOM will look something like this:
|
|
42
|
-
|
|
43
|
-
```html
|
|
44
|
-
<div>
|
|
45
|
-
<label id="label-abc" for="input-abc">First Name</label>
|
|
46
|
-
<input id="input-abc" aria-labelledby="label-abc" />
|
|
47
|
-
</div>
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Some components, like `MultiSelect`, have an additional `role=listbox` element that also needs to
|
|
51
|
-
link to the `label` element. The resulting DOM will look something like:
|
|
52
|
-
|
|
53
|
-
```html
|
|
54
|
-
<div>
|
|
55
|
-
<label id="label-abc" for="input-abc">States you've lived in</label>
|
|
56
|
-
<input id="input-abc" aria-labelledby="label-abc" role="combobox" ... />
|
|
57
|
-
<ul role="listbox" aria-labelledby="label-abc">
|
|
58
|
-
<li>Texas</li>
|
|
59
|
-
<li>California</li>
|
|
60
|
-
</ul>
|
|
61
|
-
</div>
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
The `MultiSelect` component gets the `aria-labelledby` from the `FormField.Input` and forwards it to
|
|
65
|
-
both the `input[role=combobox]` element and the `ul[role=listbox]` element so the screen reader
|
|
66
|
-
knows the label for both is "States you've lived in".
|
|
67
|
-
|
|
68
31
|
## Usage
|
|
69
32
|
|
|
70
33
|
### Basic
|
|
@@ -87,17 +50,19 @@ Use the caution state when a value is valid but there is additional information.
|
|
|
87
50
|
|
|
88
51
|
<ExampleCodeBlock code={Caution} />
|
|
89
52
|
|
|
53
|
+
> **Accessibility Note**: Caution state **will not** include the `aria-invalid` attribute on the
|
|
54
|
+
> input for screen readers. Use error states when values are not valid.
|
|
55
|
+
|
|
90
56
|
### Error
|
|
91
57
|
|
|
92
58
|
Use the error state when the value is no longer valid.
|
|
93
59
|
|
|
94
60
|
<ExampleCodeBlock code={Error} />
|
|
95
61
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<ExampleCodeBlock code={Disabled} />
|
|
62
|
+
> **Accessibility Note**: Error states include visual color changes to the input border and
|
|
63
|
+
> **require** supplemental "error" text for colorblind users to distinguish between fields in an
|
|
64
|
+
> error state from fields with standard hint text. Read more about
|
|
65
|
+
> [Failure of Success Criterion 1.4.1 due to identifying required or error fields using color differences only](https://www.w3.org/WAI/WCAG22/Techniques/failures/F81)
|
|
101
66
|
|
|
102
67
|
### Hint
|
|
103
68
|
|
|
@@ -106,6 +71,22 @@ ensure proper alignment.
|
|
|
106
71
|
|
|
107
72
|
<ExampleCodeBlock code={Hint} />
|
|
108
73
|
|
|
74
|
+
> **Accessibility Note**: Hints are automatically associated to the input field with the
|
|
75
|
+
> `aria-describedby` attribute. This ensures that screen readers can automatically announce the hint
|
|
76
|
+
> text to users when the input field is focused.
|
|
77
|
+
|
|
78
|
+
### Disabled
|
|
79
|
+
|
|
80
|
+
Set the `disabled` prop of `FormField.Input` to prevent users from interacting with it.
|
|
81
|
+
|
|
82
|
+
<ExampleCodeBlock code={Disabled} />
|
|
83
|
+
|
|
84
|
+
> **Accessibility Note**: Disabled form elements are exempt from
|
|
85
|
+
> [WCAG minimum contrast guidelines](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html).
|
|
86
|
+
> Despite this exemption, disabled fields are more difficult for low vision and colorblind users to
|
|
87
|
+
> perceive and may harm the usability of the form. Consider using text elements instead, or
|
|
88
|
+
> read-only fields if users cannot modify data.
|
|
89
|
+
|
|
109
90
|
### Label Position
|
|
110
91
|
|
|
111
92
|
Set the `orientation` prop of the Form Field to designate the position of the label relative to the
|
|
@@ -146,12 +127,16 @@ for required fields are suffixed by a red asterisk.
|
|
|
146
127
|
|
|
147
128
|
<ExampleCodeBlock code={Required} />
|
|
148
129
|
|
|
130
|
+
> **Accessibility Note**: The HTML `required` attribute will be added to the input field and
|
|
131
|
+
> announced by screen readers. Consider adding a note at the top of your form indicating that fields
|
|
132
|
+
> marked with an asterisk (\*) are required. This provides context for all users.
|
|
133
|
+
|
|
149
134
|
### Grouped Inputs
|
|
150
135
|
|
|
151
136
|
Use `FormFieldGroup` when you have a group of inputs that need to be associated to one another, like
|
|
152
137
|
`RadioGroup` or a group of `Checkbox`'s. `FormFieldGroup` renders a `fieldset` element and
|
|
153
|
-
`FormFieldGroup.Label` renders a `legend` element. These elements will allow
|
|
154
|
-
|
|
138
|
+
`FormFieldGroup.Label` renders a `legend` element. These elements will allow screen readers to
|
|
139
|
+
automatically announce the legend's context when focusing on the inputs in the group.
|
|
155
140
|
|
|
156
141
|
`FormFieldGroup` supports the same props of `FormField`:
|
|
157
142
|
|
|
@@ -161,6 +146,11 @@ accessibility of grouped inputs.
|
|
|
161
146
|
|
|
162
147
|
<ExampleCodeBlock code={GroupedInputs} />
|
|
163
148
|
|
|
149
|
+
> **Accessibility Note**: In addition to radio button and checkbox groups, `FormFieldGroup` can be
|
|
150
|
+
> useful in any situation where the form needs to have multiple sets of identical input fields. For
|
|
151
|
+
> example, a form with identical fields for a Shipping address and a Billing address. The legend
|
|
152
|
+
> provides critical context for screen reader users in these situations.
|
|
153
|
+
|
|
164
154
|
### Custom
|
|
165
155
|
|
|
166
156
|
If you need full customization you can use the `FormField` behavior hooks to build your own
|
|
@@ -191,6 +181,10 @@ In cases where you want to hide the label while still meeting accessibility stan
|
|
|
191
181
|
|
|
192
182
|
<ExampleCodeBlock code={HiddenLabel} />
|
|
193
183
|
|
|
184
|
+
> **Accessibility Note**: Hidden labels are typically not recommended. In this example, a
|
|
185
|
+
> universally recognizable icon like a magnifying glass signaling "search" may be a suitable
|
|
186
|
+
> alternative to visible text labels.
|
|
187
|
+
|
|
194
188
|
### Themed Errors
|
|
195
189
|
|
|
196
190
|
You can theme your error rings by wrapping an input in a `CanvasProvider` and defining
|
|
@@ -204,6 +198,64 @@ Form Field and its subcomponents support custom styling via the `cs` prop. For m
|
|
|
204
198
|
check our
|
|
205
199
|
["How To Customize Styles"](https://workday.github.io/canvas-kit/?path=/docs/styling-guides-customizing-styles--docs).
|
|
206
200
|
|
|
201
|
+
## Accessibility
|
|
202
|
+
|
|
203
|
+
`FormField` provides essential accessibility features to ensure form inputs are properly labeled and
|
|
204
|
+
described for all users, including those using assistive technologies. This section covers both the
|
|
205
|
+
technical implementation and best practices for creating accessible forms.
|
|
206
|
+
|
|
207
|
+
### Label Association
|
|
208
|
+
|
|
209
|
+
The `FormField` adds a `for` attribute to the `FormField.Label` (`<label>` element) element that
|
|
210
|
+
matches the `id` attribute of the `FormField.Input` which is usually a `input` element. This both
|
|
211
|
+
labels the input for screen readers and other assistive technology as well as will focus on the
|
|
212
|
+
input when the user clicks on the label. If your form field input component is more complicated, the
|
|
213
|
+
`FormField` will also add an `id` to the `FormField.Label` and an `aria-labelledby` to the
|
|
214
|
+
`FormField.Input` component. You can then forward the `aria-labelledby` to whatever elements you
|
|
215
|
+
need for the proper accessibility.
|
|
216
|
+
|
|
217
|
+
For example, the DOM will look something like this:
|
|
218
|
+
|
|
219
|
+
```html
|
|
220
|
+
<div>
|
|
221
|
+
<label id="label-abc" for="input-abc">First Name</label>
|
|
222
|
+
<input id="input-abc" aria-labelledby="label-abc" />
|
|
223
|
+
</div>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Some components, like `MultiSelect`, have an additional `role=listbox` element that also needs to
|
|
227
|
+
link to the `label` element. The resulting DOM will look something like:
|
|
228
|
+
|
|
229
|
+
```html
|
|
230
|
+
<div>
|
|
231
|
+
<label id="label-abc" for="input-abc">States you've lived in</label>
|
|
232
|
+
<input id="input-abc" aria-labelledby="label-abc" role="combobox" ... />
|
|
233
|
+
<ul role="listbox" aria-labelledby="label-abc">
|
|
234
|
+
<li>Texas</li>
|
|
235
|
+
<li>California</li>
|
|
236
|
+
</ul>
|
|
237
|
+
</div>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The `MultiSelect` component gets the `aria-labelledby` from the `FormField.Input` and forwards it to
|
|
241
|
+
both the `input[role=combobox]` element and the `ul[role=listbox]` element so the screen reader
|
|
242
|
+
knows the label for both is "States you've lived in".
|
|
243
|
+
|
|
244
|
+
### Label Text Best Practices
|
|
245
|
+
|
|
246
|
+
- **Be Clear and Concise**: Labels should clearly describe the purpose of the input field.
|
|
247
|
+
- **Use Visible Labels Instead of Only Placeholders**: Always provide a persistent and accessible
|
|
248
|
+
label with `FormField.Label`. Do not rely solely on placeholder text, as it can disappear while
|
|
249
|
+
typing and may not be accessible to assistive technologies. Use the `isHidden` prop on
|
|
250
|
+
`FormField.Label` if a hidden label is required for visual design.
|
|
251
|
+
|
|
252
|
+
### Screen Reader Experience
|
|
253
|
+
|
|
254
|
+
- The label is announced when the input receives focus.
|
|
255
|
+
- Required, disabled, and invalid statuses are announced automatically.
|
|
256
|
+
- Help text and error messages are announced automatically when focused.
|
|
257
|
+
- For grouped inputs, the group label (`legend`) is announced automatically when focused.
|
|
258
|
+
|
|
207
259
|
## Component API
|
|
208
260
|
|
|
209
261
|
<SymbolDoc name="FormField" />
|
|
@@ -4,7 +4,7 @@ import {TextInput} from '@workday/canvas-kit-react/text-input';
|
|
|
4
4
|
import {Flex} from '@workday/canvas-kit-react/layout';
|
|
5
5
|
|
|
6
6
|
export default () => {
|
|
7
|
-
const [value, setValue] = React.useState('');
|
|
7
|
+
const [value, setValue] = React.useState('hi');
|
|
8
8
|
|
|
9
9
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
10
10
|
setValue(event.target.value);
|
|
@@ -13,10 +13,12 @@ export default () => {
|
|
|
13
13
|
return (
|
|
14
14
|
<Flex>
|
|
15
15
|
<FormField error="caution">
|
|
16
|
-
<FormField.Label>
|
|
16
|
+
<FormField.Label>Create Password</FormField.Label>
|
|
17
17
|
<FormField.Field>
|
|
18
|
-
<FormField.Input as={TextInput} value={value} onChange={handleChange} />
|
|
19
|
-
<FormField.Hint>
|
|
18
|
+
<FormField.Input as={TextInput} type="password" value={value} onChange={handleChange} />
|
|
19
|
+
<FormField.Hint>
|
|
20
|
+
Alert: Password strength is weak, using more characters is recommended.
|
|
21
|
+
</FormField.Hint>
|
|
20
22
|
</FormField.Field>
|
|
21
23
|
</FormField>
|
|
22
24
|
</Flex>
|
|
@@ -17,7 +17,7 @@ export default () => {
|
|
|
17
17
|
<FormField.Label>Password</FormField.Label>
|
|
18
18
|
<FormField.Field>
|
|
19
19
|
<FormField.Input as={TextInput} type="password" value={value} onChange={handleChange} />
|
|
20
|
-
<FormField.Hint>Must Contain a number and a capital letter</FormField.Hint>
|
|
20
|
+
<FormField.Hint>Error: Must Contain a number and a capital letter</FormField.Hint>
|
|
21
21
|
</FormField.Field>
|
|
22
22
|
</FormField>
|
|
23
23
|
</Flex>
|
|
@@ -160,7 +160,7 @@ export default () => {
|
|
|
160
160
|
})}
|
|
161
161
|
</FormFieldGroup.List>
|
|
162
162
|
<FormFieldGroup.Hint>
|
|
163
|
-
{error === 'error' && 'You must choose one topping'}
|
|
163
|
+
{error === 'error' && 'Error: You must choose one topping'}
|
|
164
164
|
</FormFieldGroup.Hint>
|
|
165
165
|
</FormFieldGroup>
|
|
166
166
|
<FormFieldGroup error={radioError} isRequired>
|
|
@@ -186,7 +186,7 @@ export default () => {
|
|
|
186
186
|
</FormFieldGroup.Input>
|
|
187
187
|
</FormFieldGroup.List>
|
|
188
188
|
<FormFieldGroup.Hint>
|
|
189
|
-
{radioError === 'error' ? 'You must choose a crust' : null}
|
|
189
|
+
{radioError === 'error' ? 'Error: You must choose a crust' : null}
|
|
190
190
|
</FormFieldGroup.Hint>
|
|
191
191
|
</FormFieldGroup.Field>
|
|
192
192
|
</FormFieldGroup>
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
FormField,
|
|
4
|
+
useFormFieldModel,
|
|
5
|
+
useFormFieldInput,
|
|
6
|
+
} from '@workday/canvas-kit-react/form-field';
|
|
3
7
|
import {Flex} from '@workday/canvas-kit-react/layout';
|
|
4
8
|
import {TextInput, InputGroup} from '@workday/canvas-kit-react/text-input';
|
|
5
9
|
import {SystemIcon} from '@workday/canvas-kit-react/icon';
|
|
6
10
|
import {searchIcon} from '@workday/canvas-system-icons-web';
|
|
7
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Using `as={InputGroup}` on `FormField.Input` will break the label associations necessary for accessibility.
|
|
14
|
+
* In this example, we've rendered `FormField.Field` as `InputGroup` and then hoisted the `id` of the input from the FormField model.
|
|
15
|
+
* This allows us to set the `id` of the `InputGroup.Input` correctly for proper label association.
|
|
16
|
+
*/
|
|
17
|
+
|
|
8
18
|
export default () => {
|
|
9
19
|
const [value, setValue] = React.useState('');
|
|
20
|
+
const model = useFormFieldModel();
|
|
21
|
+
const {id: formFieldInputId} = useFormFieldInput(model);
|
|
10
22
|
|
|
11
23
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
12
24
|
setValue(event.target.value);
|
|
@@ -14,15 +26,18 @@ export default () => {
|
|
|
14
26
|
|
|
15
27
|
return (
|
|
16
28
|
<Flex>
|
|
17
|
-
<FormField>
|
|
29
|
+
<FormField model={model}>
|
|
18
30
|
<FormField.Label isHidden>Search</FormField.Label>
|
|
19
|
-
<FormField.Field>
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
<FormField.Field as={InputGroup}>
|
|
32
|
+
<InputGroup.InnerStart>
|
|
33
|
+
<SystemIcon icon={searchIcon} size="small" />
|
|
34
|
+
</InputGroup.InnerStart>
|
|
35
|
+
<InputGroup.Input
|
|
36
|
+
as={TextInput}
|
|
37
|
+
id={formFieldInputId}
|
|
38
|
+
onChange={handleChange}
|
|
39
|
+
value={value}
|
|
40
|
+
/>
|
|
26
41
|
</FormField.Field>
|
|
27
42
|
</FormField>
|
|
28
43
|
</Flex>
|
|
@@ -4,10 +4,8 @@ import {
|
|
|
4
4
|
Specifications,
|
|
5
5
|
InformationHighlight,
|
|
6
6
|
} from '@workday/canvas-kit-docs';
|
|
7
|
-
import Caution from './examples/Caution';
|
|
8
7
|
import Basic from './examples/Basic';
|
|
9
8
|
import Disabled from './examples/Disabled';
|
|
10
|
-
import Error from './examples/Error';
|
|
11
9
|
import Grow from './examples/Grow';
|
|
12
10
|
import LabelPosition from './examples/LabelPosition';
|
|
13
11
|
import Placeholder from './examples/Placeholder';
|
|
@@ -31,8 +29,8 @@ yarn add @workday/canvas-kit-react
|
|
|
31
29
|
|
|
32
30
|
### Basic Example
|
|
33
31
|
|
|
34
|
-
Text Area should be used in tandem with [Form Field](/components/inputs/form-field/) to
|
|
35
|
-
|
|
32
|
+
Text Area should be used in tandem with [Form Field](/components/inputs/form-field/) to ensure
|
|
33
|
+
proper label association and screen reader support.
|
|
36
34
|
|
|
37
35
|
<ExampleCodeBlock code={Basic} />
|
|
38
36
|
|
|
@@ -44,11 +42,16 @@ Set the `disabled` prop of the Text Area to prevent users from interacting with
|
|
|
44
42
|
|
|
45
43
|
### Placeholder
|
|
46
44
|
|
|
47
|
-
Set the `placeholder` prop of the Text
|
|
45
|
+
Set the `placeholder` prop of the Text Area to display a sample of its expected format or value
|
|
48
46
|
before a value has been provided.
|
|
49
47
|
|
|
50
48
|
<ExampleCodeBlock code={Placeholder} />
|
|
51
49
|
|
|
50
|
+
> **Accessibility Note**: Always provide a persistent `FormField.Label` and never rely on
|
|
51
|
+
> placeholder text as the only label for a text area. Placeholders can disappear or lack sufficient
|
|
52
|
+
> contrast. Use placeholders only for short format examples, and place detailed instructions or
|
|
53
|
+
> guidance in `FormField.Hint` instead of the placeholder.
|
|
54
|
+
|
|
52
55
|
### Ref Forwarding
|
|
53
56
|
|
|
54
57
|
Text Area supports [ref forwarding](https://reactjs.org/docs/forwarding-refs.html). It will forward
|
|
@@ -68,6 +71,10 @@ accepts the following values:
|
|
|
68
71
|
|
|
69
72
|
<ExampleCodeBlock code={ResizeConstraints} />
|
|
70
73
|
|
|
74
|
+
> **Accessibility Note**: Allowing users to resize the text area (default `resize: both`) improves
|
|
75
|
+
> accessibility by letting them adjust it for comfort. Avoid disabling resizing (`resize: none`)
|
|
76
|
+
> unless necessary, and always ensure the initial size meets the needs of your content.
|
|
77
|
+
|
|
71
78
|
### Grow
|
|
72
79
|
|
|
73
80
|
Set the `grow` prop of the Text Area to `true` to configure the Text Area to expand to the width of
|
|
@@ -91,21 +98,40 @@ Labels for required fields are suffixed by a red asterisk.
|
|
|
91
98
|
|
|
92
99
|
### Error States
|
|
93
100
|
|
|
94
|
-
Set the `error` prop
|
|
95
|
-
`"error"`
|
|
96
|
-
|
|
97
|
-
|
|
101
|
+
Form Field provides error and caution states for Text Area. Set the `error` prop on Form Field to
|
|
102
|
+
`"error"` or `"caution"` and use `FormField.Hint` to provide error messages. See
|
|
103
|
+
[Form Field's Error documentation](/components/inputs/form-field/#error-states) for
|
|
104
|
+
examples and accessibility guidance.
|
|
105
|
+
|
|
106
|
+
## Accessibility
|
|
107
|
+
|
|
108
|
+
`TextArea` should be used with [Form Field](/components/inputs/form-field/) to
|
|
109
|
+
ensure proper labeling, error handling, and help text association. See
|
|
110
|
+
[FormField's accessibility documentation](/components/inputs/form-field/#accessibility)
|
|
111
|
+
for comprehensive guidance on form accessibility best practices.
|
|
112
|
+
|
|
113
|
+
### Character Limits
|
|
98
114
|
|
|
99
|
-
|
|
100
|
-
or `TextArea.ErrorType.Error` if Form Field is not being used.
|
|
115
|
+
When limiting text area length:
|
|
101
116
|
|
|
102
|
-
|
|
117
|
+
- Use the `maxLength` attribute to enforce the limit programmatically.
|
|
118
|
+
- For longer limits (100+ characters), consider adding character count information to
|
|
119
|
+
`FormField.Hint`.
|
|
120
|
+
- Avoid announcing character counts after every keystroke, as this disrupts screen reader users.
|
|
121
|
+
Check out
|
|
122
|
+
[Debouncing an AriaLiveRegion: TextArea with character limit](https://workday.github.io/canvas-kit/?path=/docs/guides-accessibility-aria-live-regions--docs#debouncing-an-arialiveregion-textarea-with-character-limit)
|
|
123
|
+
for an example of how to wait for users to stop typing before announcing the character count to
|
|
124
|
+
screen readers.
|
|
103
125
|
|
|
104
|
-
|
|
126
|
+
### Screen Reader Experience
|
|
105
127
|
|
|
106
|
-
|
|
128
|
+
When properly implemented with `FormField`, screen readers will announce:
|
|
107
129
|
|
|
108
|
-
|
|
130
|
+
- The label text when the text area receives focus.
|
|
131
|
+
- Required, disabled, or read-only status.
|
|
132
|
+
- Help text and error messages (via `aria-describedby`).
|
|
133
|
+
- The current value or "blank" if empty.
|
|
134
|
+
- That it's a multi-line text input field.
|
|
109
135
|
|
|
110
136
|
## Component API
|
|
111
137
|
|
|
@@ -4,10 +4,8 @@ import {
|
|
|
4
4
|
SymbolDoc,
|
|
5
5
|
Specifications,
|
|
6
6
|
} from '@workday/canvas-kit-docs';
|
|
7
|
-
import Caution from './examples/Caution';
|
|
8
7
|
import Basic from './examples/Basic';
|
|
9
8
|
import Disabled from './examples/Disabled';
|
|
10
|
-
import Error from './examples/Error';
|
|
11
9
|
import Grow from './examples/Grow';
|
|
12
10
|
import LabelPosition from './examples/LabelPosition';
|
|
13
11
|
import Placeholder from './examples/Placeholder';
|
|
@@ -32,8 +30,8 @@ yarn add @workday/canvas-kit-react
|
|
|
32
30
|
|
|
33
31
|
### Basic Example
|
|
34
32
|
|
|
35
|
-
Text Input should be used in tandem with [Form Field](/components/inputs/form-field/) to
|
|
36
|
-
|
|
33
|
+
Text Input should be used in tandem with [Form Field](/components/inputs/form-field/) to ensure
|
|
34
|
+
proper label association and screen reader support.
|
|
37
35
|
|
|
38
36
|
<ExampleCodeBlock code={Basic} />
|
|
39
37
|
|
|
@@ -50,6 +48,11 @@ before a value has been provided.
|
|
|
50
48
|
|
|
51
49
|
<ExampleCodeBlock code={Placeholder} />
|
|
52
50
|
|
|
51
|
+
> **Accessibility Note**: Always provide a persistent `FormField.Label` and never rely on
|
|
52
|
+
> placeholder text as the only label for an input. Placeholders can disappear or lack sufficient
|
|
53
|
+
> contrast. Use placeholders only for short format examples (e.g., "name@example.com"), and place
|
|
54
|
+
> detailed instructions or guidance in `FormField.Hint` instead of the placeholder.
|
|
55
|
+
|
|
53
56
|
### Ref Forwarding
|
|
54
57
|
|
|
55
58
|
Text Input supports [ref forwarding](https://reactjs.org/docs/forwarding-refs.html). It will forward
|
|
@@ -97,23 +100,46 @@ method to change width. The `width` prop is used to correctly position other inn
|
|
|
97
100
|
|
|
98
101
|
<ExampleCodeBlock code={Icons} />
|
|
99
102
|
|
|
103
|
+
> **Accessibility Note**: In this example, the mail icon is decorative and hidden from screen
|
|
104
|
+
> readers. If icons are used for conveying meaning in addition to the label text, a text alternative
|
|
105
|
+
> must be provided for screen readers.
|
|
106
|
+
|
|
100
107
|
### Error States
|
|
101
108
|
|
|
102
|
-
Set the `error` prop
|
|
103
|
-
`"error"`
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
Form Field provides error and caution states for Text Input. Set the `error` prop on Form Field to
|
|
110
|
+
`"error"` or `"caution"` and use `FormField.Hint` to provide error messages. See
|
|
111
|
+
[Form Field's Error documentation](/components/inputs/form-field/#error-states) for examples and
|
|
112
|
+
accessibility guidance.
|
|
113
|
+
|
|
114
|
+
## Accessibility
|
|
115
|
+
|
|
116
|
+
`TextInput` should be used with [Form Field](/components/inputs/form-field/) to ensure proper
|
|
117
|
+
labeling, error handling, and help text association. See
|
|
118
|
+
[FormField's accessibility documentation](/components/inputs/form-field/#accessibility) for
|
|
119
|
+
comprehensive guidance on form accessibility best practices.
|
|
120
|
+
|
|
121
|
+
### Autocomplete Attribute
|
|
122
|
+
|
|
123
|
+
- Add appropriate `autoComplete` values to indicate the input's purpose (e.g., `"email"`, `"name"`,
|
|
124
|
+
`"street-address"`, `"tel"`). Read more about
|
|
125
|
+
[Identify Input Purpose](https://www.w3.org/WAI/WCAG22/Understanding/identify-input-purpose.html).
|
|
126
|
+
- Autocomplete enables browser autofill and helps assistive technologies understand the field's
|
|
127
|
+
purpose, benefiting users with cognitive disabilities and motor impairments.
|
|
128
|
+
- Autocomplete also helps password managers identify the correct fields.
|
|
106
129
|
|
|
107
|
-
|
|
108
|
-
`TextInput.ErrorType.Caution` or `TextInput.ErrorType.Error` if Form Field is not being used.
|
|
130
|
+
### Input Type for Mobile Keyboards
|
|
109
131
|
|
|
110
|
-
|
|
132
|
+
`TextInput` defaults to `<input type="text">`, but for better mobile keyboard support, use more
|
|
133
|
+
specific `type` attributes (like `"email"`, `"tel"`, `"url"`, or `"search"`) as needed.
|
|
111
134
|
|
|
112
|
-
|
|
135
|
+
### Screen Reader Experience
|
|
113
136
|
|
|
114
|
-
|
|
137
|
+
When properly implemented with `FormField`, screen readers will announce:
|
|
115
138
|
|
|
116
|
-
|
|
139
|
+
- The label text when the input receives focus.
|
|
140
|
+
- Required, disabled, or read-only status.
|
|
141
|
+
- Help text and error messages (via `aria-describedby`).
|
|
142
|
+
- The current value or "blank" if empty.
|
|
117
143
|
|
|
118
144
|
## Component API
|
|
119
145
|
|
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
import {mailIcon} from '@workday/canvas-system-icons-web';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
FormField,
|
|
6
|
+
useFormFieldModel,
|
|
7
|
+
useFormFieldInput,
|
|
8
|
+
} from '@workday/canvas-kit-react/form-field';
|
|
5
9
|
import {InputGroup} from '@workday/canvas-kit-react/text-input';
|
|
6
10
|
import {SystemIcon} from '@workday/canvas-kit-react/icon';
|
|
7
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Using `as={InputGroup}` on `FormField.Input` will break the label associations necessary for accessibility.
|
|
14
|
+
* In this example, we've rendered `FormField.Field` as `InputGroup` and then hoisted the `id` of the input from the FormField model.
|
|
15
|
+
* This allows us to set the `id` of the `InputGroup.Input` correctly for proper label association.
|
|
16
|
+
*/
|
|
17
|
+
|
|
8
18
|
export default () => {
|
|
19
|
+
const model = useFormFieldModel();
|
|
20
|
+
const {id: formFieldInputId} = useFormFieldInput(model);
|
|
21
|
+
|
|
9
22
|
return (
|
|
10
|
-
<FormField>
|
|
23
|
+
<FormField model={model}>
|
|
11
24
|
<FormField.Label>Email</FormField.Label>
|
|
12
|
-
<FormField.Field>
|
|
13
|
-
<
|
|
25
|
+
<FormField.Field as={InputGroup}>
|
|
26
|
+
<InputGroup.InnerStart>
|
|
27
|
+
<SystemIcon icon={mailIcon} size="small" />
|
|
28
|
+
</InputGroup.InnerStart>
|
|
29
|
+
<InputGroup.Input id={formFieldInputId} autoComplete="email" />
|
|
30
|
+
<InputGroup.InnerEnd>
|
|
31
|
+
<InputGroup.ClearButton />
|
|
32
|
+
</InputGroup.InnerEnd>
|
|
14
33
|
</FormField.Field>
|
|
15
34
|
</FormField>
|
|
16
35
|
);
|
|
17
36
|
};
|
|
18
|
-
|
|
19
|
-
// create a prop forwarding component for FormField to forward to
|
|
20
|
-
const InputGroupFormFieldForwarder = (props: {}) => {
|
|
21
|
-
return (
|
|
22
|
-
<FormField.Input as={InputGroup} width={280}>
|
|
23
|
-
<InputGroup.InnerStart pointerEvents="none">
|
|
24
|
-
<SystemIcon icon={mailIcon} size="small" />
|
|
25
|
-
</InputGroup.InnerStart>
|
|
26
|
-
<InputGroup.Input {...props} />
|
|
27
|
-
<InputGroup.InnerEnd>
|
|
28
|
-
<InputGroup.ClearButton />
|
|
29
|
-
</InputGroup.InnerEnd>
|
|
30
|
-
</FormField.Input>
|
|
31
|
-
);
|
|
32
|
-
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workday/canvas-kit-docs",
|
|
3
|
-
"version": "14.2.
|
|
3
|
+
"version": "14.2.27",
|
|
4
4
|
"description": "Documentation components of Canvas Kit components",
|
|
5
5
|
"author": "Workday, Inc. (https://www.workday.com)",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"@emotion/styled": "^11.6.0",
|
|
46
46
|
"@stackblitz/sdk": "^1.11.0",
|
|
47
47
|
"@storybook/csf": "0.0.1",
|
|
48
|
-
"@workday/canvas-kit-labs-react": "^14.2.
|
|
49
|
-
"@workday/canvas-kit-preview-react": "^14.2.
|
|
50
|
-
"@workday/canvas-kit-react": "^14.2.
|
|
51
|
-
"@workday/canvas-kit-styling": "^14.2.
|
|
48
|
+
"@workday/canvas-kit-labs-react": "^14.2.27",
|
|
49
|
+
"@workday/canvas-kit-preview-react": "^14.2.27",
|
|
50
|
+
"@workday/canvas-kit-react": "^14.2.27",
|
|
51
|
+
"@workday/canvas-kit-styling": "^14.2.27",
|
|
52
52
|
"@workday/canvas-system-icons-web": "^3.0.36",
|
|
53
53
|
"@workday/canvas-tokens-web": "^3.1.6",
|
|
54
54
|
"markdown-to-jsx": "^7.2.0",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"mkdirp": "^1.0.3",
|
|
62
62
|
"typescript": "5.0"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "c01ab812eade9574a72bf84c008b2a62b51e2649"
|
|
65
65
|
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {FormField} from '@workday/canvas-kit-react/form-field';
|
|
3
|
-
import {TextArea} from '@workday/canvas-kit-react/text-area';
|
|
4
|
-
|
|
5
|
-
export default () => {
|
|
6
|
-
const [value, setValue] = React.useState('');
|
|
7
|
-
|
|
8
|
-
const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
9
|
-
setValue(event.target.value);
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<FormField error="caution">
|
|
14
|
-
<FormField.Label>Please enter your review.</FormField.Label>
|
|
15
|
-
<FormField.Field>
|
|
16
|
-
<FormField.Input as={TextArea} onChange={handleChange} value={value} />
|
|
17
|
-
<FormField.Hint>Please enter your review.</FormField.Hint>
|
|
18
|
-
</FormField.Field>
|
|
19
|
-
</FormField>
|
|
20
|
-
);
|
|
21
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {FormField} from '@workday/canvas-kit-react/form-field';
|
|
3
|
-
import {TextArea} from '@workday/canvas-kit-react/text-area';
|
|
4
|
-
|
|
5
|
-
export default () => {
|
|
6
|
-
const [value, setValue] = React.useState('');
|
|
7
|
-
|
|
8
|
-
const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
9
|
-
setValue(event.target.value);
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<FormField error="error">
|
|
14
|
-
<FormField.Label>Leave a Review</FormField.Label>
|
|
15
|
-
<FormField.Field>
|
|
16
|
-
<FormField.Input as={TextArea} onChange={handleChange} value={value} />
|
|
17
|
-
<FormField.Hint>Please enter your review.</FormField.Hint>
|
|
18
|
-
</FormField.Field>
|
|
19
|
-
</FormField>
|
|
20
|
-
);
|
|
21
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {FormField} from '@workday/canvas-kit-react/form-field';
|
|
3
|
-
import {TextInput} from '@workday/canvas-kit-react/text-input';
|
|
4
|
-
|
|
5
|
-
export default () => {
|
|
6
|
-
const [value, setValue] = React.useState('invalid@email');
|
|
7
|
-
|
|
8
|
-
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
9
|
-
setValue(event.target.value);
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<FormField error="caution">
|
|
14
|
-
<FormField.Label>Email</FormField.Label>
|
|
15
|
-
<FormField.Field>
|
|
16
|
-
<FormField.Input as={TextInput} onChange={handleChange} value={value} />
|
|
17
|
-
<FormField.Hint>Please enter a valid email.</FormField.Hint>
|
|
18
|
-
</FormField.Field>
|
|
19
|
-
</FormField>
|
|
20
|
-
);
|
|
21
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {FormField} from '@workday/canvas-kit-react/form-field';
|
|
3
|
-
import {TextInput} from '@workday/canvas-kit-react/text-input';
|
|
4
|
-
|
|
5
|
-
export default () => {
|
|
6
|
-
const [value, setValue] = React.useState('invalid@email');
|
|
7
|
-
|
|
8
|
-
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
9
|
-
setValue(event.target.value);
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<FormField error="error">
|
|
14
|
-
<FormField.Label>Email</FormField.Label>
|
|
15
|
-
<FormField.Field>
|
|
16
|
-
<FormField.Input as={TextInput} onChange={handleChange} value={value} />
|
|
17
|
-
<FormField.Hint>Please enter a valid email.</FormField.Hint>
|
|
18
|
-
</FormField.Field>
|
|
19
|
-
</FormField>
|
|
20
|
-
);
|
|
21
|
-
};
|
|
File without changes
|