@okta/odyssey-react-mui 1.9.12 → 1.9.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okta/odyssey-react-mui",
3
- "version": "1.9.12",
3
+ "version": "1.9.13",
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.12",
54
+ "@okta/odyssey-design-tokens": "1.9.13",
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": "42787825aa3492c1e9f6bb4f6389691a7ff83522"
66
+ "gitHead": "747861bf33f9c65b901782e306c16b0ccf26a942"
67
67
  }
package/src/Badge.tsx CHANGED
@@ -51,20 +51,17 @@ const Badge = ({
51
51
  }: BadgeProps) => {
52
52
  const odysseyDesignTokens = useOdysseyDesignTokens();
53
53
 
54
- const greaterThanZeroContentMax = badgeContentMax > 0 ? badgeContentMax : 1;
55
- const threeDigitLimitedMax =
56
- greaterThanZeroContentMax > 999 ? 999 : greaterThanZeroContentMax;
57
- const isOverContentMax = Boolean(
58
- badgeContent && badgeContent > threeDigitLimitedMax
59
- );
60
- const overContentMaxMessage = `${greaterThanZeroContentMax}+`;
61
- const formattedContent = isOverContentMax
62
- ? overContentMaxMessage
63
- : badgeContent;
64
- const contentIsLongerThanOneChar = formattedContent?.toString()?.length > 1;
54
+ const renderBadge = useMemo(() => {
55
+ const greaterThanZeroContentMax = badgeContentMax > 0 ? badgeContentMax : 1;
56
+ const threeDigitLimitedMax =
57
+ greaterThanZeroContentMax > 999 ? 999 : greaterThanZeroContentMax;
58
+ const isOverContentMax = badgeContent > threeDigitLimitedMax;
59
+ const overContentMaxMessage = `${greaterThanZeroContentMax}+`;
60
+ const formattedContent = isOverContentMax
61
+ ? overContentMaxMessage
62
+ : badgeContent.toString();
65
63
 
66
- const badgeStyles = useMemo<CSSProperties>(
67
- () => ({
64
+ const badgeStyles: CSSProperties = {
68
65
  display: "inline-flex",
69
66
  alignItems: "center",
70
67
  justifyContent: "center",
@@ -72,31 +69,38 @@ const Badge = ({
72
69
  height: `calc(${odysseyDesignTokens.Spacing4} + ${odysseyDesignTokens.Spacing1})`,
73
70
  minHeight: `calc(${odysseyDesignTokens.Spacing4} + ${odysseyDesignTokens.Spacing1})`,
74
71
  // 6px horizontal padding per design requirements
75
- padding: `0 calc(${odysseyDesignTokens.Spacing1} * 1.5)`,
72
+ padding: "0 6px",
76
73
  backgroundColor: badgeTypeColors(odysseyDesignTokens)[type].background,
77
74
  color: badgeTypeColors(odysseyDesignTokens)[type].font,
78
- borderRadius: contentIsLongerThanOneChar
79
- ? `${odysseyDesignTokens.BorderRadiusOuter}`
80
- : "50%",
75
+ borderRadius:
76
+ formattedContent.length > 1
77
+ ? `${odysseyDesignTokens.BorderRadiusOuter}`
78
+ : "50%",
81
79
  fontSize: `${odysseyDesignTokens.TypographyScale0}`,
82
80
  fontFamily: `${odysseyDesignTokens.TypographyFamilyMono}`,
83
81
  fontWeight: `${odysseyDesignTokens.TypographyWeightBodyBold}`,
84
82
  lineHeight: 1,
85
- }),
86
- [type, contentIsLongerThanOneChar, odysseyDesignTokens]
87
- );
83
+ transitionDuration: `${odysseyDesignTokens.TransitionDurationMain}`,
84
+ transitionProperty: `background-color, color`,
85
+ };
88
86
 
89
- const shouldHideBadge = badgeContent <= 0 || !badgeContent;
87
+ const hasNotificationCount = badgeContent && badgeContent > 0;
90
88
 
91
- if (shouldHideBadge) {
92
- return null;
93
- }
89
+ return hasNotificationCount ? (
90
+ <Box sx={badgeStyles} data-se={testId} translate={translate}>
91
+ {formattedContent}
92
+ </Box>
93
+ ) : null;
94
+ }, [
95
+ badgeContent,
96
+ badgeContentMax,
97
+ odysseyDesignTokens,
98
+ testId,
99
+ translate,
100
+ type,
101
+ ]);
94
102
 
95
- return (
96
- <Box sx={badgeStyles} data-se={testId} translate={translate}>
97
- {formattedContent}
98
- </Box>
99
- );
103
+ return renderBadge;
100
104
  };
101
105
 
102
106
  const MemoizedBadge = memo(Badge);
package/src/Tabs.tsx CHANGED
@@ -10,13 +10,6 @@
10
10
  * See the License for the specific language governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import {
14
- TabContext as MuiTabContext,
15
- TabList as MuiTabList,
16
- TabListProps as MuiTabListProps,
17
- TabPanel as MuiTabPanel,
18
- } from "@mui/lab";
19
- import { Tab as MuiTab } from "@mui/material";
20
13
  import {
21
14
  ReactElement,
22
15
  ReactNode,
@@ -25,7 +18,18 @@ import {
25
18
  useEffect,
26
19
  useState,
27
20
  } from "react";
21
+ import {
22
+ TabContext as MuiTabContext,
23
+ TabList as MuiTabList,
24
+ TabListProps as MuiTabListProps,
25
+ TabPanel as MuiTabPanel,
26
+ } from "@mui/lab";
27
+ import { Tab as MuiTab } from "@mui/material";
28
+
29
+ import { useOdysseyDesignTokens } from "./OdysseyDesignTokensContext";
30
+ import { Badge, BadgeProps } from "./Badge";
28
31
  import { AllowedProps } from "./AllowedProps";
32
+ import { Box } from "./Box";
29
33
 
30
34
  export type TabItemProps = {
31
35
  /**
@@ -48,6 +52,9 @@ export type TabItemProps = {
48
52
  * The value associated with the TabItem
49
53
  */
50
54
  value?: string;
55
+ } & {
56
+ notificationCount?: BadgeProps["badgeContent"];
57
+ notificationCountMax?: BadgeProps["badgeContentMax"];
51
58
  } & AllowedProps;
52
59
 
53
60
  export type TabsProps = {
@@ -74,6 +81,42 @@ export type TabsProps = {
74
81
  onChange?: MuiTabListProps["onChange"];
75
82
  };
76
83
 
84
+ const TabLabel = ({
85
+ label,
86
+ notificationCount,
87
+ notificationCountMax,
88
+ tabState,
89
+ value,
90
+ }: Pick<
91
+ TabItemProps,
92
+ "label" | "notificationCount" | "notificationCountMax" | "value"
93
+ > & {
94
+ tabState: string;
95
+ }) => {
96
+ const odysseyDesignTokens = useOdysseyDesignTokens();
97
+
98
+ return (
99
+ <>
100
+ {label}
101
+ {notificationCount !== undefined && notificationCount > 0 && (
102
+ <Box
103
+ sx={{
104
+ marginInlineStart: notificationCount
105
+ ? odysseyDesignTokens.Spacing2
106
+ : 0,
107
+ }}
108
+ >
109
+ <Badge
110
+ badgeContent={notificationCount}
111
+ badgeContentMax={notificationCountMax}
112
+ type={value === tabState ? "attention" : "default"}
113
+ />
114
+ </Box>
115
+ )}
116
+ </>
117
+ );
118
+ };
119
+
77
120
  const Tabs = ({
78
121
  ariaLabel,
79
122
  initialValue,
@@ -97,19 +140,44 @@ const Tabs = ({
97
140
  }
98
141
  }, [value]);
99
142
 
143
+ const renderTab = useCallback(
144
+ (tab, index) => {
145
+ const {
146
+ testId,
147
+ isDisabled,
148
+ label,
149
+ startIcon,
150
+ value,
151
+ notificationCount,
152
+ notificationCountMax,
153
+ } = tab;
154
+
155
+ return (
156
+ <MuiTab
157
+ data-se={testId}
158
+ disabled={isDisabled}
159
+ icon={startIcon}
160
+ label={
161
+ <TabLabel
162
+ label={label}
163
+ notificationCount={notificationCount}
164
+ notificationCountMax={notificationCountMax}
165
+ tabState={tabState}
166
+ value={value}
167
+ />
168
+ }
169
+ value={value ? value : index.toString()}
170
+ key={value ? value : index.toString()}
171
+ />
172
+ );
173
+ },
174
+ [tabState]
175
+ );
176
+
100
177
  return (
101
178
  <MuiTabContext value={tabState}>
102
179
  <MuiTabList onChange={onChange} aria-label={ariaLabel}>
103
- {tabs.map((tab, index) => (
104
- <MuiTab
105
- data-se={tab.testId}
106
- disabled={tab.isDisabled}
107
- icon={tab.startIcon}
108
- label={tab.label}
109
- value={tab.value ? tab.value : index.toString()}
110
- key={tab.value ? tab.value : index.toString()}
111
- />
112
- ))}
180
+ {tabs.map((tab, index) => renderTab(tab, index))}
113
181
  </MuiTabList>
114
182
  {tabs.map((tab, index) => (
115
183
  <MuiTabPanel
@@ -2208,7 +2208,7 @@ export const components = ({
2208
2208
  root: ({ ownerState }) => ({
2209
2209
  maxWidth: `calc(${odysseyTokens.TypographyLineLengthMax} / 2)`,
2210
2210
  minWidth: "unset",
2211
- minHeight: "unset",
2211
+ minHeight: odysseyTokens.Spacing9,
2212
2212
  padding: `${odysseyTokens.Spacing4} ${odysseyTokens.Spacing1}`,
2213
2213
  fontSize: odysseyTokens.TypographySizeHeading6,
2214
2214
  fontFamily: odysseyTokens.TypographyFamilyHeading,