@okta/odyssey-react-mui 1.9.9 → 1.9.11
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 +8 -0
- package/dist/@types/react-augment.d.js.map +1 -1
- package/dist/Button.js +12 -1
- package/dist/Button.js.map +1 -1
- package/dist/Checkbox.js +12 -1
- package/dist/Checkbox.js.map +1 -1
- package/dist/Link.js +12 -1
- package/dist/Link.js.map +1 -1
- package/dist/PasswordField.js +12 -1
- package/dist/PasswordField.js.map +1 -1
- package/dist/Radio.js +12 -1
- package/dist/Radio.js.map +1 -1
- package/dist/Select.js +12 -1
- package/dist/Select.js.map +1 -1
- package/dist/TextField.js +12 -1
- package/dist/TextField.js.map +1 -1
- package/dist/Typography.js +13 -1
- package/dist/Typography.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/labs/Switch.js +157 -0
- package/dist/labs/Switch.js.map +1 -0
- package/dist/labs/index.js +1 -0
- package/dist/labs/index.js.map +1 -1
- package/dist/src/Button.d.ts +6 -1
- package/dist/src/Button.d.ts.map +1 -1
- package/dist/src/Checkbox.d.ts +6 -1
- package/dist/src/Checkbox.d.ts.map +1 -1
- package/dist/src/Link.d.ts +6 -1
- package/dist/src/Link.d.ts.map +1 -1
- package/dist/src/PasswordField.d.ts +9 -0
- package/dist/src/PasswordField.d.ts.map +1 -1
- package/dist/src/Radio.d.ts +6 -1
- package/dist/src/Radio.d.ts.map +1 -1
- package/dist/src/Select.d.ts +6 -1
- package/dist/src/Select.d.ts.map +1 -1
- package/dist/src/TextField.d.ts +9 -0
- package/dist/src/TextField.d.ts.map +1 -1
- package/dist/src/Typography.d.ts +6 -1
- package/dist/src/Typography.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/labs/Switch.d.ts +41 -0
- package/dist/src/labs/Switch.d.ts.map +1 -0
- package/dist/src/labs/index.d.ts +1 -0
- package/dist/src/labs/index.d.ts.map +1 -1
- package/dist/src/theme/components.d.ts.map +1 -1
- package/dist/theme/components.js +87 -28
- package/dist/theme/components.js.map +1 -1
- package/dist/tsconfig.production.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/@types/react-augment.d.ts +4 -0
- package/src/Button.tsx +28 -1
- package/src/Checkbox.tsx +22 -1
- package/src/Link.tsx +42 -19
- package/src/PasswordField.tsx +22 -0
- package/src/Radio.tsx +22 -2
- package/src/Select.tsx +30 -1
- package/src/TextField.tsx +22 -0
- package/src/Typography.tsx +30 -1
- package/src/index.ts +1 -0
- package/src/labs/Switch.tsx +246 -0
- package/src/labs/index.ts +2 -0
- package/src/theme/components.tsx +56 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@okta/odyssey-react-mui",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.11",
|
|
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.9.
|
|
54
|
+
"@okta/odyssey-design-tokens": "1.9.11",
|
|
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": "0482f9184d2bb88f465ed5b9296806bc540e1fa3"
|
|
67
67
|
}
|
package/src/Button.tsx
CHANGED
|
@@ -12,11 +12,18 @@
|
|
|
12
12
|
|
|
13
13
|
import { Button as MuiButton } from "@mui/material";
|
|
14
14
|
import type { ButtonProps as MuiButtonProps } from "@mui/material";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
memo,
|
|
17
|
+
ReactElement,
|
|
18
|
+
useCallback,
|
|
19
|
+
useImperativeHandle,
|
|
20
|
+
useRef,
|
|
21
|
+
} from "react";
|
|
16
22
|
|
|
17
23
|
import { MuiPropsContext, useMuiProps } from "./MuiPropsContext";
|
|
18
24
|
import { Tooltip } from "./Tooltip";
|
|
19
25
|
import type { SeleniumProps } from "./SeleniumProps";
|
|
26
|
+
import { FocusHandle } from "./@types/react-augment";
|
|
20
27
|
|
|
21
28
|
export const buttonSizeValues = ["small", "medium", "large"] as const;
|
|
22
29
|
export const buttonTypeValues = ["button", "submit", "reset"] as const;
|
|
@@ -41,6 +48,10 @@ export type ButtonProps = {
|
|
|
41
48
|
* The ID of the element that describes the Button
|
|
42
49
|
*/
|
|
43
50
|
ariaDescribedBy?: string;
|
|
51
|
+
/**
|
|
52
|
+
* The ref forwarded to the Button to expose focus()
|
|
53
|
+
*/
|
|
54
|
+
buttonFocusRef?: React.RefObject<FocusHandle>;
|
|
44
55
|
/**
|
|
45
56
|
* The icon element to display at the end of the Button
|
|
46
57
|
*/
|
|
@@ -108,6 +119,7 @@ const Button = ({
|
|
|
108
119
|
ariaDescribedBy,
|
|
109
120
|
ariaLabel,
|
|
110
121
|
ariaLabelledBy,
|
|
122
|
+
buttonFocusRef,
|
|
111
123
|
endIcon,
|
|
112
124
|
id,
|
|
113
125
|
isDisabled,
|
|
@@ -123,6 +135,20 @@ const Button = ({
|
|
|
123
135
|
}: ButtonProps) => {
|
|
124
136
|
const muiProps = useMuiProps();
|
|
125
137
|
|
|
138
|
+
const ref = useRef<HTMLButtonElement>(null);
|
|
139
|
+
useImperativeHandle(
|
|
140
|
+
buttonFocusRef,
|
|
141
|
+
() => {
|
|
142
|
+
const element = ref.current;
|
|
143
|
+
return {
|
|
144
|
+
focus: () => {
|
|
145
|
+
element && element.focus();
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
[]
|
|
150
|
+
);
|
|
151
|
+
|
|
126
152
|
const renderButton = useCallback(
|
|
127
153
|
(muiProps) => (
|
|
128
154
|
<MuiButton
|
|
@@ -136,6 +162,7 @@ const Button = ({
|
|
|
136
162
|
fullWidth={isFullWidth}
|
|
137
163
|
id={id}
|
|
138
164
|
onClick={onClick}
|
|
165
|
+
ref={ref}
|
|
139
166
|
size={size}
|
|
140
167
|
startIcon={startIcon}
|
|
141
168
|
type={type}
|
package/src/Checkbox.tsx
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { useTranslation } from "react-i18next";
|
|
14
|
-
import { memo, useCallback, useMemo, useRef } from "react";
|
|
14
|
+
import { memo, useCallback, useMemo, useRef, useImperativeHandle } from "react";
|
|
15
15
|
import {
|
|
16
16
|
Checkbox as MuiCheckbox,
|
|
17
17
|
CheckboxProps as MuiCheckboxProps,
|
|
@@ -25,6 +25,7 @@ import { Typography } from "./Typography";
|
|
|
25
25
|
import type { SeleniumProps } from "./SeleniumProps";
|
|
26
26
|
import { ComponentControlledState, getControlState } from "./inputUtils";
|
|
27
27
|
import { CheckedFieldProps } from "./FormCheckedProps";
|
|
28
|
+
import { FocusHandle } from "./@types/react-augment";
|
|
28
29
|
|
|
29
30
|
export const checkboxValidityValues = ["valid", "invalid", "inherit"] as const;
|
|
30
31
|
|
|
@@ -41,6 +42,10 @@ export type CheckboxProps = {
|
|
|
41
42
|
* The id of the `input` element.
|
|
42
43
|
*/
|
|
43
44
|
id?: string;
|
|
45
|
+
/**
|
|
46
|
+
* The ref forwarded to the Checkbox to expose focus()
|
|
47
|
+
*/
|
|
48
|
+
inputFocusRef?: React.RefObject<FocusHandle>;
|
|
44
49
|
/**
|
|
45
50
|
* Determines whether the Checkbox is disabled
|
|
46
51
|
*/
|
|
@@ -81,6 +86,7 @@ const Checkbox = ({
|
|
|
81
86
|
ariaLabel,
|
|
82
87
|
ariaLabelledBy,
|
|
83
88
|
id: idOverride,
|
|
89
|
+
inputFocusRef,
|
|
84
90
|
isChecked,
|
|
85
91
|
isDefaultChecked,
|
|
86
92
|
isDisabled,
|
|
@@ -109,6 +115,20 @@ const Checkbox = ({
|
|
|
109
115
|
return { defaultChecked: isDefaultChecked };
|
|
110
116
|
}, [isDefaultChecked, isChecked]);
|
|
111
117
|
|
|
118
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
119
|
+
useImperativeHandle(
|
|
120
|
+
inputFocusRef,
|
|
121
|
+
() => {
|
|
122
|
+
const element = inputRef.current;
|
|
123
|
+
return {
|
|
124
|
+
focus: () => {
|
|
125
|
+
element && element.focus();
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
[]
|
|
130
|
+
);
|
|
131
|
+
|
|
112
132
|
const label = useMemo(() => {
|
|
113
133
|
return (
|
|
114
134
|
<>
|
|
@@ -158,6 +178,7 @@ const Checkbox = ({
|
|
|
158
178
|
indeterminate={isIndeterminate}
|
|
159
179
|
onChange={onChange}
|
|
160
180
|
required={isRequired}
|
|
181
|
+
inputRef={inputRef}
|
|
161
182
|
sx={() => ({
|
|
162
183
|
marginBlockStart: "2px",
|
|
163
184
|
})}
|
package/src/Link.tsx
CHANGED
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
* See the License for the specific language governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { memo, ReactElement } from "react";
|
|
13
|
+
import { memo, ReactElement, useImperativeHandle, useRef } from "react";
|
|
14
14
|
import { ExternalLinkIcon } from "./icons.generated";
|
|
15
15
|
import type { SeleniumProps } from "./SeleniumProps";
|
|
16
16
|
|
|
17
17
|
import { Link as MuiLink, LinkProps as MuiLinkProps } from "@mui/material";
|
|
18
|
+
import { FocusHandle } from "./@types/react-augment";
|
|
18
19
|
|
|
19
20
|
export const linkVariantValues = ["default", "monochrome"] as const;
|
|
20
21
|
|
|
@@ -31,6 +32,10 @@ export type LinkProps = {
|
|
|
31
32
|
* An optional Icon component at the start of the Link
|
|
32
33
|
*/
|
|
33
34
|
icon?: ReactElement;
|
|
35
|
+
/**
|
|
36
|
+
* The ref forwarded to the TextField to expose focus()
|
|
37
|
+
*/
|
|
38
|
+
linkFocusRef?: React.RefObject<FocusHandle>;
|
|
34
39
|
/**
|
|
35
40
|
* The click event handler for the Link
|
|
36
41
|
*/
|
|
@@ -58,31 +63,49 @@ const Link = ({
|
|
|
58
63
|
children,
|
|
59
64
|
href,
|
|
60
65
|
icon,
|
|
66
|
+
linkFocusRef,
|
|
61
67
|
rel,
|
|
62
68
|
target,
|
|
63
69
|
testId,
|
|
64
70
|
variant,
|
|
65
71
|
onClick,
|
|
66
|
-
}: LinkProps) =>
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
}: LinkProps) => {
|
|
73
|
+
const ref = useRef<HTMLAnchorElement>(null);
|
|
74
|
+
useImperativeHandle(
|
|
75
|
+
linkFocusRef,
|
|
76
|
+
() => {
|
|
77
|
+
const element = ref.current;
|
|
78
|
+
return {
|
|
79
|
+
focus: () => {
|
|
80
|
+
element && element.focus();
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
[]
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<MuiLink
|
|
89
|
+
data-se={testId}
|
|
90
|
+
href={href}
|
|
91
|
+
ref={ref}
|
|
92
|
+
rel={rel}
|
|
93
|
+
target={target}
|
|
94
|
+
variant={variant}
|
|
95
|
+
onClick={onClick}
|
|
96
|
+
>
|
|
97
|
+
{icon && <span className="Link-icon">{icon}</span>}
|
|
76
98
|
|
|
77
|
-
|
|
99
|
+
{children}
|
|
78
100
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
);
|
|
101
|
+
{target === "_blank" && (
|
|
102
|
+
<span className="Link-indicator" role="presentation">
|
|
103
|
+
<ExternalLinkIcon />
|
|
104
|
+
</span>
|
|
105
|
+
)}
|
|
106
|
+
</MuiLink>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
86
109
|
|
|
87
110
|
const MemoizedLink = memo(Link);
|
|
88
111
|
|
package/src/PasswordField.tsx
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
forwardRef,
|
|
18
18
|
memo,
|
|
19
19
|
useCallback,
|
|
20
|
+
useImperativeHandle,
|
|
20
21
|
useRef,
|
|
21
22
|
useState,
|
|
22
23
|
} from "react";
|
|
@@ -27,6 +28,7 @@ import { FieldComponentProps } from "./FieldComponentProps";
|
|
|
27
28
|
import type { SeleniumProps } from "./SeleniumProps";
|
|
28
29
|
import { useTranslation } from "react-i18next";
|
|
29
30
|
import { getControlState, useInputValues } from "./inputUtils";
|
|
31
|
+
import { FocusHandle } from "./@types/react-augment";
|
|
30
32
|
|
|
31
33
|
export type PasswordFieldProps = {
|
|
32
34
|
/**
|
|
@@ -47,6 +49,10 @@ export type PasswordFieldProps = {
|
|
|
47
49
|
* If `true`, the show/hide icon is not shown to the user
|
|
48
50
|
*/
|
|
49
51
|
hasShowPassword?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* The ref forwarded to the TextField to expose focus()
|
|
54
|
+
*/
|
|
55
|
+
inputFocusRef?: React.RefObject<FocusHandle>;
|
|
50
56
|
/**
|
|
51
57
|
* The label for the `input` element.
|
|
52
58
|
*/
|
|
@@ -83,6 +89,7 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
83
89
|
hasInitialFocus,
|
|
84
90
|
hint,
|
|
85
91
|
id: idOverride,
|
|
92
|
+
inputFocusRef,
|
|
86
93
|
isDisabled = false,
|
|
87
94
|
isFullWidth = false,
|
|
88
95
|
isOptional = false,
|
|
@@ -120,6 +127,20 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
120
127
|
controlState: controlledStateRef.current,
|
|
121
128
|
});
|
|
122
129
|
|
|
130
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
131
|
+
useImperativeHandle(
|
|
132
|
+
inputFocusRef,
|
|
133
|
+
() => {
|
|
134
|
+
const element = inputRef.current;
|
|
135
|
+
return {
|
|
136
|
+
focus: () => {
|
|
137
|
+
element && element.focus();
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
[]
|
|
142
|
+
);
|
|
143
|
+
|
|
123
144
|
const onChange = useCallback<
|
|
124
145
|
ChangeEventHandler<HTMLTextAreaElement | HTMLInputElement>
|
|
125
146
|
>(
|
|
@@ -161,6 +182,7 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
161
182
|
// role: "textbox" Added because password inputs don't have an implicit role assigned. This causes problems with element selection.
|
|
162
183
|
role: "textbox",
|
|
163
184
|
}}
|
|
185
|
+
inputRef={inputRef}
|
|
164
186
|
name={nameOverride ?? id}
|
|
165
187
|
onChange={onChange}
|
|
166
188
|
onFocus={onFocus}
|
package/src/Radio.tsx
CHANGED
|
@@ -16,12 +16,17 @@ import {
|
|
|
16
16
|
Radio as MuiRadio,
|
|
17
17
|
RadioProps as MuiRadioProps,
|
|
18
18
|
} from "@mui/material";
|
|
19
|
-
import { memo, useCallback } from "react";
|
|
19
|
+
import { memo, useCallback, useRef, useImperativeHandle } from "react";
|
|
20
20
|
|
|
21
21
|
import { FieldComponentProps } from "./FieldComponentProps";
|
|
22
22
|
import type { SeleniumProps } from "./SeleniumProps";
|
|
23
|
+
import { FocusHandle } from "./@types/react-augment";
|
|
23
24
|
|
|
24
25
|
export type RadioProps = {
|
|
26
|
+
/**
|
|
27
|
+
* The ref forwarded to the Radio to expose focus()
|
|
28
|
+
*/
|
|
29
|
+
inputFocusRef?: React.RefObject<FocusHandle>;
|
|
25
30
|
/**
|
|
26
31
|
* If `true`, the Radio is selected
|
|
27
32
|
*/
|
|
@@ -50,6 +55,7 @@ export type RadioProps = {
|
|
|
50
55
|
SeleniumProps;
|
|
51
56
|
|
|
52
57
|
const Radio = ({
|
|
58
|
+
inputFocusRef,
|
|
53
59
|
isChecked,
|
|
54
60
|
isDisabled,
|
|
55
61
|
isInvalid,
|
|
@@ -60,6 +66,20 @@ const Radio = ({
|
|
|
60
66
|
onChange: onChangeProp,
|
|
61
67
|
onBlur: onBlurProp,
|
|
62
68
|
}: RadioProps) => {
|
|
69
|
+
const ref = useRef<HTMLInputElement>(null);
|
|
70
|
+
useImperativeHandle(
|
|
71
|
+
inputFocusRef,
|
|
72
|
+
() => {
|
|
73
|
+
const element = ref.current;
|
|
74
|
+
return {
|
|
75
|
+
focus: () => {
|
|
76
|
+
element && element.focus();
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
[]
|
|
81
|
+
);
|
|
82
|
+
|
|
63
83
|
const onChange = useCallback<NonNullable<MuiRadioProps["onChange"]>>(
|
|
64
84
|
(event, checked) => {
|
|
65
85
|
onChangeProp?.(event, checked);
|
|
@@ -78,7 +98,7 @@ const Radio = ({
|
|
|
78
98
|
<FormControlLabel
|
|
79
99
|
checked={isChecked}
|
|
80
100
|
className={isInvalid ? "Mui-error" : ""}
|
|
81
|
-
control={<MuiRadio onChange={onChange} />}
|
|
101
|
+
control={<MuiRadio inputRef={ref} onChange={onChange} />}
|
|
82
102
|
data-se={testId}
|
|
83
103
|
disabled={isDisabled}
|
|
84
104
|
label={label}
|
package/src/Select.tsx
CHANGED
|
@@ -10,7 +10,15 @@
|
|
|
10
10
|
* See the License for the specific language governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
memo,
|
|
15
|
+
useCallback,
|
|
16
|
+
useEffect,
|
|
17
|
+
useMemo,
|
|
18
|
+
useRef,
|
|
19
|
+
useState,
|
|
20
|
+
useImperativeHandle,
|
|
21
|
+
} from "react";
|
|
14
22
|
import {
|
|
15
23
|
Box,
|
|
16
24
|
Checkbox as MuiCheckbox,
|
|
@@ -31,6 +39,7 @@ import {
|
|
|
31
39
|
useInputValues,
|
|
32
40
|
getControlState,
|
|
33
41
|
} from "./inputUtils";
|
|
42
|
+
import { FocusHandle } from "./@types/react-augment";
|
|
34
43
|
|
|
35
44
|
export type SelectOption = {
|
|
36
45
|
text: string;
|
|
@@ -53,6 +62,10 @@ export type SelectProps<
|
|
|
53
62
|
* If `true`, the Select allows multiple selections
|
|
54
63
|
*/
|
|
55
64
|
hasMultipleChoices?: HasMultipleChoices;
|
|
65
|
+
/**
|
|
66
|
+
* The ref forwarded to the Select to expose focus()
|
|
67
|
+
*/
|
|
68
|
+
inputFocusRef?: React.RefObject<FocusHandle>;
|
|
56
69
|
/**
|
|
57
70
|
* @deprecated Use `hasMultipleChoices` instead.
|
|
58
71
|
*/
|
|
@@ -121,6 +134,7 @@ const Select = <
|
|
|
121
134
|
hint,
|
|
122
135
|
HintLinkComponent,
|
|
123
136
|
id: idOverride,
|
|
137
|
+
inputFocusRef,
|
|
124
138
|
isDisabled = false,
|
|
125
139
|
isFullWidth = false,
|
|
126
140
|
isMultiSelect,
|
|
@@ -147,6 +161,20 @@ const Select = <
|
|
|
147
161
|
const [internalSelectedValues, setInternalSelectedValues] = useState(
|
|
148
162
|
controlledStateRef.current === CONTROLLED ? value : defaultValue
|
|
149
163
|
);
|
|
164
|
+
const inputRef = useRef<HTMLSelectElement>(null);
|
|
165
|
+
|
|
166
|
+
useImperativeHandle(
|
|
167
|
+
inputFocusRef,
|
|
168
|
+
() => {
|
|
169
|
+
const element = inputRef.current;
|
|
170
|
+
return {
|
|
171
|
+
focus: () => {
|
|
172
|
+
element && element.focus();
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
[]
|
|
177
|
+
);
|
|
150
178
|
|
|
151
179
|
useEffect(() => {
|
|
152
180
|
if (controlledStateRef.current === CONTROLLED) {
|
|
@@ -261,6 +289,7 @@ const Select = <
|
|
|
261
289
|
children={children}
|
|
262
290
|
data-se={testId}
|
|
263
291
|
id={id}
|
|
292
|
+
inputRef={inputRef}
|
|
264
293
|
labelId={labelElementId}
|
|
265
294
|
multiple={hasMultipleChoices}
|
|
266
295
|
name={nameOverride ?? id}
|
package/src/TextField.tsx
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
memo,
|
|
19
19
|
ReactElement,
|
|
20
20
|
useCallback,
|
|
21
|
+
useImperativeHandle,
|
|
21
22
|
useRef,
|
|
22
23
|
} from "react";
|
|
23
24
|
import { InputAdornment, InputBase } from "@mui/material";
|
|
@@ -26,6 +27,7 @@ import { FieldComponentProps } from "./FieldComponentProps";
|
|
|
26
27
|
import { Field } from "./Field";
|
|
27
28
|
import { SeleniumProps } from "./SeleniumProps";
|
|
28
29
|
import { useInputValues, getControlState } from "./inputUtils";
|
|
30
|
+
import { FocusHandle } from "./@types/react-augment";
|
|
29
31
|
|
|
30
32
|
export const textFieldTypeValues = [
|
|
31
33
|
"email",
|
|
@@ -54,6 +56,10 @@ export type TextFieldProps = {
|
|
|
54
56
|
* If `true`, the component will receive focus automatically.
|
|
55
57
|
*/
|
|
56
58
|
hasInitialFocus?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* The ref forwarded to the TextField to expose focus()
|
|
61
|
+
*/
|
|
62
|
+
inputFocusRef?: React.RefObject<FocusHandle>;
|
|
57
63
|
/**
|
|
58
64
|
* If `true`, a [TextareaAutosize](/material-ui/react-textarea-autosize/) element is rendered.
|
|
59
65
|
*/
|
|
@@ -104,6 +110,7 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
104
110
|
hint,
|
|
105
111
|
HintLinkComponent,
|
|
106
112
|
id: idOverride,
|
|
113
|
+
inputFocusRef,
|
|
107
114
|
isDisabled = false,
|
|
108
115
|
isFullWidth = false,
|
|
109
116
|
isMultiline = false,
|
|
@@ -134,6 +141,20 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
134
141
|
controlState: controlledStateRef.current,
|
|
135
142
|
});
|
|
136
143
|
|
|
144
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
145
|
+
useImperativeHandle(
|
|
146
|
+
inputFocusRef,
|
|
147
|
+
() => {
|
|
148
|
+
const element = inputRef.current;
|
|
149
|
+
return {
|
|
150
|
+
focus: () => {
|
|
151
|
+
element && element.focus();
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
[]
|
|
156
|
+
);
|
|
157
|
+
|
|
137
158
|
const onChange = useCallback<
|
|
138
159
|
NonNullable<ChangeEventHandler<HTMLTextAreaElement | HTMLInputElement>>
|
|
139
160
|
>(
|
|
@@ -162,6 +183,7 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
162
183
|
)
|
|
163
184
|
}
|
|
164
185
|
id={id}
|
|
186
|
+
inputRef={inputRef}
|
|
165
187
|
multiline={isMultiline}
|
|
166
188
|
name={nameOverride ?? id}
|
|
167
189
|
onBlur={onBlur}
|
package/src/Typography.tsx
CHANGED
|
@@ -14,8 +14,16 @@ import {
|
|
|
14
14
|
Typography as MuiTypography,
|
|
15
15
|
TypographyProps as MuiTypographyProps,
|
|
16
16
|
} from "@mui/material";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
ElementType,
|
|
19
|
+
ReactNode,
|
|
20
|
+
memo,
|
|
21
|
+
useMemo,
|
|
22
|
+
useRef,
|
|
23
|
+
useImperativeHandle,
|
|
24
|
+
} from "react";
|
|
18
25
|
import { SeleniumProps } from "./SeleniumProps";
|
|
26
|
+
import { FocusHandle } from "./@types/react-augment";
|
|
19
27
|
|
|
20
28
|
export type TypographyVariantValue =
|
|
21
29
|
| "h1"
|
|
@@ -78,6 +86,10 @@ export type TypographyProps = {
|
|
|
78
86
|
* The HTML element the component should render, if different from the default.
|
|
79
87
|
*/
|
|
80
88
|
component?: ElementType;
|
|
89
|
+
/**
|
|
90
|
+
* The ref forwarded to the Typography to expose focus()
|
|
91
|
+
*/
|
|
92
|
+
typographyFocusRef?: React.RefObject<FocusHandle>;
|
|
81
93
|
/**
|
|
82
94
|
* The variant of Typography to render.
|
|
83
95
|
*/
|
|
@@ -92,6 +104,7 @@ const Typography = ({
|
|
|
92
104
|
color,
|
|
93
105
|
component: componentProp,
|
|
94
106
|
testId,
|
|
107
|
+
typographyFocusRef,
|
|
95
108
|
variant = "body",
|
|
96
109
|
}: TypographyProps) => {
|
|
97
110
|
const component = useMemo(() => {
|
|
@@ -107,6 +120,20 @@ const Typography = ({
|
|
|
107
120
|
return componentProp;
|
|
108
121
|
}, [componentProp, variant]);
|
|
109
122
|
|
|
123
|
+
const ref = useRef<HTMLElement>(null);
|
|
124
|
+
useImperativeHandle(
|
|
125
|
+
typographyFocusRef,
|
|
126
|
+
() => {
|
|
127
|
+
const element = ref.current;
|
|
128
|
+
return {
|
|
129
|
+
focus: () => {
|
|
130
|
+
element && element.focus();
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
[]
|
|
135
|
+
);
|
|
136
|
+
|
|
110
137
|
return (
|
|
111
138
|
<MuiTypography
|
|
112
139
|
aria-describedby={ariaDescribedBy}
|
|
@@ -116,6 +143,8 @@ const Typography = ({
|
|
|
116
143
|
color={color}
|
|
117
144
|
component={component}
|
|
118
145
|
data-se={testId}
|
|
146
|
+
ref={ref}
|
|
147
|
+
tabIndex={-1}
|
|
119
148
|
variant={typographyVariantMapping[variant]}
|
|
120
149
|
/>
|
|
121
150
|
);
|
package/src/index.ts
CHANGED