@workday/canvas-kit-docs 16.1.4 → 16.2.0-0604-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/es6/lib/docs.js +18352 -5202
  2. package/dist/es6/lib/specs.js +204 -20
  3. package/dist/es6/lib/stackblitzFiles/packageJSONFile.js +5 -5
  4. package/dist/es6/lib/stackblitzFiles/packageJSONFile.ts +5 -5
  5. package/dist/mdx/accessibility/AccessibilityOverview.mdx +1 -3
  6. package/dist/mdx/labs-react/selection-group/SelectionGroup.mdx +135 -0
  7. package/dist/mdx/labs-react/selection-group/examples/Basic.tsx +11 -0
  8. package/dist/mdx/labs-react/selection-group/examples/Caution.tsx +27 -0
  9. package/dist/mdx/labs-react/selection-group/examples/ColumnWidth.tsx +17 -0
  10. package/dist/mdx/labs-react/selection-group/examples/ContentWidth.tsx +11 -0
  11. package/dist/mdx/labs-react/selection-group/examples/Disabled.tsx +33 -0
  12. package/dist/mdx/labs-react/selection-group/examples/EqualWidth.tsx +11 -0
  13. package/dist/mdx/labs-react/selection-group/examples/Error.tsx +27 -0
  14. package/dist/mdx/labs-react/selection-group/examples/MultiSelect.tsx +41 -0
  15. package/dist/mdx/labs-react/selection-group/examples/RTL.tsx +13 -0
  16. package/dist/mdx/labs-react/selection-group/examples/SingleSelect.tsx +33 -0
  17. package/dist/mdx/preview-react/_examples/mdx/FormLibraryExample.mdx +1 -33
  18. package/dist/mdx/preview-react/_examples/mdx/examples/TextInputWithReactHookForm.tsx +65 -94
  19. package/dist/mdx/react/_examples/mdx/Density.mdx +3 -19
  20. package/dist/mdx/react/_examples/mdx/FormsWithFormik.mdx +9 -0
  21. package/dist/mdx/react/_examples/mdx/examples/Density.tsx +212 -139
  22. package/dist/mdx/react/_examples/mdx/examples/SelectWithFormik.tsx +84 -0
  23. package/dist/mdx/react/expandable/Expandable.mdx +55 -161
  24. package/dist/mdx/react/select/Select.mdx +77 -239
  25. package/dist/mdx/react/select/examples/GroupedItems.tsx +30 -24
  26. package/dist/mdx/react/select/examples/WithIcons.tsx +1 -1
  27. package/package.json +6 -6
@@ -1,39 +1,7 @@
1
1
  import {ExampleCodeBlock} from '@workday/canvas-kit-docs';
2
-
3
2
  import TextInputWithReactHookForm from './examples/TextInputWithReactHookForm';
4
3
 
5
4
 
