From 553b2316b9122dfda92756f4d2cb04e1e1752e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Cort=C3=A9s?= Date: Thu, 16 Nov 2023 10:02:17 -0600 Subject: [PATCH 01/28] use mui theming --- frontend/packages/app/src/index.css | 4 - .../packages/core/src/AppProvider/themes.tsx | 113 ++---------------- frontend/packages/core/src/Theme/palette.tsx | 18 +-- frontend/packages/core/src/Theme/theme.tsx | 53 ++++---- frontend/packages/core/src/landing.tsx | 1 - 5 files changed, 45 insertions(+), 144 deletions(-) diff --git a/frontend/packages/app/src/index.css b/frontend/packages/app/src/index.css index e68f0302b5..dd13f809e6 100644 --- a/frontend/packages/app/src/index.css +++ b/frontend/packages/app/src/index.css @@ -4,10 +4,6 @@ html, body, #root, #App { min-height: 100vh; } -#App { - background-color: #f9fafe; -} - input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus, diff --git a/frontend/packages/core/src/AppProvider/themes.tsx b/frontend/packages/core/src/AppProvider/themes.tsx index 71a8f6dd41..98151bdc1a 100644 --- a/frontend/packages/core/src/AppProvider/themes.tsx +++ b/frontend/packages/core/src/AppProvider/themes.tsx @@ -1,15 +1,12 @@ import React from "react"; -import { ThemeProvider as EmotionThemeProvider } from "@emotion/react"; -import type { Theme as MuiTheme } from "@mui/material"; -import { - CssBaseline, - DeprecatedThemeOptions, - StyledEngineProvider, - ThemeProvider, -} from "@mui/material"; -import { createTheme, PaletteOptions, useTheme as useMuiTheme } from "@mui/material/styles"; +import type { Theme as MuiTheme, ThemeOptions } from "@mui/material"; +import { CssBaseline, StyledEngineProvider } from "@mui/material"; +import { PaletteOptions, useTheme as useMuiTheme } from "@mui/material/styles"; import { StylesProvider } from "@mui/styles"; +import { ThemeProvider } from "../Theme"; +import type { ClutchColors } from "../Theme/types"; + declare module "@mui/styles/defaultTheme" { interface DefaultTheme extends MuiTheme {} } @@ -23,96 +20,11 @@ interface ClutchPalette extends PaletteOptions { }; } -interface ClutchTheme extends DeprecatedThemeOptions { +interface ClutchTheme extends ThemeOptions { palette: ClutchPalette; + colors: ClutchColors; } -const WHITE = "#ffffff"; -const GRAY = "#D7DADB"; -const TEAL = "#02acbe"; -const RED = "#EF474D"; -const NAVY = "#2D3F50"; - -const lightPalette = (): ClutchPalette => { - return { - accent: { - main: TEAL, - }, - destructive: { - main: RED, - }, - primary: { - main: WHITE, - }, - secondary: { - main: NAVY, - }, - background: { - default: WHITE, - paper: WHITE, - }, - text: { - primary: NAVY, - secondary: GRAY, - }, - }; -}; - -const lightTheme = createTheme({ - // adaptV4Theme({ - palette: lightPalette(), - transitions: { - // https://material-ui.com/getting-started/faq/#how-can-i-disable-transitions-globally - create: () => "none", - }, - components: { - MuiButtonBase: { - // https://material-ui.com/getting-started/faq/#how-can-i-disable-the-ripple-effect-globally - defaultProps: { - disableRipple: true, - }, - }, - MuiCssBaseline: { - styleOverrides: { - body: { - // Default as MUI changed fontSize to 1rem - fontSize: "0.875rem", - }, - }, - }, - MuiSelect: { - styleOverrides: { - select: { - fontSize: "0.875rem", - height: "20px", - }, - }, - }, - MuiAccordion: { - styleOverrides: { - root: { - "&$expanded": { - // remove the additional margin rule when expanded so the original margin is used. - margin: null, - }, - }, - }, - }, - MuiTypography: { - styleOverrides: { - root: { - colorPrimary: { - color: NAVY, - }, - colorSecondary: { - color: GRAY, - }, - }, - }, - }, - }, -}); - const useTheme = () => { return useMuiTheme() as ClutchTheme; }; @@ -124,14 +36,11 @@ interface ThemeProps { } const Theme: React.FC = ({ children }) => { - const theme = lightTheme; return ( - - - - {children} - + + + {children} ); diff --git a/frontend/packages/core/src/Theme/palette.tsx b/frontend/packages/core/src/Theme/palette.tsx index 608126c1e4..53fb2cc4e2 100644 --- a/frontend/packages/core/src/Theme/palette.tsx +++ b/frontend/packages/core/src/Theme/palette.tsx @@ -1,7 +1,7 @@ import type { PaletteOptions as MuiPaletteOptions } from "@mui/material/styles"; import { alpha, TypeText } from "@mui/material/styles"; -import { DARK_COLORS, LIGHT_COLORS, STATE_OPACITY } from "./colors"; +import { DARK_COLORS, LIGHT_COLORS } from "./colors"; import type { ClutchColors, ThemeVariant } from "./types"; interface PaletteOptions extends MuiPaletteOptions { @@ -25,7 +25,6 @@ const darkText: Partial = { const palette = (variant: ThemeVariant): PaletteOptions => { const isLightMode = variant === "light"; const color = (isLightMode ? LIGHT_COLORS : DARK_COLORS) as ClutchColors; - const inverseColor = (isLightMode ? DARK_COLORS : LIGHT_COLORS) as ClutchColors; // TODO: add all clutch colors to "common colors" return { @@ -38,22 +37,9 @@ const palette = (variant: ThemeVariant): PaletteOptions => { success: color.green, grey: color.neutral, background: { - default: inverseColor.neutral[900], - // secondary + default: color.blue[50], }, text: isLightMode ? lightText : darkText, - action: { - active: color.blue[600], - activatedOpacity: STATE_OPACITY.pressed, - hover: color.blue[600], - hoverOpacity: STATE_OPACITY.hover, - selected: color.blue[600], - selectedOpacity: STATE_OPACITY.selected, - focus: color.blue[600], - focusOpacity: STATE_OPACITY.focused, - disabled: color.neutral[900], - disabledOpacity: STATE_OPACITY.disabled, - }, }; }; diff --git a/frontend/packages/core/src/Theme/theme.tsx b/frontend/packages/core/src/Theme/theme.tsx index e99a8ab429..db4e4bb91f 100644 --- a/frontend/packages/core/src/Theme/theme.tsx +++ b/frontend/packages/core/src/Theme/theme.tsx @@ -1,8 +1,6 @@ import React from "react"; -import { ThemeProvider as EmotionThemeProvider } from "@emotion/react"; import type { Theme as MuiTheme } from "@mui/material"; import { - adaptV4Theme, createTheme as createMuiTheme, CssBaseline, StyledEngineProvider, @@ -20,23 +18,23 @@ declare module "@mui/styles/defaultTheme" { // Create a Material UI theme is propagated to all children. const createTheme = (variant: ThemeVariant): MuiTheme => { - return createMuiTheme( - adaptV4Theme({ - // inject in custom colors - ...clutchColors(variant), - palette: palette(variant), - transitions: { - // https://material-ui.com/getting-started/faq/#how-can-i-disable-transitions-globally - create: () => "none", - }, - props: { - MuiButtonBase: { + return createMuiTheme({ + // inject in custom colors + ...clutchColors(variant), + palette: palette(variant), + transitions: { + // https://material-ui.com/getting-started/faq/#how-can-i-disable-transitions-globally + create: () => "none", + }, + components: { + MuiButtonBase: { + defaultProps: { // https://material-ui.com/getting-started/faq/#how-can-i-disable-the-ripple-effect-globally disableRipple: true, }, }, - overrides: { - MuiAccordion: { + MuiAccordion: { + styleOverrides: { root: { "&$expanded": { // remove the additional margin rule when expanded so the original margin is used. @@ -45,8 +43,23 @@ const createTheme = (variant: ThemeVariant): MuiTheme => { }, }, }, - }) - ); + MuiCssBaseline: { + styleOverrides: { + body: { + fontSize: "0.875rem", + }, + }, + }, + MuiSelect: { + styleOverrides: { + select: { + fontSize: "0.875rem", + height: "20px", + }, + }, + }, + }, + }); }; interface ThemeProps { @@ -57,10 +70,8 @@ interface ThemeProps { const ThemeProvider = ({ children, variant = "light" }: ThemeProps) => ( - - - {children} - + + {children} ); diff --git a/frontend/packages/core/src/landing.tsx b/frontend/packages/core/src/landing.tsx index 0a5ce86021..60407d9884 100644 --- a/frontend/packages/core/src/landing.tsx +++ b/frontend/packages/core/src/landing.tsx @@ -11,7 +11,6 @@ import { useAppContext } from "./Contexts"; import { useNavigate } from "./navigation"; const StyledLanding = styled.div({ - backgroundColor: "#f9f9fe", display: "flex", flexDirection: "column", flexGrow: 1, From 814d002016b5033b406f98634a87d84548657fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Cort=C3=A9s?= Date: Thu, 16 Nov 2023 10:25:17 -0600 Subject: [PATCH 02/28] update tests --- .../core/src/tests/__snapshots__/landing.test.tsx.snap | 2 +- .../core/src/tests/__snapshots__/not-found.test.tsx.snap | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/packages/core/src/tests/__snapshots__/landing.test.tsx.snap b/frontend/packages/core/src/tests/__snapshots__/landing.test.tsx.snap index 795d58d771..3cbcaeae9d 100644 --- a/frontend/packages/core/src/tests/__snapshots__/landing.test.tsx.snap +++ b/frontend/packages/core/src/tests/__snapshots__/landing.test.tsx.snap @@ -3,7 +3,7 @@ exports[`renders correctly 1`] = `

