@okta/odyssey-react-mui 1.11.0 → 1.12.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.
- package/CHANGELOG.md +12 -0
- package/dist/@types/react-augment.d.js.map +1 -1
- package/dist/Autocomplete.js +5 -3
- package/dist/Autocomplete.js.map +1 -1
- package/dist/Button.js +5 -6
- package/dist/Button.js.map +1 -1
- package/dist/Checkbox.js +8 -7
- package/dist/Checkbox.js.map +1 -1
- package/dist/CheckboxGroup.js +2 -0
- package/dist/CheckboxGroup.js.map +1 -1
- package/dist/Field.js +3 -2
- package/dist/Field.js.map +1 -1
- package/dist/FieldComponentProps.js.map +1 -1
- package/dist/Link.js +5 -6
- package/dist/Link.js.map +1 -1
- package/dist/NativeSelect.js +18 -4
- package/dist/NativeSelect.js.map +1 -1
- package/dist/PasswordField.js +8 -7
- package/dist/PasswordField.js.map +1 -1
- package/dist/Radio.js +8 -7
- package/dist/Radio.js.map +1 -1
- package/dist/RadioGroup.js +2 -0
- package/dist/RadioGroup.js.map +1 -1
- package/dist/SearchField.js +2 -0
- package/dist/SearchField.js.map +1 -1
- package/dist/Select.js +10 -7
- package/dist/Select.js.map +1 -1
- package/dist/TextField.js +8 -7
- package/dist/TextField.js.map +1 -1
- package/dist/Typography.js +5 -6
- package/dist/Typography.js.map +1 -1
- package/dist/inputUtils.js.map +1 -1
- package/dist/labs/Switch.js +3 -3
- package/dist/labs/Switch.js.map +1 -1
- package/dist/labs/VirtualizedAutocomplete.js +5 -3
- package/dist/labs/VirtualizedAutocomplete.js.map +1 -1
- package/dist/src/Autocomplete.d.ts +2 -2
- package/dist/src/Autocomplete.d.ts.map +1 -1
- package/dist/src/Button.d.ts +4 -4
- package/dist/src/Button.d.ts.map +1 -1
- package/dist/src/Checkbox.d.ts +4 -4
- package/dist/src/Checkbox.d.ts.map +1 -1
- package/dist/src/CheckboxGroup.d.ts +2 -2
- package/dist/src/CheckboxGroup.d.ts.map +1 -1
- package/dist/src/Field.d.ts +1 -1
- package/dist/src/Field.d.ts.map +1 -1
- package/dist/src/FieldComponentProps.d.ts +4 -0
- package/dist/src/FieldComponentProps.d.ts.map +1 -1
- package/dist/src/Link.d.ts +4 -4
- package/dist/src/Link.d.ts.map +1 -1
- package/dist/src/NativeSelect.d.ts +13 -2
- package/dist/src/NativeSelect.d.ts.map +1 -1
- package/dist/src/PasswordField.d.ts +5 -5
- package/dist/src/PasswordField.d.ts.map +1 -1
- package/dist/src/Radio.d.ts +4 -4
- package/dist/src/Radio.d.ts.map +1 -1
- package/dist/src/RadioGroup.d.ts +2 -2
- package/dist/src/RadioGroup.d.ts.map +1 -1
- package/dist/src/SearchField.d.ts +2 -2
- package/dist/src/SearchField.d.ts.map +1 -1
- package/dist/src/Select.d.ts +5 -5
- package/dist/src/Select.d.ts.map +1 -1
- package/dist/src/TextField.d.ts +5 -5
- package/dist/src/TextField.d.ts.map +1 -1
- package/dist/src/Typography.d.ts +4 -4
- package/dist/src/Typography.d.ts.map +1 -1
- package/dist/src/inputUtils.d.ts +3 -0
- package/dist/src/inputUtils.d.ts.map +1 -1
- package/dist/src/labs/Switch.d.ts.map +1 -1
- package/dist/src/labs/VirtualizedAutocomplete.d.ts +2 -2
- package/dist/src/labs/VirtualizedAutocomplete.d.ts.map +1 -1
- package/dist/tsconfig.production.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/@types/react-augment.d.ts +8 -4
- package/src/Autocomplete.tsx +6 -1
- package/src/Button.tsx +8 -9
- package/src/Checkbox.tsx +15 -11
- package/src/CheckboxGroup.tsx +3 -0
- package/src/Field.tsx +8 -4
- package/src/FieldComponentProps.ts +4 -0
- package/src/Link.tsx +8 -9
- package/src/NativeSelect.tsx +36 -2
- package/src/PasswordField.tsx +11 -11
- package/src/Radio.tsx +16 -10
- package/src/RadioGroup.tsx +3 -0
- package/src/SearchField.tsx +6 -1
- package/src/Select.tsx +12 -10
- package/src/TextField.tsx +11 -11
- package/src/Typography.tsx +8 -9
- package/src/inputUtils.ts +4 -0
- package/src/labs/Switch.tsx +2 -1
- package/src/labs/VirtualizedAutocomplete.tsx +20 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@okta/odyssey-react-mui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "React MUI components for Odyssey, Okta's design system",
|
|
5
5
|
"author": "Okta, Inc.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@mui/system": "^5.14.9",
|
|
52
52
|
"@mui/utils": "^5.11.2",
|
|
53
53
|
"@mui/x-date-pickers": "^5.0.15",
|
|
54
|
-
"@okta/odyssey-design-tokens": "1.
|
|
54
|
+
"@okta/odyssey-design-tokens": "1.12.0",
|
|
55
55
|
"date-fns": "^2.30.0",
|
|
56
56
|
"i18next": "^23.5.1",
|
|
57
57
|
"material-react-table": "^2.0.2",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"react": ">=17 <19",
|
|
64
64
|
"react-dom": ">=17 <19"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "1bffe33f2c5946144232f60f983bfe76b6339453"
|
|
67
67
|
}
|
|
@@ -11,13 +11,17 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { FC } from "react";
|
|
14
|
-
|
|
15
14
|
export interface ForwardRefWithType extends FC<WithForwardRefProps<Option>> {
|
|
16
15
|
<T extends Option>(props: WithForwardRefProps<T>): ReturnType<
|
|
17
16
|
FC<WithForwardRefProps<T>>
|
|
18
17
|
>;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
declare module "react" {
|
|
21
|
+
type DataAttributeKey = `data-${string}`;
|
|
22
|
+
interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
|
|
23
|
+
// Allows data-* props to be passed to inputProps in nested MUI components
|
|
24
|
+
// see: https://github.com/mui/material-ui/issues/20160
|
|
25
|
+
[dataAttribute: DataAttributeKey]: string | undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
package/src/Autocomplete.tsx
CHANGED
|
@@ -161,6 +161,7 @@ export type AutocompleteProps<
|
|
|
161
161
|
getIsOptionEqualToValue?: (option: OptionType, value: OptionType) => boolean;
|
|
162
162
|
} & Pick<
|
|
163
163
|
FieldComponentProps,
|
|
164
|
+
| "ariaDescribedBy"
|
|
164
165
|
| "errorMessage"
|
|
165
166
|
| "errorMessageList"
|
|
166
167
|
| "hint"
|
|
@@ -177,6 +178,7 @@ const Autocomplete = <
|
|
|
177
178
|
HasMultipleChoices extends boolean | undefined,
|
|
178
179
|
IsCustomValueAllowed extends boolean | undefined
|
|
179
180
|
>({
|
|
181
|
+
ariaDescribedBy,
|
|
180
182
|
defaultValue,
|
|
181
183
|
errorMessage,
|
|
182
184
|
errorMessageList,
|
|
@@ -245,6 +247,7 @@ const Autocomplete = <
|
|
|
245
247
|
const renderInput = useCallback(
|
|
246
248
|
({ InputLabelProps, InputProps, ...params }) => (
|
|
247
249
|
<Field
|
|
250
|
+
ariaDescribedBy={ariaDescribedBy}
|
|
248
251
|
errorMessage={errorMessage}
|
|
249
252
|
errorMessageList={errorMessageList}
|
|
250
253
|
fieldType="single"
|
|
@@ -267,6 +270,7 @@ const Autocomplete = <
|
|
|
267
270
|
...params.inputProps,
|
|
268
271
|
"aria-errormessage": errorMessageElementId,
|
|
269
272
|
"aria-labelledby": labelElementId,
|
|
273
|
+
"data-se": testId,
|
|
270
274
|
}}
|
|
271
275
|
aria-describedby={ariaDescribedBy}
|
|
272
276
|
id={id}
|
|
@@ -277,6 +281,7 @@ const Autocomplete = <
|
|
|
277
281
|
/>
|
|
278
282
|
),
|
|
279
283
|
[
|
|
284
|
+
ariaDescribedBy,
|
|
280
285
|
errorMessage,
|
|
281
286
|
errorMessageList,
|
|
282
287
|
hint,
|
|
@@ -284,6 +289,7 @@ const Autocomplete = <
|
|
|
284
289
|
isOptional,
|
|
285
290
|
label,
|
|
286
291
|
nameOverride,
|
|
292
|
+
testId,
|
|
287
293
|
]
|
|
288
294
|
);
|
|
289
295
|
const onChange = useCallback<
|
|
@@ -324,7 +330,6 @@ const Autocomplete = <
|
|
|
324
330
|
{...inputValueProp}
|
|
325
331
|
// AutoComplete is wrapped in a div within MUI which does not get the disabled attr. So this aria-disabled gets set in the div
|
|
326
332
|
aria-disabled={isDisabled}
|
|
327
|
-
data-se={testId}
|
|
328
333
|
disableCloseOnSelect={hasMultipleChoices}
|
|
329
334
|
disabled={isDisabled}
|
|
330
335
|
freeSolo={isCustomValueAllowed}
|
package/src/Button.tsx
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
import { MuiPropsContext, useMuiProps } from "./MuiPropsContext";
|
|
24
24
|
import { Tooltip } from "./Tooltip";
|
|
25
25
|
import type { AllowedProps } from "./AllowedProps";
|
|
26
|
-
import { FocusHandle } from "
|
|
26
|
+
import { FocusHandle } from "./inputUtils";
|
|
27
27
|
|
|
28
28
|
export const buttonSizeValues = ["small", "medium", "large"] as const;
|
|
29
29
|
export const buttonTypeValues = ["button", "submit", "reset"] as const;
|
|
@@ -49,9 +49,9 @@ export type ButtonProps = {
|
|
|
49
49
|
*/
|
|
50
50
|
ariaDescribedBy?: string;
|
|
51
51
|
/**
|
|
52
|
-
* The ref forwarded to the Button
|
|
52
|
+
* The ref forwarded to the Button
|
|
53
53
|
*/
|
|
54
|
-
|
|
54
|
+
buttonRef?: React.RefObject<FocusHandle>;
|
|
55
55
|
/**
|
|
56
56
|
* The icon element to display at the end of the Button
|
|
57
57
|
*/
|
|
@@ -119,7 +119,7 @@ const Button = ({
|
|
|
119
119
|
ariaDescribedBy,
|
|
120
120
|
ariaLabel,
|
|
121
121
|
ariaLabelledBy,
|
|
122
|
-
|
|
122
|
+
buttonRef,
|
|
123
123
|
endIcon,
|
|
124
124
|
id,
|
|
125
125
|
isDisabled,
|
|
@@ -136,14 +136,13 @@ const Button = ({
|
|
|
136
136
|
}: ButtonProps) => {
|
|
137
137
|
const muiProps = useMuiProps();
|
|
138
138
|
|
|
139
|
-
const
|
|
139
|
+
const localButtonRef = useRef<HTMLButtonElement>(null);
|
|
140
140
|
useImperativeHandle(
|
|
141
|
-
|
|
141
|
+
buttonRef,
|
|
142
142
|
() => {
|
|
143
|
-
const element = ref.current;
|
|
144
143
|
return {
|
|
145
144
|
focus: () => {
|
|
146
|
-
|
|
145
|
+
localButtonRef.current?.focus();
|
|
147
146
|
},
|
|
148
147
|
};
|
|
149
148
|
},
|
|
@@ -163,7 +162,7 @@ const Button = ({
|
|
|
163
162
|
fullWidth={isFullWidth}
|
|
164
163
|
id={id}
|
|
165
164
|
onClick={onClick}
|
|
166
|
-
ref={
|
|
165
|
+
ref={localButtonRef}
|
|
167
166
|
size={size}
|
|
168
167
|
startIcon={startIcon}
|
|
169
168
|
translate={translate}
|
package/src/Checkbox.tsx
CHANGED
|
@@ -23,9 +23,12 @@ import {
|
|
|
23
23
|
import { FieldComponentProps } from "./FieldComponentProps";
|
|
24
24
|
import { Typography } from "./Typography";
|
|
25
25
|
import type { AllowedProps } from "./AllowedProps";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
ComponentControlledState,
|
|
28
|
+
FocusHandle,
|
|
29
|
+
getControlState,
|
|
30
|
+
} from "./inputUtils";
|
|
27
31
|
import { CheckedFieldProps } from "./FormCheckedProps";
|
|
28
|
-
import { FocusHandle } from "./@types/react-augment";
|
|
29
32
|
|
|
30
33
|
export const checkboxValidityValues = ["valid", "invalid", "inherit"] as const;
|
|
31
34
|
|
|
@@ -43,9 +46,9 @@ export type CheckboxProps = {
|
|
|
43
46
|
*/
|
|
44
47
|
id?: string;
|
|
45
48
|
/**
|
|
46
|
-
* The ref forwarded to the Checkbox
|
|
49
|
+
* The ref forwarded to the Checkbox
|
|
47
50
|
*/
|
|
48
|
-
|
|
51
|
+
inputRef?: React.RefObject<FocusHandle>;
|
|
49
52
|
/**
|
|
50
53
|
* Determines whether the Checkbox is disabled
|
|
51
54
|
*/
|
|
@@ -86,7 +89,7 @@ const Checkbox = ({
|
|
|
86
89
|
ariaLabel,
|
|
87
90
|
ariaLabelledBy,
|
|
88
91
|
id: idOverride,
|
|
89
|
-
|
|
92
|
+
inputRef,
|
|
90
93
|
isChecked,
|
|
91
94
|
isDefaultChecked,
|
|
92
95
|
isDisabled,
|
|
@@ -116,14 +119,13 @@ const Checkbox = ({
|
|
|
116
119
|
return { defaultChecked: isDefaultChecked };
|
|
117
120
|
}, [isDefaultChecked, isChecked]);
|
|
118
121
|
|
|
119
|
-
const
|
|
122
|
+
const localInputRef = useRef<HTMLInputElement>(null);
|
|
120
123
|
useImperativeHandle(
|
|
121
|
-
|
|
124
|
+
inputRef,
|
|
122
125
|
() => {
|
|
123
|
-
const element = inputRef.current;
|
|
124
126
|
return {
|
|
125
127
|
focus: () => {
|
|
126
|
-
|
|
128
|
+
localInputRef.current?.focus();
|
|
127
129
|
},
|
|
128
130
|
};
|
|
129
131
|
},
|
|
@@ -179,13 +181,15 @@ const Checkbox = ({
|
|
|
179
181
|
indeterminate={isIndeterminate}
|
|
180
182
|
onChange={onChange}
|
|
181
183
|
required={isRequired}
|
|
182
|
-
|
|
184
|
+
inputProps={{
|
|
185
|
+
"data-se": testId,
|
|
186
|
+
}}
|
|
187
|
+
inputRef={localInputRef}
|
|
183
188
|
sx={() => ({
|
|
184
189
|
marginBlockStart: "2px",
|
|
185
190
|
})}
|
|
186
191
|
/>
|
|
187
192
|
}
|
|
188
|
-
data-se={testId}
|
|
189
193
|
disabled={isDisabled}
|
|
190
194
|
id={idOverride}
|
|
191
195
|
label={label}
|
package/src/CheckboxGroup.tsx
CHANGED
|
@@ -35,6 +35,7 @@ export type CheckboxGroupProps = {
|
|
|
35
35
|
label: string;
|
|
36
36
|
} & Pick<
|
|
37
37
|
FieldComponentProps,
|
|
38
|
+
| "ariaDescribedBy"
|
|
38
39
|
| "errorMessage"
|
|
39
40
|
| "errorMessageList"
|
|
40
41
|
| "hint"
|
|
@@ -45,6 +46,7 @@ export type CheckboxGroupProps = {
|
|
|
45
46
|
AllowedProps;
|
|
46
47
|
|
|
47
48
|
const CheckboxGroup = ({
|
|
49
|
+
ariaDescribedBy,
|
|
48
50
|
children,
|
|
49
51
|
errorMessage,
|
|
50
52
|
errorMessageList,
|
|
@@ -75,6 +77,7 @@ const CheckboxGroup = ({
|
|
|
75
77
|
|
|
76
78
|
return (
|
|
77
79
|
<Field
|
|
80
|
+
ariaDescribedBy={ariaDescribedBy}
|
|
78
81
|
errorMessage={errorMessage}
|
|
79
82
|
errorMessageList={errorMessageList}
|
|
80
83
|
fieldType="group"
|
package/src/Field.tsx
CHANGED
|
@@ -80,6 +80,7 @@ export type FieldProps = {
|
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
const Field = ({
|
|
83
|
+
ariaDescribedBy,
|
|
83
84
|
errorMessage,
|
|
84
85
|
errorMessageList,
|
|
85
86
|
fieldType,
|
|
@@ -96,6 +97,7 @@ const Field = ({
|
|
|
96
97
|
}: FieldProps &
|
|
97
98
|
Pick<
|
|
98
99
|
FieldComponentProps,
|
|
100
|
+
| "ariaDescribedBy"
|
|
99
101
|
| "errorMessage"
|
|
100
102
|
| "errorMessageList"
|
|
101
103
|
| "hint"
|
|
@@ -113,9 +115,11 @@ const Field = ({
|
|
|
113
115
|
errorMessage || errorMessageList ? `${id}-error` : undefined;
|
|
114
116
|
const labelElementId = `${id}-label`;
|
|
115
117
|
|
|
116
|
-
const
|
|
117
|
-
() =>
|
|
118
|
-
|
|
118
|
+
const localAriaDescribedBy = useMemo(
|
|
119
|
+
() =>
|
|
120
|
+
[hintId, errorMessageElementId, ariaDescribedBy].join(" ").trim() ||
|
|
121
|
+
undefined,
|
|
122
|
+
[ariaDescribedBy, errorMessageElementId, hintId]
|
|
119
123
|
);
|
|
120
124
|
|
|
121
125
|
const { isDisabled: isFieldsetDisabled } = useFieldset();
|
|
@@ -160,7 +164,7 @@ const Field = ({
|
|
|
160
164
|
)}
|
|
161
165
|
|
|
162
166
|
{renderFieldComponent({
|
|
163
|
-
ariaDescribedBy,
|
|
167
|
+
ariaDescribedBy: localAriaDescribedBy,
|
|
164
168
|
errorMessageElementId,
|
|
165
169
|
id,
|
|
166
170
|
labelElementId,
|
|
@@ -15,6 +15,10 @@ import { ReactElement } from "react";
|
|
|
15
15
|
import { HintLink } from "./HintLink";
|
|
16
16
|
|
|
17
17
|
export type FieldComponentProps = {
|
|
18
|
+
/**
|
|
19
|
+
* The ID of the element that describes the Field
|
|
20
|
+
*/
|
|
21
|
+
ariaDescribedBy?: string;
|
|
18
22
|
/**
|
|
19
23
|
* If `error` is not undefined, the `input` will indicate an error.
|
|
20
24
|
*/
|
package/src/Link.tsx
CHANGED
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
import { memo, ReactElement, useImperativeHandle, useRef } from "react";
|
|
14
14
|
import { ExternalLinkIcon } from "./icons.generated";
|
|
15
15
|
import type { AllowedProps } from "./AllowedProps";
|
|
16
|
+
import { FocusHandle } from "./inputUtils";
|
|
16
17
|
|
|
17
18
|
import { Link as MuiLink, LinkProps as MuiLinkProps } from "@mui/material";
|
|
18
|
-
import { FocusHandle } from "./@types/react-augment";
|
|
19
19
|
|
|
20
20
|
export const linkVariantValues = ["default", "monochrome"] as const;
|
|
21
21
|
|
|
@@ -33,9 +33,9 @@ export type LinkProps = {
|
|
|
33
33
|
*/
|
|
34
34
|
icon?: ReactElement;
|
|
35
35
|
/**
|
|
36
|
-
* The ref forwarded to the TextField
|
|
36
|
+
* The ref forwarded to the TextField
|
|
37
37
|
*/
|
|
38
|
-
|
|
38
|
+
linkRef?: React.RefObject<FocusHandle>;
|
|
39
39
|
/**
|
|
40
40
|
* The click event handler for the Link
|
|
41
41
|
*/
|
|
@@ -63,7 +63,7 @@ const Link = ({
|
|
|
63
63
|
children,
|
|
64
64
|
href,
|
|
65
65
|
icon,
|
|
66
|
-
|
|
66
|
+
linkRef,
|
|
67
67
|
rel,
|
|
68
68
|
target,
|
|
69
69
|
testId,
|
|
@@ -71,14 +71,13 @@ const Link = ({
|
|
|
71
71
|
variant,
|
|
72
72
|
onClick,
|
|
73
73
|
}: LinkProps) => {
|
|
74
|
-
const
|
|
74
|
+
const localLinkRef = useRef<HTMLAnchorElement>(null);
|
|
75
75
|
useImperativeHandle(
|
|
76
|
-
|
|
76
|
+
linkRef,
|
|
77
77
|
() => {
|
|
78
|
-
const element = ref.current;
|
|
79
78
|
return {
|
|
80
79
|
focus: () => {
|
|
81
|
-
|
|
80
|
+
localLinkRef.current?.focus();
|
|
82
81
|
},
|
|
83
82
|
};
|
|
84
83
|
},
|
|
@@ -89,7 +88,7 @@ const Link = ({
|
|
|
89
88
|
<MuiLink
|
|
90
89
|
data-se={testId}
|
|
91
90
|
href={href}
|
|
92
|
-
ref={
|
|
91
|
+
ref={localLinkRef}
|
|
93
92
|
rel={rel}
|
|
94
93
|
target={target}
|
|
95
94
|
translate={translate}
|
package/src/NativeSelect.tsx
CHANGED
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import React, {
|
|
14
|
+
InputHTMLAttributes,
|
|
14
15
|
ReactElement,
|
|
15
16
|
forwardRef,
|
|
16
17
|
memo,
|
|
17
18
|
useCallback,
|
|
19
|
+
useImperativeHandle,
|
|
18
20
|
useMemo,
|
|
19
21
|
useRef,
|
|
20
22
|
} from "react";
|
|
@@ -25,7 +27,7 @@ import {
|
|
|
25
27
|
import { Field } from "./Field";
|
|
26
28
|
import { FieldComponentProps } from "./FieldComponentProps";
|
|
27
29
|
import type { AllowedProps } from "./AllowedProps";
|
|
28
|
-
import { getControlState, useInputValues } from "./inputUtils";
|
|
30
|
+
import { FocusHandle, getControlState, useInputValues } from "./inputUtils";
|
|
29
31
|
import { ForwardRefWithType } from "./@types/react-augment";
|
|
30
32
|
|
|
31
33
|
export type NativeSelectOption = {
|
|
@@ -41,6 +43,12 @@ export type NativeSelectProps<
|
|
|
41
43
|
Value extends NativeSelectValueType<HasMultipleChoices>,
|
|
42
44
|
HasMultipleChoices extends boolean
|
|
43
45
|
> = {
|
|
46
|
+
/**
|
|
47
|
+
* This prop helps users to fill forms faster, especially on mobile devices.
|
|
48
|
+
* The name can be confusing, as it's more like an autofill.
|
|
49
|
+
* @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill
|
|
50
|
+
*/
|
|
51
|
+
autoCompleteType?: InputHTMLAttributes<HTMLInputElement>["autoComplete"];
|
|
44
52
|
/**
|
|
45
53
|
* The options or optgroup elements within the NativeSelect
|
|
46
54
|
*/
|
|
@@ -53,6 +61,10 @@ export type NativeSelectProps<
|
|
|
53
61
|
* If `true`, the Select allows multiple selections
|
|
54
62
|
*/
|
|
55
63
|
hasMultipleChoices?: HasMultipleChoices;
|
|
64
|
+
/**
|
|
65
|
+
* The ref forwarded to the NativeSelect
|
|
66
|
+
*/
|
|
67
|
+
inputRef?: React.RefObject<FocusHandle>;
|
|
56
68
|
/**
|
|
57
69
|
* @deprecated Use `hasMultipleChoices` instead
|
|
58
70
|
*/
|
|
@@ -81,6 +93,7 @@ export type NativeSelectProps<
|
|
|
81
93
|
value?: Value;
|
|
82
94
|
} & Pick<
|
|
83
95
|
FieldComponentProps,
|
|
96
|
+
| "ariaDescribedBy"
|
|
84
97
|
| "errorMessage"
|
|
85
98
|
| "errorMessageList"
|
|
86
99
|
| "hint"
|
|
@@ -98,6 +111,8 @@ const NativeSelect: ForwardRefWithType = forwardRef(
|
|
|
98
111
|
HasMultipleChoices extends boolean
|
|
99
112
|
>(
|
|
100
113
|
{
|
|
114
|
+
ariaDescribedBy,
|
|
115
|
+
autoCompleteType,
|
|
101
116
|
defaultValue,
|
|
102
117
|
errorMessage,
|
|
103
118
|
errorMessageList,
|
|
@@ -105,6 +120,7 @@ const NativeSelect: ForwardRefWithType = forwardRef(
|
|
|
105
120
|
hint,
|
|
106
121
|
HintLinkComponent,
|
|
107
122
|
id: idOverride,
|
|
123
|
+
inputRef,
|
|
108
124
|
isDisabled = false,
|
|
109
125
|
isFullWidth = false,
|
|
110
126
|
isMultiSelect,
|
|
@@ -126,6 +142,20 @@ const NativeSelect: ForwardRefWithType = forwardRef(
|
|
|
126
142
|
uncontrolledValue: defaultValue,
|
|
127
143
|
})
|
|
128
144
|
);
|
|
145
|
+
const localInputRef = useRef<HTMLSelectElement>(null);
|
|
146
|
+
|
|
147
|
+
useImperativeHandle(
|
|
148
|
+
inputRef,
|
|
149
|
+
() => {
|
|
150
|
+
return {
|
|
151
|
+
focus: () => {
|
|
152
|
+
localInputRef.current?.focus();
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
[]
|
|
157
|
+
);
|
|
158
|
+
|
|
129
159
|
const inputValues = useInputValues({
|
|
130
160
|
defaultValue,
|
|
131
161
|
value,
|
|
@@ -153,13 +183,15 @@ const NativeSelect: ForwardRefWithType = forwardRef(
|
|
|
153
183
|
<MuiSelect
|
|
154
184
|
{...inputValues}
|
|
155
185
|
aria-describedby={ariaDescribedBy}
|
|
186
|
+
autoComplete={autoCompleteType}
|
|
156
187
|
children={children}
|
|
157
|
-
data-se={testId}
|
|
158
188
|
id={idOverride}
|
|
159
189
|
inputProps={{
|
|
160
190
|
"aria-errormessage": errorMessageElementId,
|
|
161
191
|
"aria-labelledby": labelElementId,
|
|
192
|
+
"data-se": testId,
|
|
162
193
|
}}
|
|
194
|
+
inputRef={localInputRef}
|
|
163
195
|
name={idOverride}
|
|
164
196
|
multiple={hasMultipleChoices}
|
|
165
197
|
native={true}
|
|
@@ -171,6 +203,7 @@ const NativeSelect: ForwardRefWithType = forwardRef(
|
|
|
171
203
|
/>
|
|
172
204
|
),
|
|
173
205
|
[
|
|
206
|
+
autoCompleteType,
|
|
174
207
|
children,
|
|
175
208
|
idOverride,
|
|
176
209
|
inputValues,
|
|
@@ -186,6 +219,7 @@ const NativeSelect: ForwardRefWithType = forwardRef(
|
|
|
186
219
|
|
|
187
220
|
return (
|
|
188
221
|
<Field
|
|
222
|
+
ariaDescribedBy={ariaDescribedBy}
|
|
189
223
|
errorMessage={errorMessage}
|
|
190
224
|
errorMessageList={errorMessageList}
|
|
191
225
|
fieldType="single"
|
package/src/PasswordField.tsx
CHANGED
|
@@ -27,8 +27,7 @@ import { Field } from "./Field";
|
|
|
27
27
|
import { FieldComponentProps } from "./FieldComponentProps";
|
|
28
28
|
import type { AllowedProps } from "./AllowedProps";
|
|
29
29
|
import { useTranslation } from "react-i18next";
|
|
30
|
-
import { getControlState, useInputValues } from "./inputUtils";
|
|
31
|
-
import { FocusHandle } from "./@types/react-augment";
|
|
30
|
+
import { FocusHandle, getControlState, useInputValues } from "./inputUtils";
|
|
32
31
|
|
|
33
32
|
export type PasswordFieldProps = {
|
|
34
33
|
/**
|
|
@@ -50,9 +49,9 @@ export type PasswordFieldProps = {
|
|
|
50
49
|
*/
|
|
51
50
|
hasShowPassword?: boolean;
|
|
52
51
|
/**
|
|
53
|
-
* The ref forwarded to the TextField
|
|
52
|
+
* The ref forwarded to the TextField
|
|
54
53
|
*/
|
|
55
|
-
|
|
54
|
+
inputRef?: React.RefObject<FocusHandle>;
|
|
56
55
|
/**
|
|
57
56
|
* The label for the `input` element.
|
|
58
57
|
*/
|
|
@@ -83,6 +82,7 @@ export type PasswordFieldProps = {
|
|
|
83
82
|
const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
84
83
|
(
|
|
85
84
|
{
|
|
85
|
+
ariaDescribedBy,
|
|
86
86
|
autoCompleteType,
|
|
87
87
|
defaultValue,
|
|
88
88
|
errorMessage,
|
|
@@ -90,7 +90,7 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
90
90
|
hasInitialFocus,
|
|
91
91
|
hint,
|
|
92
92
|
id: idOverride,
|
|
93
|
-
|
|
93
|
+
inputRef,
|
|
94
94
|
isDisabled = false,
|
|
95
95
|
isFullWidth = false,
|
|
96
96
|
isOptional = false,
|
|
@@ -129,14 +129,13 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
129
129
|
controlState: controlledStateRef.current,
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
-
const
|
|
132
|
+
const localInputRef = useRef<HTMLInputElement>(null);
|
|
133
133
|
useImperativeHandle(
|
|
134
|
-
|
|
134
|
+
inputRef,
|
|
135
135
|
() => {
|
|
136
|
-
const element = inputRef.current;
|
|
137
136
|
return {
|
|
138
137
|
focus: () => {
|
|
139
|
-
|
|
138
|
+
localInputRef.current?.focus();
|
|
140
139
|
},
|
|
141
140
|
};
|
|
142
141
|
},
|
|
@@ -160,7 +159,6 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
160
159
|
autoComplete={inputType === "password" ? autoCompleteType : "off"}
|
|
161
160
|
/* eslint-disable-next-line jsx-a11y/no-autofocus */
|
|
162
161
|
autoFocus={hasInitialFocus}
|
|
163
|
-
data-se={testId}
|
|
164
162
|
endAdornment={
|
|
165
163
|
hasShowPassword && (
|
|
166
164
|
<InputAdornment position="end">
|
|
@@ -183,10 +181,11 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
183
181
|
inputProps={{
|
|
184
182
|
"aria-errormessage": errorMessageElementId,
|
|
185
183
|
"aria-labelledby": labelElementId,
|
|
184
|
+
"data-se": testId,
|
|
186
185
|
// role: "textbox" Added because password inputs don't have an implicit role assigned. This causes problems with element selection.
|
|
187
186
|
role: "textbox",
|
|
188
187
|
}}
|
|
189
|
-
inputRef={
|
|
188
|
+
inputRef={localInputRef}
|
|
190
189
|
name={nameOverride ?? id}
|
|
191
190
|
onChange={onChange}
|
|
192
191
|
onFocus={onFocus}
|
|
@@ -222,6 +221,7 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
222
221
|
|
|
223
222
|
return (
|
|
224
223
|
<Field
|
|
224
|
+
ariaDescribedBy={ariaDescribedBy}
|
|
225
225
|
errorMessage={errorMessage}
|
|
226
226
|
errorMessageList={errorMessageList}
|
|
227
227
|
fieldType="single"
|
package/src/Radio.tsx
CHANGED
|
@@ -20,13 +20,13 @@ import { memo, useCallback, useRef, useImperativeHandle } from "react";
|
|
|
20
20
|
|
|
21
21
|
import { FieldComponentProps } from "./FieldComponentProps";
|
|
22
22
|
import type { AllowedProps } from "./AllowedProps";
|
|
23
|
-
import { FocusHandle } from "
|
|
23
|
+
import { FocusHandle } from "./inputUtils";
|
|
24
24
|
|
|
25
25
|
export type RadioProps = {
|
|
26
26
|
/**
|
|
27
|
-
* The ref forwarded to the Radio
|
|
27
|
+
* The ref forwarded to the Radio
|
|
28
28
|
*/
|
|
29
|
-
|
|
29
|
+
inputRef?: React.RefObject<FocusHandle>;
|
|
30
30
|
/**
|
|
31
31
|
* If `true`, the Radio is selected
|
|
32
32
|
*/
|
|
@@ -55,7 +55,7 @@ export type RadioProps = {
|
|
|
55
55
|
AllowedProps;
|
|
56
56
|
|
|
57
57
|
const Radio = ({
|
|
58
|
-
|
|
58
|
+
inputRef,
|
|
59
59
|
isChecked,
|
|
60
60
|
isDisabled,
|
|
61
61
|
isInvalid,
|
|
@@ -67,14 +67,13 @@ const Radio = ({
|
|
|
67
67
|
onChange: onChangeProp,
|
|
68
68
|
onBlur: onBlurProp,
|
|
69
69
|
}: RadioProps) => {
|
|
70
|
-
const
|
|
70
|
+
const localInputRef = useRef<HTMLInputElement>(null);
|
|
71
71
|
useImperativeHandle(
|
|
72
|
-
|
|
72
|
+
inputRef,
|
|
73
73
|
() => {
|
|
74
|
-
const element = ref.current;
|
|
75
74
|
return {
|
|
76
75
|
focus: () => {
|
|
77
|
-
|
|
76
|
+
localInputRef.current?.focus();
|
|
78
77
|
},
|
|
79
78
|
};
|
|
80
79
|
},
|
|
@@ -99,8 +98,15 @@ const Radio = ({
|
|
|
99
98
|
<FormControlLabel
|
|
100
99
|
checked={isChecked}
|
|
101
100
|
className={isInvalid ? "Mui-error" : ""}
|
|
102
|
-
control={
|
|
103
|
-
|
|
101
|
+
control={
|
|
102
|
+
<MuiRadio
|
|
103
|
+
inputProps={{
|
|
104
|
+
"data-se": testId,
|
|
105
|
+
}}
|
|
106
|
+
inputRef={localInputRef}
|
|
107
|
+
onChange={onChange}
|
|
108
|
+
/>
|
|
109
|
+
}
|
|
104
110
|
disabled={isDisabled}
|
|
105
111
|
label={label}
|
|
106
112
|
name={name}
|
package/src/RadioGroup.tsx
CHANGED
|
@@ -45,6 +45,7 @@ export type RadioGroupProps = {
|
|
|
45
45
|
value?: RadioProps["value"];
|
|
46
46
|
} & Pick<
|
|
47
47
|
FieldComponentProps,
|
|
48
|
+
| "ariaDescribedBy"
|
|
48
49
|
| "errorMessage"
|
|
49
50
|
| "errorMessageList"
|
|
50
51
|
| "hint"
|
|
@@ -56,6 +57,7 @@ export type RadioGroupProps = {
|
|
|
56
57
|
AllowedProps;
|
|
57
58
|
|
|
58
59
|
const RadioGroup = ({
|
|
60
|
+
ariaDescribedBy,
|
|
59
61
|
children,
|
|
60
62
|
defaultValue,
|
|
61
63
|
errorMessage,
|
|
@@ -107,6 +109,7 @@ const RadioGroup = ({
|
|
|
107
109
|
|
|
108
110
|
return (
|
|
109
111
|
<Field
|
|
112
|
+
ariaDescribedBy={ariaDescribedBy}
|
|
110
113
|
errorMessage={errorMessage}
|
|
111
114
|
errorMessageList={errorMessageList}
|
|
112
115
|
fieldType="group"
|
package/src/SearchField.tsx
CHANGED
|
@@ -78,12 +78,16 @@ export type SearchFieldProps = {
|
|
|
78
78
|
* The value of the `input` element, to use when controlled.
|
|
79
79
|
*/
|
|
80
80
|
value?: string;
|
|
81
|
-
} & Pick<
|
|
81
|
+
} & Pick<
|
|
82
|
+
FieldComponentProps,
|
|
83
|
+
"ariaDescribedBy" | "id" | "isDisabled" | "name" | "isFullWidth"
|
|
84
|
+
> &
|
|
82
85
|
AllowedProps;
|
|
83
86
|
|
|
84
87
|
const SearchField = forwardRef<HTMLInputElement, SearchFieldProps>(
|
|
85
88
|
(
|
|
86
89
|
{
|
|
90
|
+
ariaDescribedBy,
|
|
87
91
|
autoCompleteType,
|
|
88
92
|
defaultValue,
|
|
89
93
|
hasInitialFocus,
|
|
@@ -186,6 +190,7 @@ const SearchField = forwardRef<HTMLInputElement, SearchFieldProps>(
|
|
|
186
190
|
|
|
187
191
|
return (
|
|
188
192
|
<Field
|
|
193
|
+
ariaDescribedBy={ariaDescribedBy}
|
|
189
194
|
fieldType="single"
|
|
190
195
|
hasVisibleLabel={false}
|
|
191
196
|
id={idOverride}
|