6
- # Form Validation with React Hook Form and Yup
7
-
8
- Accessible form validation is not a library feature. It is a composition of **labels**, **error text
9
- tied to the field**, **`aria-invalid`**, and **keyboard focus**. Canvas Kit
10
- [`FormField`](?path=/docs/components-inputs-form-field--docs#accessibility) owns the first three
11
- when you use its subcomponents. The application owns when validation runs and where focus goes after
12
- submit.
13
-
14
- 1. **Identify every field, including required ones** Set `autoComplete` with a
15
- [standard token](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill)
16
- when the field collects a known personal data type (`username`, `current-password`, `email`,
17
- `name`). That supports
18
- [WCAG 1.3.5 Identify Input Purpose](https://www.w3.org/WAI/WCAG22/Understanding/identify-input-purpose.html).
19
- 2. **Move focus to the first invalid field after submit** Failed submit must not leave focus on the
20
- submit button. Keyboard and screen-reader users need to land on the first field that is still
21
- invalid, in **DOM order**.
22
- 3. **Choose when to validate** Prefer validating **on submit**, then on **blur / touched** for
23
- fields the user has already visited (`mode: 'onTouched'` in this example). Validating on every
24
- keystroke while the field is still incomplete is noisy and can announce `aria-invalid` too early.
25
- (Relevant criteria:
26
- [3.3.1 Error Identification](https://www.w3.org/WAI/WCAG22/Understanding/error-identification.html),
27
- [3.3.3 Error Suggestion](https://www.w3.org/WAI/WCAG22/Understanding/error-suggestion.html).)
28
- 4. **Do not use a live region for every field error** Field errors belong on the field
29
- (`aria-invalid` + `aria-describedby`). They are announced when the field **receives focus**. A
30
- page-level live region that restates every invalid field on submit is easy to overdo. Live
31
- announcements cannot be replayed, can interrupt, and have uneven support. If you add a form-level
32
- summary, keep it short, do not duplicate every message, and read
33
- [ARIA Live Regions](?path=/docs/guides-accessibility-aria-live-regions--docs) first.
34
-
35
- This example wires `FormField`, `TextInput`, `Select`, and `InputGroup` to
36
- [React Hook Form](https://www.react-hook-form.com/) with [Yup](https://github.com/jquense/yup). The
37
- same practices apply if you use Formik, a server round-trip, or hand-rolled state.
5
+ ## Uncontrolled Text Inputs Using React Hook Form
38
6
 
39
7
  <ExampleCodeBlock code={TextInputWithReactHookForm} />
@@ -1,18 +1,13 @@
1
1
  import React from 'react';
2
- import {FieldErrors, Resolver, useForm} from 'react-hook-form';
3
- import {SchemaOf, ValidationError, object, string} from 'yup';
2
+ import {FieldErrorsImpl, useForm} from 'react-hook-form';
3
+ import {SchemaOf, object, string} from 'yup';
4
4
 
5
5
  import {PrimaryButton, TertiaryButton} from '@workday/canvas-kit-react/button';
6
- import {ErrorType, useUniqueId} from '@workday/canvas-kit-react/common';
7
- import {
8
- FormField,
9
- useFormFieldInput,
10
- useFormFieldModel,
11
- } from '@workday/canvas-kit-react/form-field';
6
+ import {useUniqueId} from '@workday/canvas-kit-react/common';
7
+ import {FormField} from '@workday/canvas-kit-react/form-field';
12
8
  import {Flex} from '@workday/canvas-kit-react/layout';
13
9
  import {Select} from '@workday/canvas-kit-react/select';
14
- import {InputGroup, TextInput} from '@workday/canvas-kit-react/text-input';
15
- import {Tooltip} from '@workday/canvas-kit-react/tooltip';
10
+ import {TextInput} from '@workday/canvas-kit-react/text-input';
16
11
  import {createStyles, px2rem} from '@workday/canvas-kit-styling';
17
12
  import {visibleIcon, visibleStrikethroughIcon} from '@workday/canvas-system-icons-web';
18
13
  import {system} from '@workday/canvas-tokens-web';
@@ -23,30 +18,31 @@ const styles = createStyles({
23
18
  alignItems: 'flex-start',
24
19
  });
25
20
 
26
- const useYupValidationResolver = <T extends {}>(validationSchema: SchemaOf<T>): Resolver<T> => {
27
- return React.useCallback<Resolver<T>>(
21
+ type YupValidationResolver = <T extends {}>(
22
+ validationSchema: SchemaOf<T>
23
+ ) => (data: T) => Promise<{values: T; errors: {}} | {values: {}; errors: FieldErrorsImpl<T>}>;
24
+
25
+ const useYupValidationResolver: YupValidationResolver = validationSchema => {
26
+ return React.useCallback(
28
27
  async data => {
29
28
  try {
30
- await validationSchema.validate(data, {abortEarly: false});
31
- return {values: data, errors: {}};
32
- } catch (error) {
33
- // Yup throws a `ValidationError` for failed validation. Anything else is a real error.
34
- if (!ValidationError.isError(error)) {
35
- throw error;
36
- }
37
-
38
- const errors = error.inner.reduce<Record<string, {type: string; message: string}>>(
39
- (allErrors, currentError) => ({
40
- ...allErrors,
41
- [currentError.path ?? '']: {
42
- type: currentError.type ?? 'validation',
43
- message: currentError.message,
44
- },
45
- }),
46
- {}
47
- );
48
-
49
- return {values: {}, errors: errors as FieldErrors<T>};
29
+ const values = await validationSchema.validate(data, {abortEarly: false});
30
+ return {values, errors: {}};
31
+ } catch (errors) {
32
+ return {
33
+ values: {},
34
+ //@ts-ignore
35
+ errors: errors.inner.reduce(
36
+ (allErrors, currentError) => ({
37
+ ...allErrors,
38
+ [currentError.path]: {
39
+ type: currentError.type ?? 'validation',
40
+ message: currentError.message,
41
+ },
42
+ }),
43
+ {}
44
+ ),
45
+ };
50
46
  }
51
47
  },
52
48
  [validationSchema]
@@ -78,9 +74,6 @@ const options = [
78
74
  ];
79
75
 
80
76
  export default () => {
81
- const formRef = React.useRef<HTMLFormElement>(null);
82
- const [showPassword, setShowPassword] = React.useState(false);
83
- const [submitAttempt, setSubmitAttempt] = React.useState(0);
84
77
  const {
85
78
  handleSubmit,
86
79
  register,
@@ -91,36 +84,19 @@ export default () => {
91
84
  password: 'foobarbaz',
92
85
  role: '',
93
86
  },
94
- resolver: useYupValidationResolver<LoginSchema>(validationSchema),
87
+ resolver: useYupValidationResolver(validationSchema),
95
88
  mode: 'onTouched',
96
- shouldFocusError: true,
97
89
  });
98
90
 
99
- const onSubmit = handleSubmit(
100
- values => {
101
- setShowPassword(false);
102
- // Send data to server
103
- setTimeout(() => {
104
- alert(JSON.stringify(values, null, 2));
105
- }, 0);
106
- },
107
- () => {
108
- // Count failed submits so we can move focus after FormField paints `aria-invalid`.
109
- setSubmitAttempt(count => count + 1);
110
- }
111
- );
112
-
113
- React.useEffect(() => {
114
- if (submitAttempt === 0) {
115
- return;
116
- }
117
-
118
- const firstInvalidField = formRef.current?.querySelector<HTMLElement>(
119
- '[aria-invalid="true"]:not([aria-hidden="true"]):not([tabindex="-1"])'
120
- );
121
- firstInvalidField?.focus();
122
- }, [submitAttempt, errors]);
91
+ const onSubmit = handleSubmit(values => {
92
+ setShowPassword(false);
93
+ // Send data to server
94
+ setTimeout(() => {
95
+ alert(JSON.stringify(values, null, 2));
96
+ }, 0);
97
+ });
123
98
 
99
+ const [showPassword, setShowPassword] = React.useState(false);
124
100
  const passwordId = useUniqueId();
125
101
  const passwordRef = React.useRef<HTMLInputElement | null>(null);
126
102
  const {ref: passwordCallbackRef, ...passwordRegistration} = register('password');
@@ -128,15 +104,8 @@ export default () => {
128
104
  passwordCallbackRef(ref);
129
105
  passwordRef.current = ref;
130
106
  };
131
- // `InputGroup` renders the input, so hoist the model to keep the label, hint, and error wiring.
132
- const passwordModel = useFormFieldModel({
133
- id: passwordId,
134
- isRequired: true,
135
- error: errors.password ? 'error' : undefined,
136
- });
137
- const passwordInputProps = useFormFieldInput(passwordModel);
138
107
  return (
139
- <form ref={formRef} onSubmit={onSubmit} action="." noValidate={true}>
108
+ <form onSubmit={onSubmit} action="." noValidate={true}>
140
109
  <Flex cs={styles}>
141
110
  <FormField
142
111
  orientation="vertical"
@@ -174,33 +143,35 @@ export default () => {
174
143
  </FormField.Field>
175
144
  <FormField.Hint>{errors.email?.message}</FormField.Hint>
176
145
  </FormField>
177
- <FormField model={passwordModel} orientation="vertical">
146
+ <FormField
147
+ orientation="vertical"
148
+ id={passwordId}
149
+ isRequired={true}
150
+ error={!!errors.password ? 'error' : undefined}
151
+ >
178
152
  <FormField.Label>Password</FormField.Label>
179
- <FormField.Field as={InputGroup}>
180
- <InputGroup.Input
181
- {...passwordInputProps}
182
- {...passwordRegistration}
183
- error={errors.password ? ErrorType.Error : undefined}
184
- type={showPassword ? 'text' : 'password'}
185
- autoComplete="current-password"
186
- spellCheck={false}
187
- ref={combinePasswordRef}
153
+ <Flex cs={{gap: system.gap.md}}>
154
+ <FormField.Field>
155
+ <FormField.Input
156
+ as={TextInput}
157
+ {...passwordRegistration}
158
+ type={showPassword ? 'text' : 'password'}
159
+ autoComplete="current-password"
160
+ spellCheck={false}
161
+ ref={combinePasswordRef}
162
+ />
163
+ </FormField.Field>
164
+ <TertiaryButton
165
+ type="button"
166
+ icon={showPassword ? visibleStrikethroughIcon : visibleIcon}
167
+ aria-label={showPassword ? 'Hide Password' : 'Show Password'}
168
+ aria-controls={`input-${passwordId}`}
169
+ onClick={() => {
170
+ setShowPassword(state => !state);
171
+ passwordRef.current?.focus();
172
+ }}
188
173
  />
189
- <InputGroup.InnerEnd>
190
- <Tooltip title={showPassword ? 'Hide Password' : 'Show Password'}>
191
- <TertiaryButton
192
- type="button"
193
- size="small"
194
- icon={showPassword ? visibleStrikethroughIcon : visibleIcon}
195
- aria-controls={`input-${passwordId}`}
196
- onClick={() => {
197
- setShowPassword(state => !state);
198
- passwordRef.current?.focus();
199
- }}
200
- />
201
- </Tooltip>
202
- </InputGroup.InnerEnd>
203
- </FormField.Field>
174
+ </Flex>
204
175
  <FormField.Hint>{errors.password?.message || passwordHint}</FormField.Hint>
205
176
  </FormField>
206
177
 
@@ -4,24 +4,8 @@ import Density from './examples/Density';
4
4
 
5
5
  # Canvas Kit Density Examples
6
6
 
7
- Canvas Kit does not ship a density system. Forms often still need compact, default, and
8
- comfortable spacing. This example shows how to style Canvas Kit form elements with
9
- `createStencil` to get three densities.
10
-
11
- Density in this demo changes:
12
-
13
- - **Input height and padding** (`system.size.sm` / `md` / `md + 4px`)
14
- - **Label and input type** (`subtext.medium` / `subtext.large` / `body.small`)
15
- - **Space between fields** (`system.gap.md` / `lg` / `xl`)
16
- - **Space between a label and its input** (tighter when labels are vertical)
17
-
18
- | Density | Input height | Field spacing | Type |
19
- | ------- | ------------ | ------------- | ---- |
20
- | High | 32px | 16px | `subtext.medium` |
21
- | Medium | 40px | 24px | `subtext.large` |
22
- | Low | 48px | 32px | `body.small` |
23
-
24
- `Switch` does not expose a size API, so its dimensions stay the same across densities. Only its
25
- label type and surrounding field spacing change.
7
+ Although we currently don't support a density system, it's common practice to provide varying
8
+ options to users, especially in the context of forms. Below is an example of how you can style our
9
+ from elements to achieve the desired density.
26
10
 
27
11
  <ExampleCodeBlock code={Density} />
@@ -0,0 +1,9 @@
1
+ import {ExampleCodeBlock} from '@workday/canvas-kit-docs';
2
+ import SelectWithFormik from './examples/SelectWithFormik';
3
+
4
+
5
+ # Canvas Kit Examples
6
+
7
+ ## Select Using Formik
8
+
9
+ <ExampleCodeBlock code={SelectWithFormik} />