< 404 Not Found >
From b156baa23d67eb6d447c2e6de641112c530396cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Cort=C3=A9s?= Date: Thu, 30 Nov 2023 13:33:14 -0600 Subject: [PATCH 03/28] fix theme typing --- frontend/.storybook/preview.js | 10 ++-- .../packages/core/src/AppProvider/index.tsx | 6 +-- .../packages/core/src/AppProvider/themes.tsx | 46 ++++++------------- frontend/packages/core/src/Theme/colors.tsx | 3 -- frontend/packages/core/src/Theme/palette.tsx | 2 + frontend/packages/core/src/Theme/theme.tsx | 3 +- .../core/src/tests/not-found.test.tsx | 2 +- 7 files changed, 26 insertions(+), 46 deletions(-) diff --git a/frontend/.storybook/preview.js b/frontend/.storybook/preview.js index 4a63b2f2bf..f42f37126e 100644 --- a/frontend/.storybook/preview.js +++ b/frontend/.storybook/preview.js @@ -1,5 +1,5 @@ import React from "react"; -import { Theme } from "./../packages/core/src/AppProvider/themes"; +import Theme from "./../packages/core/src/AppProvider/themes"; export const decorators = [ (Story) => ( @@ -9,7 +9,7 @@ export const decorators = [ ), ]; -export const parameters = { +export const parameters = { backgrounds: { default: "clutch", values: [ @@ -21,6 +21,6 @@ export const parameters = { name: "light", value: "#ffffff", }, - ] - } -}; \ No newline at end of file + ], + }, +}; diff --git a/frontend/packages/core/src/AppProvider/index.tsx b/frontend/packages/core/src/AppProvider/index.tsx index d09503a29c..ecb3320e5c 100644 --- a/frontend/packages/core/src/AppProvider/index.tsx +++ b/frontend/packages/core/src/AppProvider/index.tsx @@ -17,7 +17,7 @@ import NotFound from "../not-found"; import { registeredWorkflows } from "./registrar"; import ShortLinkProxy, { ShortLinkBaseRoute } from "./short-link-proxy"; import ShortLinkStateHydrator from "./short-link-state-hydrator"; -import { Theme } from "./themes"; +import Theme from "./themes"; import type { ConfiguredRoute, Workflow, WorkflowConfiguration } from "./workflow"; import ErrorBoundary from "./workflow"; @@ -160,9 +160,7 @@ const ClutchApp = ({ return ( - {/* TODO: use the ThemeProvider for proper theming in the future - See https://github.com/lyft/clutch/commit/f6c6706b9ba29c4d4c3e5d0ac0c5d0f038203937 */} - +
diff --git a/frontend/packages/core/src/AppProvider/themes.tsx b/frontend/packages/core/src/AppProvider/themes.tsx index 98151bdc1a..30fb33b3b4 100644 --- a/frontend/packages/core/src/AppProvider/themes.tsx +++ b/frontend/packages/core/src/AppProvider/themes.tsx @@ -1,44 +1,28 @@ import React from "react"; -import type { Theme as MuiTheme, ThemeOptions } from "@mui/material"; import { CssBaseline, StyledEngineProvider } from "@mui/material"; -import { PaletteOptions, useTheme as useMuiTheme } from "@mui/material/styles"; import { StylesProvider } from "@mui/styles"; import { ThemeProvider } from "../Theme"; import type { ClutchColors } from "../Theme/types"; -declare module "@mui/styles/defaultTheme" { - interface DefaultTheme extends MuiTheme {} +declare module "@mui/material/styles" { + interface Theme { + colors: ClutchColors; + } + interface ThemeOptions { + colors?: ClutchColors; + } } -interface ClutchPalette extends PaletteOptions { - accent: { - main: string; - }; - destructive: { - main: string; - }; -} - -interface ClutchTheme extends ThemeOptions { - palette: ClutchPalette; - colors: ClutchColors; -} - -const useTheme = () => { - return useMuiTheme() as ClutchTheme; -}; - -interface ThemeProps { - // disabling temporarily as we figure out theming - // eslint-disable-next-line react/no-unused-prop-types - variant?: "light"; -} - -const Theme: React.FC = ({ children }) => { +const Theme: React.FC = ({ children }) => { + // Uncomment to use dark mode + /* // Detect system color mode + const prefersDarkMode = + window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches; */ + const prefersDarkMode = false; return ( - + {children} @@ -46,4 +30,4 @@ const Theme: React.FC = ({ children }) => { ); }; -export { Theme, useTheme }; +export default Theme; diff --git a/frontend/packages/core/src/Theme/colors.tsx b/frontend/packages/core/src/Theme/colors.tsx index 8db1d92f37..19a1d6682c 100644 --- a/frontend/packages/core/src/Theme/colors.tsx +++ b/frontend/packages/core/src/Theme/colors.tsx @@ -177,9 +177,6 @@ export const STATE_OPACITY: { [key in ComponentState]: number } = { const clutchColors = (variant: ThemeVariant) => { const colors = variant === "light" ? LIGHT_COLORS : DARK_COLORS; return { - typography: {}, - stroke: {}, - background: {}, ...colors, }; }; diff --git a/frontend/packages/core/src/Theme/palette.tsx b/frontend/packages/core/src/Theme/palette.tsx index 53fb2cc4e2..a45f58bc8d 100644 --- a/frontend/packages/core/src/Theme/palette.tsx +++ b/frontend/packages/core/src/Theme/palette.tsx @@ -29,6 +29,7 @@ const palette = (variant: ThemeVariant): PaletteOptions => { // TODO: add all clutch colors to "common colors" return { type: variant, + mode: variant, primary: color.blue, secondary: color.neutral, error: color.red, @@ -38,6 +39,7 @@ const palette = (variant: ThemeVariant): PaletteOptions => { grey: color.neutral, background: { default: color.blue[50], + paper: isLightMode ? color.neutral.A100 : "#1c1e3c", }, text: isLightMode ? lightText : darkText, }; diff --git a/frontend/packages/core/src/Theme/theme.tsx b/frontend/packages/core/src/Theme/theme.tsx index db4e4bb91f..06e8ad2fba 100644 --- a/frontend/packages/core/src/Theme/theme.tsx +++ b/frontend/packages/core/src/Theme/theme.tsx @@ -19,8 +19,7 @@ declare module "@mui/styles/defaultTheme" { // Create a Material UI theme is propagated to all children. const createTheme = (variant: ThemeVariant): MuiTheme => { return createMuiTheme({ - // inject in custom colors - ...clutchColors(variant), + colors: clutchColors(variant), palette: palette(variant), transitions: { // https://material-ui.com/getting-started/faq/#how-can-i-disable-transitions-globally diff --git a/frontend/packages/core/src/tests/not-found.test.tsx b/frontend/packages/core/src/tests/not-found.test.tsx index dad562df99..dadd2730b5 100644 --- a/frontend/packages/core/src/tests/not-found.test.tsx +++ b/frontend/packages/core/src/tests/not-found.test.tsx @@ -3,7 +3,7 @@ import { render } from "@testing-library/react"; import "@testing-library/jest-dom"; -import { Theme } from "../AppProvider/themes"; +import Theme from "../AppProvider/themes"; import NotFound from "../not-found"; test("renders correctly", () => { From e5922df0c92421afdfedc7c9abf4910ecdb5fd8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Cort=C3=A9s?= Date: Mon, 4 Dec 2023 13:26:41 -0600 Subject: [PATCH 04/28] add contrast color --- frontend/packages/core/src/AppProvider/themes.tsx | 3 +++ frontend/packages/core/src/Theme/palette.tsx | 2 ++ 2 files changed, 5 insertions(+) diff --git a/frontend/packages/core/src/AppProvider/themes.tsx b/frontend/packages/core/src/AppProvider/themes.tsx index 30fb33b3b4..3a7ed0061f 100644 --- a/frontend/packages/core/src/AppProvider/themes.tsx +++ b/frontend/packages/core/src/AppProvider/themes.tsx @@ -12,6 +12,9 @@ declare module "@mui/material/styles" { interface ThemeOptions { colors?: ClutchColors; } + interface Palette { + contrastColor: string; + } } const Theme: React.FC = ({ children }) => { diff --git a/frontend/packages/core/src/Theme/palette.tsx b/frontend/packages/core/src/Theme/palette.tsx index a45f58bc8d..d833e24e5f 100644 --- a/frontend/packages/core/src/Theme/palette.tsx +++ b/frontend/packages/core/src/Theme/palette.tsx @@ -6,6 +6,7 @@ import type { ClutchColors, ThemeVariant } from "./types"; interface PaletteOptions extends MuiPaletteOptions { type: ThemeVariant; + contrastColor: string; } const lightText: Partial = { @@ -42,6 +43,7 @@ const palette = (variant: ThemeVariant): PaletteOptions => { paper: isLightMode ? color.neutral.A100 : "#1c1e3c", }, text: isLightMode ? lightText : darkText, + contrastColor: isLightMode ? "#ffffff" : "#000000", // Either black or white depending on theme }; }; From ee86ed2786cd0467fb468b0de55f4b0a8ab0c214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Cort=C3=A9s?= Date: Tue, 5 Dec 2023 15:57:28 -0600 Subject: [PATCH 05/28] augment emotion theme with mui's --- frontend/packages/core/src/Theme/theme.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/packages/core/src/Theme/theme.tsx b/frontend/packages/core/src/Theme/theme.tsx index 06e8ad2fba..8ddf321376 100644 --- a/frontend/packages/core/src/Theme/theme.tsx +++ b/frontend/packages/core/src/Theme/theme.tsx @@ -16,6 +16,10 @@ declare module "@mui/styles/defaultTheme" { interface DefaultTheme extends MuiTheme {} } +declare module "@emotion/react" { + export interface Theme extends MuiTheme {} +} + // Create a Material UI theme is propagated to all children. const createTheme = (variant: ThemeVariant): MuiTheme => { return createMuiTheme({ From de59ef5e1bf2eb9d6d026a22cd515cd78ed5f3c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luc=C3=ADa=20Echenique=20=C3=81lvarez?= <25833665+lucechal14@users.noreply.github.com> Date: Wed, 6 Dec 2023 13:25:43 -0600 Subject: [PATCH 06/28] frontend: leverage theme palette for Feedback components (#2857) ### Description Replace hardcoded colors with theme palette references ### Testing Performed Manual --- frontend/packages/core/src/Feedback/alert.tsx | 68 ++++++----- .../core/src/Feedback/error/details.tsx | 54 ++++---- .../core/src/Feedback/error/index.tsx | 14 +-- frontend/packages/core/src/Feedback/hint.tsx | 9 +- .../packages/core/src/Feedback/tooltip.tsx | 9 +- .../core/src/NPS/tests/emojiRatings.test.tsx | 39 ++++-- .../core/src/NPS/tests/feedback.test.tsx | 115 +++++++++++++++--- .../core/src/NPS/tests/wizard.test.tsx | 19 ++- 8 files changed, 226 insertions(+), 101 deletions(-) diff --git a/frontend/packages/core/src/Feedback/alert.tsx b/frontend/packages/core/src/Feedback/alert.tsx index e114c59acb..baf004a5d5 100644 --- a/frontend/packages/core/src/Feedback/alert.tsx +++ b/frontend/packages/core/src/Feedback/alert.tsx @@ -4,24 +4,23 @@ import MuiErrorIcon from "@mui/icons-material/Error"; import MuiInfoIcon from "@mui/icons-material/Info"; import MuiWarningIcon from "@mui/icons-material/Warning"; import type { AlertProps as MuiAlertProps } from "@mui/lab"; -import { Alert as MuiAlert, AlertTitle as MuiAlertTitle, Grid } from "@mui/material"; +import { + Alert as MuiAlert, + AlertTitle as MuiAlertTitle, + alpha, + Grid, + useTheme, +} from "@mui/material"; import styled from "../styled"; -const backgroundColors = { - error: "linear-gradient(to right, #DB3615 8px, #FDE9E7 0%)", - info: "linear-gradient(to right, #3548D4 8px, #EBEDFB 0%)", - success: "linear-gradient(to right, #1E942E 8px, #E6F7EB 0%)", - warning: "linear-gradient(to right, #FFCC80 8px, #FFFDE6 0%)", -}; - const StyledAlert = styled(MuiAlert)<{ severity: MuiAlertProps["severity"] }>( - { + ({ theme }) => ({ borderRadius: "8px", padding: "16px", paddingLeft: "24px", paddingBottom: "20px", - color: "rgba(13, 16, 48, 0.6)", + color: alpha(theme.palette.secondary[900], 0.6), fontSize: "14px", overflow: "auto", ".MuiAlert-icon": { @@ -34,36 +33,45 @@ const StyledAlert = styled(MuiAlert)<{ severity: MuiAlertProps["severity"] }>( padding: "0", ".MuiAlertTitle-root": { marginBottom: "0", - color: "#0D1030", + color: theme.palette.secondary[900], }, }, - }, - props => ({ - background: backgroundColors[props.severity], - }) + }), + props => ({ theme }) => { + const backgroundColors = { + error: `linear-gradient(to right, ${theme.palette.error[600]} 8px, ${theme.palette.error[100]} 0%)`, + info: `linear-gradient(to right, ${theme.palette.primary[600]} 8px, ${theme.palette.primary[200]} 0%)`, + success: `linear-gradient(to right, ${theme.palette.success[500]} 8px, ${theme.palette.success[100]} 0%)`, + warning: `linear-gradient(to right, ${theme.palette.warning[500]} 8px, ${theme.palette.warning[100]} 0%)`, + }; + + return { + background: backgroundColors[props.severity], + }; + } ); -const ErrorIcon = styled(MuiErrorIcon)({ - color: "#db3716", -}); +const ErrorIcon = styled(MuiErrorIcon)(({ theme }) => ({ + color: theme.palette.error[700], +})); -const InfoIcon = styled(MuiInfoIcon)({ - color: "#3548d4", -}); +const InfoIcon = styled(MuiInfoIcon)(({ theme }) => ({ + color: theme.palette.primary[600], +})); -const SuccessIcon = styled(MuiSuccessIcon)({ - color: "#1e942d", -}); +const SuccessIcon = styled(MuiSuccessIcon)(({ theme }) => ({ + color: theme.palette.success[500], +})); -const WarningIcon = styled(MuiWarningIcon)({ - color: "#ffcc80", -}); +const WarningIcon = styled(MuiWarningIcon)(({ theme }) => ({ + color: theme.palette.warning[500], +})); -const AlertTitle = styled(MuiAlertTitle)({ - color: "#0D1030", +const AlertTitle = styled(MuiAlertTitle)(({ theme }) => ({ + color: theme.palette.secondary[900], fontWeight: 600, fontSize: "16px", -}); +})); const iconMappings = { error: , diff --git a/frontend/packages/core/src/Feedback/error/details.tsx b/frontend/packages/core/src/Feedback/error/details.tsx index 83775264b4..cb3ee3de99 100644 --- a/frontend/packages/core/src/Feedback/error/details.tsx +++ b/frontend/packages/core/src/Feedback/error/details.tsx @@ -5,9 +5,11 @@ import { Accordion as MuiAccordion, AccordionDetails as MuiAccordionDetails, AccordionSummary as MuiAccordionSummary, + alpha, Button, Grid, useControlled, + useTheme, } from "@mui/material"; import type { ClutchError } from "../../Network/errors"; @@ -19,11 +21,14 @@ import ErrorDetailsDialog from "./dialog"; const ERROR_DETAILS_RENDER_MAX = 4; -const ErrorDetailDivider = styled("div")({ - background: "linear-gradient(to right, #DB3615 8px, rgba(219, 54, 21, 0.4) 0%)", +const ErrorDetailDivider = styled("div")(({ theme }) => ({ + background: `linear-gradient(to right, ${theme.palette.error[600]} 8px, ${alpha( + theme.palette.error[600], + 0.4 + )} 0%)`, height: "1px", width: "100%", -}); +})); const Accordion = styled(MuiAccordion)({ "&.MuiAccordion-root.Mui-expanded": { @@ -35,9 +40,9 @@ const Accordion = styled(MuiAccordion)({ }); const AccordionSummary = styled(MuiAccordionSummary)<{ $expanded: boolean }>( - { - background: "linear-gradient(to right, #DB3615 8px, #FDE9E7 0%)", - color: "#0D1030", + ({ theme }) => ({ + background: `linear-gradient(to right, ${theme.palette.error[600]} 8px, ${theme.palette.error[100]} 0%)`, + color: theme.palette.secondary[900], fontSize: "14px", fontWeight: 400, padding: "12px 16px 12px 24px", @@ -49,56 +54,57 @@ const AccordionSummary = styled(MuiAccordionSummary)<{ $expanded: boolean }>( "&.MuiAccordionSummary-root.Mui-expanded": { minHeight: "unset", }, - }, + }), props => ({ borderBottomLeftRadius: props.$expanded ? "0" : "8px", borderBottomRightRadius: props.$expanded ? "0" : "8px", }) ); -const AccordionDetails = styled(MuiAccordionDetails)({ - background: "linear-gradient(to right, #DB3615 8px, #FFFFFF 0%)", +const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({ + background: `linear-gradient(to right, ${theme.palette.error[600]} 8px, ${theme.palette.common.white} 0%)`, padding: "0", paddingLeft: "8px", borderBottomLeftRadius: "8px", borderBottomRightRadius: "8px", display: "flex", flexDirection: "column", -}); +})); -const ListItem = styled("li")({ +const ListItem = styled("li")(({ theme }) => ({ "::marker": { - color: "rgba(13, 16, 48, 0.6)", + color: alpha(theme.palette.secondary[900], 0.6), }, padding: "2px 0", -}); +})); -const ErrorDetailContainer = styled("div")({ +const ErrorDetailContainer = styled("div")(({ theme }) => ({ width: "100%", - border: "1px solid #E7E7EA", + border: `1px solid ${theme.palette.secondary[200]}`, padding: "16px 16px 16px 24px", borderBottomRightRadius: "8px", borderTop: "unset", -}); +})); -const ErrorDetailText = styled("div")({ - color: "rgba(13, 16, 48, 0.6)", +const ErrorDetailText = styled("div")(({ theme }) => ({ + color: alpha(theme.palette.secondary[900], 0.6), fontSize: "14px", lineHeight: "24px", -}); +})); -const DialogButton = styled(Button)({ - color: "#3548D4", +const DialogButton = styled(Button)(({ theme }) => ({ + color: theme.palette.primary[600], fontWeight: 700, fontSize: "14px", padding: "9px 32px", -}); +})); interface ErrorDetailsProps { error: ClutchError; } const ErrorDetails = ({ error }: ErrorDetailsProps) => { + const theme = useTheme(); const [detailsOpen, setDetailsOpen] = React.useState(false); const [expanded, setExpanded] = useControlled({ controlled: undefined, @@ -139,7 +145,7 @@ const ErrorDetails = ({ error }: ErrorDetailsProps) => { {hasWrappedErrorDetails && (
- + The following errors were encountered:
    @@ -152,7 +158,7 @@ const ErrorDetails = ({ error }: ErrorDetailsProps) => { <> {renderItems.map((wrapped, idx) => { // TODO: This color should be colored according to status code - const color = "#DB3615"; + const color = theme.palette.secondary[600]; return ( // eslint-disable-next-line react/no-array-index-key diff --git a/frontend/packages/core/src/Feedback/error/index.tsx b/frontend/packages/core/src/Feedback/error/index.tsx index 6ed03ebd4b..263b815299 100644 --- a/frontend/packages/core/src/Feedback/error/index.tsx +++ b/frontend/packages/core/src/Feedback/error/index.tsx @@ -1,35 +1,35 @@ import React from "react"; -import styled from "@emotion/styled"; import MuiOpenInNewIcon from "@mui/icons-material/OpenInNew"; import RefreshIcon from "@mui/icons-material/Refresh"; -import { IconButton } from "@mui/material"; +import { alpha, IconButton } from "@mui/material"; import { Link } from "../../link"; import type { ClutchError } from "../../Network/errors"; import { isHelpDetails } from "../../Network/errors"; +import styled from "../../styled"; import { Alert } from "../alert"; import ErrorDetails from "./details"; -const ErrorSummaryContainer = styled.div({ +const ErrorSummaryContainer = styled("div")({ width: "100%", display: "flex", flexDirection: "column", }); -const ErrorSummaryMessage = styled.div({ +const ErrorSummaryMessage = styled("div")({ lineHeight: "24px", margin: "4px 0", flex: "1", }); -const ErrorSummaryLink = styled(Link)({ +const ErrorSummaryLink = styled(Link)(({ theme }) => ({ fontSize: "14px", fontWeight: 400, - color: "rgb(13,16,48,0.6)", + color: alpha(theme.palette.secondary[900], 0.6), display: "flex", alignItems: "center", -}); +})); const ErrorAlert = styled(Alert)(props => props["data-detailed"] diff --git a/frontend/packages/core/src/Feedback/hint.tsx b/frontend/packages/core/src/Feedback/hint.tsx index 467e540c50..6f9a70b82d 100644 --- a/frontend/packages/core/src/Feedback/hint.tsx +++ b/frontend/packages/core/src/Feedback/hint.tsx @@ -1,13 +1,14 @@ import React from "react"; -import styled from "@emotion/styled"; import HelpIcon from "@mui/icons-material/Help"; import { Popover, Typography } from "@mui/material"; -const HelpIconContainer = styled.div({ +import styled from "../styled"; + +const HelpIconContainer = styled("div")(({ theme }) => ({ display: "flex", - color: "#D7DADB", + color: theme.palette.secondary[300], padding: "5px", -}); +})); const Hint: React.FC = ({ children }) => { const [anchorEl, setAnchorEl] = React.useState(null); diff --git a/frontend/packages/core/src/Feedback/tooltip.tsx b/frontend/packages/core/src/Feedback/tooltip.tsx index 5933bc11f4..20db6ad443 100644 --- a/frontend/packages/core/src/Feedback/tooltip.tsx +++ b/frontend/packages/core/src/Feedback/tooltip.tsx @@ -1,16 +1,17 @@ import * as React from "react"; -import styled from "@emotion/styled"; import type { TooltipProps as MuiTooltipProps } from "@mui/material"; import { Tooltip as MuiTooltip } from "@mui/material"; +import styled from "../styled"; + const BaseTooltip = ({ className, ...props }: MuiTooltipProps) => ( ); // TODO: sync with Design on margins for each possible placement -const StyledTooltip = styled(BaseTooltip)((props: { maxwidth?: string }) => ({ +const StyledTooltip = styled(BaseTooltip)((props: { maxwidth?: string }) => ({ theme }) => ({ maxWidth: props.maxwidth, - backgroundColor: "#0D1030", + backgroundColor: theme.palette.secondary[900], borderRadius: "6px", "&.MuiTooltip-tooltipPlacementLeft": { margin: "0 2px", @@ -45,7 +46,7 @@ const Tooltip = ({ children, maxWidth = "300px", title, ...props }: TooltipProps }; // sets the spacing between multiline content -const TooltipContainer = styled.div({ +const TooltipContainer = styled("div")({ margin: "4px 0px", }); diff --git a/frontend/packages/core/src/NPS/tests/emojiRatings.test.tsx b/frontend/packages/core/src/NPS/tests/emojiRatings.test.tsx index 25b92d090a..dde2d1183d 100644 --- a/frontend/packages/core/src/NPS/tests/emojiRatings.test.tsx +++ b/frontend/packages/core/src/NPS/tests/emojiRatings.test.tsx @@ -5,6 +5,7 @@ import { capitalize } from "lodash"; import "@testing-library/jest-dom"; +import { ThemeProvider } from "../../Theme"; import EmojiRatings from "../emojiRatings"; const stringExample = [ @@ -29,7 +30,11 @@ const emojiMap = { }; test("will display a given list of emojis and their capitalized labels", () => { - render( {}} />); + render( + + {}} /> + + ); const elements = screen.getAllByRole("button"); @@ -41,7 +46,11 @@ test("will display a given list of emojis and their capitalized labels", () => { }); test("all emojis have an initial opacity of 0.5 when not selected", () => { - render( {}} />); + render( + + {}} /> + + ); const elements = screen.getAllByRole("button"); @@ -52,7 +61,11 @@ test("all emojis have an initial opacity of 0.5 when not selected", () => { test("emojis will have a tooltip show on hover", async () => { const user = userEvent.setup(); - render( {}} />); + render( + + {}} /> + + ); await user.hover(screen.getByLabelText(/Great/i)); expect(await screen.findByText("Great")).toHaveClass("MuiTooltip-tooltip"); @@ -62,7 +75,11 @@ test("emojis will have a tooltip show on hover", async () => { test("emojis will update opacity to 1 on selection", async () => { const user = userEvent.setup(); - render( {}} />); + render( + + {}} /> + + ); await user.click(screen.getByLabelText(/Great/i)); expect(screen.getByLabelText(/Great/i)).toHaveStyle("opacity: 1"); @@ -73,12 +90,14 @@ test("will return a given emoji on select", async () => { let selected: any = null; render( - { - selected = rating; - }} - /> + + { + selected = rating; + }} + /> + ); expect(selected).toBeNull(); diff --git a/frontend/packages/core/src/NPS/tests/feedback.test.tsx b/frontend/packages/core/src/NPS/tests/feedback.test.tsx index 1efcad5951..b94d9f26c8 100644 --- a/frontend/packages/core/src/NPS/tests/feedback.test.tsx +++ b/frontend/packages/core/src/NPS/tests/feedback.test.tsx @@ -7,6 +7,7 @@ import "@testing-library/jest-dom"; import contextValues from "../../Contexts/tests/testContext"; import { client } from "../../Network"; +import { ThemeProvider } from "../../Theme"; import NPSFeedback, { defaults, FEEDBACK_MAX_LENGTH } from "../feedback"; import { generateFeedbackTypes } from "../header"; @@ -53,13 +54,21 @@ describe("api success", () => { }); test("renders survey text prompt", async () => { - render(); + render( + + + + ); expect(await screen.findByText(defaultResult.prompt)).toBeVisible(); }); test("renders emojis to ", async () => { - render(); + render( + + + + ); const emojiButtons = await screen.findAllByRole("button"); @@ -70,7 +79,11 @@ describe("api success", () => { test("renders text placeholder", async () => { const user = userEvent.setup(); - render(); + render( + + + + ); await user.click(await screen.findByLabelText(/Great/i)); @@ -79,7 +92,11 @@ describe("api success", () => { test("displays a successful submission alert after submit", async () => { const user = userEvent.setup(); - render(); + render( + + + + ); await user.click(await screen.findByLabelText(/Great/i)); await user.click(await screen.findByText("Submit")); @@ -89,7 +106,11 @@ describe("api success", () => { test("sends feedback upon emoji selection change", async () => { const user = userEvent.setup(); - render(); + render( + + + + ); spy.mockClear(); expect(spy).not.toHaveBeenCalled(); @@ -117,13 +138,21 @@ describe("api failure", () => { }); test("renders default text prompt", async () => { - render(); + render( + + + + ); expect(await screen.findByText(defaults.prompt as string)).toBeVisible(); }); test("renders default emojis to ", async () => { - render(); + render( + + + + ); const emojiButtons = await screen.findAllByRole("button"); @@ -138,7 +167,11 @@ describe("api failure", () => { test("renders default text placeholder", async () => { const user = userEvent.setup(); - render(); + render( + + + + ); await user.click(await screen.findByLabelText(/Great/i)); @@ -174,20 +207,32 @@ describe("basic rendering", () => { }); test("will not display feedback form or submit unless emoji is selected", () => { - render(); + render( + + + + ); expect(screen.getByTestId("feedback-items-container").childElementCount).toBe(2); }); test("will display text prompt at top", async () => { - render(); + render( + + + + ); const feedbackItems = await screen.findByTestId("feedback-items-container"); expect(feedbackItems.childNodes[0].firstChild).toHaveTextContent(defaultResult.prompt); }); test("will display below prompt", async () => { - render(); + render( + + + + ); const feedbackItems = await screen.findByTestId("feedback-items-container"); feedbackItems.childNodes[1].childNodes.forEach(node => { @@ -197,7 +242,11 @@ describe("basic rendering", () => { test("displays a text form and submit buttons after selection of emoji", async () => { const user = userEvent.setup(); - render(); + render( + + + + ); expect(screen.getByTestId("feedback-items-container").childElementCount).toBe(2); await user.click(await screen.findByLabelText(/Great/i)); @@ -214,7 +263,11 @@ describe("basic rendering", () => { test("will update the length on feedback if input is given", async () => { const testValue = "Some Feedback Text"; const user = userEvent.setup(); - const { container } = render(); + const { container } = render( + + + + ); await user.click(await screen.findByLabelText(/Great/i)); @@ -235,7 +288,11 @@ describe("basic rendering", () => { test("will display an error on feedback if more input is given than maxLength", async () => { const testValue = generateRandomString(FEEDBACK_MAX_LENGTH + 1); const user = userEvent.setup(); - const { container } = render(); + const { container } = render( + + + + ); user.click(await screen.findByLabelText(/Great/i)); @@ -259,7 +316,11 @@ describe("basic rendering", () => { test("will disable the submit button upon error", async () => { const testValue = generateRandomString(FEEDBACK_MAX_LENGTH + 1); const user = userEvent.setup(); - render(); + render( + + + + ); user.click(await screen.findByLabelText(/Great/i)); @@ -293,14 +354,22 @@ describe("Wizard Origin Rendering", () => { }); test("renders correctly", () => { - render(); + render( + + + + ); expect(screen.getByTestId("feedback-component")).toBeVisible(); }); test("styles the submit button correctly", async () => { const user = userEvent.setup(); - render(); + render( + + + + ); user.click(await screen.findByLabelText(/Great/i)); @@ -328,14 +397,22 @@ describe("Header Origin Rendering", () => { }); test("renders correctly", () => { - render(); + render( + + + + ); expect(screen.getByTestId("feedback-component")).toBeVisible(); }); test("styles the submit button correctly", async () => { const user = userEvent.setup(); - render(); + render( + + + + ); user.click(await screen.findByLabelText(/Great/i)); diff --git a/frontend/packages/core/src/NPS/tests/wizard.test.tsx b/frontend/packages/core/src/NPS/tests/wizard.test.tsx index e29a6216d9..ad8021b7a5 100644 --- a/frontend/packages/core/src/NPS/tests/wizard.test.tsx +++ b/frontend/packages/core/src/NPS/tests/wizard.test.tsx @@ -5,6 +5,7 @@ import userEvent from "@testing-library/user-event"; import "@testing-library/jest-dom"; import { client } from "../../Network"; +import { ThemeProvider } from "../../Theme"; import { NPSWizard } from ".."; const defaultResult = { @@ -46,13 +47,21 @@ afterEach(() => { }); test("renders correctly", async () => { - render(); + render( + + + + ); expect(await screen.findByTestId("nps-wizard")).toBeVisible(); }); test("renders the container with a bluish background", async () => { - render(); + render( + + + + ); expect(await screen.findByTestId("nps-wizard")).toHaveStyle({ background: "#F9F9FE", @@ -61,7 +70,11 @@ test("renders the container with a bluish background", async () => { test("removes the bluish background", async () => { const user = userEvent.setup(); - render(); + render( + + + + ); const emojiButton = await screen.findByLabelText(/Great/i); await user.click(emojiButton); From e67a4c064a56b54151329c7aa6003b38683e7907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Cort=C3=A9s?= Date: Thu, 7 Dec 2023 09:39:25 -0600 Subject: [PATCH 07/28] frontend: leverage theme palette for AppLayout components (#2852) Replace hardcoded colors with theme palette references for components in AppLayout Also updated the `landing` component Testing Performed manual, unit tests --- .../packages/core/src/AppLayout/drawer.tsx | 42 +++++++------- .../packages/core/src/AppLayout/header.tsx | 12 ++-- .../core/src/AppLayout/notifications.tsx | 33 +++++------ .../packages/core/src/AppLayout/search.tsx | 56 ++++++++++--------- .../core/src/AppLayout/shortLinker.tsx | 19 ++++--- .../src/AppLayout/stories/header.stories.tsx | 1 + .../stories/notifications.stories.tsx | 6 +- .../AppLayout/stories/searchfield.stories.tsx | 7 ++- .../stories/user-information.stories.tsx | 6 +- .../tests/__snapshots__/layout.test.tsx.snap | 11 ++-- .../core/src/AppLayout/tests/layout.test.tsx | 5 +- frontend/packages/core/src/AppLayout/user.tsx | 51 ++++++++--------- .../packages/core/src/AppProvider/themes.tsx | 1 + frontend/packages/core/src/Theme/palette.tsx | 4 ++ frontend/packages/core/src/landing.tsx | 13 +++-- .../tests/__snapshots__/landing.test.tsx.snap | 2 +- .../packages/core/src/tests/landing.test.tsx | 5 +- 17 files changed, 146 insertions(+), 128 deletions(-) diff --git a/frontend/packages/core/src/AppLayout/drawer.tsx b/frontend/packages/core/src/AppLayout/drawer.tsx index fd0d412d40..7c8a7567c5 100644 --- a/frontend/packages/core/src/AppLayout/drawer.tsx +++ b/frontend/packages/core/src/AppLayout/drawer.tsx @@ -2,6 +2,7 @@ import * as React from "react"; import { Link as RouterLink, useLocation } from "react-router-dom"; import styled from "@emotion/styled"; import { + alpha, Avatar as MuiAvatar, Drawer as MuiDrawer, List, @@ -20,18 +21,19 @@ import { Popper, PopperItem } from "../popper"; import { filterHiddenRoutes, routesByGrouping, sortedGroupings, workflowByRoute } from "./utils"; // sidebar -const DrawerPanel = styled(MuiDrawer)({ +const DrawerPanel = styled(MuiDrawer)(({ theme }) => ({ width: "100px", overflowY: "auto", ".MuiDrawer-paper": { top: "unset", width: "inherit", - backgroundColor: "#FFFFFF", - boxShadow: "0px 5px 15px rgba(53, 72, 212, 0.2)", + backgroundColor: + theme.palette.mode === "light" ? theme.palette.common.white : theme.palette.background.paper, + boxShadow: `0px 5px 15px ${alpha(theme.palette.primary[400], 0.2)}`, position: "relative", display: "flex", }, -}); +})); // sidebar groupings const GroupList = styled(List)({ @@ -39,42 +41,42 @@ const GroupList = styled(List)({ }); const GroupListItem = styled(ListItemButton)<{ icon: number }>( - { + ({ theme }) => ({ flexDirection: "column", minHeight: "82px", padding: "16px 8px 16px 8px", height: "fit-content", "&:hover": { - backgroundColor: "#F5F6FD", + backgroundColor: theme.palette.primary[100], }, "&:active": { - backgroundColor: "#D7DAF6", + backgroundColor: theme.palette.primary[300], }, "&.Mui-selected": { - backgroundColor: "#EBEDFB", + backgroundColor: theme.palette.primary[200], "&:hover": { - backgroundColor: "#F5F6FD", + backgroundColor: theme.palette.primary[100], }, "&:active": { - backgroundColor: "#D7DAF6", + backgroundColor: theme.palette.primary[300], }, }, - }, + }), props => ({ // avatar and label "&:hover, &:active, &.Mui-selected": { ".MuiAvatar-root": { - backgroundColor: props.icon ? "unset" : "#3548D4", + backgroundColor: props.icon ? "unset" : props.theme.palette.primary[600], }, ".MuiTypography-root": { - color: "#3548D4", + color: props.theme.palette.primary[600], }, }, }) ); -const GroupHeading = styled(Typography)({ - color: "rgba(13, 16, 48, 0.6)", +const GroupHeading = styled(Typography)(({ theme }) => ({ + color: alpha(theme.palette.secondary[900], 0.6), fontWeight: 500, fontSize: "14px", lineHeight: "18px", @@ -83,19 +85,19 @@ const GroupHeading = styled(Typography)({ width: "100%", textOverflow: "ellipsis", overflow: "hidden", -}); +})); const IconAvatar = styled(MuiAvatar)({ height: "24px", width: "24px", }); -const Avatar = styled(IconAvatar)({ - background: "rgba(13, 16, 48, 0.6)", - color: "#FFFFFF", +const Avatar = styled(IconAvatar)(({ theme }) => ({ + background: alpha(theme.palette.secondary[900], 0.6), + color: theme.palette.contrastColor, fontSize: "14px", borderRadius: "4px", -}); +})); interface GroupProps { heading: string; diff --git a/frontend/packages/core/src/AppLayout/header.tsx b/frontend/packages/core/src/AppLayout/header.tsx index e4d1410381..3575877488 100644 --- a/frontend/packages/core/src/AppLayout/header.tsx +++ b/frontend/packages/core/src/AppLayout/header.tsx @@ -45,23 +45,23 @@ interface HeaderProps extends AppConfiguration { userInfo?: boolean; } -const AppBar = styled(MuiAppBar)({ +const AppBar = styled(MuiAppBar)(({ theme }) => ({ minWidth: "fit-content", - background: "linear-gradient(90deg, #38106b 4.58%, #131c5f 89.31%)", + background: theme.palette.headerGradient, zIndex: 1201, height: APP_BAR_HEIGHT, -}); +})); // Since the AppBar is fixed we need a div to take up its height in order to push other content down. const ClearAppBar = styled.div({ height: APP_BAR_HEIGHT }); -const Title = styled(Typography)({ +const Title = styled(Typography)(({ theme }) => ({ margin: "12px 0px 12px 8px", fontWeight: "bold", fontSize: "30px", paddingLeft: "5px", - color: "rgba(255, 255, 255, 0.87)", -}); + color: theme.palette.common.white, +})); const StyledLogo = styled("img")({ width: "48px", diff --git a/frontend/packages/core/src/AppLayout/notifications.tsx b/frontend/packages/core/src/AppLayout/notifications.tsx index 5a03e9ab46..3df7c806e2 100644 --- a/frontend/packages/core/src/AppLayout/notifications.tsx +++ b/frontend/packages/core/src/AppLayout/notifications.tsx @@ -2,6 +2,7 @@ import React from "react"; import styled from "@emotion/styled"; import NotificationsIcon from "@mui/icons-material/Notifications"; import { + alpha, ClickAwayListener, Grow as MuiGrow, IconButton, @@ -12,48 +13,48 @@ import { Popper as MuiPopper, } from "@mui/material"; -const StyledNotificationsIcon = styled(IconButton)({ - color: "#ffffff", +const StyledNotificationsIcon = styled(IconButton)(({ theme }) => ({ + color: theme.palette.contrastColor, marginRight: "8px", padding: "12px", "&:hover": { - background: "#2d3db4", + background: theme.palette.primary[600], }, "&:active": { - background: "#2938a5", + background: theme.palette.primary[700], }, -}); +})); const Popper = styled(MuiPopper)({ padding: "0 12px", marginLeft: "12px", }); -const Paper = styled(MuiPaper)({ +const Paper = styled(MuiPaper)(({ theme }) => ({ width: "242px", - border: "1px solid #E7E7EA", - boxShadow: "0px 5px 15px rgba(53, 72, 212, 0.2)", -}); + border: `1px solid ${theme.palette.secondary[100]}`, + boxShadow: `0px 5px 15px ${alpha(theme.palette.primary[600], 0.2)}`, +})); -const MenuItem = styled(MuiMenuItem)({ +const MenuItem = styled(MuiMenuItem)(({ theme }) => ({ height: "48px", padding: "12px", "&:hover": { - backgroundColor: "#E7E7EA", + backgroundColor: theme.palette.secondary[200], }, "&:active": { - backgroundColor: "#EBEDFB", + backgroundColor: theme.palette.primary[200], }, -}); +})); -const ListItemText = styled(MuiListItemText)({ +const ListItemText = styled(MuiListItemText)(({ theme }) => ({ margin: "0px", ".MuiTypography-root": { - color: "#0D1030", + color: theme.palette.secondary[900], fontSize: "14px", lineHeight: "24px", }, -}); +})); const Grow = styled(MuiGrow)((props: { placement: string }) => ({ transformOrigin: props.placement, diff --git a/frontend/packages/core/src/AppLayout/search.tsx b/frontend/packages/core/src/AppLayout/search.tsx index cb67fb3d23..1dde6cec65 100644 --- a/frontend/packages/core/src/AppLayout/search.tsx +++ b/frontend/packages/core/src/AppLayout/search.tsx @@ -3,6 +3,7 @@ import styled from "@emotion/styled"; import CloseIcon from "@mui/icons-material/Close"; import SearchIcon from "@mui/icons-material/Search"; import { + alpha, ClickAwayListener, Grid, Icon, @@ -10,6 +11,7 @@ import { InputAdornment as MuiInputAdornment, Popper as MuiPopper, TextField, + TextFieldProps, Typography, } from "@mui/material"; import type { AutocompleteRenderInputParams } from "@mui/material/Autocomplete"; @@ -25,7 +27,7 @@ import { filterHiddenRoutes, searchIndexes } from "./utils"; const hotKey = "/"; -const InputField = styled(TextField)({ +const InputField: React.FC = styled(TextField)(({ theme }) => ({ // input field maxWidth: "551px", minWidth: "551px", @@ -34,13 +36,13 @@ const InputField = styled(TextField)({ }, ".MuiInputBase-root": { height: "46px", - border: "1px solid #3548d4", + border: `1px solid ${theme.palette.primary[600]}`, borderRadius: "4px", - background: "#ffffff", + background: theme.palette.contrastColor, }, // input text color ".MuiAutocomplete-input": { - color: "#0d1030", + color: theme.palette.secondary[900], }, // close icon's container @@ -51,14 +53,14 @@ const InputField = styled(TextField)({ borderRadius: "30px", marginRight: "8px", "&:hover": { - background: "#e7e7ea", + background: theme.palette.secondary[200], }, "&:active": { - background: "#DBDBE0", + background: theme.palette.secondary[300], }, }, }, -}); +})); // search's result options container const ResultGrid = styled(Grid)({ @@ -67,42 +69,42 @@ const ResultGrid = styled(Grid)({ }); // search's result options -const ResultLabel = styled(Typography)({ - color: "#0d1030", +const ResultLabel = styled(Typography)(({ theme }) => ({ + color: theme.palette.secondary[900], fontSize: "14px", -}); +})); // main search icon on header -const SearchIconButton = styled(IconButton)({ - color: "#ffffff", +const SearchIconButton = styled(IconButton)(({ theme }) => ({ + color: theme.palette.contrastColor, fontSize: "24px", padding: "12px", marginRight: "8px", "&:hover": { - background: "#2d3db4", + background: theme.palette.primary[600], }, "&:active": { - background: "#2938a5", + background: theme.palette.primary[700], }, -}); +})); // search icon in input field -const StartInputAdornment = styled(MuiInputAdornment)({ - color: "#0c0b31", +const StartInputAdornment = styled(MuiInputAdornment)(({ theme }) => ({ + color: theme.palette.secondary[900], marginLeft: "8px", -}); +})); // closed icon svg -const StyledCloseIcon = styled(Icon)({ - color: "#0c0b31", +const StyledCloseIcon = styled(Icon)(({ theme }) => ({ + color: theme.palette.secondary[900], fontSize: "24px", -}); +})); // popper containing the search result options -const Popper = styled(MuiPopper)({ +const Popper = styled(MuiPopper)(({ theme }) => ({ ".MuiPaper-root": { - border: "1px solid #e7e7ea", - boxShadow: "0px 5px 15px rgba(53, 72, 212, 0.2)", + border: `1px solid ${theme.palette.secondary[700]}`, + boxShadow: `0px 5px 15px ${alpha(theme.palette.primary[400], 0.2)}`, "> .MuiAutocomplete-listbox": { "> .MuiAutocomplete-option": { @@ -110,16 +112,16 @@ const Popper = styled(MuiPopper)({ padding: "0px", "&.Mui-focused": { - background: "#ebedfb", + background: theme.palette.secondary[800], }, }, }, }, ".MuiAutocomplete-noOptions": { fontSize: "14px", - color: "#0d1030", + color: theme.palette.secondary[900], }, -}); +})); const renderPopper = props => { return ; diff --git a/frontend/packages/core/src/AppLayout/shortLinker.tsx b/frontend/packages/core/src/AppLayout/shortLinker.tsx index 8da62b9726..939443cf8f 100644 --- a/frontend/packages/core/src/AppLayout/shortLinker.tsx +++ b/frontend/packages/core/src/AppLayout/shortLinker.tsx @@ -3,6 +3,7 @@ import { useLocation } from "react-router-dom"; import type { clutch as IClutch } from "@clutch-sh/api"; import LinkIcon from "@mui/icons-material/Link"; import { + alpha, ClickAwayListener, Grid, Grow as MuiGrow, @@ -33,28 +34,28 @@ const Popper = styled(MuiPopper)({ zIndex: 1201, }); -const Paper = styled(MuiPaper)({ +const Paper = styled(MuiPaper)(({ theme }) => ({ width: "400px", height: "100px", padding: "15px", - boxShadow: "0px 15px 35px rgba(53, 72, 212, 0.2)", + boxShadow: `0px 5px 35px ${alpha(theme.palette.primary[400], 0.2)}`, borderRadius: "8px", -}); +})); const StyledLinkIcon = styled(IconButton)<{ $open: boolean }>( - { - color: "#ffffff", + ({ theme }) => ({ + color: theme.palette.contrastColor, marginRight: "8px", padding: "12px", "&:hover": { - background: "#2d3db4", + background: theme.palette.primary[600], }, "&:active": { - background: "#2938a5", + background: theme.palette.primary[700], }, - }, + }), props => ({ - background: props.$open ? "#2d3db4" : "unset", + background: props.$open ? props.theme.palette.primary[600] : "unset", }) ); diff --git a/frontend/packages/core/src/AppLayout/stories/header.stories.tsx b/frontend/packages/core/src/AppLayout/stories/header.stories.tsx index 1df0cd3b66..6ffc6ede49 100644 --- a/frontend/packages/core/src/AppLayout/stories/header.stories.tsx +++ b/frontend/packages/core/src/AppLayout/stories/header.stories.tsx @@ -24,6 +24,7 @@ export default { developer: { name: "Lyft", contactUrl: "mailto:hello@clutch.sh" }, displayName: "EC2", group: "AWS", + icon: { path: "" }, path: "ec2", routes: [ { diff --git a/frontend/packages/core/src/AppLayout/stories/notifications.stories.tsx b/frontend/packages/core/src/AppLayout/stories/notifications.stories.tsx index 5db23216b1..7c3d4da954 100644 --- a/frontend/packages/core/src/AppLayout/stories/notifications.stories.tsx +++ b/frontend/packages/core/src/AppLayout/stories/notifications.stories.tsx @@ -11,10 +11,10 @@ export default { component: NotificationsComponent, } as Meta; -const Grid = styled(MuiGrid)({ +const Grid = styled(MuiGrid)(({ theme }) => ({ height: "64px", - backgroundColor: "#131C5F", -}); + backgroundColor: theme.palette.primary[900], +})); const Template = (props: NotificationsProp) => ( diff --git a/frontend/packages/core/src/AppLayout/stories/searchfield.stories.tsx b/frontend/packages/core/src/AppLayout/stories/searchfield.stories.tsx index 414a6c5ca3..eb4b6d3953 100644 --- a/frontend/packages/core/src/AppLayout/stories/searchfield.stories.tsx +++ b/frontend/packages/core/src/AppLayout/stories/searchfield.stories.tsx @@ -28,6 +28,7 @@ export default { developer: { name: "Lyft", contactUrl: "mailto:hello@clutch.sh" }, displayName: "EC2", group: "AWS", + icon: { path: "" }, path: "ec2", routes: [ { @@ -59,10 +60,10 @@ export default { ], } as Meta; -const Grid = styled(MuiGrid)({ +const Grid = styled(MuiGrid)(({ theme }) => ({ height: "64px", - backgroundColor: "#131C5F", -}); + backgroundColor: theme.palette.primary[900], +})); const Template = () => ( diff --git a/frontend/packages/core/src/AppLayout/stories/user-information.stories.tsx b/frontend/packages/core/src/AppLayout/stories/user-information.stories.tsx index 1a31391e75..57af53858f 100644 --- a/frontend/packages/core/src/AppLayout/stories/user-information.stories.tsx +++ b/frontend/packages/core/src/AppLayout/stories/user-information.stories.tsx @@ -11,10 +11,10 @@ export default { component: UserInformationComponent, } as Meta; -const Grid = styled(MuiGrid)({ +const Grid = styled(MuiGrid)(({ theme }) => ({ height: "64px", - backgroundColor: "#131C5F", -}); + backgroundColor: theme.palette.primary[900], +})); const Template = (props: UserInformationProps) => ( diff --git a/frontend/packages/core/src/AppLayout/tests/__snapshots__/layout.test.tsx.snap b/frontend/packages/core/src/AppLayout/tests/__snapshots__/layout.test.tsx.snap index dc4a3f0555..7b2a590513 100644 --- a/frontend/packages/core/src/AppLayout/tests/__snapshots__/layout.test.tsx.snap +++ b/frontend/packages/core/src/AppLayout/tests/__snapshots__/layout.test.tsx.snap @@ -7,7 +7,7 @@ exports[`renders correctly 1`] = ` data-testid="app-layout-component" >

    clutch

    @@ -82,18 +82,15 @@ exports[`renders correctly 1`] = ` />
diff --git a/frontend/packages/core/src/AppLayout/tests/layout.test.tsx b/frontend/packages/core/src/AppLayout/tests/layout.test.tsx index 4d250afc57..353f75ad34 100644 --- a/frontend/packages/core/src/AppLayout/tests/layout.test.tsx +++ b/frontend/packages/core/src/AppLayout/tests/layout.test.tsx @@ -6,6 +6,7 @@ import "@testing-library/jest-dom"; import * as appContext from "../../Contexts/app-context"; import { client } from "../../Network"; +import { ThemeProvider } from "../../Theme"; import AppLayout from ".."; jest.spyOn(appContext, "useAppContext").mockReturnValue({ workflows: [] }); @@ -20,7 +21,9 @@ jest.spyOn(client, "post").mockReturnValue( test("renders correctly", async () => { const { asFragment } = render( - + + + ); diff --git a/frontend/packages/core/src/AppLayout/user.tsx b/frontend/packages/core/src/AppLayout/user.tsx index a5ad3b1aec..b70dfed190 100644 --- a/frontend/packages/core/src/AppLayout/user.tsx +++ b/frontend/packages/core/src/AppLayout/user.tsx @@ -1,6 +1,7 @@ import React from "react"; import styled from "@emotion/styled"; import { + alpha, Avatar as MuiAvatar, ClickAwayListener, Divider as MuiDivider, @@ -17,13 +18,13 @@ import Cookies from "js-cookie"; import jwtDecode from "jwt-decode"; import * as _ from "lodash"; -const UserPhoto = styled(IconButton)({ +const UserPhoto = styled(IconButton)(({ theme }) => ({ padding: "12px", "&:hover": { - background: "#2d3db4", + background: theme.palette.primary[600], }, "&:active": { - background: "#2938a5", + background: theme.palette.primary[700], }, // avatar on header ".MuiAvatar-root": { @@ -32,20 +33,20 @@ const UserPhoto = styled(IconButton)({ fontSize: "14px", lineHeight: "18px", }, -}); +})); // header and menu avatar -const Avatar = styled(MuiAvatar)({ - backgroundColor: "#727FE1", - color: "#FFFFFF", +const Avatar = styled(MuiAvatar)(({ theme }) => ({ + backgroundColor: theme.palette.primary[500], + color: theme.palette.contrastColor, fontWeight: 500, -}); +})); -const Paper = styled(MuiPaper)({ +const Paper = styled(MuiPaper)(({ theme }) => ({ width: "242px", - border: "1px solid #E7E7EA", - boxShadow: "0px 5px 15px rgba(53, 72, 212, 0.2)", -}); + border: `1px solid ${theme.palette.secondary[100]}`, + boxShadow: `0px 5px 15px ${alpha(theme.palette.primary[600], 0.2)}`, +})); const Popper = styled(MuiPopper)({ padding: "0 12px", @@ -53,18 +54,18 @@ const Popper = styled(MuiPopper)({ zIndex: 1201, }); -const MenuList = styled(MuiMenuList)({ +const MenuList = styled(MuiMenuList)(({ theme }) => ({ padding: "0px", borderRadius: "4px", ".MuiMenuItem-root": { "&:hover": { - backgroundColor: "#E7E7EA", + backgroundColor: theme.palette.secondary[200], }, "&:active": { - backgroundColor: "#EBEDFB", + backgroundColor: theme.palette.primary[200], }, }, -}); +})); // user details menu item const AvatarMenuItem = styled(MuiMenuItem)({ @@ -85,15 +86,15 @@ const AvatarListItemIcon = styled(ListItemIcon)({ }, }); -const AvatarListItemText = styled(MuiListItemText)({ +const AvatarListItemText = styled(MuiListItemText)(({ theme }) => ({ paddingLeft: "16px", margin: "0px", ".MuiTypography-root": { - color: "rgba(13, 16, 48, 0.6)", + color: alpha(theme.palette.secondary[900], 0.9), fontSize: "14px", lineHeight: "24px", }, -}); +})); // default menu items const MenuItem = styled(MuiMenuItem)({ @@ -101,18 +102,18 @@ const MenuItem = styled(MuiMenuItem)({ padding: "12px", }); -const ListItemText = styled(MuiListItemText)({ +const ListItemText = styled(MuiListItemText)(({ theme }) => ({ margin: "0px", ".MuiTypography-root": { - color: "#0D1030", + color: theme.palette.secondary[900], fontSize: "14px", lineHeight: "24px", }, -}); +})); -const Divider = styled(MuiDivider)({ - backgroundColor: "#E7E7EA", -}); +const Divider = styled(MuiDivider)(({ theme }) => ({ + backgroundColor: theme.palette.secondary[100], +})); const Grow = styled(MuiGrow)((props: { placement: string }) => ({ transformOrigin: props.placement, diff --git a/frontend/packages/core/src/AppProvider/themes.tsx b/frontend/packages/core/src/AppProvider/themes.tsx index 3a7ed0061f..f5d1376931 100644 --- a/frontend/packages/core/src/AppProvider/themes.tsx +++ b/frontend/packages/core/src/AppProvider/themes.tsx @@ -14,6 +14,7 @@ declare module "@mui/material/styles" { } interface Palette { contrastColor: string; + headerGradient: string; } } diff --git a/frontend/packages/core/src/Theme/palette.tsx b/frontend/packages/core/src/Theme/palette.tsx index d833e24e5f..72f21b22a9 100644 --- a/frontend/packages/core/src/Theme/palette.tsx +++ b/frontend/packages/core/src/Theme/palette.tsx @@ -7,6 +7,7 @@ import type { ClutchColors, ThemeVariant } from "./types"; interface PaletteOptions extends MuiPaletteOptions { type: ThemeVariant; contrastColor: string; + headerGradient: string; } const lightText: Partial = { @@ -44,6 +45,9 @@ const palette = (variant: ThemeVariant): PaletteOptions => { }, text: isLightMode ? lightText : darkText, contrastColor: isLightMode ? "#ffffff" : "#000000", // Either black or white depending on theme + headerGradient: isLightMode + ? "linear-gradient(90deg, #38106b 4.58%, #131c5f 89.31%)" + : "#0D1030", }; }; diff --git a/frontend/packages/core/src/landing.tsx b/frontend/packages/core/src/landing.tsx index 60407d9884..1d66ef9a94 100644 --- a/frontend/packages/core/src/landing.tsx +++ b/frontend/packages/core/src/landing.tsx @@ -1,6 +1,6 @@ import React from "react"; import styled from "@emotion/styled"; -import { Grid } from "@mui/material"; +import { alpha, Grid } from "@mui/material"; import Typography from "@mui/material/Typography"; import { userId } from "./AppLayout/user"; @@ -10,13 +10,14 @@ import { LandingCard } from "./card"; import { useAppContext } from "./Contexts"; import { useNavigate } from "./navigation"; -const StyledLanding = styled.div({ +const StyledLanding = styled.div(({ theme }) => ({ display: "flex", flexDirection: "column", flexGrow: 1, "& .welcome": { display: "flex", - backgroundColor: "white", + backgroundColor: + theme.palette.mode === "light" ? theme.palette.common.white : theme.palette.background.paper, padding: "32px 80px", }, @@ -32,19 +33,19 @@ const StyledLanding = styled.div({ "& .welcome .title": { fontWeight: "bold", fontSize: "22px", - color: "#0d1030", + color: theme.palette.text.primary[900], }, "& .welcome .subtitle": { fontSize: "16px", fontWeight: "normal", - color: "rgba(13, 16, 48, 0.6)", + color: alpha(theme.palette.secondary[900], 0.6), }, "& .content": { padding: "32px 80px", }, -}); +})); const Landing: React.FC<{}> = () => { const navigate = useNavigate(); diff --git a/frontend/packages/core/src/tests/__snapshots__/landing.test.tsx.snap b/frontend/packages/core/src/tests/__snapshots__/landing.test.tsx.snap index 3cbcaeae9d..48f4d51d23 100644 --- a/frontend/packages/core/src/tests/__snapshots__/landing.test.tsx.snap +++ b/frontend/packages/core/src/tests/__snapshots__/landing.test.tsx.snap @@ -3,7 +3,7 @@ exports[`renders correctly 1`] = `
{ const { asFragment } = render( - + + + ); From 0ac0f440796f974b1a549b60cde1509e03f178a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luc=C3=ADa=20Echenique=20=C3=81lvarez?= <25833665+lucechal14@users.noreply.github.com> Date: Mon, 11 Dec 2023 23:21:37 -0600 Subject: [PATCH 08/28] frontend: leverage theme palette for input components (#2860) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hardcoded colors with theme palette references for components in input Testing Performed manual, unit tests ### Before (left) vs After (right) **Checkbox Panel** ![Screenshot 2023-12-07 at 2 58 18 p m](https://github.com/lyft/clutch/assets/25833665/e98a1672-77ce-4ad6-b934-2f7794524b8d) **Checkbox** ![Screenshot 2023-12-07 at 2 58 45 p m](https://github.com/lyft/clutch/assets/25833665/93dbf0c6-7854-4de9-bebe-c8a3e88c2b02) **DateTimePicker** ![Screenshot 2023-12-07 at 2 59 26 p m](https://github.com/lyft/clutch/assets/25833665/3a9ade30-d665-4406-b21a-01cc71828066) **Form** ![Screenshot 2023-12-07 at 3 00 29 p m](https://github.com/lyft/clutch/assets/25833665/f8c61df6-56e1-49f8-92f9-e33cc6e6a80d) **MultiSelect** ![Screenshot 2023-12-07 at 3 00 57 p m](https://github.com/lyft/clutch/assets/25833665/43a45a21-a6b8-42d2-a521-8a2b9afd5d25) **RadioGroup** ![Screenshot 2023-12-07 at 3 01 29 p m](https://github.com/lyft/clutch/assets/25833665/c8105c5b-e4e0-4c12-9653-047c49deb81e) **Radio** ![Screenshot 2023-12-07 at 3 01 57 p m](https://github.com/lyft/clutch/assets/25833665/184c618f-859e-4cd1-bb6a-2bfaf4845d47) **Select** ![Screenshot 2023-12-07 at 3 02 20 p m](https://github.com/lyft/clutch/assets/25833665/21abc090-05c1-4eb2-9904-a9c221cc6412) **Switch** ![Screenshot 2023-12-07 at 3 02 45 p m](https://github.com/lyft/clutch/assets/25833665/dc6875b2-126c-402f-8ef4-af310d92c791) **TextField** ![Screenshot 2023-12-07 at 3 03 05 p m](https://github.com/lyft/clutch/assets/25833665/34e1e6f4-e85a-4740-acc0-2cbdeebcc2da) **TimePicker** ![Screenshot 2023-12-07 at 3 03 30 p m](https://github.com/lyft/clutch/assets/25833665/08ba8853-6a15-41c7-a9dd-781352f6b05d) **ToggleButtonGroup** ![Screenshot 2023-12-07 at 3 03 55 p m](https://github.com/lyft/clutch/assets/25833665/9b1e9b22-a9f0-406e-9450-69af42538707) --- frontend/packages/core/src/Feedback/alert.tsx | 8 +- frontend/packages/core/src/Input/checkbox.tsx | 31 +-- .../packages/core/src/Input/radio-group.tsx | 6 +- frontend/packages/core/src/Input/radio.tsx | 34 ++-- frontend/packages/core/src/Input/select.tsx | 51 ++--- .../packages/core/src/Input/switchToggle.tsx | 36 ++-- .../core/src/Input/tests/date-time.test.tsx | 37 +++- .../core/src/Input/tests/select.test.tsx | 22 +- .../core/src/Input/tests/time-picker.test.tsx | 37 +++- .../packages/core/src/Input/text-field.tsx | 189 +++++++++--------- .../core/src/Input/toggle-button-group.tsx | 24 +-- .../core/src/Table/accordion-table.tsx | 4 +- frontend/packages/core/src/Table/table.tsx | 10 +- frontend/packages/core/src/button.tsx | 133 ++++++------ frontend/packages/core/src/card.tsx | 6 +- frontend/packages/core/src/popper.tsx | 6 +- frontend/packages/core/src/stepper.tsx | 53 ++--- frontend/packages/core/src/tab.tsx | 8 +- .../tests/__snapshots__/button.test.tsx.snap | 10 +- .../packages/core/src/tests/button.test.tsx | 36 +++- frontend/packages/core/src/tests/tab.test.tsx | 12 +- .../reboot-instance.test.tsx.snap | 26 +-- .../__snapshots__/resize-asg.test.tsx.snap | 27 +-- .../terminate-instance.test.tsx.snap | 26 +-- .../ec2/src/tests/reboot-instance.test.tsx | 5 +- .../ec2/src/tests/resize-asg.test.tsx | 6 +- .../ec2/src/tests/terminate-instance.test.tsx | 5 +- .../__snapshots__/remote-triage.test.tsx.snap | 33 ++- .../envoy/src/tests/remote-triage.test.tsx | 5 +- .../list-experiments.test.tsx.snap | 33 ++- .../src/tests/list-experiments.test.tsx | 5 +- .../__snapshots__/delete-pod.test.tsx.snap | 26 +-- .../__snapshots__/resize-hpa.test.tsx.snap | 26 +-- .../scale-resources.test.tsx.snap | 26 +-- .../k8s/src/tests/delete-pod.test.tsx | 5 +- .../k8s/src/tests/resize-hpa.test.tsx | 5 +- .../k8s/src/tests/scale-resources.test.tsx | 5 +- .../projectSelector/src/project-selector.tsx | 8 +- .../experiment-details.test.tsx.snap | 54 +++-- .../start-experiment.test.tsx.snap | 54 +++-- .../src/tests/experiment-details.test.tsx | 5 +- .../src/tests/start-experiment.test.tsx | 5 +- .../experiment-details.test.tsx.snap | 140 ++++++------- .../start-experiment.test.tsx.snap | 140 ++++++------- .../src/tests/experiment-details.test.tsx | 5 +- .../src/tests/start-experiment.test.tsx | 9 +- 46 files changed, 770 insertions(+), 667 deletions(-) diff --git a/frontend/packages/core/src/Feedback/alert.tsx b/frontend/packages/core/src/Feedback/alert.tsx index baf004a5d5..fe35bf8b23 100644 --- a/frontend/packages/core/src/Feedback/alert.tsx +++ b/frontend/packages/core/src/Feedback/alert.tsx @@ -4,13 +4,7 @@ import MuiErrorIcon from "@mui/icons-material/Error"; import MuiInfoIcon from "@mui/icons-material/Info"; import MuiWarningIcon from "@mui/icons-material/Warning"; import type { AlertProps as MuiAlertProps } from "@mui/lab"; -import { - Alert as MuiAlert, - AlertTitle as MuiAlertTitle, - alpha, - Grid, - useTheme, -} from "@mui/material"; +import { Alert as MuiAlert, AlertTitle as MuiAlertTitle, alpha, Grid } from "@mui/material"; import styled from "../styled"; diff --git a/frontend/packages/core/src/Input/checkbox.tsx b/frontend/packages/core/src/Input/checkbox.tsx index c2fb2c3ee2..0fc510e745 100644 --- a/frontend/packages/core/src/Input/checkbox.tsx +++ b/frontend/packages/core/src/Input/checkbox.tsx @@ -2,6 +2,7 @@ import * as React from "react"; import CheckIcon from "@mui/icons-material/Check"; import type { CheckboxProps as MuiCheckboxProps } from "@mui/material"; import { + alpha, Checkbox as MuiCheckbox, FormControl as MuiFormControl, FormControlLabel, @@ -16,31 +17,31 @@ const FormControl = styled(MuiFormControl)({ width: "75%", }); -const StyledCheckbox = styled(MuiCheckbox)({ - color: "#6e7083", +const StyledCheckbox = styled(MuiCheckbox)(({ theme }) => ({ + color: theme.palette.secondary[400], borderRadius: "50%", "&:hover": { - background: "#f5f6fd", + background: theme.palette.primary[100], }, "&:active": { - background: "#d7daf6", + background: theme.palette.primary[300], }, "&.Mui-checked": { - color: "#ffffff", + color: theme.palette.common.white, "&:hover": { - background: "#f5f6fd", + background: theme.palette.primary[100], }, "&:active": { - background: "#d7daf6", + background: theme.palette.primary[300], }, "&.Mui-disabled": { - color: "#e7e7ea", + color: theme.palette.secondary[200], ".MuiIconButton-label": { - color: "rgba(13, 16, 48, 0.38)", + color: alpha(theme.palette.secondary[900], 0.38), }, }, }, -}); +})); type Size = "20px" | "24px"; @@ -54,10 +55,12 @@ const Icon = styled("div")( borderRadius: "2px", boxSizing: "border-box", }, - props => ({ + props => ({ theme }) => ({ height: props.$size, width: props.$size, - border: props.$disabled ? "1px solid #e7e7ea" : "1px solid #6e7083", + border: props.$disabled + ? `1px solid ${theme.palette.secondary[200]}` + : `1px solid ${theme.palette.secondary[400]}`, }) ); @@ -69,10 +72,10 @@ const SelectedIcon = styled("div")( display: "block", }, }, - props => ({ + props => ({ theme }) => ({ height: props.$size, width: props.$size, - background: props.$disabled ? "#e7e7eA" : "#3548d4", + background: props.$disabled ? theme.palette.secondary[200] : theme.palette.primary[600], ".MuiSvgIcon-root": { height: props.$size, width: props.$size, diff --git a/frontend/packages/core/src/Input/radio-group.tsx b/frontend/packages/core/src/Input/radio-group.tsx index 2ca90e2232..c6b2b510c2 100644 --- a/frontend/packages/core/src/Input/radio-group.tsx +++ b/frontend/packages/core/src/Input/radio-group.tsx @@ -9,16 +9,16 @@ import { import Radio from "./radio"; -const FormLabel = styled(MuiFormLabel)({ +const FormLabel = styled(MuiFormLabel)(({ theme }) => ({ "&&": { - color: "#2D3F50", + color: theme.palette.secondary[700], }, fontWeight: "bold", position: "relative", "&.Mui-disabled": { opacity: "0.75", }, -}); +})); const FormControl = styled(MuiFormControl)({ margin: "16px 0", diff --git a/frontend/packages/core/src/Input/radio.tsx b/frontend/packages/core/src/Input/radio.tsx index 4f10e1b23c..d21e2ae71a 100644 --- a/frontend/packages/core/src/Input/radio.tsx +++ b/frontend/packages/core/src/Input/radio.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import type { RadioProps as MuiRadioProps } from "@mui/material"; -import { Radio as MuiRadio } from "@mui/material"; +import { alpha, Radio as MuiRadio } from "@mui/material"; import styled from "../styled"; @@ -13,43 +13,47 @@ const StyledRadio = styled(MuiRadio)<{ checked: RadioProps["selected"] }>( boxSizing: "border-box", }, }, - props => ({ + props => ({ theme }) => ({ "&:hover > .MuiIconButton-label > div": { - border: props.checked ? "1px solid #283CD2" : "1px solid #2E45DC", + border: props.checked + ? `1px solid ${theme.palette.primary[700]}` + : `1px solid ${theme.palette.primary[600]}`, }, }) ); const Icon = styled("div")<{ $disabled?: MuiRadioProps["disabled"] }>( - { + ({ theme }) => ({ height: "24px", width: "24px", - border: "1px solid rgba(13, 16, 48, 0.38)", + border: `1px solid ${alpha(theme.palette.secondary[900], 0.38)}`, borderRadius: "100px", boxSizing: "border-box", - }, - props => ({ - border: props.$disabled ? "1px solid #DFE2E4" : "1px solid rgba(13, 16, 48, 0.38)", + }), + props => ({ theme }) => ({ + border: props.$disabled + ? `1px solid ${theme.palette.secondary[200]}` + : `1px solid ${alpha(theme.palette.secondary[900], 0.38)}`, }) ); -const SelectedIcon = styled("div")({ +const SelectedIcon = styled("div")(({ theme }) => ({ height: "24px", width: "24px", - background: "#2E45DC", - border: "1px solid #283CD2", + background: theme.palette.primary[600], + border: `1px solid ${theme.palette.primary[700]}`, borderRadius: "100px", boxSizing: "border-box", -}); +})); -const SelectedCenter = styled("div")({ +const SelectedCenter = styled("div")(({ theme }) => ({ height: "12px", width: "12px", - background: "#FFFFFF", + background: theme.palette.common.white, borderRadius: "100px", boxSizing: "border-box", margin: "5px 5px", -}); +})); export interface RadioProps extends Pick { diff --git a/frontend/packages/core/src/Input/select.tsx b/frontend/packages/core/src/Input/select.tsx index d3f5a8192e..97daa50793 100644 --- a/frontend/packages/core/src/Input/select.tsx +++ b/frontend/packages/core/src/Input/select.tsx @@ -4,6 +4,7 @@ import ErrorIcon from "@mui/icons-material/Error"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import type { SelectProps as MuiSelectProps } from "@mui/material"; import { + alpha, FormControl as MuiFormControl, FormHelperText as MuiFormHelperText, InputLabel as MuiInputLabel, @@ -15,7 +16,7 @@ import { flatten } from "lodash"; import { Chip } from "../chip"; -const StyledFormHelperText = styled(MuiFormHelperText)({ +const StyledFormHelperText = styled(MuiFormHelperText)(({ theme }) => ({ alignItems: "center", display: "flex", position: "relative", @@ -26,7 +27,7 @@ const StyledFormHelperText = styled(MuiFormHelperText)({ color: "grey", "&.Mui-error": { - color: "#db3615", + color: theme.palette.error[600], }, svg: { @@ -34,19 +35,19 @@ const StyledFormHelperText = styled(MuiFormHelperText)({ width: "16px", marginRight: "4px", }, -}); +})); -const StyledInputLabel = styled(MuiInputLabel)({ - "--label-default-color": "rgba(13, 16, 48, 0.6)", +const StyledInputLabel = styled(MuiInputLabel)(({ theme }) => ({ + "--label-default-color": alpha(theme.palette.secondary[900], 0.6), color: "var(--label-default-color)", "&.Mui-focused": { color: "var(--label-default-color)", }, "&.Mui-error": { - color: "#db3615", + color: theme.palette.secondary[600], }, -}); +})); const SelectIcon = (props: any) => (
@@ -71,28 +72,28 @@ const BaseSelect = ({ className, ...props }: MuiSelectProps) => ( /> ); -const StyledSelect = styled(BaseSelect)({ +const StyledSelect = styled(BaseSelect)(({ theme }) => ({ "--notched-border-width": "1px", padding: "0", - backgroundColor: "#FFFFFF", + backgroundColor: theme.palette.common.white, minWidth: "fit-content", ".MuiOutlinedInput-notchedOutline": { - borderColor: "rgba(13, 16, 48, 0.38)", + borderColor: alpha(theme.palette.secondary[900], 0.38), borderWidth: "var(--notched-border-width)", }, "&.Mui-focused": { "> .MuiSelect-icon > svg": { - color: "#0d1030", + color: theme.palette.secondary[900], }, "> .MuiOutlinedInput-notchedOutline": { - borderColor: "#3458d4", + borderColor: theme.palette.primary[600], borderWidth: "var(--notched-border-width)", }, }, "&.Mui-error > .MuiOutlinedInput-notchedOutline": { - borderColor: "#db3615", + borderColor: theme.palette.error[600], borderWidth: "var(--notched-border-width)", }, @@ -106,17 +107,17 @@ const StyledSelect = styled(BaseSelect)({ }, "&.Mui-disabled": { - backgroundColor: "rgba(13, 16, 48, 0.12)", + backgroundColor: alpha(theme.palette.secondary[900], 0.12), }, }, ul: { borderRadius: "4px", - border: "1px solid rgba(13, 16, 48, 0.1)", + border: `1px solid ${alpha(theme.palette.secondary[900], 0.1)}`, }, ".MuiMenuItem-root": { - color: "#0d1030", + color: theme.palette.secondary[900], height: "48px", ":first-of-type": { @@ -124,15 +125,15 @@ const StyledSelect = styled(BaseSelect)({ }, ":last-child": {}, ":hover": { - backgroundColor: "#e7e7ea", + backgroundColor: theme.palette.secondary[200], }, ":active": { - backgroundColor: "#dbdbe0", + backgroundColor: theme.palette.secondary[200], }, "&.Mui-selected": { - backgroundColor: "rgba(53, 72, 212, 0.1)", + backgroundColor: alpha(theme.palette.primary[600], 0.1), ":hover": { - backgroundColor: "rgba(53, 72, 212, 0.1)", + backgroundColor: alpha(theme.palette.primary[600], 0.1), }, }, }, @@ -140,7 +141,7 @@ const StyledSelect = styled(BaseSelect)({ "&.MuiMenu-paper": { marginTop: "5px", border: "none", - boxShadow: "0px 5px 15px rgba(53, 72, 212, 0.2)", + boxShadow: `0px 5px 15px ${alpha(theme.palette.primary[600], 0.2)}`, maxHeight: "25vh", }, @@ -155,7 +156,7 @@ const StyledSelect = styled(BaseSelect)({ boxSizing: "border-box", "> svg": { - color: "rgba(13, 16, 48, 0.6)", + color: alpha(theme.palette.secondary[900], 0.6), position: "absolute", }, @@ -164,16 +165,16 @@ const StyledSelect = styled(BaseSelect)({ }, "&.Mui-disabled > svg": { - color: "rgba(13, 16, 48, 0.6)", + color: alpha(theme.palette.secondary[900], 0.6), }, }, ".MuiListSubheader-root": { - color: "#939495", + color: theme.palette.secondary[400], cursor: "default", pointerEvents: "none", // disables the select from closing on clicking the subheader }, -}); +})); interface BaseSelectOptions { label: string; diff --git a/frontend/packages/core/src/Input/switchToggle.tsx b/frontend/packages/core/src/Input/switchToggle.tsx index a5ce50403b..07a70b7eee 100644 --- a/frontend/packages/core/src/Input/switchToggle.tsx +++ b/frontend/packages/core/src/Input/switchToggle.tsx @@ -7,65 +7,65 @@ import * as React from "react"; import styled from "@emotion/styled"; import type { SwitchProps as MuiSwitchProps } from "@mui/material"; -import { Switch as MuiSwitch } from "@mui/material"; +import { alpha, Switch as MuiSwitch } from "@mui/material"; -const SwitchContainer = styled(MuiSwitch)({ +const SwitchContainer = styled(MuiSwitch)(({ theme }) => ({ ".MuiSwitch-switchBase": { ":hover": { - backgroundColor: "rgba(13, 16, 48, 0.1)", + backgroundColor: alpha(theme.palette.secondary[900], 0.1), }, ":focus": { - backgroundColor: "rgba(13, 16, 48, 0.12)", + backgroundColor: alpha(theme.palette.secondary[900], 0.12), }, ":active": { - backgroundColor: "rgba(13, 16, 48, 0.15)", + backgroundColor: alpha(theme.palette.secondary[900], 0.15), }, ".MuiSwitch-thumb": { - boxShadow: "0px 1px 1px rgba(0, 0, 0, 0.25)", - color: "#FFFFFF", + boxShadow: `0px 1px 1px ${alpha(theme.palette.common.black, 0.25)}`, + color: theme.palette.common.white, }, }, ".MuiSwitch-track": { - backgroundColor: "#6E7083", + backgroundColor: theme.palette.secondary[400], opacity: 1, }, ".Mui-disabled": { ".MuiSwitch-thumb": { - color: "rgba(248, 248, 249, 1)", + color: theme.palette.secondary[50], }, }, ".Mui-disabled + .MuiSwitch-track": { - backgroundColor: "#E7E7EA", + backgroundColor: theme.palette.secondary[200], opacity: 1, }, ".Mui-checked": { ":hover": { - backgroundColor: "rgba(53, 72, 212, 0.05)", + backgroundColor: alpha(theme.palette.primary[600], 0.05), }, ":focus": { - backgroundColor: "rgba(53, 72, 212, 0.1)", + backgroundColor: alpha(theme.palette.primary[600], 0.1), }, ":active": { - backgroundColor: "rgba(53, 72, 212, 0.2)", + backgroundColor: alpha(theme.palette.primary[600], 0.2), }, ".MuiSwitch-thumb": { - color: "#3548D4", + color: theme.palette.primary[600], }, }, ".Mui-checked + .MuiSwitch-track": { - backgroundColor: "#C2C8F2", + backgroundColor: theme.palette.primary[400], opacity: 1, }, ".Mui-checked.Mui-disabled": { ".MuiSwitch-thumb": { - color: "#E7E7EA", + color: theme.palette.secondary[200], }, }, ".Mui-checked.Mui-disabled + .MuiSwitch-track": { - backgroundColor: "#A3A4B0", + backgroundColor: theme.palette.secondary[400], opacity: 1, }, -}); +})); export interface SwitchProps extends Pick {} diff --git a/frontend/packages/core/src/Input/tests/date-time.test.tsx b/frontend/packages/core/src/Input/tests/date-time.test.tsx index 92e00f8161..505753498a 100644 --- a/frontend/packages/core/src/Input/tests/date-time.test.tsx +++ b/frontend/packages/core/src/Input/tests/date-time.test.tsx @@ -3,6 +3,7 @@ import { fireEvent, render, screen } from "@testing-library/react"; import "@testing-library/jest-dom"; +import { ThemeProvider } from "../../Theme"; import DateTimePicker from "../date-time"; afterEach(() => { @@ -11,7 +12,11 @@ afterEach(() => { const onChange = jest.fn(); test("has padding", () => { - const { container } = render(); + const { container } = render( + + + + ); expect(container.querySelectorAll(".MuiInputBase-adornedEnd")).toHaveLength(1); expect(container.querySelector(".MuiInputBase-adornedEnd")).toHaveStyle({ @@ -20,7 +25,11 @@ test("has padding", () => { }); test("onChange is called when valid value", () => { - render(); + render( + + + + ); expect(screen.getByPlaceholderText("mm/dd/yyyy hh:mm (a|p)m")).toBeVisible(); fireEvent.change(screen.getByPlaceholderText("mm/dd/yyyy hh:mm (a|p)m"), { @@ -30,7 +39,11 @@ test("onChange is called when valid value", () => { }); test("onChange is not called when invalid value", () => { - render(); + render( + + + + ); expect(screen.getByPlaceholderText("mm/dd/yyyy hh:mm (a|p)m")).toBeVisible(); fireEvent.change(screen.getByPlaceholderText("mm/dd/yyyy hh:mm (a|p)m"), { @@ -51,20 +64,32 @@ test("sets passed value correctly", () => { minute: "2-digit", }).format(date); const formattedDate = `${formattedDMY} ${formattedTime}`; - render(); + render( + + + + ); expect(screen.getByPlaceholderText("mm/dd/yyyy hh:mm (a|p)m")).toHaveValue(formattedDate); }); test("displays label correctly", () => { const label = "testing"; - render(); + render( + + + + ); expect(screen.getByLabelText(label)).toBeVisible(); }); test("is disabled", () => { - render(); + render( + + + + ); expect(screen.getByPlaceholderText("mm/dd/yyyy hh:mm (a|p)m")).toBeDisabled(); }); diff --git a/frontend/packages/core/src/Input/tests/select.test.tsx b/frontend/packages/core/src/Input/tests/select.test.tsx index cf41025e4c..3273f635a2 100644 --- a/frontend/packages/core/src/Input/tests/select.test.tsx +++ b/frontend/packages/core/src/Input/tests/select.test.tsx @@ -3,11 +3,14 @@ import { render } from "@testing-library/react"; import "@testing-library/jest-dom"; +import { ThemeProvider } from "../../Theme"; import { MultiSelect, Select } from "../select"; test("select has lower bound", () => { const { container } = render( - + ); expect(container.querySelector("#foobar-select")).toBeInTheDocument(); @@ -16,7 +19,9 @@ test("select has lower bound", () => { test("select has upper bound", () => { const { container } = render( - + ); expect(container.querySelector("#foobar-select")).toBeInTheDocument(); @@ -25,11 +30,14 @@ test("select has upper bound", () => { test("multi select handles multiple", () => { const { container } = render( - + + {" "} + + ); expect(container.querySelector("#foobar-multi-select")).toBeInTheDocument(); diff --git a/frontend/packages/core/src/Input/tests/time-picker.test.tsx b/frontend/packages/core/src/Input/tests/time-picker.test.tsx index 90e8d2c6c0..ec99f8e0e3 100644 --- a/frontend/packages/core/src/Input/tests/time-picker.test.tsx +++ b/frontend/packages/core/src/Input/tests/time-picker.test.tsx @@ -3,6 +3,7 @@ import { fireEvent, render, screen } from "@testing-library/react"; import "@testing-library/jest-dom"; +import { ThemeProvider } from "../../Theme"; import TimePicker from "../time-picker"; afterEach(() => { @@ -11,7 +12,11 @@ afterEach(() => { const onChange = jest.fn(); test("has padding", () => { - const { container } = render(); + const { container } = render( + + + + ); expect(container.querySelectorAll(".MuiInputBase-adornedEnd")).toHaveLength(1); expect(container.querySelector(".MuiInputBase-adornedEnd")).toHaveStyle({ @@ -20,7 +25,11 @@ test("has padding", () => { }); test("onChange is called when valid value", () => { - render(); + render( + + + + ); expect(screen.getByPlaceholderText("hh:mm (a|p)m")).toBeVisible(); fireEvent.change(screen.getByPlaceholderText("hh:mm (a|p)m"), { @@ -30,7 +39,11 @@ test("onChange is called when valid value", () => { }); test("onChange is not called when invalid value", () => { - render(); + render( + + + + ); expect(screen.getByPlaceholderText("hh:mm (a|p)m")).toBeVisible(); fireEvent.change(screen.getByPlaceholderText("hh:mm (a|p)m"), { @@ -46,20 +59,32 @@ test("sets passed value correctly", () => { minute: "2-digit", }).format(date); const formattedDate = `${formattedTime}`; - render(); + render( + + + + ); expect(screen.getByPlaceholderText("hh:mm (a|p)m")).toHaveValue(formattedDate); }); test("displays label correctly", () => { const label = "testing"; - render(); + render( + + + + ); expect(screen.getByLabelText(label)).toBeVisible(); }); test("is disabled", () => { - render(); + render( + + + + ); expect(screen.getByPlaceholderText("hh:mm (a|p)m")).toBeDisabled(); }); diff --git a/frontend/packages/core/src/Input/text-field.tsx b/frontend/packages/core/src/Input/text-field.tsx index d04e99ce80..782eaba2d4 100644 --- a/frontend/packages/core/src/Input/text-field.tsx +++ b/frontend/packages/core/src/Input/text-field.tsx @@ -8,6 +8,7 @@ import type { StandardTextFieldProps as MuiStandardTextFieldProps, } from "@mui/material"; import { + alpha, Autocomplete, Grid, IconButton as MuiIconButton, @@ -45,107 +46,109 @@ const StyledAutocomplete = styled(Autocomplete)({ }, }); -const TEXT_FIELD_COLOR_MAP = { - default: "rgba(13, 16, 48, 0.6)", - inputDefault: "rgba(13, 16, 48, 0.38)", - inputHover: "#2D3F50", - inputFocused: "#3548d4", - primary: "#3548D4", - secondary: "#D7DAF6", - info: "#3548D4", - success: "#1E942D", - warning: "#FCD34D", - error: "#DB3615", -}; - const StyledTextField = styled(BaseTextField)<{ $color?: MuiStandardTextFieldProps["color"]; -}>({}, props => ({ - height: "unset", - ".MuiInputLabel-root": { - color: `${TEXT_FIELD_COLOR_MAP[props.color] || TEXT_FIELD_COLOR_MAP.default}`, - "&.Mui-focused": { - color: `${TEXT_FIELD_COLOR_MAP[props.color] || TEXT_FIELD_COLOR_MAP.default}`, - }, - "&.Mui-error": { - color: `${TEXT_FIELD_COLOR_MAP.error}`, - }, - }, - ".MuiInputBase-root": { - "--input-border-width": "1px", - borderRadius: "4px", - fontSize: "16px", - backgroundColor: "#FFFFFF", - - "&.Mui-error fieldset": { - borderColor: `${TEXT_FIELD_COLOR_MAP.error}`, - borderWidth: "var(--input-border-width)", - }, +}>({}, props => ({ theme }) => { + const TEXT_FIELD_COLOR_MAP = { + default: alpha(theme.palette.secondary[900], 0.6), + inputDefault: alpha(theme.palette.secondary[900], 0.38), + inputHover: theme.palette.secondary[700], + inputFocused: theme.palette.primary[600], + primary: theme.palette.primary[600], + secondary: theme.palette.primary[300], + info: theme.palette.primary[600], + success: theme.palette.success[500], + warning: theme.palette.warning[300], + error: theme.palette.error[600], + }; - "&:not(.Mui-error)": { - "&:not(.Mui-focused):not(:hover) fieldset": { - borderColor: `${TEXT_FIELD_COLOR_MAP[props.color] || TEXT_FIELD_COLOR_MAP.inputDefault}`, + return { + height: "unset", + ".MuiInputLabel-root": { + color: `${TEXT_FIELD_COLOR_MAP[props.color] || TEXT_FIELD_COLOR_MAP.default}`, + "&.Mui-focused": { + color: `${TEXT_FIELD_COLOR_MAP[props.color] || TEXT_FIELD_COLOR_MAP.default}`, }, - "&:hover fieldset": { - borderColor: `${TEXT_FIELD_COLOR_MAP[props.color] || TEXT_FIELD_COLOR_MAP.inputHover}`, + "&.Mui-error": { + color: `${TEXT_FIELD_COLOR_MAP.error}`, }, - "&.Mui-focused fieldset": { - borderColor: `${TEXT_FIELD_COLOR_MAP[props.color] || TEXT_FIELD_COLOR_MAP.inputFocused}`, + }, + ".MuiInputBase-root": { + "--input-border-width": "1px", + borderRadius: "4px", + fontSize: "16px", + backgroundColor: theme.palette.common.white, + + "&.Mui-error fieldset": { + borderColor: `${TEXT_FIELD_COLOR_MAP.error}`, borderWidth: "var(--input-border-width)", }, - }, - "&.Mui-disabled fieldset": { - backgroundColor: "rgba(13, 16, 48, 0.12)", - }, - "& .MuiInputBase-input": { - textOverflow: "ellipsis", - }, - "> .MuiInputBase-input": { - "--input-padding": "14px 16px", - padding: "var(--input-padding)", - height: "20px", + "&:not(.Mui-error)": { + "&:not(.Mui-focused):not(:hover) fieldset": { + borderColor: `${TEXT_FIELD_COLOR_MAP[props.color] || TEXT_FIELD_COLOR_MAP.inputDefault}`, + }, + "&:hover fieldset": { + borderColor: `${TEXT_FIELD_COLOR_MAP[props.color] || TEXT_FIELD_COLOR_MAP.inputHover}`, + }, + "&.Mui-focused fieldset": { + borderColor: `${TEXT_FIELD_COLOR_MAP[props.color] || TEXT_FIELD_COLOR_MAP.inputFocused}`, + borderWidth: "var(--input-border-width)", + }, + }, - "&.MuiAutocomplete-input": { - padding: "var(--input-padding)", + "&.Mui-disabled fieldset": { + backgroundColor: alpha(theme.palette.secondary[900], 0.12), }, + "& .MuiInputBase-input": { + textOverflow: "ellipsis", + }, + "> .MuiInputBase-input": { + "--input-padding": "14px 16px", + padding: "var(--input-padding)", + height: "20px", - "::placeholder": { - color: `${TEXT_FIELD_COLOR_MAP.inputDefault}`, - opacity: 1, + "&.MuiAutocomplete-input": { + padding: "var(--input-padding)", + }, + + "::placeholder": { + color: `${TEXT_FIELD_COLOR_MAP.inputDefault}`, + opacity: 1, + }, }, }, - }, - - ".MuiInputBase-adornedEnd": { - paddingRight: "unset", - }, - ".MuiFormHelperText-root": { - alignItems: "center", - display: "flex", - position: "relative", - fontSize: "12px", - marginTop: "7px", - lineHeight: "16px", - marginLeft: "0px", - color: `${TEXT_FIELD_COLOR_MAP[props.color] || TEXT_FIELD_COLOR_MAP.default}`, - "&.Mui-error": { - color: `${TEXT_FIELD_COLOR_MAP.error}`, + ".MuiInputBase-adornedEnd": { + paddingRight: "unset", }, - "> svg": { - height: "16px", - width: "16px", - marginRight: "4px", + ".MuiFormHelperText-root": { + alignItems: "center", + display: "flex", + position: "relative", + fontSize: "12px", + marginTop: "7px", + lineHeight: "16px", + marginLeft: "0px", + color: `${TEXT_FIELD_COLOR_MAP[props.color] || TEXT_FIELD_COLOR_MAP.default}`, + "&.Mui-error": { + color: `${TEXT_FIELD_COLOR_MAP.error}`, + }, + + "> svg": { + height: "16px", + width: "16px", + marginRight: "4px", + }, }, - }, -})); + }; +}); // popper containing the search result options -const Popper = styled(MuiPopper)({ +const Popper = styled(MuiPopper)(({ theme }) => ({ ".MuiPaper-root": { - boxShadow: "0px 5px 15px rgba(53, 72, 212, 0.2)", + boxShadow: `0px 5px 15px ${alpha(theme.palette.primary[600], 0.2)}`, "> .MuiAutocomplete-listbox": { "> .MuiAutocomplete-option": { @@ -153,16 +156,16 @@ const Popper = styled(MuiPopper)({ padding: "0px", "&.Mui-focused": { - background: "#ebedfb", + background: theme.palette.primary[200], }, }, }, }, ".MuiAutocomplete-noOptions": { fontSize: "14px", - color: "#0d1030", + color: theme.palette.secondary[900], }, -}); +})); // search's result options container const ResultGrid = styled(Grid)({ @@ -171,24 +174,24 @@ const ResultGrid = styled(Grid)({ }); // search's result options -const ResultLabel = styled(Typography)({ - color: "#0d1030", +const ResultLabel = styled(Typography)(({ theme }) => ({ + color: theme.palette.secondary[900], fontSize: "14px", -}); +})); -const IconButton = styled(MuiIconButton)({ +const IconButton = styled(MuiIconButton)(({ theme }) => ({ borderRadius: "0", - backgroundColor: "#3548D4", - color: "#FFFFFF", + backgroundColor: theme.palette.primary[600], + color: theme.palette.common.white, borderBottomRightRadius: "3px", borderTopRightRadius: "3px", "&:hover": { - backgroundColor: "#2D3DB4", + backgroundColor: theme.palette.primary[500], }, "&:active": { - backgroundColor: "#2938A5", + backgroundColor: theme.palette.primary[600], }, -}); +})); interface AutocompleteResultProps { id?: string; diff --git a/frontend/packages/core/src/Input/toggle-button-group.tsx b/frontend/packages/core/src/Input/toggle-button-group.tsx index 86b68c4371..f73d36ef4c 100644 --- a/frontend/packages/core/src/Input/toggle-button-group.tsx +++ b/frontend/packages/core/src/Input/toggle-button-group.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import styled from "@emotion/styled"; import type { ToggleButtonGroupProps as MuiToggleButtonGroupProps } from "@mui/lab"; -import { ToggleButtonGroup as MuiToggleButtonGroup } from "@mui/material"; +import { alpha, ToggleButtonGroup as MuiToggleButtonGroup } from "@mui/material"; export { ToggleButton } from "@mui/material"; @@ -14,11 +14,11 @@ export interface ToggleButtonGroupProps multiple?: boolean; } -const StyledMuiToggleButtonGroup = styled(MuiToggleButtonGroup)(({ size }) => ({ - border: "1px solid rgba(13, 16, 48, 0.45)", +const StyledMuiToggleButtonGroup = styled(MuiToggleButtonGroup)(({ size, theme }) => ({ + border: `1px solid ${alpha(theme.palette.secondary[900], 0.45)}`, padding: "8px", gap: "8px", - background: "#FFFFFF", + background: theme.palette.common.white, ".MuiToggleButton-root": { flexDirection: "column", justifyContent: "center", @@ -29,21 +29,21 @@ const StyledMuiToggleButtonGroup = styled(MuiToggleButtonGroup)(({ size }) => ({ border: "none", width: "100%", "&.MuiToggleButton-root:hover:not(.Mui-selected)": { - background: "#0D10300D", + background: alpha(theme.palette.secondary[900], 0.05), }, "&.MuiToggleButton-root:active:not(.Mui-selected)": { - background: "#0D10302E", + background: alpha(theme.palette.secondary[900], 0.18), }, "&.Mui-selected, &.Mui-selected:hover": { - background: "#3548D4", - color: "#FFFFFF", + background: theme.palette.primary[600], + color: theme.palette.common.white, }, "&.Mui-disabled": { - background: "rgba(13, 16, 48, 0.03)", - color: "rgba(13, 16, 48, 0.48)", + background: alpha(theme.palette.secondary[900], 0.03), + color: alpha(theme.palette.secondary[900], 0.48), }, - background: "#FFFFFF", - color: "#0D1030", + background: theme.palette.common.white, + color: theme.palette.secondary[900], textTransform: "none", }, })); diff --git a/frontend/packages/core/src/Table/accordion-table.tsx b/frontend/packages/core/src/Table/accordion-table.tsx index b85450461a..3ad647508d 100644 --- a/frontend/packages/core/src/Table/accordion-table.tsx +++ b/frontend/packages/core/src/Table/accordion-table.tsx @@ -13,8 +13,8 @@ const IconButton = styled(MuiIconButton)({ color: "#0D1030", }); -const ChevronRight = styled(ChevronRightIcon)<{ $disabled: boolean }>(props => ({ - color: props?.$disabled ? "#E7E7EA" : "unset", +const ChevronRight = styled(ChevronRightIcon)<{ $disabled: boolean }>(props => ({ theme }) => ({ + color: props?.$disabled ? theme.palette.secondary[200] : "unset", })); export interface AccordionRowProps { diff --git a/frontend/packages/core/src/Table/table.tsx b/frontend/packages/core/src/Table/table.tsx index b3cce40854..7591abc4be 100644 --- a/frontend/packages/core/src/Table/table.tsx +++ b/frontend/packages/core/src/Table/table.tsx @@ -22,9 +22,9 @@ import { Popper, PopperItem } from "../popper"; import styled from "../styled"; import { Typography } from "../typography"; -const StyledPaper = styled(MuiPaper)({ - border: "1px solid #E7E7EA", -}); +const StyledPaper = styled(MuiPaper)(({ theme }) => ({ + border: `1px solid ${theme.palette.secondary[200]}`, +})); const StyledTable = styled(MuiTable)<{ $hasActionsColumn?: TableProps["actionsColumn"]; @@ -88,8 +88,8 @@ const StyledTableCell = styled(MuiTableCell)<{ background: "inherit", minHeight: "100%", }, - props => ({ - borderBottom: props?.$border ? "1px solid #E7E7EA" : "0", + props => ({ theme }) => ({ + borderBottom: props?.$border ? `1px solid ${theme.palette.secondary[200]}` : "0", display: props.$responsive ? "flex" : "", width: !props.$responsive && props.$action ? "80px" : "", }) diff --git a/frontend/packages/core/src/button.tsx b/frontend/packages/core/src/button.tsx index 1d423f1f52..5aa51cb459 100644 --- a/frontend/packages/core/src/button.tsx +++ b/frontend/packages/core/src/button.tsx @@ -5,7 +5,7 @@ import type { ButtonProps as MuiButtonProps, IconButtonProps as MuiIconButtonProps, } from "@mui/material"; -import { Button as MuiButton, Grid, IconButton as MuiIconButton } from "@mui/material"; +import { Button as MuiButton, Grid, IconButton as MuiIconButton, useTheme } from "@mui/material"; import { Tooltip } from "./Feedback/tooltip"; import type { GridJustification } from "./grid"; @@ -26,57 +26,6 @@ interface ButtonPalette { }; } -const COLORS = { - neutral: { - background: { - primary: "transparent", - hover: "#E7E7EA", - active: "#CFD3D7", - disabled: "#FFFFFF", - }, - font: { - primary: "#0D1030", - disabled: "#0D1030", - }, - }, - primary: { - background: { - primary: "#3548D4", - hover: "#2D3DB4", - active: "#2938A5", - disabled: "#E7E7EA", - }, - font: { - primary: "#FFFFFF", - disabled: "rgba(13, 16, 48, 0.38)", - }, - }, - danger: { - background: { - primary: "#DB3615", - hover: "#BA2E12", - active: "#AB2A10", - disabled: "#F1B3A6", - }, - font: { - primary: "#FFFFFF", - disabled: "#FFFFFF", - }, - }, - secondary: { - background: { - primary: "transparent", - hover: "#F5F6FD", - active: "#D7DAF6", - disabled: "transparent", - }, - font: { - primary: "#3548D4", - disabled: "#0D1030", - }, - }, -} as { [key: string]: ButtonPalette }; - const colorCss = (palette: ButtonPalette) => { return { color: palette.font.primary, @@ -163,7 +112,57 @@ export type IconButtonSize = keyof typeof ICON_BUTTON_STYLE_MAP; export const ICON_BUTTON_VARIANTS = Object.keys(ICON_BUTTON_STYLE_MAP); /** A color palette from a @type ButtonPalette */ -const variantPalette = (variant: ButtonVariant): ButtonPalette => { +const variantPalette = (variant: ButtonVariant, theme): ButtonPalette => { + const COLORS = { + neutral: { + background: { + primary: "transparent", + hover: theme.palette.secondary[200], + active: "#CFD3D7", + disabled: "#FFFFFF", + }, + font: { + primary: "#0D1030", + disabled: "#0D1030", + }, + }, + primary: { + background: { + primary: "#3548D4", + hover: "#2D3DB4", + active: "#2938A5", + disabled: theme.palette.secondary[200], + }, + font: { + primary: "#FFFFFF", + disabled: "rgba(13, 16, 48, 0.38)", + }, + }, + danger: { + background: { + primary: theme.palette.error[600], + hover: "#BA2E12", + active: "#AB2A10", + disabled: "#F1B3A6", + }, + font: { + primary: "#FFFFFF", + disabled: "#FFFFFF", + }, + }, + secondary: { + background: { + primary: "transparent", + hover: "#F5F6FD", + active: "#D7DAF6", + disabled: "transparent", + }, + font: { + primary: "#3548D4", + disabled: "#0D1030", + }, + }, + } as { [key: string]: ButtonPalette }; const v = variant === "destructive" ? "danger" : variant; return COLORS?.[v] || COLORS.primary; }; @@ -188,7 +187,8 @@ export interface ButtonProps /** A button with default themes based on use case. */ const Button = ({ text, variant = "primary", size = "medium", ...props }: ButtonProps) => { - const palette = variantPalette(variant); + const theme = useTheme(); + const palette = variantPalette(variant, theme); const ButtonVariant = variant === "neutral" ? StyledBorderButton : StyledButton; return ( @@ -226,11 +226,20 @@ export interface IconButtonProps * @returns rendered IconButton component */ const IconButton = React.forwardRef( - ({ variant = "primary", size = "medium", children, ...props }: IconButtonProps, ref) => ( - - {children} - - ) + ({ variant = "primary", size = "medium", children, ...props }: IconButtonProps, ref) => { + const theme = useTheme(); + + return ( + + {children} + + ); + } ); const ButtonGroupContainer = styled(Grid)( @@ -239,16 +248,16 @@ const ButtonGroupContainer = styled(Grid)( margin: "12px 8px", }, }, - props => + props => ({ theme }) => props["data-border"] === "bottom" ? { marginBottom: "12px", - borderBottom: "1px solid #E7E7EA", + borderBottom: `1px solid ${theme.palette.secondary[200]}`, marginTop: "0", } : { marginTop: "12px", - borderTop: "1px solid #E7E7EA", + borderTop: `1px solid ${theme.palette.secondary[200]}`, marginBottom: "0", } ); diff --git a/frontend/packages/core/src/card.tsx b/frontend/packages/core/src/card.tsx index bb66e751fe..20a5bd0d3c 100644 --- a/frontend/packages/core/src/card.tsx +++ b/frontend/packages/core/src/card.tsx @@ -58,11 +58,11 @@ const StyledCardHeaderAvatar = styled.div({ }); // TODO: make the divider a core component -const StyledDivider = styled(Divider)({ - color: "#A3A4B0", +const StyledDivider = styled(Divider)(({ theme }) => ({ + color: theme.palette.secondary[400], height: "24px", alignSelf: "center", -}); +})); const StyledGridItem = styled(Grid)({ textAlign: "center", diff --git a/frontend/packages/core/src/popper.tsx b/frontend/packages/core/src/popper.tsx index fd94340452..2736af7100 100644 --- a/frontend/packages/core/src/popper.tsx +++ b/frontend/packages/core/src/popper.tsx @@ -20,9 +20,9 @@ const StyledPopper = styled(MuiPopper)({ paddingTop: "16px", }); -const Paper = styled(MuiPaper)({ +const Paper = styled(MuiPaper)(({ theme }) => ({ minWidth: "fit-content", - border: "1px solid #E7E7EA", + border: `1px solid ${theme.palette.secondary[200]}`, boxShadow: "0px 10px 24px rgba(35, 48, 143, 0.3)", ".MuiListItem-root[id='popperItem']": { backgroundColor: "#FFFFFF", @@ -48,7 +48,7 @@ const Paper = styled(MuiPaper)({ }, }, }, -}); +})); const ListItem = styled(MuiListItem)({ padding: "0", diff --git a/frontend/packages/core/src/stepper.tsx b/frontend/packages/core/src/stepper.tsx index 1a61023b8b..fec9bcbf87 100644 --- a/frontend/packages/core/src/stepper.tsx +++ b/frontend/packages/core/src/stepper.tsx @@ -7,9 +7,10 @@ import { StepConnector as MuiStepConnector, StepLabel as MuiStepLabel, Stepper as MuiStepper, + useTheme, } from "@mui/material"; -const StepContainer = styled.div({ +const StepContainer = styled.div(({ theme }) => ({ margin: "0px 2px 30px 2px", ".MuiStepLabel-label": { fontWeight: 500, @@ -67,7 +68,7 @@ const StepContainer = styled.div({ ".MuiStepConnector-line": { height: "5px", border: 0, - backgroundColor: "#E7E7EA", + backgroundColor: theme.palette.secondary[200], borderRadius: "4px", }, @@ -78,7 +79,7 @@ const StepContainer = styled.div({ ".Mui-completed .MuiStepConnector-line": { backgroundColor: "#3548D4", }, -}); +})); const Circle = styled.div((props: { background: string; border: string }) => ({ backgroundColor: props.background, @@ -115,30 +116,30 @@ export interface StepIconProps { variant: StepIconVariant; } -const stepIconVariants = { - active: { - background: "#FFFFFF", - border: "1px solid #3548D4", - font: "#3548D4", - }, - pending: { - background: "#E7E7EA", - border: "#E7E7EA", - font: "rgba(13, 16, 48, 0.38)", - }, - success: { - background: "#3548D4", - border: "#3548D4", - font: "#FFFFFF", - }, - failed: { - background: "#DB3615", - border: "#DB3615", - font: "#FFFFFF", - }, -}; - const StepIcon: React.FC = ({ index, variant }) => { + const theme = useTheme(); + const stepIconVariants = { + active: { + background: "#FFFFFF", + border: "1px solid #3548D4", + font: "#3548D4", + }, + pending: { + background: theme.palette.secondary[200], + border: theme.palette.secondary[200], + font: "rgba(13, 16, 48, 0.38)", + }, + success: { + background: "#3548D4", + border: "#3548D4", + font: "#FFFFFF", + }, + failed: { + background: theme.palette.error[600], + border: theme.palette.error[600], + font: "#FFFFFF", + }, + }; const color = stepIconVariants[variant || "pending"]; let Icon = <>{index}; if (variant === "success") { diff --git a/frontend/packages/core/src/tab.tsx b/frontend/packages/core/src/tab.tsx index 30515adf23..bd32380105 100644 --- a/frontend/packages/core/src/tab.tsx +++ b/frontend/packages/core/src/tab.tsx @@ -4,12 +4,12 @@ import { TabContext, TabList, TabPanel as MuiTabPanel } from "@mui/lab"; import type { TabProps as MuiTabProps, TabsProps as MuiTabsProps } from "@mui/material"; import { Tab as MuiTab } from "@mui/material"; -const StyledTab = styled(MuiTab)({ +const StyledTab = styled(MuiTab)(({ theme }) => ({ minWidth: "111px", height: "46px", padding: "12px 32px", color: "rgba(13, 16, 48, 0.6)", - borderBottom: "3px solid #E7E7EA", + borderBottom: `3px solid ${theme.palette.secondary[200]}`, fontSize: "14px", fontWeight: "bold", opacity: "1", @@ -21,7 +21,7 @@ const StyledTab = styled(MuiTab)({ }, "&:hover": { color: "rgba(13, 16, 48, 0.6)", - backgroundColor: "#E7E7EA", + backgroundColor: theme.palette.secondary[200], outline: "none", }, "&:focus": { @@ -39,7 +39,7 @@ const StyledTab = styled(MuiTab)({ ".MuiTab-wrapper": { margin: "auto", }, -}); +})); const StyledTabs = styled(TabList)({ ".MuiTabs-indicator": { diff --git a/frontend/packages/core/src/tests/__snapshots__/button.test.tsx.snap b/frontend/packages/core/src/tests/__snapshots__/button.test.tsx.snap index 5a634f8c53..b5b3764f5e 100644 --- a/frontend/packages/core/src/tests/__snapshots__/button.test.tsx.snap +++ b/frontend/packages/core/src/tests/__snapshots__/button.test.tsx.snap @@ -1,11 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Destructive Button Component 1`] = `""`; +exports[`Destructive Button Component 1`] = `""`; -exports[`Large Button Component 1`] = `""`; +exports[`Large Button Component 1`] = `""`; -exports[`Neutral Button Component 1`] = `""`; +exports[`Neutral Button Component 1`] = `""`; -exports[`Primary Button Component 1`] = `""`; +exports[`Primary Button Component 1`] = `""`; -exports[`Small Button Component 1`] = `""`; +exports[`Small Button Component 1`] = `""`; diff --git a/frontend/packages/core/src/tests/button.test.tsx b/frontend/packages/core/src/tests/button.test.tsx index dca3aaabf9..1bd0093b9b 100644 --- a/frontend/packages/core/src/tests/button.test.tsx +++ b/frontend/packages/core/src/tests/button.test.tsx @@ -4,6 +4,7 @@ import { render, unmountComponentAtNode } from "react-dom"; import "@testing-library/jest-dom"; import { Button } from "../button"; +import { ThemeProvider } from "../Theme"; let container: HTMLElement; beforeEach(() => { @@ -19,31 +20,56 @@ afterEach(() => { }); test("Primary Button Component", () => { - render(
diff --git a/frontend/workflows/envoy/src/tests/remote-triage.test.tsx b/frontend/workflows/envoy/src/tests/remote-triage.test.tsx index 6f96574cd5..ced199e027 100644 --- a/frontend/workflows/envoy/src/tests/remote-triage.test.tsx +++ b/frontend/workflows/envoy/src/tests/remote-triage.test.tsx @@ -1,5 +1,6 @@ import React from "react"; import { BrowserRouter } from "react-router-dom"; +import { ThemeProvider } from "@clutch-sh/core/src/Theme"; import { render } from "@testing-library/react"; import "@testing-library/jest-dom"; @@ -9,7 +10,9 @@ import RemoteTriage from "../remote-triage"; test("renders correctly", () => { const { asFragment } = render( - + + + ); diff --git a/frontend/workflows/experimentation/src/tests/__snapshots__/list-experiments.test.tsx.snap b/frontend/workflows/experimentation/src/tests/__snapshots__/list-experiments.test.tsx.snap index d162965a4b..aeee5c7e38 100644 --- a/frontend/workflows/experimentation/src/tests/__snapshots__/list-experiments.test.tsx.snap +++ b/frontend/workflows/experimentation/src/tests/__snapshots__/list-experiments.test.tsx.snap @@ -19,47 +19,44 @@ exports[`renders correctly 1`] = ` class="MuiGrid-root css-vj1n65-MuiGrid-root" >
column 1 column 2 @@ -69,7 +66,7 @@ exports[`renders correctly 1`] = `