From df4724be2b15c940db250cec132abffd02932658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Cort=C3=A9s?= Date: Thu, 6 Jun 2024 14:40:21 -0600 Subject: [PATCH 1/5] add storybook's theming addon --- frontend/.storybook/main.ts | 1 + frontend/.storybook/preview.js | 15 +++++------ .../.storybook/withClutchTheme.decorator.js | 25 +++++++++++++++++++ frontend/package.json | 1 + frontend/yarn.lock | 10 ++++++++ 5 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 frontend/.storybook/withClutchTheme.decorator.js diff --git a/frontend/.storybook/main.ts b/frontend/.storybook/main.ts index 6762bfb8a7..d6ff1f75f8 100644 --- a/frontend/.storybook/main.ts +++ b/frontend/.storybook/main.ts @@ -45,6 +45,7 @@ const config: StorybookConfig = { "@storybook/addon-essentials", "@storybook/addon-links", "@storybook/addon-a11y", + "@storybook/addon-themes" ], }; diff --git a/frontend/.storybook/preview.js b/frontend/.storybook/preview.js index 6e534887d7..ec97ebd9b9 100644 --- a/frontend/.storybook/preview.js +++ b/frontend/.storybook/preview.js @@ -1,12 +1,13 @@ -import React from "react"; -import { Theme } from "./../packages/core/src/AppProvider/themes"; +import { withClutchTheme } from "./withClutchTheme.decorator"; export const decorators = [ - (Story) => ( - - - - ), + withClutchTheme({ + themes: { + light: "light", + dark: "dark", + }, + defaultTheme: "light", + }), ]; export const parameters = { diff --git a/frontend/.storybook/withClutchTheme.decorator.js b/frontend/.storybook/withClutchTheme.decorator.js new file mode 100644 index 0000000000..e30b6f8137 --- /dev/null +++ b/frontend/.storybook/withClutchTheme.decorator.js @@ -0,0 +1,25 @@ +import { DecoratorHelpers } from "@storybook/addon-themes"; +import { ThemeProvider } from "../packages/core/src/Theme"; + +const { + initializeThemeState, + pluckThemeFromContext, + useThemeParameters, +} = DecoratorHelpers; + +export const withClutchTheme = ({ themes, defaultTheme }) => { + initializeThemeState(Object.keys(themes), defaultTheme); + + return (Story, context) => { + const selectedTheme = pluckThemeFromContext(context); + const { themeOverride } = useThemeParameters(); + + const selected = themeOverride || selectedTheme || defaultTheme; + + return ( + + + + ); + }; +}; diff --git a/frontend/package.json b/frontend/package.json index 95562f4ca5..983432d084 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -103,6 +103,7 @@ "@storybook/addon-actions": "^7.6.0", "@storybook/addon-essentials": "^7.6.0", "@storybook/addon-links": "^7.6.0", + "@storybook/addon-themes": "^7.6.17", "@storybook/node-logger": "^7.6.0", "@storybook/preset-typescript": "^3.0.0", "@storybook/react-webpack5": "^7.6.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 0353d671a2..20ac93e0dd 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1895,6 +1895,7 @@ __metadata: "@storybook/addon-actions": "npm:^7.6.0" "@storybook/addon-essentials": "npm:^7.6.0" "@storybook/addon-links": "npm:^7.6.0" + "@storybook/addon-themes": "npm:^7.6.17" "@storybook/node-logger": "npm:^7.6.0" "@storybook/preset-typescript": "npm:^3.0.0" "@storybook/react-webpack5": "npm:^7.6.0" @@ -4776,6 +4777,15 @@ __metadata: languageName: node linkType: hard +"@storybook/addon-themes@npm:^7.6.17": + version: 7.6.19 + resolution: "@storybook/addon-themes@npm:7.6.19" + dependencies: + ts-dedent: "npm:^2.0.0" + checksum: 10c0/c5932710df39b13cfd669febd8fa6d400fdff65466acc8afe857d49573da9d40e9515d8e0d3687846d50d128f76bf5ac84a37ef7634e2383671fbd9b6ab03483 + languageName: node + linkType: hard + "@storybook/addon-toolbars@npm:7.6.17": version: 7.6.17 resolution: "@storybook/addon-toolbars@npm:7.6.17" From afad3a30d378d4ef09a3504471c58c2d75f9edf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Cort=C3=A9s?= Date: Tue, 13 Feb 2024 11:12:35 -0600 Subject: [PATCH 2/5] enable auto dark mode --- frontend/packages/core/src/AppProvider/themes.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/packages/core/src/AppProvider/themes.tsx b/frontend/packages/core/src/AppProvider/themes.tsx index 6944a85902..5ae24bfb14 100644 --- a/frontend/packages/core/src/AppProvider/themes.tsx +++ b/frontend/packages/core/src/AppProvider/themes.tsx @@ -24,10 +24,10 @@ const useTheme = () => useMuiTheme() as MuiTheme; const Theme: React.FC = ({ children }) => { // Uncomment to use dark mode - /* // Detect system color mode + // Detect system color mode const prefersDarkMode = - window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches; */ - const prefersDarkMode = false; + window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches; + return ( {children} From 642ed2c04d6e66556d749ee6b38d676b53a582b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Cort=C3=A9s?= Date: Thu, 6 Jun 2024 16:11:14 -0600 Subject: [PATCH 3/5] use selected theme for background --- frontend/.storybook/preview.js | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/frontend/.storybook/preview.js b/frontend/.storybook/preview.js index ec97ebd9b9..f97832a553 100644 --- a/frontend/.storybook/preview.js +++ b/frontend/.storybook/preview.js @@ -1,4 +1,25 @@ +import { DecoratorHelpers } from "@storybook/addon-themes"; import { withClutchTheme } from "./withClutchTheme.decorator"; +import { Global, css } from "@emotion/react"; +import { clutchColors } from "../packages/core/src/Theme/colors"; + +const { pluckThemeFromContext } = DecoratorHelpers; + +const withGlobalStyles = (Story, context) => { + const selectedTheme = pluckThemeFromContext(context); + const GlobalStyles = css` + body { + background: ${clutchColors(selectedTheme).neutral["50"]}; + } + `; + + return ( + <> + + + + ); +}; export const decorators = [ withClutchTheme({ @@ -8,19 +29,20 @@ export const decorators = [ }, defaultTheme: "light", }), + withGlobalStyles, ]; export const parameters = { backgrounds: { - default: "clutch", + default: "light", values: [ { - name: "clutch", - value: "#f9fafe", + name: "light", + value: clutchColors("light").neutral["50"], }, { - name: "light", - value: "#ffffff", + name: "dark", + value: clutchColors("dark").neutral["50"], }, ], }, From 226a9de76ee87c321e2bd2aab6d25ea43a591c5f 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: Tue, 11 Jun 2024 16:47:45 -0600 Subject: [PATCH 4/5] frontend: Adjust current palette for accessibility / look for components where the switch between light/dark/else affects readability (#3038) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Search Icon Button:** in both light and dark mode the background where this component is used is dark, so there is no need to use the contrastColor value here: Screenshot 2024-06-11 at 2 04 36 p m Screenshot 2024-06-11 at 2 04 46 p m **Horizontal Rule:** Opacity was too low: Screenshot 2024-06-11 at 2 05 05 p m Screenshot 2024-06-11 at 2 05 13 p m **Stepper:** Inactive options opacity was too low: Screenshot 2024-06-11 at 2 05 35 p m Screenshot 2024-06-11 at 2 05 44 p m --- frontend/packages/core/src/AppLayout/search.tsx | 2 +- .../core/src/AppLayout/stories/searchfield.stories.tsx | 6 +++++- .../core/src/Input/stories/date-time.stories.tsx | 9 +++++---- frontend/packages/core/src/horizontal-rule.tsx | 4 ++-- frontend/packages/core/src/stepper.tsx | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/frontend/packages/core/src/AppLayout/search.tsx b/frontend/packages/core/src/AppLayout/search.tsx index b1b6e110b0..139c938a0b 100644 --- a/frontend/packages/core/src/AppLayout/search.tsx +++ b/frontend/packages/core/src/AppLayout/search.tsx @@ -80,7 +80,7 @@ const ResultLabel = styled(Typography)(({ theme }: { theme: Theme }) => ({ // main search icon on header const SearchIconButton = styled(IconButton)(({ theme }: { theme: Theme }) => ({ - color: theme.palette.contrastColor, + color: theme.palette.common.white, fontSize: "24px", padding: "12px", marginRight: "8px", diff --git a/frontend/packages/core/src/AppLayout/stories/searchfield.stories.tsx b/frontend/packages/core/src/AppLayout/stories/searchfield.stories.tsx index c378d880b5..cdf7538fbd 100644 --- a/frontend/packages/core/src/AppLayout/stories/searchfield.stories.tsx +++ b/frontend/packages/core/src/AppLayout/stories/searchfield.stories.tsx @@ -5,6 +5,7 @@ import { Box, Grid as MuiGrid, Theme } from "@mui/material"; import type { Meta } from "@storybook/react"; import { ApplicationContext } from "../../Contexts/app-context"; +import { THEME_VARIANTS } from "../../Theme/colors"; import SearchFieldComponent from "../search"; export default { @@ -62,7 +63,10 @@ export default { const Grid = styled(MuiGrid)(({ theme }: { theme: Theme }) => ({ height: "64px", - backgroundColor: theme.palette.primary[900], + backgroundColor: + theme.palette.mode === THEME_VARIANTS.light + ? theme.palette.primary[900] + : theme.palette.headerGradient, })); const Template = () => ( diff --git a/frontend/packages/core/src/Input/stories/date-time.stories.tsx b/frontend/packages/core/src/Input/stories/date-time.stories.tsx index b9dcc8955c..be8b8ac5e3 100644 --- a/frontend/packages/core/src/Input/stories/date-time.stories.tsx +++ b/frontend/packages/core/src/Input/stories/date-time.stories.tsx @@ -1,5 +1,6 @@ import * as React from "react"; import type { Meta } from "@storybook/react"; +import dayjs from "dayjs"; import type { DateTimePickerProps } from "../date-time"; import DateTimePicker from "../date-time"; @@ -46,19 +47,19 @@ WithError.args = { ...PrimaryDemo.args, error: true, helperText: "error in the field", - onChange: (newValue: unknown) => null, + onChange: (_newValue: unknown) => null, } as DateTimePickerProps; export const WithMinDate = Template.bind({}); WithMinDate.args = { ...PrimaryDemo.args, - minDate: new Date(), + minDate: dayjs(new Date()), onChange: (newValue: unknown) => null, } as DateTimePickerProps; export const WithMaxDate = Template.bind({}); WithMaxDate.args = { ...PrimaryDemo.args, - maxDate: new Date(), - onChange: (newValue: unknown) => null, + maxDate: dayjs(new Date()), + onChange: (_newValue: unknown) => null, } as DateTimePickerProps; diff --git a/frontend/packages/core/src/horizontal-rule.tsx b/frontend/packages/core/src/horizontal-rule.tsx index d37b9962f8..945ec0ff63 100644 --- a/frontend/packages/core/src/horizontal-rule.tsx +++ b/frontend/packages/core/src/horizontal-rule.tsx @@ -32,14 +32,14 @@ const StyledHorizontalRule = styled(HorizontalRuleBase)(({ theme }: { theme: The ".line > span": { display: "block", - borderTop: `1px solid ${alpha(theme.palette.secondary[900], 0.12)}`, + borderTop: `1px solid ${alpha(theme.palette.secondary[900], 0.38)}`, }, ".content": { padding: "0 16px", fontWeight: "bold", fontSize: "14px", - color: alpha(theme.palette.secondary[900], 0.38), + color: alpha(theme.palette.secondary[900], 0.6), textTransform: "uppercase", display: "inline-flex", alignItems: "center", diff --git a/frontend/packages/core/src/stepper.tsx b/frontend/packages/core/src/stepper.tsx index 8dc39ac7df..52d09ce299 100644 --- a/frontend/packages/core/src/stepper.tsx +++ b/frontend/packages/core/src/stepper.tsx @@ -56,7 +56,7 @@ const StepContainer = styled("div")<{ $orientation: StepperOrientation }>( ".MuiStepLabel-label": { fontWeight: 500, fontSize: "14px", - color: alpha(theme.palette.secondary[900], 0.38), + color: alpha(theme.palette.secondary[900], 0.5), }, ".MuiStepLabel-label.Mui-active": { color: theme.palette.secondary[900], From fcb4270774de9f6268c5d52794754d06234a7227 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: Tue, 11 Jun 2024 16:48:01 -0600 Subject: [PATCH 5/5] frontend: Implement a theme switcher input to select from multiple themes (#3039) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added theme option to user preferences: Screenshot 2024-06-11 at 4 28 08 p m Screenshot 2024-06-11 at 4 28 47 p m --- .../packages/core/src/AppLayout/header.tsx | 7 ++- .../core/src/AppLayout/theme-switcher.tsx | 48 +++++++++++++++++++ .../packages/core/src/AppProvider/index.tsx | 16 +++---- .../packages/core/src/AppProvider/themes.tsx | 23 +++++---- .../core/src/Contexts/preferences-context.tsx | 1 + 5 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 frontend/packages/core/src/AppLayout/theme-switcher.tsx diff --git a/frontend/packages/core/src/AppLayout/header.tsx b/frontend/packages/core/src/AppLayout/header.tsx index 6100e8f403..18bd8e955e 100644 --- a/frontend/packages/core/src/AppLayout/header.tsx +++ b/frontend/packages/core/src/AppLayout/header.tsx @@ -11,6 +11,7 @@ import Logo from "./logo"; import Notifications from "./notifications"; import SearchField from "./search"; import ShortLinker from "./shortLinker"; +import ThemeSwitcher from "./theme-switcher"; import { UserInformation } from "./user"; export const APP_BAR_HEIGHT = "64px"; @@ -115,7 +116,11 @@ const Header: React.FC = ({ )} {children && children} - {userInfo && } + {userInfo && ( + + + + )} diff --git a/frontend/packages/core/src/AppLayout/theme-switcher.tsx b/frontend/packages/core/src/AppLayout/theme-switcher.tsx new file mode 100644 index 0000000000..0806658f63 --- /dev/null +++ b/frontend/packages/core/src/AppLayout/theme-switcher.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import DarkModeIcon from "@mui/icons-material/DarkMode"; +import LightModeIcon from "@mui/icons-material/LightMode"; +import { Grid } from "@mui/material"; +import get from "lodash/get"; + +import { useUserPreferences } from "../Contexts"; +import { Select } from "../Input"; +import { THEME_VARIANTS } from "../Theme/colors"; + +const ThemeSwitcher = () => { + const { preferences, dispatch } = useUserPreferences(); + + const options = [ + { + label: THEME_VARIANTS.light, + startAdornment: , + }, + { + label: THEME_VARIANTS.dark, + startAdornment: , + }, + ]; + + const handleOnChange = (value: string) => { + dispatch({ + type: "SetPref", + payload: { key: "theme", value }, + }); + }; + + return ( + + Theme + +