From 04df1a9ea6565bd0e1d1707a46c47f41a1ada209 Mon Sep 17 00:00:00 2001 From: benlister-okta Date: Tue, 28 Jan 2025 15:43:55 -0800 Subject: [PATCH] fix: feedback optimizations, rebase updates --- packages/odyssey-react-mui/src/index.ts | 3 +- .../src/labs/Stepper/StepIcon.tsx | 125 ++++--- .../src/labs/Stepper/Stepper.test.tsx | 12 +- .../src/labs/Stepper/Stepper.tsx | 334 ++++++++++-------- .../src/labs/Stepper/Stepper.types.ts | 24 +- .../src/labs/Stepper/Stepper.utils.ts | 8 +- .../src/labs/Stepper/StepperNavigation.tsx | 95 +++-- .../src/labs/Stepper/index.ts | 8 +- packages/odyssey-react-mui/src/labs/index.ts | 4 +- .../odyssey-labs/Stepper/Stepper.stories.tsx | 2 +- 10 files changed, 354 insertions(+), 261 deletions(-) diff --git a/packages/odyssey-react-mui/src/index.ts b/packages/odyssey-react-mui/src/index.ts index 0c7347189b..c5569e97c0 100644 --- a/packages/odyssey-react-mui/src/index.ts +++ b/packages/odyssey-react-mui/src/index.ts @@ -105,7 +105,6 @@ export * from "./shadow-dom/index.js"; export * from "./Stack.js"; export * from "./Status.js"; export * from "./Surface.js"; -export * from "./Stepper"; export * from "./Switch.js"; export * from "./Tabs.js"; export * from "./Tag.js"; @@ -116,4 +115,4 @@ export * from "./Toast.js"; export * from "./ToastStack.js"; export * from "./Tooltip.js"; export * from "./Typography.js"; -export * from "./useUniqueId.js"; \ No newline at end of file +export * from "./useUniqueId.js"; diff --git a/packages/odyssey-react-mui/src/labs/Stepper/StepIcon.tsx b/packages/odyssey-react-mui/src/labs/Stepper/StepIcon.tsx index a116447260..33b237dd5d 100644 --- a/packages/odyssey-react-mui/src/labs/Stepper/StepIcon.tsx +++ b/packages/odyssey-react-mui/src/labs/Stepper/StepIcon.tsx @@ -11,13 +11,13 @@ */ import styled from "@emotion/styled"; -import { CheckIcon } from "../../icons.generated"; -import { DesignTokens } from "../../OdysseyDesignTokensContext"; -import type { StepIconProps } from "./Stepper.types"; +import { CheckIcon } from "../../icons.generated/index.js"; +import { DesignTokens } from "../../OdysseyDesignTokensContext.js"; +import type { StepIconProps } from "./Stepper.types.js"; import { shouldForwardStepIconContainerProps, shouldForwardStepNumberProps, -} from "./Stepper.utils"; +} from "./Stepper.utils.js"; const StyledStepNumber = styled("span", { shouldForwardProp: shouldForwardStepNumberProps, @@ -28,16 +28,24 @@ const StyledStepNumber = styled("span", { nonLinear: boolean; }>(({ completed, active, nonLinear, odysseyDesignTokens }) => ({ fontWeight: odysseyDesignTokens.TypographyWeightHeadingBold, - color: - completed || active - ? odysseyDesignTokens.HueNeutralWhite - : odysseyDesignTokens.HueNeutral600, + //Base color + color: odysseyDesignTokens.HueNeutral600, + //Override color for completed or active states + ...(completed && { + color: odysseyDesignTokens.HueNeutralWhite, + }), + ...(active && { + color: odysseyDesignTokens.HueNeutralWhite, + }), + + //Hover state ".MuiStep-root:hover &": { - color: - !active && !completed && nonLinear - ? odysseyDesignTokens.HueNeutral900 - : undefined, + ...(!active && + !completed && + nonLinear && { + color: odysseyDesignTokens.HueNeutral900, + }), }, })); @@ -49,53 +57,64 @@ const StyledStepIconContainer = styled("div", { nonLinear: boolean; odysseyDesignTokens: DesignTokens; variant: "numeric" | "nonNumeric"; -}>(({ active, completed, variant, nonLinear, odysseyDesignTokens }) => ({ - width: - variant === "numeric" +}>(({ active, completed, variant, nonLinear, odysseyDesignTokens }) => { + const isNumeric = variant === "numeric"; + + return { + width: isNumeric ? odysseyDesignTokens.Spacing5 : odysseyDesignTokens.Spacing4, - height: - variant === "numeric" + height: isNumeric ? odysseyDesignTokens.Spacing5 : odysseyDesignTokens.Spacing4, - borderRadius: "50%", - display: "flex", - alignItems: "center", - justifyContent: "center", - color: - completed || active - ? odysseyDesignTokens.HueNeutralWhite - : odysseyDesignTokens.HueNeutral700, - border: `1px solid ${ - completed - ? odysseyDesignTokens.HueGreen400 - : active - ? odysseyDesignTokens.HueBlue600 - : odysseyDesignTokens.HueNeutral600 - }`, - background: completed - ? odysseyDesignTokens.HueGreen400 - : active - ? odysseyDesignTokens.HueBlue600 - : "transparent", - transition: `all ${odysseyDesignTokens.TransitionDurationMain}`, + borderRadius: "50%", + display: "flex", + alignItems: "center", + justifyContent: "center", + transition: `color ${odysseyDesignTokens.TransitionDurationMain}, + background-color ${odysseyDesignTokens.TransitionDurationMain}, + border ${odysseyDesignTokens.TransitionDurationMain}`, - ".MuiStep-root:hover &": - !active && !completed && nonLinear - ? { border: `1px solid ${odysseyDesignTokens.HueNeutral900}` } - : undefined, + //Base color state + color: odysseyDesignTokens.HueNeutral700, + backgroundColor: "transparent", + border: "1px solid", + borderColor: `1px solid ${odysseyDesignTokens.HueNeutral600}`, - "& svg": { - width: - variant === "numeric" - ? odysseyDesignTokens.Spacing4 - : odysseyDesignTokens.Spacing3, - height: - variant === "numeric" - ? odysseyDesignTokens.Spacing4 - : odysseyDesignTokens.Spacing3, - }, -})); + ...(completed && { + color: odysseyDesignTokens.HueNeutralWhite, + backgroundColor: odysseyDesignTokens.HueGreen400, + borderColor: odysseyDesignTokens.HueGreen400, + }), + + ...(active && { + color: odysseyDesignTokens.HueNeutralWhite, + backgroundColor: odysseyDesignTokens.HueBlue600, + borderColor: odysseyDesignTokens.HueBlue600, + }), + + //Hover state for non-linear, non-active, non-completed + ".MuiStep-root:hover &": { + ...(!active && + !completed && + nonLinear && { + border: `1px solid ${odysseyDesignTokens.HueNeutral900}`, + }), + }, + + svg: { + ...(isNumeric + ? { + width: odysseyDesignTokens.Spacing4, + height: odysseyDesignTokens.Spacing4, + } + : { + width: odysseyDesignTokens.Spacing3, + height: odysseyDesignTokens.Spacing3, + }), + }, + }; +}); export const StepIcon = ({ active, diff --git a/packages/odyssey-react-mui/src/labs/Stepper/Stepper.test.tsx b/packages/odyssey-react-mui/src/labs/Stepper/Stepper.test.tsx index 40554f2cd0..72928ff915 100644 --- a/packages/odyssey-react-mui/src/labs/Stepper/Stepper.test.tsx +++ b/packages/odyssey-react-mui/src/labs/Stepper/Stepper.test.tsx @@ -11,13 +11,13 @@ */ import { render, screen } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; +import { userEvent } from "@testing-library/user-event"; import { vi } from "vitest"; -import { Stepper } from "./Stepper"; -import { StepperNavigation } from "./StepperNavigation"; -import { OdysseyProvider } from "../../OdysseyProvider"; -import { useOdysseyDesignTokens } from "../../OdysseyDesignTokensContext"; -import { StepperNavigationProps } from "./Stepper.types"; +import { Stepper } from "./Stepper.js"; +import { StepperNavigation } from "./StepperNavigation.js"; +import { OdysseyProvider } from "../../OdysseyProvider.js"; +import { useOdysseyDesignTokens } from "../../OdysseyDesignTokensContext.js"; +import { StepperNavigationProps } from "./Stepper.types.js"; import { useState } from "react"; const defaultSteps = [ diff --git a/packages/odyssey-react-mui/src/labs/Stepper/Stepper.tsx b/packages/odyssey-react-mui/src/labs/Stepper/Stepper.tsx index 5dd066edd3..302ab1516d 100644 --- a/packages/odyssey-react-mui/src/labs/Stepper/Stepper.tsx +++ b/packages/odyssey-react-mui/src/labs/Stepper/Stepper.tsx @@ -22,31 +22,41 @@ import { useTranslation } from "react-i18next"; import { DesignTokens, useOdysseyDesignTokens, -} from "../../OdysseyDesignTokensContext"; -import { StepIcon } from "./StepIcon"; -import { StepperProps } from "./Stepper.types"; +} from "../../OdysseyDesignTokensContext.js"; +import { StepIcon } from "./StepIcon.js"; +import { StepperProps } from "./Stepper.types.js"; import { shouldForwardStepDescriptionProps, shouldForwardStepperProps, - shouldForwardStepProps, -} from "./Stepper.utils"; + filterExcludedProps, +} from "./Stepper.utils.js"; const StyledStep = styled(MuiStep, { - shouldForwardProp: shouldForwardStepProps, + shouldForwardProp: filterExcludedProps([ + "odysseyDesignTokens", + "orientation", + "isClickable", + ]), })<{ odysseyDesignTokens: DesignTokens; orientation?: "horizontal" | "vertical"; isClickable: boolean; }>(({ orientation, odysseyDesignTokens, isClickable }) => ({ - flex: orientation === "vertical" ? 1 : "none", - padding: orientation === "vertical" ? `${odysseyDesignTokens.Spacing1} 0` : 0, - "&:focus-visible": isClickable - ? { - boxShadow: `0 0 0 2px ${odysseyDesignTokens.HueNeutralWhite}, 0 0 0 4px ${odysseyDesignTokens.PalettePrimaryMain}`, - outline: "2px solid transparent", - outlineOffset: "1px", - } - : undefined, + padding: 0, + + ...(orientation === "vertical" && { + flex: 1, + padding: `${odysseyDesignTokens.Spacing1} 0`, + }), + + //Clickable focus styles + ...(isClickable && { + "&:focus-visible": { + boxShadow: `0 0 0 2px ${odysseyDesignTokens.HueNeutralWhite}, 0 0 0 4px ${odysseyDesignTokens.PalettePrimaryMain}`, + outline: "2px solid transparent", + outlineOffset: "1px", + }, + }), })); const StepperContainer = styled(MuiStepper, { @@ -56,128 +66,137 @@ const StepperContainer = styled(MuiStepper, { odysseyDesignTokens: DesignTokens; orientation?: "horizontal" | "vertical"; stepVariant?: "numeric" | "nonNumeric"; -}>(({ nonLinear, odysseyDesignTokens, orientation, stepVariant }) => { - return { - alignItems: "start", - ...(orientation === "horizontal" && { - justifyContent: "flex-start", - "& .MuiStep-root": { - flex: "0 0 auto", - padding: `${odysseyDesignTokens.Spacing3} ${odysseyDesignTokens.Spacing4}`, - borderRadius: odysseyDesignTokens.BorderRadiusMain, - - "&:has(.Mui-active)": { - backgroundColor: odysseyDesignTokens.HueNeutralWhite, - }, +}>(({ nonLinear, odysseyDesignTokens, orientation, stepVariant }) => ({ + alignItems: "start", + padding: 0, + borderRadius: odysseyDesignTokens.BorderRadiusMain, - "&:not(:has(.Mui-active))": { - "&.Mui-completed": { - "&:hover": { - "& .MuiStepLabel-label": { - color: odysseyDesignTokens.HueNeutral800, - }, - "& .MuiStepLabel-labelContainer div": { - color: odysseyDesignTokens.HueNeutral800, - }, - }, - }, + //Horizontal orientation styles + ...(orientation === "horizontal" && { + justifyContent: "flex-start", + "& .MuiStep-root": { + flex: "0 0 auto", + padding: `${odysseyDesignTokens.Spacing3} ${odysseyDesignTokens.Spacing4}`, + borderRadius: odysseyDesignTokens.BorderRadiusMain, + + "&:has(.Mui-active)": { + backgroundColor: odysseyDesignTokens.HueNeutralWhite, + }, + + "&:not(:has(.Mui-active))": { + "&.Mui-completed": { "&:hover": { - backgroundColor: nonLinear - ? odysseyDesignTokens.HueNeutral100 - : "transparent", - cursor: nonLinear ? "pointer" : "default", "& .MuiStepLabel-label": { - color: nonLinear - ? odysseyDesignTokens.HueNeutral900 - : odysseyDesignTokens.HueNeutral600, + color: odysseyDesignTokens.HueNeutral800, }, "& .MuiStepLabel-labelContainer div": { - color: nonLinear - ? odysseyDesignTokens.HueNeutral800 - : odysseyDesignTokens.HueNeutral600, + color: odysseyDesignTokens.HueNeutral800, }, }, }, - }, - }), - padding: 0, - borderRadius: odysseyDesignTokens.BorderRadiusMain, - ...(orientation === "vertical" && { - width: "fit-content", - "& .MuiStep-root": { - position: "relative", - flex: 1, - paddingLeft: odysseyDesignTokens.Spacing5, - paddingTop: - stepVariant === "nonNumeric" ? 0 : odysseyDesignTokens.Spacing2, - paddingBottom: 0, - borderRadius: odysseyDesignTokens.BorderRadiusMain, - "&::before": { - content: '""', - position: "absolute", - left: stepVariant === "nonNumeric" ? "31.5px" : "35.5px", //Half pixel values used for absolute center positioning - top: stepVariant === "nonNumeric" ? "35px" : "40px", - height: - stepVariant === "nonNumeric" - ? "calc(100% - 35px)" - : "calc(100% - 40px)", - width: "1px", - backgroundColor: odysseyDesignTokens.HueNeutral200, - }, - "&:last-child::before": { - display: "none", - }, - "& .MuiStepLabel-labelContainer": { - minHeight: stepVariant === "nonNumeric" ? "20px" : "38px", //For proper verical alignment of the connector line - }, - "&:not(:has(.Mui-active))": { - "&.Mui-completed": { - "&:hover": { - "& .MuiStepLabel-label": { - color: odysseyDesignTokens.HueNeutral800, - }, - "& .MuiStepLabel-labelContainer div": { - color: odysseyDesignTokens.HueNeutral800, - }, - }, + "&:hover": { + ...(nonLinear && { + backgroundColor: odysseyDesignTokens.HueNeutral100, + cursor: "pointer", + }), + "& .MuiStepLabel-label": { + color: nonLinear + ? odysseyDesignTokens.HueNeutral900 + : odysseyDesignTokens.HueNeutral600, + }, + "& .MuiStepLabel-labelContainer div": { + color: nonLinear + ? odysseyDesignTokens.HueNeutral800 + : odysseyDesignTokens.HueNeutral600, }, + }, + }, + }, + }), + + //Vertical orientation styles + ...(orientation === "vertical" && { + width: "fit-content", + "& .MuiStep-root": { + position: "relative" as const, + flex: 1, + paddingLeft: odysseyDesignTokens.Spacing5, + paddingTop: + stepVariant === "nonNumeric" ? 0 : odysseyDesignTokens.Spacing2, + paddingBottom: 0, + borderRadius: odysseyDesignTokens.BorderRadiusMain, + + "&::before": { + content: '""', + position: "absolute" as const, + left: stepVariant === "nonNumeric" ? "31.5px" : "35.5px", + top: stepVariant === "nonNumeric" ? "35px" : "40px", + height: + stepVariant === "nonNumeric" + ? "calc(100% - 35px)" + : "calc(100% - 40px)", + width: "1px", + backgroundColor: odysseyDesignTokens.HueNeutral200, + }, + + "&:last-child::before": { + display: "none", + }, + + "& .MuiStepLabel-labelContainer": { + minHeight: stepVariant === "nonNumeric" ? "20px" : "38px", + }, + + "&:not(:has(.Mui-active))": { + "&.Mui-completed": { "&:hover": { - cursor: nonLinear ? "pointer" : "default", "& .MuiStepLabel-label": { - color: nonLinear - ? odysseyDesignTokens.HueNeutral900 - : odysseyDesignTokens.HueNeutral600, + color: odysseyDesignTokens.HueNeutral800, }, "& .MuiStepLabel-labelContainer div": { - color: nonLinear - ? odysseyDesignTokens.HueNeutral800 - : odysseyDesignTokens.HueNeutral600, + color: odysseyDesignTokens.HueNeutral800, }, }, }, + "&:hover": { + cursor: nonLinear ? "pointer" : "default", + "& .MuiStepLabel-label": { + color: nonLinear + ? odysseyDesignTokens.HueNeutral900 + : odysseyDesignTokens.HueNeutral600, + }, + "& .MuiStepLabel-labelContainer div": { + color: nonLinear + ? odysseyDesignTokens.HueNeutral800 + : odysseyDesignTokens.HueNeutral600, + }, + }, }, - }), - "& .MuiStepConnector-line": { - borderColor: odysseyDesignTokens.HueNeutral200, - borderWidth: "1px", - minWidth: odysseyDesignTokens.Spacing4, - minHeight: - orientation === "vertical" ? odysseyDesignTokens.Spacing4 : undefined, - }, - "& .MuiStepConnector-root": { - ...(orientation === "horizontal" - ? { - top: odysseyDesignTokens.Spacing5, - left: "calc(-50% + 20px)", - right: "calc(50% + 20px)", - margin: `auto ${odysseyDesignTokens.Spacing2}`, - } - : { - marginLeft: stepVariant === "nonNumeric" ? "31.5px" : "35.5px", - }), }, - }; -}); + }), + + //Connector styles + "& .MuiStepConnector-line": { + borderColor: odysseyDesignTokens.HueNeutral200, + borderWidth: "1px", + minWidth: odysseyDesignTokens.Spacing4, + ...(orientation === "vertical" && { + minHeight: odysseyDesignTokens.Spacing4, + }), + }, + + "& .MuiStepConnector-root": { + ...(orientation === "horizontal" && { + top: odysseyDesignTokens.Spacing5, + left: "calc(-50% + 20px)", + right: "calc(50% + 20px)", + margin: `auto ${odysseyDesignTokens.Spacing2}`, + }), + ...(orientation === "vertical" && { + marginLeft: stepVariant === "nonNumeric" ? "31.5px" : "35.5px", + }), + }, +})); const StepLabel = styled(MuiStepLabel, { shouldForwardProp: (prop) => @@ -208,40 +227,55 @@ const StepLabel = styled(MuiStepLabel, { orientation, variant, }) => ({ + "&.MuiStepLabel-root": { + paddingTop: 0, + paddingBottom: 0, + }, + "& .MuiStepLabel-iconContainer": { paddingRight: odysseyDesignTokens.Spacing3, - alignSelf: - orientation === "vertical" && variant === "nonNumeric" - ? "center" - : orientation === "horizontal" && variant === "numeric" - ? "flex-start" - : variant === "nonNumeric" && orientation === "horizontal" - ? "center" - : "flex-start", paddingTop: odysseyDesignTokens.Spacing0, + + ...(orientation === "vertical" && + variant === "numeric" && { + alignSelf: "flex-start", + }), + + ...(variant === "nonNumeric" && { + alignSelf: "center", + }), }, + + //Label styles "& .MuiStepLabel-label": { fontFamily: "inherit", fontSize: odysseyDesignTokens.TypographySizeHeading6, fontWeight: odysseyDesignTokens.TypographyWeightHeadingBold, lineHeight: odysseyDesignTokens.TypographyLineHeightHeading6, - color: active - ? odysseyDesignTokens.HueBlue600 - : completed - ? odysseyDesignTokens.HueNeutral800 - : odysseyDesignTokens.HueNeutral600, + + ...(active && { + color: odysseyDesignTokens.HueBlue600, + }), + + ...(completed && { + color: odysseyDesignTokens.HueNeutral800, + }), + + ...(!active && + !completed && { + color: odysseyDesignTokens.HueNeutral600, + }), "&.Mui-active": { color: odysseyDesignTokens.HueBlue600, }, }, - "&.MuiStepLabel-root": { paddingTop: 0, paddingBottom: 0 }, "&:hover": { - cursor: - !active && (nonLinear || (allowBackStep && completed)) - ? "pointer" - : "default", + ...(!active && + (nonLinear || (allowBackStep && completed)) && { + cursor: "pointer", + }), }, }), ); @@ -254,15 +288,23 @@ const StyledStepDescription = styled("div", { odysseyDesignTokens: DesignTokens; orientation?: "horizontal" | "vertical"; }>(({ active, completed, odysseyDesignTokens, orientation }) => ({ + //Base styles fontSize: odysseyDesignTokens.TypographySizeSubordinate, fontWeight: odysseyDesignTokens.TypographyWeightBody, lineHeight: odysseyDesignTokens.TypographyLineHeightBody, maxWidth: orientation === "horizontal" ? "200px" : "170px", - color: active - ? odysseyDesignTokens.HueBlue600 - : completed - ? odysseyDesignTokens.HueNeutral700 - : odysseyDesignTokens.HueNeutral600, + + //State-based colors + ...(active && { + color: odysseyDesignTokens.HueBlue600, + }), + ...(completed && { + color: odysseyDesignTokens.HueNeutral700, + }), + ...(!active && + !completed && { + color: odysseyDesignTokens.HueNeutral600, + }), })); const Stepper = ({ @@ -284,14 +326,14 @@ const Stepper = ({ const isCompleted = stepIndex < activeStep; if (isCompleted && allowBackStep) { - return true; // Allow clicking completed steps if allowBackStep is true + return true; //Allow clicking completed steps if allowBackStep is true } if (nonLinear) { - return !isCompleted; // Allow clicking future steps if nonLinear + return !isCompleted; //Allow clicking future steps if nonLinear } - return stepIndex === activeStep; // Only allow clicking current step in linear mode + return stepIndex === activeStep; //Only allow clicking current step in linear mode }, [activeStep, allowBackStep, nonLinear], ); @@ -307,7 +349,7 @@ const Stepper = ({ }, [onChange, isStepClickable], ); - // Generates unique IDs, used by aria attirbutes to associate and describe a step's description + //Generates unique IDs, used by aria attirbutes to associate and describe a step's description const stepDescriptionIds = useMemo( () => steps.map((_, index) => `step-description-${index}`), [steps], diff --git a/packages/odyssey-react-mui/src/labs/Stepper/Stepper.types.ts b/packages/odyssey-react-mui/src/labs/Stepper/Stepper.types.ts index d49dc0cf28..8d32bc1e0e 100644 --- a/packages/odyssey-react-mui/src/labs/Stepper/Stepper.types.ts +++ b/packages/odyssey-react-mui/src/labs/Stepper/Stepper.types.ts @@ -10,8 +10,8 @@ * See the License for the specific language governing permissions and limitations under the License. */ -import { HtmlProps } from "../../HtmlProps"; -import { DesignTokens } from "../../OdysseyDesignTokensContext"; +import { HtmlProps } from "../../HtmlProps.js"; +import { DesignTokens } from "../../OdysseyDesignTokensContext.js"; export type StepData = { /** @@ -24,7 +24,7 @@ export type StepData = { label: string; }; -// Define handler types for public API +//Define handler types for public API export type StepChangeHandler = (step: number) => void; export type StepperProps = { @@ -37,9 +37,10 @@ export type StepperProps = { */ allowBackStep?: boolean; /** - * Aria label for the stepper container, Falls back to "Progress steps" + * Aria label for the stepper container, + * Falls back to "Progress steps" */ - ariaLabel?: string; + ariaLabel?: HtmlProps["ariaLabel"]; /** * Button label for the next navigation button */ @@ -107,13 +108,18 @@ export type StepperNavigationProps = { */ isStepClickable: (step: number) => boolean; /** - * Callback fired when back button is clicked + * Callback fired when back button is clicked. + * @param currentStep - The index of the current step (before navigation) + * @param targetStep - The index of the previous step (where navigation will go) */ - onBack: () => void; + onBack: (currentStep: number, targetStep: number) => void; + /** - * Callback fired when next button is clicked + * Callback fired when next button is clicked. + * @param currentStep - The index of the current step (before navigation) + * @param targetStep - The index of the next step (where navigation will go) */ - onNext: () => void; + onNext: (currentStep: number, targetStep: number) => void; /** * Callback fired when a step dot is clicked */ diff --git a/packages/odyssey-react-mui/src/labs/Stepper/Stepper.utils.ts b/packages/odyssey-react-mui/src/labs/Stepper/Stepper.utils.ts index 9cbc049418..91068e89b3 100644 --- a/packages/odyssey-react-mui/src/labs/Stepper/Stepper.utils.ts +++ b/packages/odyssey-react-mui/src/labs/Stepper/Stepper.utils.ts @@ -14,11 +14,9 @@ export const createShouldForwardProp = (excludedProps: string[]) => (prop: string) => !excludedProps.includes(prop); -export const shouldForwardStepProps = createShouldForwardProp([ - "odysseyDesignTokens", - "orientation", - "isClickable", -]); +export const filterExcludedProps = + (excludedProps: string[]) => (prop: string) => + !excludedProps.includes(prop); export const shouldForwardStepIconContainerProps = createShouldForwardProp([ "completed", diff --git a/packages/odyssey-react-mui/src/labs/Stepper/StepperNavigation.tsx b/packages/odyssey-react-mui/src/labs/Stepper/StepperNavigation.tsx index eece67acb0..d21b159780 100644 --- a/packages/odyssey-react-mui/src/labs/Stepper/StepperNavigation.tsx +++ b/packages/odyssey-react-mui/src/labs/Stepper/StepperNavigation.tsx @@ -13,13 +13,13 @@ import styled from "@emotion/styled"; import { memo, useMemo } from "react"; import { useTranslation } from "react-i18next"; -import { Button } from "../../Buttons"; -import { StepperNavigationProps } from "./Stepper.types"; +import { Button } from "../../Buttons/index.js"; +import { StepperNavigationProps } from "./Stepper.types.js"; import { shouldForwardNavigationSectionProps, shouldForwardStepperDotProps, shouldForwardStepperNavigationProps, -} from "./Stepper.utils"; +} from "./Stepper.utils.js"; const StepperNavigationContainer = styled("div", { shouldForwardProp: shouldForwardStepperNavigationProps, @@ -29,7 +29,7 @@ const StepperNavigationContainer = styled("div", { display: "grid", gridTemplateColumns: "1fr auto 1fr", alignItems: "center", - marginTop: odysseyDesignTokens.Spacing3, + marginBlockStart: odysseyDesignTokens.Spacing3, gap: odysseyDesignTokens.Spacing3, })); @@ -51,35 +51,64 @@ const StyledStepperDot = styled("button", { odysseyDesignTokens: StepperNavigationProps["odysseyDesignTokens"]; isClickable: boolean; }>(({ status, odysseyDesignTokens, isClickable }) => ({ - width: odysseyDesignTokens.Spacing2, - height: odysseyDesignTokens.Spacing2, + //Base styles + position: "relative", + width: odysseyDesignTokens.Spacing5, + height: odysseyDesignTokens.Spacing5, padding: 0, - border: "1px solid", - borderRadius: "50%", - borderColor: - status === "current" - ? odysseyDesignTokens.HueNeutral500 - : status === "previous" - ? odysseyDesignTokens.HueNeutral400 - : odysseyDesignTokens.HueNeutral300, - background: - status === "current" ? odysseyDesignTokens.HueNeutral500 : "transparent", - margin: "0 2px", - cursor: isClickable ? "pointer" : "default", - "&:hover": isClickable - ? { - background: odysseyDesignTokens.HueNeutral300, - borderColor: odysseyDesignTokens.HueNeutral500, - } - : undefined, - "&:focus-visible": isClickable - ? { - boxShadow: `0 0 0 2px ${odysseyDesignTokens.HueNeutralWhite}, 0 0 0 4px ${odysseyDesignTokens.PalettePrimaryMain}`, - outline: "2px solid transparent", - outlineOffset: "1px", - } - : undefined, + border: "none", + background: "transparent", + + ...(isClickable && { + cursor: "pointer", + }), + + //Visual dot styles + "&::after": { + content: '""', + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: odysseyDesignTokens.Spacing2, + height: odysseyDesignTokens.Spacing2, + borderRadius: "50%", + border: "1px solid", + + //Status-based styles + ...(status === "current" && { + borderColor: odysseyDesignTokens.HueNeutral500, + background: odysseyDesignTokens.HueNeutral500, + }), + ...(status === "previous" && { + borderColor: odysseyDesignTokens.HueNeutral400, + background: "transparent", + }), + ...(status === "next" && { + borderColor: odysseyDesignTokens.HueNeutral300, + background: "transparent", + }), + }, + + ...(isClickable && { + //Hover styles - only apply when not current + "&:hover": { + ...(status !== "current" && { + "&::after": { + background: odysseyDesignTokens.HueNeutral300, + borderColor: odysseyDesignTokens.HueNeutral500, + }, + }), + }, + //Focus styles - apply for all clickable states + "&:focus-visible": { + boxShadow: `0 0 0 2px ${odysseyDesignTokens.HueNeutralWhite}, 0 0 0 4px ${odysseyDesignTokens.PalettePrimaryMain}`, + outline: "2px solid transparent", + outlineOffset: "1px", + }, + }), })); + const StyledNav = styled("nav")({ display: "flex", gap: "2px", @@ -167,7 +196,7 @@ const StepperNavigation = ({