@okta/odyssey-react-mui 0.19.0 → 0.20.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.
@@ -0,0 +1,99 @@
1
+ /*!
2
+ * Copyright (c) 2022-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
+ Button,
15
+ ButtonProps,
16
+ ChevronDownIcon,
17
+ Divider,
18
+ ListSubheader,
19
+ Menu,
20
+ MenuItem,
21
+ useUniqueId,
22
+ } from "./";
23
+ import { memo, MouseEvent, ReactElement, useMemo, useState } from "react";
24
+
25
+ export interface MenuButtonProps {
26
+ /**
27
+ * The <MenuItem> components within the Menu.
28
+ */
29
+ children: Array<
30
+ ReactElement<typeof MenuItem | typeof Divider | typeof ListSubheader>
31
+ >;
32
+ /**
33
+ * The end Icon on the trigggering Button
34
+ */
35
+ buttonEndIcon?: ReactElement;
36
+ /**
37
+ * The label on the triggering Button
38
+ */
39
+ buttonLabel?: string;
40
+ /**
41
+ * The variant of the triggering Button
42
+ */
43
+ buttonVariant?: ButtonProps["variant"];
44
+ }
45
+
46
+ const MenuButton = ({
47
+ buttonLabel = "",
48
+ children,
49
+ buttonEndIcon = <ChevronDownIcon />,
50
+ buttonVariant = "secondary",
51
+ }: MenuButtonProps) => {
52
+ const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
53
+
54
+ const open = Boolean(anchorEl);
55
+
56
+ const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
57
+ setAnchorEl(event.currentTarget);
58
+ };
59
+
60
+ const handleClose = () => {
61
+ setAnchorEl(null);
62
+ };
63
+
64
+ const uniqueId = useUniqueId();
65
+
66
+ const menuListProps = useMemo(
67
+ () => ({ "aria-labelledby": `${uniqueId}-button` }),
68
+ [uniqueId]
69
+ );
70
+
71
+ return (
72
+ <div>
73
+ <Button
74
+ endIcon={buttonEndIcon}
75
+ id={`${uniqueId}-button`}
76
+ aria-controls={open ? `${uniqueId}-menu` : undefined}
77
+ aria-haspopup="true"
78
+ aria-expanded={open ? "true" : undefined}
79
+ onClick={handleClick}
80
+ variant={buttonVariant}
81
+ >
82
+ {buttonLabel}
83
+ </Button>
84
+ <Menu
85
+ id={`${uniqueId}-menu`}
86
+ anchorEl={anchorEl}
87
+ open={open}
88
+ onClose={handleClose}
89
+ MenuListProps={menuListProps}
90
+ >
91
+ {children}
92
+ </Menu>
93
+ </div>
94
+ );
95
+ };
96
+
97
+ const MemoizedMenuButton = memo(MenuButton);
98
+
99
+ export { MemoizedMenuButton as MenuButton };
@@ -0,0 +1,50 @@
1
+ /*!
2
+ * Copyright (c) 2022-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 { memo, forwardRef } from "react";
14
+ import { MenuItem as MuiMenuItem } from "@mui/material";
15
+ import { menuItemClasses } from "@mui/material/MenuItem";
16
+ import type { MenuItemProps as MuiMenuItemProps } from "@mui/material";
17
+
18
+ export interface MenuItemProps
19
+ extends Omit<
20
+ MuiMenuItemProps,
21
+ | "component"
22
+ | "dense"
23
+ | "disableGutters"
24
+ | "divider"
25
+ | "focusVisibleClassName"
26
+ > {
27
+ /**
28
+ * Toggles whether or not the MenuItem represents a destructive action.
29
+ */
30
+ isDestructive?: boolean;
31
+ }
32
+
33
+ const MenuItem = forwardRef<HTMLLIElement, MenuItemProps>(
34
+ ({ isDestructive, ...props }, ref) => (
35
+ <MuiMenuItem
36
+ {...props}
37
+ ref={ref}
38
+ className={
39
+ isDestructive ? `${menuItemClasses.root}-destructive` : undefined
40
+ }
41
+ >
42
+ {props.children}
43
+ </MuiMenuItem>
44
+ )
45
+ );
46
+
47
+ const MemoizedMenuItem = memo(MenuItem);
48
+ MemoizedMenuItem.displayName = "MenuItem";
49
+
50
+ export { MemoizedMenuItem as MenuItem };
package/src/index.ts CHANGED
@@ -24,6 +24,7 @@ export {
24
24
  DialogContent,
25
25
  DialogContentText,
26
26
  DialogTitle,
27
+ Divider,
27
28
  FormControl,
28
29
  FormControlLabel,
29
30
  FormGroup,
@@ -38,7 +39,9 @@ export {
38
39
  ListItemIcon,
39
40
  ListItemText,
40
41
  ListSubheader,
41
- MenuItem,
42
+ Menu,
43
+ MenuList,
44
+ Paper,
42
45
  ScopedCssBaseline,
43
46
  Select,
44
47
  Snackbar,
@@ -72,6 +75,7 @@ export type {
72
75
  DialogContentProps,
73
76
  DialogContentTextProps,
74
77
  DialogTitleProps,
78
+ DividerProps,
75
79
  FormControlLabelProps,
76
80
  FormControlProps,
77
81
  FormGroupProps,
@@ -86,7 +90,9 @@ export type {
86
90
  ListItemIconProps,
87
91
  ListItemTextProps,
88
92
  ListSubheaderProps,
89
- MenuItemProps,
93
+ MenuProps,
94
+ MenuListProps,
95
+ PaperProps,
90
96
  ScopedCssBaselineProps,
91
97
  SelectChangeEvent,
92
98
  SelectProps,
@@ -121,6 +127,8 @@ export * from "./Icon";
121
127
  export * from "./iconDictionary";
122
128
  export * from "./Infobox";
123
129
  export * from "./Link";
130
+ export * from "./MenuButton";
131
+ export * from "./MenuItem";
124
132
  export * from "./OdysseyCacheProvider";
125
133
  export * from "./OdysseyThemeProvider";
126
134
  export * from "./PasswordInput";
@@ -10,13 +10,18 @@
10
10
  * See the License for the specific language governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import type { ThemeOptions } from "@mui/material";
13
+ import { ThemeOptions } from "@mui/material";
14
14
  import type {} from "@mui/lab/themeAugmentation";
15
15
  //import radioClasses from "@mui/material";
16
+ import { buttonClasses } from "@mui/material/Button";
16
17
  import { chipClasses } from "@mui/material/Chip";
17
18
  import { dialogActionsClasses } from "@mui/material/DialogActions";
19
+ import { dividerClasses } from "@mui/material/Divider";
18
20
  import { inputAdornmentClasses } from "@mui/material/InputAdornment";
19
21
  import { inputBaseClasses } from "@mui/material/InputBase";
22
+ import { listItemIconClasses } from "@mui/material/ListItemIcon";
23
+ import { listItemTextClasses } from "@mui/material/ListItemText";
24
+ import { menuItemClasses } from "@mui/material/MenuItem";
20
25
  import { svgIconClasses } from "@mui/material/SvgIcon";
21
26
  import { tableBodyClasses } from "@mui/material/TableBody";
22
27
  import { tableCellClasses } from "@mui/material/TableCell";
@@ -292,7 +297,7 @@ export const components: ThemeOptions["components"] = {
292
297
  style: {
293
298
  minWidth: "auto",
294
299
 
295
- ".MuiButton-startIcon": {
300
+ [`.${buttonClasses.endIcon}, .${buttonClasses.startIcon}`]: {
296
301
  margin: "0",
297
302
  },
298
303
  },
@@ -335,18 +340,23 @@ export const components: ThemeOptions["components"] = {
335
340
  pointerEvents: "inherit", // in order to have cursor: not-allowed, must change pointer-events from "none"
336
341
  },
337
342
 
338
- ".MuiButton-startIcon > *:nth-of-type(1)": {
339
- fontSize: `${theme.typography.ui.lineHeight}em`,
343
+ [`.${buttonClasses.startIcon}, .${buttonClasses.endIcon}`]: {
344
+ "& > *:nth-of-type(1)": {
345
+ fontSize: `${theme.typography.ui.lineHeight}em`,
346
+ },
340
347
  },
341
348
  }),
349
+
350
+ endIcon: ({ theme }) => ({
351
+ display: "inline-flex",
352
+ margin: 0,
353
+ marginInlineStart: theme.spacing(2),
354
+ }),
355
+
342
356
  startIcon: ({ theme }) => ({
343
357
  display: "inline-flex",
344
358
  margin: 0,
345
359
  marginInlineEnd: theme.spacing(2),
346
-
347
- "&:only-child": {
348
- margin: 0,
349
- },
350
360
  }),
351
361
  },
352
362
  },
@@ -735,7 +745,7 @@ export const components: ThemeOptions["components"] = {
735
745
  },
736
746
  },
737
747
 
738
- "ul, ol": {
748
+ "ul:not([class]), ol:not([class])": {
739
749
  maxWidth: theme.mixins.maxWidth,
740
750
  marginBlockStart: 0,
741
751
  marginBlockEnd: theme.spacing(4),
@@ -755,7 +765,7 @@ export const components: ThemeOptions["components"] = {
755
765
  },
756
766
  },
757
767
 
758
- li: {
768
+ "li:not([class])": {
759
769
  marginBlockEnd: theme.spacing(2),
760
770
  paddingInlineStart: theme.spacing(1),
761
771
 
@@ -1263,6 +1273,7 @@ export const components: ThemeOptions["components"] = {
1263
1273
  paddingBlock: theme.spacing(2),
1264
1274
  paddingInline: theme.spacing(4),
1265
1275
  fontSize: theme.typography.caption.fontSize,
1276
+ fontWeight: theme.typography.fontWeightBold,
1266
1277
  lineHeight: theme.typography.caption.lineHeight,
1267
1278
  color: theme.palette.text.secondary,
1268
1279
  textTransform: "uppercase",
@@ -1271,21 +1282,73 @@ export const components: ThemeOptions["components"] = {
1271
1282
  },
1272
1283
  MuiMenuItem: {
1273
1284
  styleOverrides: {
1274
- root: ({ theme }) => ({
1275
- justifyContent: "space-between",
1285
+ root: ({ theme, ownerState }) => ({
1276
1286
  gap: theme.spacing(2),
1287
+ minHeight: "unset",
1288
+ paddingBlock: theme.spacing(3),
1277
1289
 
1278
- "&.Mui-selected": {
1290
+ "&:hover": {
1291
+ textDecoration: "none",
1292
+ backgroundColor: theme.palette.grey[100],
1293
+
1294
+ // Reset on touch devices, it doesn't add specificity
1295
+ "@media (hover: none)": {
1296
+ backgroundColor: "transparent",
1297
+ },
1298
+ },
1299
+
1300
+ [`&.${menuItemClasses.root}-destructive`]: {
1301
+ color: theme.palette.error.main,
1302
+ },
1303
+
1304
+ [`&.${menuItemClasses.selected}`]: {
1279
1305
  backgroundColor: "transparent",
1280
1306
  color: theme.palette.primary.main,
1281
1307
 
1282
1308
  "&:hover": {
1283
1309
  backgroundColor: theme.palette.primary.lighter,
1310
+
1311
+ "@media (hover: none)": {
1312
+ backgroundColor: `rgba(${theme.palette.primary.main} / ${theme.palette.action.selectedOpacity})`,
1313
+ },
1284
1314
  },
1285
1315
  },
1316
+
1317
+ ...(!ownerState.disableGutters && {
1318
+ paddingInline: theme.spacing(4),
1319
+ }),
1320
+
1321
+ ...(ownerState.divider && {
1322
+ borderBlockEnd: `1px solid ${theme.palette.divider}`,
1323
+ }),
1324
+
1325
+ [`&.${menuItemClasses.disabled}`]: {
1326
+ opacity: 1,
1327
+ color: theme.palette.text.disabled,
1328
+ },
1329
+
1330
+ [`& + .${dividerClasses.root}`]: {
1331
+ marginBlock: theme.spacing(1),
1332
+ },
1333
+
1334
+ [`& .${listItemTextClasses.root}`]: {
1335
+ marginBlock: 0,
1336
+ },
1337
+
1338
+ [`& .${listItemIconClasses.root}`]: {
1339
+ minWidth: "unset",
1340
+ },
1286
1341
  }),
1287
1342
  },
1288
1343
  },
1344
+ MuiListItemIcon: {
1345
+ styleOverrides: {
1346
+ root: {
1347
+ minWidth: "unset",
1348
+ color: "inherit",
1349
+ },
1350
+ },
1351
+ },
1289
1352
  MuiNativeSelect: {
1290
1353
  defaultProps: {
1291
1354
  variant: "standard",
@@ -1306,6 +1369,9 @@ export const components: ThemeOptions["components"] = {
1306
1369
  styleOverrides: {
1307
1370
  paper: ({ theme }) => ({
1308
1371
  marginBlockStart: theme.spacing(1),
1372
+ borderWidth: theme.mixins.borderWidth,
1373
+ borderStyle: theme.mixins.borderStyle,
1374
+ borderColor: theme.palette.grey[200],
1309
1375
  }),
1310
1376
  },
1311
1377
  },