@okta/odyssey-react-mui 1.1.1 → 1.4.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 +42 -0
- package/README.md +1 -1
- package/dist/Autocomplete.js +13 -1
- package/dist/Autocomplete.js.map +1 -1
- package/dist/Breadcrumbs.js +146 -0
- package/dist/Breadcrumbs.js.map +1 -0
- package/dist/Link.js +3 -1
- package/dist/Link.js.map +1 -1
- package/dist/MenuButton.js.map +1 -1
- package/dist/OdysseyCacheProvider.js +4 -1
- package/dist/OdysseyCacheProvider.js.map +1 -1
- package/dist/OdysseyProvider.js +5 -1
- package/dist/OdysseyProvider.js.map +1 -1
- package/dist/OdysseyThemeProvider.js +5 -1
- package/dist/OdysseyThemeProvider.js.map +1 -1
- package/dist/OdysseyTranslationProvider.js +1 -1
- package/dist/OdysseyTranslationProvider.js.map +1 -1
- package/dist/PasswordField.js +11 -3
- package/dist/PasswordField.js.map +1 -1
- package/dist/Select.js +34 -33
- package/dist/Select.js.map +1 -1
- package/dist/Tabs.js +8 -6
- package/dist/Tabs.js.map +1 -1
- package/dist/Typography.js +0 -22
- package/dist/Typography.js.map +1 -1
- package/dist/createShadowDom.js +26 -0
- package/dist/createShadowDom.js.map +1 -0
- package/dist/{OdysseyI18n.js → i18n.js} +3 -2
- package/dist/i18n.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/labs/GroupPicker.js +190 -0
- package/dist/labs/GroupPicker.js.map +1 -0
- package/dist/labs/datePickerTheme.js +4 -2
- package/dist/labs/datePickerTheme.js.map +1 -1
- package/dist/labs/index.js +1 -0
- package/dist/labs/index.js.map +1 -1
- package/dist/properties/ts/odyssey-react-mui.js +4 -0
- package/dist/properties/ts/odyssey-react-mui.js.map +1 -1
- package/dist/src/Autocomplete.d.ts +23 -3
- package/dist/src/Autocomplete.d.ts.map +1 -1
- package/dist/src/Breadcrumbs.d.ts +31 -0
- package/dist/src/Breadcrumbs.d.ts.map +1 -0
- package/dist/src/Link.d.ts +6 -1
- package/dist/src/Link.d.ts.map +1 -1
- package/dist/src/MenuButton.d.ts +1 -1
- package/dist/src/MenuButton.d.ts.map +1 -1
- package/dist/src/OdysseyCacheProvider.d.ts +6 -1
- package/dist/src/OdysseyCacheProvider.d.ts.map +1 -1
- package/dist/src/OdysseyProvider.d.ts +1 -1
- package/dist/src/OdysseyProvider.d.ts.map +1 -1
- package/dist/src/OdysseyThemeProvider.d.ts +2 -1
- package/dist/src/OdysseyThemeProvider.d.ts.map +1 -1
- package/dist/src/OdysseyTranslationProvider.d.ts +1 -1
- package/dist/src/OdysseyTranslationProvider.d.ts.map +1 -1
- package/dist/src/PasswordField.d.ts +8 -0
- package/dist/src/PasswordField.d.ts.map +1 -1
- package/dist/src/Select.d.ts +1 -54
- package/dist/src/Select.d.ts.map +1 -1
- package/dist/src/Tabs.d.ts +7 -2
- package/dist/src/Tabs.d.ts.map +1 -1
- package/dist/src/Typography.d.ts +11 -15
- package/dist/src/Typography.d.ts.map +1 -1
- package/dist/src/createShadowDom.d.ts +16 -0
- package/dist/src/createShadowDom.d.ts.map +1 -0
- package/dist/src/{OdysseyI18n.d.ts → i18n.d.ts} +7 -2
- package/dist/src/i18n.d.ts.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/labs/GroupPicker.d.ts +25 -0
- package/dist/src/labs/GroupPicker.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/properties/ts/odyssey-react-mui.d.ts +4 -0
- package/dist/src/properties/ts/odyssey-react-mui.d.ts.map +1 -1
- package/dist/src/theme/components.d.ts +4 -1
- package/dist/src/theme/components.d.ts.map +1 -1
- package/dist/src/theme/createOdysseyMuiTheme.d.ts +23 -0
- package/dist/src/theme/createOdysseyMuiTheme.d.ts.map +1 -0
- package/dist/src/theme/mixins.d.ts +3 -1
- package/dist/src/theme/mixins.d.ts.map +1 -1
- package/dist/src/theme/palette.d.ts +3 -1
- package/dist/src/theme/palette.d.ts.map +1 -1
- package/dist/src/theme/shape.d.ts +3 -1
- package/dist/src/theme/shape.d.ts.map +1 -1
- package/dist/src/theme/spacing.d.ts +3 -1
- package/dist/src/theme/spacing.d.ts.map +1 -1
- package/dist/src/theme/theme.d.ts +1 -8
- package/dist/src/theme/theme.d.ts.map +1 -1
- package/dist/src/theme/typography.d.ts +3 -1
- package/dist/src/theme/typography.d.ts.map +1 -1
- package/dist/theme/components.js +112 -67
- package/dist/theme/components.js.map +1 -1
- package/dist/theme/createOdysseyMuiTheme.js +51 -0
- package/dist/theme/createOdysseyMuiTheme.js.map +1 -0
- package/dist/theme/mixins.js +4 -1
- package/dist/theme/mixins.js.map +1 -1
- package/dist/theme/palette.js +4 -1
- package/dist/theme/palette.js.map +1 -1
- package/dist/theme/shape.js +4 -1
- package/dist/theme/shape.js.map +1 -1
- package/dist/theme/spacing.js +4 -1
- package/dist/theme/spacing.js.map +1 -1
- package/dist/theme/theme.js +1 -20
- package/dist/theme/theme.js.map +1 -1
- package/dist/theme/typography.js +4 -1
- package/dist/theme/typography.js.map +1 -1
- package/dist/tsconfig.production.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/scripts/properties-to-ts.js +1 -1
- package/src/Autocomplete.tsx +47 -3
- package/src/Breadcrumbs.tsx +199 -0
- package/src/Link.tsx +7 -1
- package/src/MenuButton.tsx +2 -3
- package/src/OdysseyCacheProvider.tsx +9 -1
- package/src/OdysseyProvider.tsx +9 -2
- package/src/OdysseyThemeProvider.tsx +8 -2
- package/src/OdysseyTranslationProvider.test.tsx +2 -2
- package/src/OdysseyTranslationProvider.tsx +1 -1
- package/src/PasswordField.tsx +24 -8
- package/src/Select.tsx +147 -152
- package/src/Tabs.tsx +24 -12
- package/src/Typography.tsx +0 -26
- package/src/createShadowDom.ts +46 -0
- package/src/{OdysseyI18n.ts → i18n.ts} +2 -2
- package/src/index.ts +2 -0
- package/src/labs/GroupPicker.tsx +241 -0
- package/src/labs/README.md +1 -1
- package/src/labs/datePickerTheme.tsx +2 -2
- package/src/labs/index.ts +1 -0
- package/src/properties/odyssey-react-mui.properties +4 -0
- package/src/properties/ts/odyssey-react-mui.ts +1 -1
- package/src/theme/components.tsx +61 -13
- package/src/theme/createOdysseyMuiTheme.ts +47 -0
- package/src/theme/mixins.ts +5 -1
- package/src/theme/palette.ts +5 -3
- package/src/theme/shape.ts +5 -1
- package/src/theme/spacing.ts +5 -3
- package/src/theme/theme.ts +1 -26
- package/src/theme/typography.ts +5 -3
- package/dist/OdysseyI18n.js.map +0 -1
- package/dist/src/OdysseyI18n.d.ts.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@okta/odyssey-react-mui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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,9 +51,9 @@
|
|
|
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.4.0",
|
|
55
55
|
"date-fns": "^2.30.0",
|
|
56
|
-
"i18next": "^
|
|
56
|
+
"i18next": "^23.5.1",
|
|
57
57
|
"material-react-table": "^1.14.0",
|
|
58
58
|
"react-i18next": "^12.2.2",
|
|
59
59
|
"ts-node": "^10.9.1",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"react": ">=17 <19",
|
|
64
64
|
"react-dom": ">=17 <19"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "612e08d4ea3bd4c8acb732df7036bce16f4eda0b"
|
|
67
67
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
// Part of this has been copied over from @okta/ui-build-tools' own internal node script
|
|
14
|
-
// https://github.com/okta/ui-build-tools/blob/
|
|
14
|
+
// https://github.com/okta/ui-build-tools/blob/main/packages/clis/i18n/properties-to-json.js
|
|
15
15
|
|
|
16
16
|
const { resolve, join, basename, extname } = require("node:path");
|
|
17
17
|
const {
|
package/src/Autocomplete.tsx
CHANGED
|
@@ -25,6 +25,10 @@ export type AutocompleteProps<
|
|
|
25
25
|
HasMultipleChoices extends boolean | undefined,
|
|
26
26
|
IsCustomValueAllowed extends boolean | undefined
|
|
27
27
|
> = {
|
|
28
|
+
/**
|
|
29
|
+
* The error message for the Select
|
|
30
|
+
*/
|
|
31
|
+
errorMessage?: string;
|
|
28
32
|
/**
|
|
29
33
|
* Enables multiple choice selection
|
|
30
34
|
*/
|
|
@@ -38,6 +42,10 @@ export type AutocompleteProps<
|
|
|
38
42
|
* The hint text for the Autocomplete input
|
|
39
43
|
*/
|
|
40
44
|
hint?: string;
|
|
45
|
+
/**
|
|
46
|
+
* The id attribute of the Select
|
|
47
|
+
*/
|
|
48
|
+
id?: string;
|
|
41
49
|
/**
|
|
42
50
|
* Allows the input of custom values
|
|
43
51
|
*/
|
|
@@ -83,7 +91,20 @@ export type AutocompleteProps<
|
|
|
83
91
|
*/
|
|
84
92
|
label: string;
|
|
85
93
|
/**
|
|
86
|
-
*
|
|
94
|
+
* The name of the `input` element. Defaults to the `id` if not set.
|
|
95
|
+
*/
|
|
96
|
+
name?: string;
|
|
97
|
+
/**
|
|
98
|
+
* Callback fired when the autocomplete loses focus.
|
|
99
|
+
*/
|
|
100
|
+
onBlur?: MuiAutocompleteProps<
|
|
101
|
+
OptionType,
|
|
102
|
+
HasMultipleChoices,
|
|
103
|
+
undefined,
|
|
104
|
+
IsCustomValueAllowed
|
|
105
|
+
>["onBlur"];
|
|
106
|
+
/**
|
|
107
|
+
* Callback fired when a selection is made.
|
|
87
108
|
*/
|
|
88
109
|
onChange?: MuiAutocompleteProps<
|
|
89
110
|
OptionType,
|
|
@@ -92,7 +113,7 @@ export type AutocompleteProps<
|
|
|
92
113
|
IsCustomValueAllowed
|
|
93
114
|
>["onChange"];
|
|
94
115
|
/**
|
|
95
|
-
* Callback fired when the
|
|
116
|
+
* Callback fired when the textbox receives typed characters.
|
|
96
117
|
*/
|
|
97
118
|
onInputChange?: MuiAutocompleteProps<
|
|
98
119
|
OptionType,
|
|
@@ -100,6 +121,15 @@ export type AutocompleteProps<
|
|
|
100
121
|
undefined,
|
|
101
122
|
IsCustomValueAllowed
|
|
102
123
|
>["onInputChange"];
|
|
124
|
+
/**
|
|
125
|
+
* Callback fired when the autocomplete gains focus.
|
|
126
|
+
*/
|
|
127
|
+
onFocus?: MuiAutocompleteProps<
|
|
128
|
+
OptionType,
|
|
129
|
+
HasMultipleChoices,
|
|
130
|
+
undefined,
|
|
131
|
+
IsCustomValueAllowed
|
|
132
|
+
>["onFocus"];
|
|
103
133
|
/**
|
|
104
134
|
* The options for the Autocomplete input
|
|
105
135
|
*/
|
|
@@ -125,7 +155,9 @@ const Autocomplete = <
|
|
|
125
155
|
HasMultipleChoices extends boolean | undefined,
|
|
126
156
|
IsCustomValueAllowed extends boolean | undefined
|
|
127
157
|
>({
|
|
158
|
+
errorMessage,
|
|
128
159
|
hasMultipleChoices,
|
|
160
|
+
id: idOverride,
|
|
129
161
|
isCustomValueAllowed,
|
|
130
162
|
isDisabled,
|
|
131
163
|
isLoading,
|
|
@@ -133,8 +165,11 @@ const Autocomplete = <
|
|
|
133
165
|
isReadOnly,
|
|
134
166
|
hint,
|
|
135
167
|
label,
|
|
168
|
+
name: nameOverride,
|
|
169
|
+
onBlur,
|
|
136
170
|
onChange,
|
|
137
171
|
onInputChange,
|
|
172
|
+
onFocus,
|
|
138
173
|
options,
|
|
139
174
|
value,
|
|
140
175
|
testId,
|
|
@@ -142,6 +177,7 @@ const Autocomplete = <
|
|
|
142
177
|
const renderInput = useCallback(
|
|
143
178
|
({ InputLabelProps, InputProps, ...params }) => (
|
|
144
179
|
<Field
|
|
180
|
+
errorMessage={errorMessage}
|
|
145
181
|
fieldType="single"
|
|
146
182
|
hasVisibleLabel
|
|
147
183
|
id={InputLabelProps.htmlFor}
|
|
@@ -154,12 +190,13 @@ const Autocomplete = <
|
|
|
154
190
|
{...InputProps}
|
|
155
191
|
aria-describedby={ariaDescribedBy}
|
|
156
192
|
id={id}
|
|
193
|
+
name={nameOverride ?? id}
|
|
157
194
|
required={!isOptional}
|
|
158
195
|
/>
|
|
159
196
|
)}
|
|
160
197
|
/>
|
|
161
198
|
),
|
|
162
|
-
[hint, isOptional, label]
|
|
199
|
+
[errorMessage, hint, isOptional, label, nameOverride]
|
|
163
200
|
);
|
|
164
201
|
|
|
165
202
|
return (
|
|
@@ -170,10 +207,14 @@ const Autocomplete = <
|
|
|
170
207
|
disableCloseOnSelect={hasMultipleChoices}
|
|
171
208
|
disabled={isDisabled}
|
|
172
209
|
freeSolo={isCustomValueAllowed}
|
|
210
|
+
filterSelectedOptions={true}
|
|
211
|
+
id={idOverride}
|
|
173
212
|
loading={isLoading}
|
|
174
213
|
multiple={hasMultipleChoices}
|
|
214
|
+
onBlur={onBlur}
|
|
175
215
|
onChange={onChange}
|
|
176
216
|
onInputChange={onInputChange}
|
|
217
|
+
onFocus={onFocus}
|
|
177
218
|
options={options}
|
|
178
219
|
readOnly={isReadOnly}
|
|
179
220
|
renderInput={renderInput}
|
|
@@ -182,6 +223,9 @@ const Autocomplete = <
|
|
|
182
223
|
);
|
|
183
224
|
};
|
|
184
225
|
|
|
226
|
+
// Need the `typeof Autocomplete` because generics don't get passed through
|
|
185
227
|
const MemoizedAutocomplete = memo(Autocomplete) as typeof Autocomplete;
|
|
228
|
+
// @ts-expect-error displayName is expected to not be on `typeof Autocomplete`
|
|
229
|
+
MemoizedAutocomplete.displayName = "Autocomplete";
|
|
186
230
|
|
|
187
231
|
export { MemoizedAutocomplete as Autocomplete };
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2023-present, Okta, Inc. and/or its affiliates. All rights reserved.
|
|
3
|
+
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
|
|
4
|
+
*
|
|
5
|
+
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
|
|
6
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
7
|
+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
8
|
+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9
|
+
*
|
|
10
|
+
* See the License for the specific language governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
ButtonBase,
|
|
15
|
+
Menu,
|
|
16
|
+
MenuItem,
|
|
17
|
+
Breadcrumbs as MuiBreadcrumbs,
|
|
18
|
+
} from "@mui/material";
|
|
19
|
+
import {
|
|
20
|
+
MouseEventHandler,
|
|
21
|
+
ReactElement,
|
|
22
|
+
createContext,
|
|
23
|
+
memo,
|
|
24
|
+
useCallback,
|
|
25
|
+
useContext,
|
|
26
|
+
useMemo,
|
|
27
|
+
useState,
|
|
28
|
+
} from "react";
|
|
29
|
+
import { GroupIcon, HomeIcon, UserIcon } from "./icons.generated";
|
|
30
|
+
import { Typography } from "./Typography";
|
|
31
|
+
import { useTranslation } from "react-i18next";
|
|
32
|
+
|
|
33
|
+
export type BreadcrumbType = "listItem" | "menuItem" | "currentPage";
|
|
34
|
+
|
|
35
|
+
export type BreadcrumbProps = {
|
|
36
|
+
children?: string;
|
|
37
|
+
href: string;
|
|
38
|
+
iconName?: "user" | "group";
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type BreadcrumbsProps = {
|
|
42
|
+
children: ReactElement<typeof Breadcrumb>[];
|
|
43
|
+
homeHref?: string;
|
|
44
|
+
maxVisibleItems?: number;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type BreadcrumbContextType = {
|
|
48
|
+
breadcrumbType: BreadcrumbType;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const BreadcrumbContext = createContext<BreadcrumbContextType>({
|
|
52
|
+
breadcrumbType: "listItem",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export const Breadcrumb = ({ children, href, iconName }: BreadcrumbProps) => {
|
|
56
|
+
const { breadcrumbType } = useContext(BreadcrumbContext);
|
|
57
|
+
|
|
58
|
+
const breadcrumbContent = (
|
|
59
|
+
<>
|
|
60
|
+
{iconName === "group" ? (
|
|
61
|
+
<GroupIcon />
|
|
62
|
+
) : iconName === "user" ? (
|
|
63
|
+
<UserIcon />
|
|
64
|
+
) : null}
|
|
65
|
+
{children}
|
|
66
|
+
</>
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (breadcrumbType === "menuItem") {
|
|
70
|
+
return <MenuItem href={href}>{breadcrumbContent}</MenuItem>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (breadcrumbType === "currentPage") {
|
|
74
|
+
return <Typography>{breadcrumbContent}</Typography>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// breadcrumbType === "listItem" is the default
|
|
78
|
+
// Provided here without a conditional to get TS to be quiet
|
|
79
|
+
// about potential undefined returns
|
|
80
|
+
return <ButtonBase href={href}>{breadcrumbContent}</ButtonBase>;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const breadcrumbProviderValue: Record<
|
|
84
|
+
BreadcrumbType,
|
|
85
|
+
{ breadcrumbType: BreadcrumbType }
|
|
86
|
+
> = {
|
|
87
|
+
currentPage: {
|
|
88
|
+
breadcrumbType: "currentPage",
|
|
89
|
+
},
|
|
90
|
+
listItem: {
|
|
91
|
+
breadcrumbType: "listItem",
|
|
92
|
+
},
|
|
93
|
+
menuItem: {
|
|
94
|
+
breadcrumbType: "menuItem",
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const BreadcrumbList = ({
|
|
99
|
+
children,
|
|
100
|
+
homeHref,
|
|
101
|
+
maxVisibleItems: maxVisibleItemsOverride,
|
|
102
|
+
}: BreadcrumbsProps) => {
|
|
103
|
+
const maxVisibleItems = maxVisibleItemsOverride ?? children.length;
|
|
104
|
+
|
|
105
|
+
const { t } = useTranslation();
|
|
106
|
+
|
|
107
|
+
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
|
|
108
|
+
|
|
109
|
+
const breadcrumbSections = useMemo(() => {
|
|
110
|
+
if (children.length <= maxVisibleItems) {
|
|
111
|
+
return {
|
|
112
|
+
beforeMenu: [],
|
|
113
|
+
insideMenu: [],
|
|
114
|
+
remainingBreadcrumbs: children,
|
|
115
|
+
};
|
|
116
|
+
} else {
|
|
117
|
+
const menuStart = Math.floor(maxVisibleItems / 2);
|
|
118
|
+
const menuLength = children.length - maxVisibleItems;
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
beforeMenu: children.slice(0, menuStart),
|
|
122
|
+
insideMenu: children.slice(menuStart, menuStart + menuLength),
|
|
123
|
+
remainingBreadcrumbs: children.slice(menuStart + menuLength),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}, [children, maxVisibleItems]);
|
|
127
|
+
|
|
128
|
+
const onMenuButtonClick = useCallback<MouseEventHandler<HTMLButtonElement>>(
|
|
129
|
+
(event) => setAnchorEl(event.currentTarget),
|
|
130
|
+
[]
|
|
131
|
+
);
|
|
132
|
+
const onCloseMenu = useCallback(() => {
|
|
133
|
+
setAnchorEl(null);
|
|
134
|
+
}, []);
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<MuiBreadcrumbs
|
|
138
|
+
maxItems={children.length + 1}
|
|
139
|
+
aria-label={t("breadcrumbs.label.text")}
|
|
140
|
+
>
|
|
141
|
+
{homeHref && (
|
|
142
|
+
<ButtonBase href={homeHref} aria-label={t("breadcrumbs.home.text")}>
|
|
143
|
+
<HomeIcon />
|
|
144
|
+
</ButtonBase>
|
|
145
|
+
)}
|
|
146
|
+
|
|
147
|
+
{breadcrumbSections.beforeMenu.map((breadcrumb) => (
|
|
148
|
+
<BreadcrumbContext.Provider value={breadcrumbProviderValue.listItem}>
|
|
149
|
+
{breadcrumb}
|
|
150
|
+
</BreadcrumbContext.Provider>
|
|
151
|
+
))}
|
|
152
|
+
|
|
153
|
+
{breadcrumbSections.insideMenu && (
|
|
154
|
+
<>
|
|
155
|
+
<ButtonBase onClick={onMenuButtonClick}>…</ButtonBase>
|
|
156
|
+
<Menu
|
|
157
|
+
open={Boolean(anchorEl)}
|
|
158
|
+
onClose={onCloseMenu}
|
|
159
|
+
anchorEl={anchorEl}
|
|
160
|
+
anchorOrigin={{ horizontal: "left", vertical: "bottom" }}
|
|
161
|
+
MenuListProps={{
|
|
162
|
+
sx: {
|
|
163
|
+
minWidth: 180,
|
|
164
|
+
},
|
|
165
|
+
}}
|
|
166
|
+
>
|
|
167
|
+
<BreadcrumbContext.Provider
|
|
168
|
+
value={breadcrumbProviderValue.menuItem}
|
|
169
|
+
>
|
|
170
|
+
{breadcrumbSections.insideMenu}
|
|
171
|
+
</BreadcrumbContext.Provider>
|
|
172
|
+
</Menu>
|
|
173
|
+
</>
|
|
174
|
+
)}
|
|
175
|
+
|
|
176
|
+
{breadcrumbSections.remainingBreadcrumbs.map((breadcrumb, i) => {
|
|
177
|
+
if (i === breadcrumbSections.remainingBreadcrumbs.length - 1) {
|
|
178
|
+
return (
|
|
179
|
+
<BreadcrumbContext.Provider
|
|
180
|
+
value={breadcrumbProviderValue.currentPage}
|
|
181
|
+
>
|
|
182
|
+
{breadcrumb}
|
|
183
|
+
</BreadcrumbContext.Provider>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return (
|
|
187
|
+
<BreadcrumbContext.Provider value={breadcrumbProviderValue.listItem}>
|
|
188
|
+
{breadcrumb}
|
|
189
|
+
</BreadcrumbContext.Provider>
|
|
190
|
+
);
|
|
191
|
+
})}
|
|
192
|
+
</MuiBreadcrumbs>
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const MemoizedBreadcrumbList = memo(BreadcrumbList);
|
|
197
|
+
MemoizedBreadcrumbList.displayName = "BreadcrumbList";
|
|
198
|
+
|
|
199
|
+
export { MemoizedBreadcrumbList as BreadcrumbList };
|
package/src/Link.tsx
CHANGED
|
@@ -14,7 +14,7 @@ import { memo, ReactElement } from "react";
|
|
|
14
14
|
import { ExternalLinkIcon } from "./icons.generated";
|
|
15
15
|
import type { SeleniumProps } from "./SeleniumProps";
|
|
16
16
|
|
|
17
|
-
import { Link as MuiLink } from "@mui/material";
|
|
17
|
+
import { Link as MuiLink, LinkProps as MuiLinkProps } from "@mui/material";
|
|
18
18
|
|
|
19
19
|
export const linkVariantValues = ["default", "monochrome"] as const;
|
|
20
20
|
|
|
@@ -31,6 +31,10 @@ export type LinkProps = {
|
|
|
31
31
|
* An optional Icon component at the start of the Link
|
|
32
32
|
*/
|
|
33
33
|
icon?: ReactElement;
|
|
34
|
+
/**
|
|
35
|
+
* The click event handler for the Link
|
|
36
|
+
*/
|
|
37
|
+
onClick?: MuiLinkProps["onClick"];
|
|
34
38
|
/**
|
|
35
39
|
* The HTML `rel` attribute for the Link
|
|
36
40
|
*/
|
|
@@ -58,6 +62,7 @@ const Link = ({
|
|
|
58
62
|
target,
|
|
59
63
|
testId,
|
|
60
64
|
variant,
|
|
65
|
+
onClick,
|
|
61
66
|
}: LinkProps) => (
|
|
62
67
|
<MuiLink
|
|
63
68
|
data-se={testId}
|
|
@@ -65,6 +70,7 @@ const Link = ({
|
|
|
65
70
|
rel={rel}
|
|
66
71
|
target={target}
|
|
67
72
|
variant={variant}
|
|
73
|
+
onClick={onClick}
|
|
68
74
|
>
|
|
69
75
|
{icon && <span className="Link-icon">{icon}</span>}
|
|
70
76
|
|
package/src/MenuButton.tsx
CHANGED
|
@@ -50,9 +50,8 @@ export type MenuButtonProps = {
|
|
|
50
50
|
* The <MenuItem> components within the Menu.
|
|
51
51
|
*/
|
|
52
52
|
children: Array<
|
|
53
|
-
ReactElement<
|
|
54
|
-
|
|
55
|
-
>
|
|
53
|
+
| ReactElement<typeof MenuItem | typeof Divider | typeof ListSubheader>
|
|
54
|
+
| NullElement
|
|
56
55
|
>;
|
|
57
56
|
/**
|
|
58
57
|
* The end Icon on the trigggering Button
|
|
@@ -24,12 +24,18 @@ import { useUniqueAlphabeticalId } from "./useUniqueAlphabeticalId";
|
|
|
24
24
|
|
|
25
25
|
export type OdysseyCacheProviderProps = {
|
|
26
26
|
children: ReactNode;
|
|
27
|
+
/**
|
|
28
|
+
* Emotion renders into this HTML element.
|
|
29
|
+
* When enabling this prop, Emotion renders at the top of this component rather than the bottom like it does in the HTML `<head>`.
|
|
30
|
+
*/
|
|
31
|
+
emotionRootElement?: HTMLStyleElement;
|
|
27
32
|
nonce?: string;
|
|
28
33
|
stylisPlugins?: StylisPlugin[];
|
|
29
34
|
};
|
|
30
35
|
|
|
31
36
|
const OdysseyCacheProvider = ({
|
|
32
37
|
children,
|
|
38
|
+
emotionRootElement,
|
|
33
39
|
nonce,
|
|
34
40
|
stylisPlugins,
|
|
35
41
|
}: OdysseyCacheProviderProps) => {
|
|
@@ -38,11 +44,13 @@ const OdysseyCacheProvider = ({
|
|
|
38
44
|
const emotionCache = useMemo(
|
|
39
45
|
() =>
|
|
40
46
|
createCache({
|
|
47
|
+
container: emotionRootElement,
|
|
41
48
|
key: uniqueAlphabeticalId,
|
|
42
49
|
nonce: nonce || window.cspNonce,
|
|
50
|
+
prepend: Boolean(emotionRootElement),
|
|
43
51
|
stylisPlugins,
|
|
44
52
|
}),
|
|
45
|
-
[nonce, stylisPlugins, uniqueAlphabeticalId]
|
|
53
|
+
[emotionRootElement, nonce, stylisPlugins, uniqueAlphabeticalId]
|
|
46
54
|
);
|
|
47
55
|
|
|
48
56
|
return <CacheProvider value={emotionCache}>{children}</CacheProvider>;
|
package/src/OdysseyProvider.tsx
CHANGED
|
@@ -35,16 +35,23 @@ export type OdysseyProviderProps = OdysseyCacheProviderProps &
|
|
|
35
35
|
const OdysseyProvider = ({
|
|
36
36
|
children,
|
|
37
37
|
designTokensOverride,
|
|
38
|
+
emotionRootElement,
|
|
39
|
+
shadowRootElement,
|
|
38
40
|
languageCode,
|
|
39
41
|
nonce,
|
|
40
42
|
stylisPlugins,
|
|
41
43
|
themeOverride,
|
|
42
44
|
translationOverrides,
|
|
43
45
|
}: OdysseyProviderProps) => (
|
|
44
|
-
<OdysseyCacheProvider
|
|
46
|
+
<OdysseyCacheProvider
|
|
47
|
+
emotionRootElement={emotionRootElement}
|
|
48
|
+
nonce={nonce}
|
|
49
|
+
stylisPlugins={stylisPlugins}
|
|
50
|
+
>
|
|
45
51
|
<OdysseyThemeProvider
|
|
46
|
-
themeOverride={themeOverride}
|
|
47
52
|
designTokensOverride={designTokensOverride}
|
|
53
|
+
shadowRootElement={shadowRootElement}
|
|
54
|
+
themeOverride={themeOverride}
|
|
48
55
|
>
|
|
49
56
|
<ScopedCssBaseline>
|
|
50
57
|
<OdysseyTranslationProvider
|
|
@@ -25,12 +25,14 @@ import { OdysseyDesignTokensContext } from "./OdysseyDesignTokensContext";
|
|
|
25
25
|
export type OdysseyThemeProviderProps = {
|
|
26
26
|
children: ReactNode;
|
|
27
27
|
designTokensOverride?: DesignTokensOverride;
|
|
28
|
+
shadowRootElement?: HTMLDivElement;
|
|
28
29
|
themeOverride?: ThemeOptions;
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
const OdysseyThemeProvider = ({
|
|
32
33
|
children,
|
|
33
34
|
designTokensOverride,
|
|
35
|
+
shadowRootElement,
|
|
34
36
|
themeOverride,
|
|
35
37
|
}: OdysseyThemeProviderProps) => {
|
|
36
38
|
const odysseyTokens = useMemo(
|
|
@@ -38,8 +40,12 @@ const OdysseyThemeProvider = ({
|
|
|
38
40
|
[designTokensOverride]
|
|
39
41
|
);
|
|
40
42
|
const odysseyTheme = useMemo(
|
|
41
|
-
() =>
|
|
42
|
-
|
|
43
|
+
() =>
|
|
44
|
+
createOdysseyMuiTheme({
|
|
45
|
+
odysseyTokens,
|
|
46
|
+
shadowRootElement,
|
|
47
|
+
}),
|
|
48
|
+
[odysseyTokens, shadowRootElement]
|
|
43
49
|
);
|
|
44
50
|
|
|
45
51
|
const customOdysseyTheme = useMemo(
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
|
|
13
13
|
import { render, screen } from "@testing-library/react";
|
|
14
14
|
import { OdysseyTranslationProvider } from "./OdysseyTranslationProvider";
|
|
15
|
-
import
|
|
15
|
+
import { odysseyTranslate } from "./i18n";
|
|
16
16
|
import { TextField } from "./TextField";
|
|
17
17
|
|
|
18
18
|
describe("OdysseyTranslationProvider", () => {
|
|
19
19
|
it("defaults to 'en' translation bundle", () => {
|
|
20
20
|
render(
|
|
21
21
|
<OdysseyTranslationProvider>
|
|
22
|
-
<span>{
|
|
22
|
+
<span>{odysseyTranslate("fieldlabel.optional.text")}</span>
|
|
23
23
|
</OdysseyTranslationProvider>
|
|
24
24
|
);
|
|
25
25
|
|
|
@@ -14,7 +14,7 @@ import { ReactNode, useEffect } from "react";
|
|
|
14
14
|
|
|
15
15
|
import { SupportedLanguages } from "./OdysseyTranslationProvider.types";
|
|
16
16
|
|
|
17
|
-
import i18n,
|
|
17
|
+
import { i18n, defaultNS, resources } from "./i18n";
|
|
18
18
|
import { I18nextProvider } from "react-i18next";
|
|
19
19
|
|
|
20
20
|
export type TranslationOverrides = {
|
package/src/PasswordField.tsx
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
import { ShowIcon, HideIcon } from "./icons.generated";
|
|
24
24
|
import { Field } from "./Field";
|
|
25
25
|
import type { SeleniumProps } from "./SeleniumProps";
|
|
26
|
+
import { useTranslation } from "react-i18next";
|
|
26
27
|
|
|
27
28
|
export type PasswordFieldProps = {
|
|
28
29
|
/**
|
|
@@ -39,6 +40,10 @@ export type PasswordFieldProps = {
|
|
|
39
40
|
* If `true`, the component will receive focus automatically.
|
|
40
41
|
*/
|
|
41
42
|
hasInitialFocus?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* If `true`, the show/hide icon is not shown to the user
|
|
45
|
+
*/
|
|
46
|
+
hasShowPassword?: boolean;
|
|
42
47
|
/**
|
|
43
48
|
* The helper text content.
|
|
44
49
|
*/
|
|
@@ -99,6 +104,7 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
99
104
|
id: idOverride,
|
|
100
105
|
isDisabled = false,
|
|
101
106
|
isOptional = false,
|
|
107
|
+
hasShowPassword = true,
|
|
102
108
|
isReadOnly,
|
|
103
109
|
label,
|
|
104
110
|
name: nameOverride,
|
|
@@ -111,6 +117,7 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
111
117
|
},
|
|
112
118
|
ref
|
|
113
119
|
) => {
|
|
120
|
+
const { t } = useTranslation();
|
|
114
121
|
const [inputType, setInputType] = useState("password");
|
|
115
122
|
|
|
116
123
|
const togglePasswordVisibility = useCallback(() => {
|
|
@@ -128,16 +135,23 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
128
135
|
autoFocus={hasInitialFocus}
|
|
129
136
|
data-se={testId}
|
|
130
137
|
endAdornment={
|
|
131
|
-
|
|
132
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
138
|
+
hasShowPassword && (
|
|
139
|
+
<InputAdornment position="end">
|
|
140
|
+
<IconButton
|
|
141
|
+
aria-label={
|
|
142
|
+
inputType === "password"
|
|
143
|
+
? t("passwordfield.icon.label.show")
|
|
144
|
+
: t("passwordfield.icon.label.hide")
|
|
145
|
+
}
|
|
146
|
+
onClick={togglePasswordVisibility}
|
|
147
|
+
>
|
|
148
|
+
{inputType === "password" ? <ShowIcon /> : <HideIcon />}
|
|
149
|
+
</IconButton>
|
|
150
|
+
</InputAdornment>
|
|
151
|
+
)
|
|
139
152
|
}
|
|
140
153
|
id={id}
|
|
154
|
+
inputProps={{ role: "textbox" }}
|
|
141
155
|
name={nameOverride ?? id}
|
|
142
156
|
onChange={onChange}
|
|
143
157
|
onFocus={onFocus}
|
|
@@ -153,6 +167,7 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
153
167
|
[
|
|
154
168
|
autoCompleteType,
|
|
155
169
|
hasInitialFocus,
|
|
170
|
+
t,
|
|
156
171
|
togglePasswordVisibility,
|
|
157
172
|
inputType,
|
|
158
173
|
nameOverride,
|
|
@@ -162,6 +177,7 @@ const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(
|
|
|
162
177
|
placeholder,
|
|
163
178
|
isOptional,
|
|
164
179
|
isReadOnly,
|
|
180
|
+
hasShowPassword,
|
|
165
181
|
ref,
|
|
166
182
|
testId,
|
|
167
183
|
value,
|