@workday/canvas-kit-docs 14.2.25 → 14.2.26

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.
@@ -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.25",
22
- "@workday/canvas-kit-preview-react": "14.2.25",
23
- "@workday/canvas-kit-react": "14.2.25",
24
- "@workday/canvas-kit-react-fonts": "^14.2.25",
25
- "@workday/canvas-kit-styling": "14.2.25",
21
+ "@workday/canvas-kit-labs-react": "14.2.26",
22
+ "@workday/canvas-kit-preview-react": "14.2.26",
23
+ "@workday/canvas-kit-react": "14.2.26",
24
+ "@workday/canvas-kit-react-fonts": "^14.2.26",
25
+ "@workday/canvas-kit-styling": "14.2.26",
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.25",
22
- "@workday/canvas-kit-preview-react": "14.2.25",
23
- "@workday/canvas-kit-react": "14.2.25",
24
- "@workday/canvas-kit-react-fonts": "^14.2.25",
25
- "@workday/canvas-kit-styling": "14.2.25",
21
+ "@workday/canvas-kit-labs-react": "14.2.26",
22
+ "@workday/canvas-kit-preview-react": "14.2.26",
23
+ "@workday/canvas-kit-react": "14.2.26",
24
+ "@workday/canvas-kit-react-fonts": "^14.2.26",
25
+ "@workday/canvas-kit-styling": "14.2.26",
26
26
  "@workday/canvas-system-icons-web": "3.0.36",
27
27
  "@workday/canvas-tokens-web": "3.1.2"
28
28
  },
@@ -0,0 +1,2 @@
1
+ export declare const CommentBoxWithCharLimit: () => import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=CommentBoxWithCharLimit.d.ts.map
@@ -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 TextInputWithLiveError from './examples/AriaLiveRegions/TextInputWithLiveError';
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
- ## Text input with live inline error
63
+ ## Debouncing an `AriaLiveRegion`: `TextArea` with character limit
64
64
 
65
- In this example, a live region is applied to the inline error message that will appear below the
66
- text input. Listen for the screen reader to automatically describe the error message as you leave
67
- the input field blank.
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:** Use this example with discretion. Using live regions for automatically announcing form
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
- **Note:** The `<AriaLiveRegion>` component is used inside of the `Hint` to ensure the live region
76
- remains in the browser DOM at all times. The `Hint` is only rendered in the DOM when it contains
77
- content, so it will not work reliably as a live region for screen readers using the
78
- `as={AriaLiveRegion}` prop.
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={TextInputWithLiveError} />
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
- ### Disabled
97
-
98
- Set the `disabled` prop of `FormField.Input` to prevent users from interacting with it.
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 for correct
154
- accessibility of grouped inputs.
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>First Name</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>Cannot contain numbers</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 {FormField} from '@workday/canvas-kit-react/form-field';
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
- <FormField.Input as={InputGroup}>
21
- <InputGroup.InnerStart pointerEvents="none">
22
- <SystemIcon icon={searchIcon} size="small" />
23
- </InputGroup.InnerStart>
24
- <InputGroup.Input as={TextInput} onChange={handleChange} />
25
- </FormField.Input>
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 meet
35
- accessibility standards.
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 Input to display a sample of its expected format or value
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 of the wrapping Form Field to `"caution"` or
95
- `"error"` to set the Text Area to the Caution or Error state, respectively. You will
96
- also need to set the `hintId` and `hintText` props on the Form Field to meet accessibility
97
- standards.
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
- The `error` prop may be applied directly to the Text Area with a value of `"TextArea.ErrorType.Caution"`
100
- or `TextArea.ErrorType.Error` if Form Field is not being used.
115
+ When limiting text area length:
101
116
 
102
- #### Caution
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
- <ExampleCodeBlock code={Caution} />
126
+ ### Screen Reader Experience
105
127
 
106
- #### Error
128
+ When properly implemented with `FormField`, screen readers will announce:
107
129
 
108
- <ExampleCodeBlock code={Error} />
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 meet
36
- accessibility standards.
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 of the wrapping Form Field to `"caution"` or
103
- `"error"` to set the Text Input to the Caution or Error state, respectively. You
104
- will also need to set the `hintId` and `hintText` props on the Form Field to meet accessibility
105
- standards.
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
- The `error` prop may be applied directly to the Text Input with a value of
108
- `TextInput.ErrorType.Caution` or `TextInput.ErrorType.Error` if Form Field is not being used.
130
+ ### Input Type for Mobile Keyboards
109
131
 
110
- #### Caution
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
- <ExampleCodeBlock code={Caution} />
135
+ ### Screen Reader Experience
113
136
 
114
- #### Error
137
+ When properly implemented with `FormField`, screen readers will announce:
115
138
 
116
- <ExampleCodeBlock code={Error} />
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 {FormField} from '@workday/canvas-kit-react/form-field';
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
- <InputGroupFormFieldForwarder />
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.25",
3
+ "version": "14.2.26",
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.25",
49
- "@workday/canvas-kit-preview-react": "^14.2.25",
50
- "@workday/canvas-kit-react": "^14.2.25",
51
- "@workday/canvas-kit-styling": "^14.2.25",
48
+ "@workday/canvas-kit-labs-react": "^14.2.26",
49
+ "@workday/canvas-kit-preview-react": "^14.2.26",
50
+ "@workday/canvas-kit-react": "^14.2.26",
51
+ "@workday/canvas-kit-styling": "^14.2.26",
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": "4dfa021c4798122cacd6b18b660de072701863e4"
64
+ "gitHead": "2817fe79397ba200c146bdfe38679041579e6a95"
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
- };