diff --git a/docs/development/feature.md b/docs/development/feature.md index 5b152cac0e..1a0ba2dec2 100644 --- a/docs/development/feature.md +++ b/docs/development/feature.md @@ -511,10 +511,10 @@ There are two new imports (`@clutch-sh/core` and `@clutch-sh/data-layout`) added }, "dependencies": { // highlight-start - "@clutch-sh/core": "^2.0.0-beta", - "@clutch-sh/data-layout": "^2.0.0-beta", + "@clutch-sh/core": "^3.0.0-beta", + "@clutch-sh/data-layout": "^3.0.0-beta", // highlight-end - "@clutch-sh/wizard": "^2.0.0-beta", + "@clutch-sh/wizard": "^3.0.0-beta", "react": "^17.0.2", "react-dom": "^17.0.2" }, @@ -629,9 +629,9 @@ There is another new import (`lodash`) added in the code above. Let's also add t "test:watch": "yarn run test --watch" }, "dependencies": { - "@clutch-sh/core": "^2.0.0-beta", - "@clutch-sh/data-layout": "^2.0.0-beta", - "@clutch-sh/wizard": "^2.0.0-beta", + "@clutch-sh/core": "^3.0.0-beta", + "@clutch-sh/data-layout": "^3.0.0-beta", + "@clutch-sh/wizard": "^3.0.0-beta", // highlight-next-line "lodash": "^4.17.21", "react": "^17.0.2", diff --git a/examples/amiibo/frontend/workflows/amiibo/package.json b/examples/amiibo/frontend/workflows/amiibo/package.json index eadb631351..b40063a180 100644 --- a/examples/amiibo/frontend/workflows/amiibo/package.json +++ b/examples/amiibo/frontend/workflows/amiibo/package.json @@ -18,9 +18,9 @@ "test:watch": "yarn run test --watch" }, "dependencies": { - "@clutch-sh/core": "^2.0.0-beta", - "@clutch-sh/data-layout": "^2.0.0-beta", - "@clutch-sh/wizard": "^2.0.0-beta", + "@clutch-sh/core": "^3.0.0-beta", + "@clutch-sh/data-layout": "^3.0.0-beta", + "@clutch-sh/wizard": "^3.0.0-beta", "lodash": "^4.17.21", "react": "^17.0.2", "react-dom": "^17.0.2" diff --git a/frontend/.storybook/preview.js b/frontend/.storybook/preview.js index 4a63b2f2bf..63839e611f 100644 --- a/frontend/.storybook/preview.js +++ b/frontend/.storybook/preview.js @@ -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/lerna.json b/frontend/lerna.json index 35d40319f9..323894700f 100644 --- a/frontend/lerna.json +++ b/frontend/lerna.json @@ -6,5 +6,5 @@ "packages/*", "workflows/*" ], - "version": "2.0.0-beta" + "version": "3.0.0-beta" } diff --git a/frontend/packages/app/package.json b/frontend/packages/app/package.json index fa329c8226..f3fb35f779 100644 --- a/frontend/packages/app/package.json +++ b/frontend/packages/app/package.json @@ -18,7 +18,7 @@ "test:e2e": "cypress run" }, "dependencies": { - "@clutch-sh/core": "^2.0.0-beta", + "@clutch-sh/core": "^3.0.0-beta", "react": "^17.0.2", "react-app-rewired": "^2.1.8", "react-dom": "^17.0.2" 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/package.json b/frontend/packages/core/package.json index 6f7f4b1c3f..23db25e230 100644 --- a/frontend/packages/core/package.json +++ b/frontend/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@clutch-sh/core", - "version": "2.0.0-beta", + "version": "3.0.0-beta", "description": "Clutch Core Components", "homepage": "https://clutch.sh/docs/development/frontend/overview#clutch-shcore", "license": "Apache-2.0", diff --git a/frontend/packages/core/src/AppLayout/drawer.tsx b/frontend/packages/core/src/AppLayout/drawer.tsx index fd0d412d40..e06ececf22 100644 --- a/frontend/packages/core/src/AppLayout/drawer.tsx +++ b/frontend/packages/core/src/AppLayout/drawer.tsx @@ -2,10 +2,12 @@ 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, ListItemButton, + Theme, Typography, } from "@mui/material"; import _ from "lodash"; @@ -16,22 +18,26 @@ import { useAppContext } from "../Contexts"; import { useNavigate } from "../navigation"; import type { PopperItemProps } from "../popper"; import { Popper, PopperItem } from "../popper"; +import { THEME_VARIANTS } from "../Theme/colors"; import { filterHiddenRoutes, routesByGrouping, sortedGroupings, workflowByRoute } from "./utils"; // sidebar -const DrawerPanel = styled(MuiDrawer)({ +const DrawerPanel = styled(MuiDrawer)(({ theme }: { theme: 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 === THEME_VARIANTS.light + ? theme.palette.contrastColor + : 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 +45,42 @@ const GroupList = styled(List)({ }); const GroupListItem = styled(ListItemButton)<{ icon: number }>( - { + ({ theme }: { theme: 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 }: { theme: Theme }) => ({ + color: alpha(theme.palette.secondary[900], 0.6), fontWeight: 500, fontSize: "14px", lineHeight: "18px", @@ -83,19 +89,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 }: { theme: 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..6100e8f403 100644 --- a/frontend/packages/core/src/AppLayout/header.tsx +++ b/frontend/packages/core/src/AppLayout/header.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Link } from "react-router-dom"; import styled from "@emotion/styled"; -import { AppBar as MuiAppBar, Box, Grid, Toolbar, Typography } from "@mui/material"; +import { AppBar as MuiAppBar, Box, Grid, Theme, Toolbar, Typography } from "@mui/material"; import type { AppConfiguration } from "../AppProvider"; import { FeatureOn, SimpleFeatureFlag } from "../flags"; @@ -45,23 +45,23 @@ interface HeaderProps extends AppConfiguration { userInfo?: boolean; } -const AppBar = styled(MuiAppBar)({ +const AppBar = styled(MuiAppBar)(({ theme }: { theme: 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 }: { theme: 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..558f0ebdd0 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, @@ -10,50 +11,51 @@ import { MenuList, Paper as MuiPaper, Popper as MuiPopper, + Theme, } from "@mui/material"; -const StyledNotificationsIcon = styled(IconButton)({ - color: "#ffffff", +const StyledNotificationsIcon = styled(IconButton)(({ theme }: { theme: 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 }: { theme: 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 }: { theme: 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 }: { theme: 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..b1b6e110b0 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,8 @@ import { InputAdornment as MuiInputAdornment, Popper as MuiPopper, TextField, + TextFieldProps, + Theme, Typography, } from "@mui/material"; import type { AutocompleteRenderInputParams } from "@mui/material/Autocomplete"; @@ -25,7 +28,7 @@ import { filterHiddenRoutes, searchIndexes } from "./utils"; const hotKey = "/"; -const InputField = styled(TextField)({ +const InputField: React.FC = styled(TextField)(({ theme }: { theme: Theme }) => ({ // input field maxWidth: "551px", minWidth: "551px", @@ -34,13 +37,16 @@ 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, + "&.Mui-focused fieldset": { + border: "none", + }, }, // input text color ".MuiAutocomplete-input": { - color: "#0d1030", + color: theme.palette.secondary[900], }, // close icon's container @@ -51,14 +57,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 +73,42 @@ const ResultGrid = styled(Grid)({ }); // search's result options -const ResultLabel = styled(Typography)({ - color: "#0d1030", +const ResultLabel = styled(Typography)(({ theme }: { theme: Theme }) => ({ + color: theme.palette.secondary[900], fontSize: "14px", -}); +})); // main search icon on header -const SearchIconButton = styled(IconButton)({ - color: "#ffffff", +const SearchIconButton = styled(IconButton)(({ theme }: { theme: 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 }: { theme: Theme }) => ({ + color: theme.palette.secondary[900], marginLeft: "8px", -}); +})); // closed icon svg -const StyledCloseIcon = styled(Icon)({ - color: "#0c0b31", +const StyledCloseIcon = styled(Icon)(({ theme }: { theme: Theme }) => ({ + color: theme.palette.secondary[900], fontSize: "24px", -}); +})); // popper containing the search result options -const Popper = styled(MuiPopper)({ +const Popper = styled(MuiPopper)(({ theme }: { theme: Theme }) => ({ ".MuiPaper-root": { - 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)}`, "> .MuiAutocomplete-listbox": { "> .MuiAutocomplete-option": { @@ -110,16 +116,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], }, -}); +})); const renderPopper = props => { return ; diff --git a/frontend/packages/core/src/AppLayout/shortLinker.tsx b/frontend/packages/core/src/AppLayout/shortLinker.tsx index 8da62b9726..200cd354dd 100644 --- a/frontend/packages/core/src/AppLayout/shortLinker.tsx +++ b/frontend/packages/core/src/AppLayout/shortLinker.tsx @@ -3,12 +3,14 @@ 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, MenuList, Paper as MuiPaper, Popper as MuiPopper, + Theme, } from "@mui/material"; import { generateShortLinkRoute } from "../AppProvider/short-link-proxy"; @@ -33,28 +35,28 @@ const Popper = styled(MuiPopper)({ zIndex: 1201, }); -const Paper = styled(MuiPaper)({ +const Paper = styled(MuiPaper)(({ theme }: { theme: 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 }: { theme: 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..5db9e022c9 100644 --- a/frontend/packages/core/src/AppLayout/stories/notifications.stories.tsx +++ b/frontend/packages/core/src/AppLayout/stories/notifications.stories.tsx @@ -1,8 +1,8 @@ import * as React from "react"; -import styled from "@emotion/styled"; -import { Grid as MuiGrid } from "@mui/material"; +import { Grid as MuiGrid, Theme } from "@mui/material"; import type { Meta } from "@storybook/react"; +import styled from "../../styled"; import type { NotificationsProp } from "../notifications"; import NotificationsComponent from "../notifications"; @@ -11,10 +11,10 @@ export default { component: NotificationsComponent, } as Meta; -const Grid = styled(MuiGrid)({ +const Grid = styled(MuiGrid)(({ theme }: { theme: 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..c378d880b5 100644 --- a/frontend/packages/core/src/AppLayout/stories/searchfield.stories.tsx +++ b/frontend/packages/core/src/AppLayout/stories/searchfield.stories.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import { MemoryRouter } from "react-router"; import styled from "@emotion/styled"; -import { Box, Grid as MuiGrid } from "@mui/material"; +import { Box, Grid as MuiGrid, Theme } from "@mui/material"; import type { Meta } from "@storybook/react"; import { ApplicationContext } from "../../Contexts/app-context"; @@ -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 }: { theme: 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..2262ffe5d8 100644 --- a/frontend/packages/core/src/AppLayout/stories/user-information.stories.tsx +++ b/frontend/packages/core/src/AppLayout/stories/user-information.stories.tsx @@ -1,8 +1,8 @@ import * as React from "react"; -import styled from "@emotion/styled"; import { Grid as MuiGrid } from "@mui/material"; import type { Meta } from "@storybook/react"; +import styled from "../../styled"; import type { UserInformationProps } from "../user"; import { UserInformation as UserInformationComponent } from "../user"; @@ -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..03fff2691d 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..854710e9b0 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, @@ -12,18 +13,19 @@ import { MenuList as MuiMenuList, Paper as MuiPaper, Popper as MuiPopper, + Theme, } from "@mui/material"; import Cookies from "js-cookie"; import jwtDecode from "jwt-decode"; import * as _ from "lodash"; -const UserPhoto = styled(IconButton)({ +const UserPhoto = styled(IconButton)(({ theme }: { theme: 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 +34,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 }: { theme: Theme }) => ({ + backgroundColor: theme.palette.primary[500], + color: theme.palette.contrastColor, fontWeight: 500, -}); +})); -const Paper = styled(MuiPaper)({ +const Paper = styled(MuiPaper)(({ theme }: { theme: 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 +55,18 @@ const Popper = styled(MuiPopper)({ zIndex: 1201, }); -const MenuList = styled(MuiMenuList)({ +const MenuList = styled(MuiMenuList)(({ theme }: { theme: 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 +87,15 @@ const AvatarListItemIcon = styled(ListItemIcon)({ }, }); -const AvatarListItemText = styled(MuiListItemText)({ +const AvatarListItemText = styled(MuiListItemText)(({ theme }: { theme: 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 +103,18 @@ const MenuItem = styled(MuiMenuItem)({ padding: "12px", }); -const ListItemText = styled(MuiListItemText)({ +const ListItemText = styled(MuiListItemText)(({ theme }: { theme: 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 }: { theme: Theme }) => ({ + backgroundColor: theme.palette.secondary[100], +})); const Grow = styled(MuiGrow)((props: { placement: string }) => ({ transformOrigin: props.placement, diff --git a/frontend/packages/core/src/AppProvider/index.tsx b/frontend/packages/core/src/AppProvider/index.tsx index d09503a29c..3ee75119ac 100644 --- a/frontend/packages/core/src/AppProvider/index.tsx +++ b/frontend/packages/core/src/AppProvider/index.tsx @@ -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 71a8f6dd41..6944a85902 100644 --- a/frontend/packages/core/src/AppProvider/themes.tsx +++ b/frontend/packages/core/src/AppProvider/themes.tsx @@ -1,139 +1,37 @@ 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 { StylesProvider } from "@mui/styles"; - -declare module "@mui/styles/defaultTheme" { - interface DefaultTheme extends MuiTheme {} -} - -interface ClutchPalette extends PaletteOptions { - accent: { - main: string; - }; - destructive: { - main: string; - }; +import { useTheme as useMuiTheme } from "@mui/material"; +import type { Theme as MuiTheme } from "@mui/material/styles"; + +import { ThemeProvider } from "../Theme"; +import { THEME_VARIANTS } from "../Theme/colors"; +import type { ClutchColors } from "../Theme/types"; + +declare module "@mui/material/styles" { + interface Theme { + colors: ClutchColors; + } + interface ThemeOptions { + colors?: ClutchColors; + } + interface Palette { + contrastColor: string; + headerGradient: string; + brandColor: string; + } } -interface ClutchTheme extends DeprecatedThemeOptions { - palette: ClutchPalette; -} - -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; -}; - -interface ThemeProps { - // disabling temporarily as we figure out theming - // eslint-disable-next-line react/no-unused-prop-types - variant?: "light"; -} +const useTheme = () => useMuiTheme() as MuiTheme; -const Theme: React.FC = ({ children }) => { - const theme = lightTheme; +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} - - - + + {children} + ); }; diff --git a/frontend/packages/core/src/Feedback/alert.tsx b/frontend/packages/core/src/Feedback/alert.tsx index e114c59acb..e4ee38845c 100644 --- a/frontend/packages/core/src/Feedback/alert.tsx +++ b/frontend/packages/core/src/Feedback/alert.tsx @@ -4,24 +4,17 @@ 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, Theme } 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 }: { theme: Theme }) => ({ borderRadius: "8px", padding: "16px", paddingLeft: "24px", paddingBottom: "20px", - color: "rgba(13, 16, 48, 0.6)", + color: alpha(theme.palette.secondary[900], 0.75), fontSize: "14px", overflow: "auto", ".MuiAlert-icon": { @@ -34,36 +27,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 }: { theme: 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 }: { theme: Theme }) => ({ + color: theme.palette.error[700], +})); -const InfoIcon = styled(MuiInfoIcon)({ - color: "#3548d4", -}); +const InfoIcon = styled(MuiInfoIcon)(({ theme }: { theme: Theme }) => ({ + color: theme.palette.primary[600], +})); -const SuccessIcon = styled(MuiSuccessIcon)({ - color: "#1e942d", -}); +const SuccessIcon = styled(MuiSuccessIcon)(({ theme }: { theme: Theme }) => ({ + color: theme.palette.success[500], +})); -const WarningIcon = styled(MuiWarningIcon)({ - color: "#ffcc80", -}); +const WarningIcon = styled(MuiWarningIcon)(({ theme }: { theme: Theme }) => ({ + color: theme.palette.warning[500], +})); -const AlertTitle = styled(MuiAlertTitle)({ - color: "#0D1030", +const AlertTitle = styled(MuiAlertTitle)(({ theme }: { theme: 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..c65cda3646 100644 --- a/frontend/packages/core/src/Feedback/error/details.tsx +++ b/frontend/packages/core/src/Feedback/error/details.tsx @@ -5,9 +5,12 @@ import { Accordion as MuiAccordion, AccordionDetails as MuiAccordionDetails, AccordionSummary as MuiAccordionSummary, + alpha, Button, Grid, + Theme, useControlled, + useTheme, } from "@mui/material"; import type { ClutchError } from "../../Network/errors"; @@ -19,11 +22,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 }: { theme: 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 +41,9 @@ const Accordion = styled(MuiAccordion)({ }); const AccordionSummary = styled(MuiAccordionSummary)<{ $expanded: boolean }>( - { - background: "linear-gradient(to right, #DB3615 8px, #FDE9E7 0%)", - color: "#0D1030", + ({ theme }: { theme: 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 +55,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 }: { theme: Theme }) => ({ + background: `linear-gradient(to right, ${theme.palette.error[600]} 8px, ${theme.palette.contrastColor} 0%)`, padding: "0", paddingLeft: "8px", borderBottomLeftRadius: "8px", borderBottomRightRadius: "8px", display: "flex", flexDirection: "column", -}); +})); -const ListItem = styled("li")({ +const ListItem = styled("li")(({ theme }: { theme: 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 }: { theme: 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 }: { theme: Theme }) => ({ + color: alpha(theme.palette.secondary[900], 0.6), fontSize: "14px", lineHeight: "24px", -}); +})); -const DialogButton = styled(Button)({ - color: "#3548D4", +const DialogButton = styled(Button)(({ theme }: { theme: 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 +146,7 @@ const ErrorDetails = ({ error }: ErrorDetailsProps) => { {hasWrappedErrorDetails && (
- + The following errors were encountered:
    @@ -152,7 +159,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.error[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..cf7833d2e8 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, Theme } 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 }: { theme: 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..7386eee922 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"; +import { Popover, Theme, Typography } from "@mui/material"; -const HelpIconContainer = styled.div({ +import styled from "../styled"; + +const HelpIconContainer = styled("div")(({ theme }: { theme: 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..e579c7f23d 100644 --- a/frontend/packages/core/src/Feedback/tooltip.tsx +++ b/frontend/packages/core/src/Feedback/tooltip.tsx @@ -1,30 +1,33 @@ import * as React from "react"; -import styled from "@emotion/styled"; -import type { TooltipProps as MuiTooltipProps } from "@mui/material"; +import type { Theme, 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 }) => ({ - maxWidth: props.maxwidth, - backgroundColor: "#0D1030", - borderRadius: "6px", - "&.MuiTooltip-tooltipPlacementLeft": { - margin: "0 2px", - }, - "&.MuiTooltip-tooltipPlacementRight": { - margin: "0 2px", - }, - "&.MuiTooltip-tooltipPlacementTop": { - margin: "2px 0", - }, - "&.MuiTooltip-tooltipPlacementBottom": { - margin: "2px 0", - }, -})); +const StyledTooltip = styled(BaseTooltip)( + (props: { maxwidth?: string }) => ({ theme }: { theme: Theme }) => ({ + maxWidth: props.maxwidth, + backgroundColor: theme.palette.secondary[900], + borderRadius: "6px", + "&.MuiTooltip-tooltipPlacementLeft": { + margin: "0 2px", + }, + "&.MuiTooltip-tooltipPlacementRight": { + margin: "0 2px", + }, + "&.MuiTooltip-tooltipPlacementTop": { + margin: "2px 0", + }, + "&.MuiTooltip-tooltipPlacementBottom": { + margin: "2px 0", + }, + }) +); export interface TooltipProps extends Pick { @@ -45,7 +48,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/Input/checkbox.tsx b/frontend/packages/core/src/Input/checkbox.tsx index c2fb2c3ee2..cd2373c91e 100644 --- a/frontend/packages/core/src/Input/checkbox.tsx +++ b/frontend/packages/core/src/Input/checkbox.tsx @@ -1,7 +1,8 @@ import * as React from "react"; import CheckIcon from "@mui/icons-material/Check"; -import type { CheckboxProps as MuiCheckboxProps } from "@mui/material"; +import type { CheckboxProps as MuiCheckboxProps, Theme } 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 }: { theme: 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.contrastColor, "&: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 }: { theme: 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 }: { theme: 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..3a9fb138d5 100644 --- a/frontend/packages/core/src/Input/radio-group.tsx +++ b/frontend/packages/core/src/Input/radio-group.tsx @@ -5,20 +5,21 @@ import { FormControlLabel, FormLabel as MuiFormLabel, RadioGroup as MuiRadioGroup, + Theme, } from "@mui/material"; import Radio from "./radio"; -const FormLabel = styled(MuiFormLabel)({ +const FormLabel = styled(MuiFormLabel)(({ theme }: { theme: 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..8bfbf58643 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 type { RadioProps as MuiRadioProps, Theme } 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 }: { theme: 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 }: { theme: 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 }: { theme: 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 }: { theme: 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 }: { theme: Theme }) => ({ height: "12px", width: "12px", - background: "#FFFFFF", + background: theme.palette.contrastColor, 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..01e5bc39de 100644 --- a/frontend/packages/core/src/Input/select.tsx +++ b/frontend/packages/core/src/Input/select.tsx @@ -2,8 +2,9 @@ import * as React from "react"; import styled from "@emotion/styled"; import ErrorIcon from "@mui/icons-material/Error"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import type { SelectProps as MuiSelectProps } from "@mui/material"; +import type { SelectProps as MuiSelectProps, Theme } 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 }: { theme: 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 }: { theme: 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.error[600], }, -}); +})); const SelectIcon = (props: any) => (
    @@ -71,28 +72,28 @@ const BaseSelect = ({ className, ...props }: MuiSelectProps) => ( /> ); -const StyledSelect = styled(BaseSelect)({ +const StyledSelect = styled(BaseSelect)(({ theme }: { theme: Theme }) => ({ "--notched-border-width": "1px", padding: "0", - backgroundColor: "#FFFFFF", + backgroundColor: theme.palette.contrastColor, 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..8ecfdf5f8a 100644 --- a/frontend/packages/core/src/Input/switchToggle.tsx +++ b/frontend/packages/core/src/Input/switchToggle.tsx @@ -6,66 +6,69 @@ 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 type { SwitchProps as MuiSwitchProps, Theme } from "@mui/material"; +import { alpha, Switch as MuiSwitch } from "@mui/material"; -const SwitchContainer = styled(MuiSwitch)({ +const SwitchContainer = styled(MuiSwitch)(({ theme }: { theme: 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.getContrastText(theme.palette.contrastColor), + 0.25 + )}`, + color: theme.palette.contrastColor, }, }, ".MuiSwitch-track": { - backgroundColor: "#6E7083", + backgroundColor: theme.palette.secondary[500], 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[300], 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..e995f924da 100644 --- a/frontend/packages/core/src/Input/text-field.tsx +++ b/frontend/packages/core/src/Input/text-field.tsx @@ -6,8 +6,10 @@ import WarningIcon from "@mui/icons-material/Warning"; import type { InputProps as MuiInputProps, StandardTextFieldProps as MuiStandardTextFieldProps, + Theme, } from "@mui/material"; import { + alpha, Autocomplete, Grid, IconButton as MuiIconButton, @@ -45,107 +47,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 }: { theme: 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.contrastColor, + + "&.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 }: { theme: 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 +157,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 +175,24 @@ const ResultGrid = styled(Grid)({ }); // search's result options -const ResultLabel = styled(Typography)({ - color: "#0d1030", +const ResultLabel = styled(Typography)(({ theme }: { theme: Theme }) => ({ + color: theme.palette.secondary[900], fontSize: "14px", -}); +})); -const IconButton = styled(MuiIconButton)({ +const IconButton = styled(MuiIconButton)(({ theme }: { theme: Theme }) => ({ borderRadius: "0", - backgroundColor: "#3548D4", - color: "#FFFFFF", + backgroundColor: theme.palette.primary[600], + color: theme.palette.contrastColor, 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..26ec426cda 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.contrastColor, ".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.contrastColor, }, "&.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.contrastColor, + color: theme.palette.secondary[900], textTransform: "none", }, })); diff --git a/frontend/packages/core/src/NPS/feedback.tsx b/frontend/packages/core/src/NPS/feedback.tsx index a078e81cf1..7307ae50d5 100644 --- a/frontend/packages/core/src/NPS/feedback.tsx +++ b/frontend/packages/core/src/NPS/feedback.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { clutch as IClutch } from "@clutch-sh/api"; import MuiSuccessIcon from "@mui/icons-material/CheckCircle"; -import { Grid as MuiGrid } from "@mui/material"; +import { Grid as MuiGrid, useTheme } from "@mui/material"; import { debounce } from "lodash"; import { v4 as uuid } from "uuid"; @@ -70,9 +70,11 @@ const StyledTextField = styled(TextField)<{ $origin: Origin }>( ); const FeedbackAlert = () => { + const theme = useTheme(); + const AlertProps = { iconMapping: { - info: , + info: , }, style: { margin: "32px", diff --git a/frontend/packages/core/src/NPS/header.tsx b/frontend/packages/core/src/NPS/header.tsx index 3e661162b6..e554b4c01d 100644 --- a/frontend/packages/core/src/NPS/header.tsx +++ b/frontend/packages/core/src/NPS/header.tsx @@ -1,11 +1,13 @@ import React from "react"; import ChatBubbleOutlineIcon from "@mui/icons-material/ChatBubbleOutline"; import { + alpha, ClickAwayListener, Grow as MuiGrow, MenuList, Paper as MuiPaper, Popper as MuiPopper, + Theme, } from "@mui/material"; import { get, sortBy } from "lodash"; @@ -27,26 +29,26 @@ const Popper = styled(MuiPopper)({ zIndex: 1201, }); -const Paper = styled(MuiPaper)({ +const Paper = styled(MuiPaper)(({ theme }: { theme: Theme }) => ({ width: "350px", - boxShadow: "0px 15px 35px rgba(53, 72, 212, 0.2)", + boxShadow: `0px 15px 35px ${alpha(theme.palette.primary[600], 0.2)}`, borderRadius: "8px", -}); +})); const StyledFeedbackIcon = styled(IconButton)<{ $open: boolean }>( - { - color: "#ffffff", + ({ theme }: { theme: 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", + }), + props => ({ theme }: { theme: Theme }) => ({ + background: props.$open ? theme.palette.primary[600] : "unset", }) ); diff --git a/frontend/packages/core/src/NPS/tests/banner.test.tsx b/frontend/packages/core/src/NPS/tests/banner.test.tsx index 1749791c1b..27d7f86b62 100644 --- a/frontend/packages/core/src/NPS/tests/banner.test.tsx +++ b/frontend/packages/core/src/NPS/tests/banner.test.tsx @@ -4,10 +4,11 @@ import { render, screen } from "@testing-library/react"; import "@testing-library/jest-dom"; import { ApplicationContext } from "../../Contexts"; -import { HeaderItems } from "../../Contexts/app-context"; -import { Banner, BannerFeedbackProps } from "../banner"; +import { HeaderItem } from "../../Contexts/app-context"; +import { ThemeProvider } from "../../Theme"; +import { Banner, FeedbackBannerProps } from "../banner"; -const customRender = ({ ...props }: BannerFeedbackProps) => { +const customRender = ({ ...props }: FeedbackBannerProps) => { let triggeredHeaderData = { NPS: {} }; return render( @@ -15,7 +16,7 @@ const customRender = ({ ...props }: BannerFeedbackProps) => { // eslint-disable-next-line react/jsx-no-constructed-context-values value={{ workflows: [], - triggerHeaderItem: (item: HeaderItems, data: unknown) => { + triggerHeaderItem: (item: HeaderItem, data: unknown) => { triggeredHeaderData = { ...triggeredHeaderData, [item]: { @@ -26,7 +27,9 @@ const customRender = ({ ...props }: BannerFeedbackProps) => { triggeredHeaderData, }} > - + + + ); }; 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/header.test.tsx b/frontend/packages/core/src/NPS/tests/header.test.tsx index 7f415ea17a..da364cadbc 100644 --- a/frontend/packages/core/src/NPS/tests/header.test.tsx +++ b/frontend/packages/core/src/NPS/tests/header.test.tsx @@ -6,6 +6,7 @@ import "@testing-library/jest-dom"; import * as ApplicationContext from "../../Contexts/app-context"; import contextValues from "../../Contexts/tests/testContext"; +import { ThemeProvider } from "../../Theme"; import { NPSHeader } from ".."; beforeEach(() => { @@ -14,7 +15,11 @@ beforeEach(() => { }); test("renders correctly", () => { - render(); + render( + + + + ); expect(screen.getAllByRole("button")).toHaveLength(1); expect(screen.getByRole("button")).toHaveAttribute("id", "headerFeedbackIcon"); @@ -22,7 +27,11 @@ test("renders correctly", () => { test("opens a popper on click of feedback icon", async () => { const user = userEvent.setup({ delay: null }); - render(); + render( + + + + ); const feedbackButton = await screen.findByRole("button"); await user.click(feedbackButton); 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); diff --git a/frontend/packages/core/src/NPS/wizard.tsx b/frontend/packages/core/src/NPS/wizard.tsx index 4d1c267d1e..814c868953 100644 --- a/frontend/packages/core/src/NPS/wizard.tsx +++ b/frontend/packages/core/src/NPS/wizard.tsx @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import type { Theme } from "@mui/material"; import styled from "../styled"; @@ -10,8 +11,8 @@ const NPSContainer = styled("div")<{ $submit: boolean }>( margin: "auto", borderRadius: "8px", }, - props => ({ - background: props.$submit ? "unset" : "#F9F9FE", + props => ({ theme }: { theme: Theme }) => ({ + background: props.$submit ? "unset" : theme.palette.primary[50], }) ); diff --git a/frontend/packages/core/src/Table/accordion-table.tsx b/frontend/packages/core/src/Table/accordion-table.tsx index b85450461a..151719b0de 100644 --- a/frontend/packages/core/src/Table/accordion-table.tsx +++ b/frontend/packages/core/src/Table/accordion-table.tsx @@ -1,22 +1,24 @@ import * as React from "react"; import ChevronRightIcon from "@mui/icons-material/ChevronRight"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; -import { IconButton as MuiIconButton, TableRow } from "@mui/material"; +import { IconButton as MuiIconButton, TableRow, Theme } from "@mui/material"; import styled from "../styled"; import type { TableRowProps } from "./table"; import { TableCell } from "./table"; -const IconButton = styled(MuiIconButton)({ +const IconButton = styled(MuiIconButton)(({ theme }: { theme: Theme }) => ({ padding: "0", - color: "#0D1030", -}); - -const ChevronRight = styled(ChevronRightIcon)<{ $disabled: boolean }>(props => ({ - color: props?.$disabled ? "#E7E7EA" : "unset", + color: theme.palette.secondary[900], })); +const ChevronRight = styled(ChevronRightIcon)<{ $disabled: boolean }>( + props => ({ theme }: { theme: Theme }) => ({ + color: props?.$disabled ? theme.palette.secondary[200] : "unset", + }) +); + export interface AccordionRowProps { columns: React.ReactElement[]; children: React.ReactElement | React.ReactElement[]; diff --git a/frontend/packages/core/src/Table/metadata-table.tsx b/frontend/packages/core/src/Table/metadata-table.tsx index cf9d02c10c..8e2219125c 100644 --- a/frontend/packages/core/src/Table/metadata-table.tsx +++ b/frontend/packages/core/src/Table/metadata-table.tsx @@ -5,6 +5,7 @@ import { DevTool } from "@hookform/devtools"; import { yupResolver } from "@hookform/resolvers/yup"; import ChevronRightIcon from "@mui/icons-material/ChevronRight"; import { + alpha, Grid as MuiGrid, StandardTextFieldProps, Table as MuiTable, @@ -12,6 +13,7 @@ import { TableCell as MuiTableCell, TableContainer as MuiTableContainer, TableRow, + Theme, } from "@mui/material"; import _ from "lodash"; import type { BaseSchema } from "yup"; @@ -55,11 +57,11 @@ const TableContainer = styled(MuiTableContainer)<{ }) ); -const Table = styled(MuiTable)({ - border: "1px solid rgba(13, 16, 48, 0.12)", +const Table = styled(MuiTable)(({ theme }: { theme: Theme }) => ({ + border: `1px solid ${alpha(theme.palette.secondary[900], 0.12)}`, borderRadius: "4px", borderCollapse: "unset", -}); +})); const TableBody = styled(MuiTableBody)({ "tr:first-of-type > td:first-of-type": { @@ -79,13 +81,13 @@ const TableBody = styled(MuiTableBody)({ }, }); -const TableCell = styled(MuiTableCell)({ - color: "#0D1030", +const TableCell = styled(MuiTableCell)(({ theme }: { theme: Theme }) => ({ + color: theme.palette.secondary[900], fontSize: "14px", fontWeight: "normal", height: "48px", padding: "8px 16px", -}); +})); const Grid = styled(MuiGrid)({ display: "flex", @@ -107,11 +109,11 @@ const Grid = styled(MuiGrid)({ }, }); -const KeyCellContainer = styled(TableCell)({ +const KeyCellContainer = styled(TableCell)(({ theme }: { theme: Theme }) => ({ width: "45%", - background: "rgba(13, 16, 48, 0.03)", + background: alpha(theme.palette.secondary[900], 0.03), fontWeight: 500, -}); +})); interface KeyCellProps { data: IdentifiableRowData; diff --git a/frontend/packages/core/src/Table/table.tsx b/frontend/packages/core/src/Table/table.tsx index b3cce40854..ce590a2a42 100644 --- a/frontend/packages/core/src/Table/table.tsx +++ b/frontend/packages/core/src/Table/table.tsx @@ -4,6 +4,7 @@ import type { TableCellProps as MuiTableCellProps, TableProps as MuiTableProps, TableRowProps as MuiTableRowProps, + Theme, } from "@mui/material"; import { IconButton, @@ -22,9 +23,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 }: { theme: Theme }) => ({ + border: `1px solid ${theme.palette.secondary[200]}`, +})); const StyledTable = styled(MuiTable)<{ $hasActionsColumn?: TableProps["actionsColumn"]; @@ -53,22 +54,22 @@ const StyledTableBody = styled(MuiTableBody)({ display: "contents", }); -const StyledTableHeadRow = styled(MuiTableRow)({ +const StyledTableHeadRow = styled(MuiTableRow)(({ theme }: { theme: Theme }) => ({ display: "contents", - backgroundColor: "#D7DAF6", -}); + backgroundColor: theme.palette.primary[300], +})); const StyledTableRow = styled(MuiTableRow)<{ $responsive?: TableRowProps["responsive"]; }>( - { + ({ theme }: { theme: Theme }) => ({ ":nth-of-type(even)": { - background: "#F8F8F9", + background: theme.palette.secondary[50], }, ":hover": { - background: "#EBEDFB", + background: theme.palette.primary[200], }, - }, + }), props => ({ display: props.$responsive ? "contents" : "", }) @@ -79,17 +80,17 @@ const StyledTableCell = styled(MuiTableCell)<{ $responsive?: TableCellProps["responsive"]; $action?: TableCellProps["action"]; }>( - { + ({ theme }: { theme: Theme }) => ({ alignItems: "center", fontSize: "14px", padding: "15px 16px", - color: "#0D1030", + color: theme.palette.secondary[900], overflow: "hidden", background: "inherit", minHeight: "100%", - }, - props => ({ - borderBottom: props?.$border ? "1px solid #E7E7EA" : "0", + }), + props => ({ theme }: { theme: 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/Theme/colors.tsx b/frontend/packages/core/src/Theme/colors.tsx index 8db1d92f37..54aa2dd4ea 100644 --- a/frontend/packages/core/src/Theme/colors.tsx +++ b/frontend/packages/core/src/Theme/colors.tsx @@ -1,5 +1,12 @@ import type { ClutchColors, ComponentState, ThemeVariant } from "./types"; +export enum THEME_VARIANTS { + light = "light", + dark = "dark", +} + +export const brandColor = "#02acbe"; + export const LIGHT_COLORS: ClutchColors = { neutral: { 50: "#F8F8F9", @@ -12,10 +19,10 @@ export const LIGHT_COLORS: ClutchColors = { 700: "#3D4059", 800: "#252845", 900: "#0D1030", - A100: "#FFFFFF", - A200: "#FFFFFF", - A400: "#FFFFFF", - A700: "#FFFFFF", + A100: "#0D1030", + A200: "#0D1030", + A400: "#0D1030", + A700: "#0D1030", }, blue: { 50: "#F9F9FE", @@ -28,10 +35,10 @@ export const LIGHT_COLORS: ClutchColors = { 700: "#1629B9", 800: "#0A1CA6", 900: "#011082", - A100: "#FFFFFF", - A200: "#FFFFFF", - A400: "#FFFFFF", - A700: "#FFFFFF", + A100: "#011082", + A200: "#011082", + A400: "#011082", + A700: "#011082", }, green: { 50: "#E5FCE8", @@ -44,10 +51,10 @@ export const LIGHT_COLORS: ClutchColors = { 700: "#106E1D", 800: "#086515", 900: "#02590E", - A100: "#FFFFFF", - A200: "#FFFFFF", - A400: "#FFFFFF", - A700: "#FFFFFF", + A100: "#02590E", + A200: "#02590E", + A400: "#02590E", + A700: "#02590E", }, amber: { 50: "#FFFBEB", @@ -60,10 +67,10 @@ export const LIGHT_COLORS: ClutchColors = { 700: "#B45309", 800: "#92400E", 900: "#78350F", - A100: "#FFFFFF", - A200: "#FFFFFF", - A400: "#FFFFFF", - A700: "#FFFFFF", + A100: "#78350F", + A200: "#78350F", + A400: "#78350F", + A700: "#78350F", }, red: { 50: "#FFF4F3", @@ -76,10 +83,10 @@ export const LIGHT_COLORS: ClutchColors = { 700: "#A1301C", 800: "#792111", 900: "#571608", - A100: "#FFFFFF", - A200: "#FFFFFF", - A400: "#FFFFFF", - A700: "#FFFFFF", + A100: "#571608", + A200: "#571608", + A400: "#571608", + A700: "#571608", }, }; @@ -111,10 +118,10 @@ export const DARK_COLORS: ClutchColors = { 700: "#8CC4F8", 800: "#C2E1FE", 900: "#DCECFB", - A100: "#FFFFFF", - A200: "#FFFFFF", - A400: "#FFFFFF", - A700: "#FFFFFF", + A100: "#DCECFB", + A200: "#DCECFB", + A400: "#DCECFB", + A700: "#DCECFB", }, green: { 50: "#002C05", @@ -127,10 +134,10 @@ export const DARK_COLORS: ClutchColors = { 700: "#9CD29E", 800: "#C3E4C4", 900: "#E6F4E7", - A100: "#FFFFFF", - A200: "#FFFFFF", - A400: "#FFFFFF", - A700: "#FFFFFF", + A100: "#E6F4E7", + A200: "#E6F4E7", + A400: "#E6F4E7", + A700: "#E6F4E7", }, amber: { 50: "#352215", @@ -143,10 +150,10 @@ export const DARK_COLORS: ClutchColors = { 700: "#EFC67F", 800: "#F6DCB1", 900: "#FBF1E0", - A100: "#FFFFFF", - A200: "#FFFFFF", - A400: "#FFFFFF", - A700: "#FFFFFF", + A100: "#FBF1E0", + A200: "#FBF1E0", + A400: "#FBF1E0", + A700: "#FBF1E0", }, red: { 50: "#501306", @@ -175,11 +182,8 @@ export const STATE_OPACITY: { [key in ComponentState]: number } = { }; const clutchColors = (variant: ThemeVariant) => { - const colors = variant === "light" ? LIGHT_COLORS : DARK_COLORS; + const colors = variant === THEME_VARIANTS.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 608126c1e4..4b7ecb457e 100644 --- a/frontend/packages/core/src/Theme/palette.tsx +++ b/frontend/packages/core/src/Theme/palette.tsx @@ -1,11 +1,14 @@ 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 { brandColor, DARK_COLORS, LIGHT_COLORS, THEME_VARIANTS } from "./colors"; import type { ClutchColors, ThemeVariant } from "./types"; interface PaletteOptions extends MuiPaletteOptions { type: ThemeVariant; + contrastColor: string; + headerGradient: string; + brandColor: string; } const lightText: Partial = { @@ -23,13 +26,14 @@ const darkText: Partial = { }; const palette = (variant: ThemeVariant): PaletteOptions => { - const isLightMode = variant === "light"; + const isLightMode = variant === THEME_VARIANTS.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 { type: variant, + mode: variant, + brandColor, primary: color.blue, secondary: color.neutral, error: color.red, @@ -38,22 +42,14 @@ const palette = (variant: ThemeVariant): PaletteOptions => { success: color.green, grey: color.neutral, background: { - default: inverseColor.neutral[900], - // secondary + default: color.blue[50], + paper: isLightMode ? "#fff" : "#1c1e3c", }, 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, - }, + 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/Theme/theme.tsx b/frontend/packages/core/src/Theme/theme.tsx index e99a8ab429..d2ea3cd33e 100644 --- a/frontend/packages/core/src/Theme/theme.tsx +++ b/frontend/packages/core/src/Theme/theme.tsx @@ -1,42 +1,38 @@ 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, ThemeProvider as MuiThemeProvider, } from "@mui/material"; -import { StylesProvider } from "@mui/styles"; -import { clutchColors } from "./colors"; +import { clutchColors, THEME_VARIANTS } from "./colors"; import palette from "./palette"; import type { ThemeVariant } from "./types"; -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( - 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({ + 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,25 +41,46 @@ const createTheme = (variant: ThemeVariant): MuiTheme => { }, }, }, - }) - ); + MuiCssBaseline: { + styleOverrides: { + body: { + fontSize: "0.875rem", + }, + }, + }, + MuiSelect: { + styleOverrides: { + select: { + fontSize: "0.875rem", + height: "20px", + }, + }, + }, + MuiLink: { + styleOverrides: { + underlineAlways: { + "&:not(:hover)": { + textDecoration: "none", + }, + }, + }, + }, + }, + }); }; interface ThemeProps { - variant?: "light" | "dark"; + variant?: ThemeVariant; children: React.ReactNode; } -const ThemeProvider = ({ children, variant = "light" }: ThemeProps) => ( +const ThemeProvider = ({ children, variant = THEME_VARIANTS.light }: ThemeProps) => ( - - - {children} - + + {children} ); -// Note that ThemeProvider can't be used until the Theme component can be replaced. export default ThemeProvider; diff --git a/frontend/packages/core/src/Theme/types.tsx b/frontend/packages/core/src/Theme/types.tsx index 7af0171807..6260220318 100644 --- a/frontend/packages/core/src/Theme/types.tsx +++ b/frontend/packages/core/src/Theme/types.tsx @@ -2,6 +2,11 @@ import type { Color } from "@mui/material"; export type ThemeVariant = "light" | "dark"; +export enum THEME_VARIANTS { + light = "light", + dark = "dark", +} + export interface StrokeColor { primary: string; secondary: string; diff --git a/frontend/packages/core/src/accordion.tsx b/frontend/packages/core/src/accordion.tsx index cca4ad6df2..f1b91f0dcd 100644 --- a/frontend/packages/core/src/accordion.tsx +++ b/frontend/packages/core/src/accordion.tsx @@ -2,17 +2,18 @@ import * as React from "react"; import styled from "@emotion/styled"; import AddIcon from "@mui/icons-material/Add"; import RemoveIcon from "@mui/icons-material/Remove"; -import type { AccordionProps as MuiAccordionProps } from "@mui/material"; +import type { AccordionProps as MuiAccordionProps, Theme } from "@mui/material"; import { Accordion as MuiAccordion, AccordionActions as MuiAccordionActions, AccordionDetails as MuiAccordionDetails, AccordionSummary as MuiAccordionSummary, + alpha, Divider as MuiDivider, useControlled, } from "@mui/material"; -const StyledAccordion = styled(MuiAccordion)({ +const StyledAccordion = styled(MuiAccordion)(({ theme }: { theme: Theme }) => ({ borderRadius: "4px", boxShadow: "none", border: "1px solid transparent", @@ -21,16 +22,16 @@ const StyledAccordion = styled(MuiAccordion)({ overflowWrap: "anywhere", "&.Mui-expanded": { - boxShadow: "0px 4px 6px rgba(53, 72, 212, 0.2)", - borderColor: "rgba(13, 16, 48, 0.12)", + boxShadow: `0px 4px 6px ${alpha(theme.palette.primary[600], 0.2)}`, + borderColor: alpha(theme.palette.secondary[900], 0.12), }, ".MuiIconButton-root": { - color: "rgba(13, 16, 48, 0.38)", + color: alpha(theme.palette.secondary[900], 0.38), }, ".MuiIconButton-root.Mui-expanded": { - color: "rgba(13, 16, 48, 0.6)", + color: alpha(theme.palette.secondary[900], 0.6), }, ".MuiAccordionDetails-root": { @@ -45,7 +46,7 @@ const StyledAccordion = styled(MuiAccordion)({ "&:before": { display: "none", }, -}); +})); const AccordionSummaryBase = ({ children, collapsible, expanded, ...props }) => { return ( @@ -58,30 +59,32 @@ const AccordionSummaryBase = ({ children, collapsible, expanded, ...props }) => ); }; -export const StyledAccordionSummary = styled(AccordionSummaryBase)({ - backgroundColor: "#fafafb", - borderRadius: "4px", - color: "#0d1030", - height: "48px", +export const StyledAccordionSummary = styled(AccordionSummaryBase)( + ({ theme }: { theme: Theme }) => ({ + backgroundColor: theme.palette.secondary[50], + borderRadius: "4px", + color: theme.palette.secondary[900], + height: "48px", - "&:hover": { - backgroundColor: "rgba(13, 16, 48, 0.03)", - }, + "&:hover": { + backgroundColor: alpha(theme.palette.secondary[900], 0.03), + }, - "&:active": { - backgroundColor: "rgba(13, 16, 48, 0.12)", - }, + "&:active": { + backgroundColor: alpha(theme.palette.secondary[900], 0.12), + }, - ".MuiAccordionSummary-content": { - margin: "12px 0", - fontSize: "16px", - }, + ".MuiAccordionSummary-content": { + margin: "12px 0", + fontSize: "16px", + }, - "&.Mui-expanded": { - backgroundColor: "#e1e4f9", - minHeight: "48px", - }, -}); + "&.Mui-expanded": { + backgroundColor: theme.palette.primary[200], + minHeight: "48px", + }, + }) +); const StyledAccordionGroup = styled.div({ width: "100%", diff --git a/frontend/packages/core/src/button.tsx b/frontend/packages/core/src/button.tsx index 1d423f1f52..334d197e3b 100644 --- a/frontend/packages/core/src/button.tsx +++ b/frontend/packages/core/src/button.tsx @@ -4,8 +4,15 @@ import FileCopyOutlinedIcon from "@mui/icons-material/FileCopyOutlined"; import type { ButtonProps as MuiButtonProps, IconButtonProps as MuiIconButtonProps, + Theme, +} from "@mui/material"; +import { + alpha, + Button as MuiButton, + Grid, + IconButton as MuiIconButton, + useTheme, } from "@mui/material"; -import { Button as MuiButton, Grid, IconButton as MuiIconButton } from "@mui/material"; import { Tooltip } from "./Feedback/tooltip"; import type { GridJustification } from "./grid"; @@ -26,57 +33,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, @@ -116,7 +72,10 @@ const BUTTON_SIZE_MAP = { export type ButtonSize = keyof typeof BUTTON_SIZE_MAP; -const StyledButton = styled(MuiButton)<{ palette: ButtonPalette; size: ButtonSize }>( +const StyledButton = styled(MuiButton)<{ + palette: ButtonPalette; + size: ButtonSize; +}>( { borderRadius: "4px", fontWeight: 500, @@ -134,12 +93,12 @@ const StyledButton = styled(MuiButton)<{ palette: ButtonPalette; size: ButtonSiz }) ); -const StyledBorderButton = styled(StyledButton)({ - border: "1px solid #0D1030", +const StyledBorderButton = styled(StyledButton)(({ theme }: { theme: Theme }) => ({ + border: `1px solid ${theme.palette.secondary[900]}`, "&.Mui-disabled": { - borderColor: "rgba(13, 16, 48, 0.1)", + borderColor: alpha(theme.palette.secondary[900], 0.1), }, -}); +})); /** Provides feedback to the user in regards to the action of the button. */ type ButtonVariant = "neutral" | "primary" | "danger" | "destructive" | "secondary"; @@ -163,7 +122,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: Theme): ButtonPalette => { + const COLORS = { + neutral: { + background: { + primary: "transparent", + hover: theme.palette.secondary[200], + active: theme.palette.secondary[300], + disabled: theme.palette.contrastColor, + }, + font: { + primary: theme.palette.secondary[900], + disabled: theme.palette.secondary[900], + }, + }, + primary: { + background: { + primary: theme.palette.primary[600], + hover: theme.palette.primary[700], + active: theme.palette.primary[800], + disabled: theme.palette.secondary[200], + }, + font: { + primary: theme.palette.contrastColor, + disabled: alpha(theme.palette.secondary[900], 0.38), + }, + }, + danger: { + background: { + primary: theme.palette.error[600], + hover: theme.palette.error[700], + active: theme.palette.error[800], + disabled: theme.palette.error[200], + }, + font: { + primary: theme.palette.contrastColor, + disabled: theme.palette.contrastColor, + }, + }, + secondary: { + background: { + primary: "transparent", + hover: theme.palette.primary[100], + active: theme.palette.primary[300], + disabled: "transparent", + }, + font: { + primary: theme.palette.primary[600], + disabled: theme.palette.secondary[900], + }, + }, + } as { [key: string]: ButtonPalette }; const v = variant === "destructive" ? "danger" : variant; return COLORS?.[v] || COLORS.primary; }; @@ -188,8 +197,9 @@ 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 ButtonVariant = variant === "neutral" ? StyledBorderButton : StyledButton; + const theme = useTheme(); + const palette = variantPalette(variant, theme); + const ButtonVariant = (variant === "neutral" ? StyledBorderButton : StyledButton) as any; return ( @@ -226,11 +236,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 +258,16 @@ const ButtonGroupContainer = styled(Grid)( margin: "12px 8px", }, }, - props => + props => ({ theme }: { theme: 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", } ); @@ -269,12 +288,12 @@ const ButtonGroup = ({ children, justify = "flex-end", border = "top" }: ButtonG ); -const StyledClipboardIconButton = styled(MuiIconButton)({ - color: "#000000", +const StyledClipboardIconButton = styled(MuiIconButton)(({ theme }: { theme: Theme }) => ({ + color: theme.palette.getContrastText(theme.palette.contrastColor), ":hover": { backgroundColor: "transparent", }, -}); +})); export interface ClipboardButtonProps { /** Case-sensitive text to be copied. */ diff --git a/frontend/packages/core/src/card.tsx b/frontend/packages/core/src/card.tsx index bb66e751fe..f837e4df06 100644 --- a/frontend/packages/core/src/card.tsx +++ b/frontend/packages/core/src/card.tsx @@ -1,27 +1,30 @@ import * as React from "react"; -import styled from "@emotion/styled"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; import { + alpha, Avatar, Card as MuiCard, CardActionArea, CardActionAreaProps, Divider, Grid, + Theme, + useTheme, } from "@mui/material"; import type { SpacingProps as MuiSpacingProps } from "@mui/system"; import { spacing } from "@mui/system"; import { IconButton } from "./button"; +import styled from "./styled"; import { Typography, TypographyProps } from "./typography"; // TODO: seperate out the different card parts into various files -const StyledCard = styled(MuiCard)({ - boxShadow: "0px 4px 6px rgba(53, 72, 212, 0.2)", - border: "1px solid rgba(13, 16, 48, 0.1)", -}); +const StyledCard = styled(MuiCard)(({ theme }: { theme: Theme }) => ({ + boxShadow: `0px 4px 6px ${alpha(theme.palette.primary[600], 0.2)}`, + border: `1px solid ${alpha(theme.palette.secondary[900], 0.1)}`, +})); export interface CardProps { children?: React.ReactNode | React.ReactNode[]; @@ -29,9 +32,9 @@ export interface CardProps { const Card = ({ children, ...props }: CardProps) => {children}; -const StyledCardHeaderContainer = styled.div({ - background: "#EBEDFB", -}); +const StyledCardHeaderContainer = styled("div")(({ theme }: { theme: Theme }) => ({ + background: theme.palette.primary[200], +})); const StyledCardHeader = styled(Grid)({ padding: "6px 8px", @@ -41,7 +44,7 @@ const StyledCardHeader = styled(Grid)({ }, }); -const StyledCardHeaderAvatarContainer = styled.div({ +const StyledCardHeaderAvatarContainer = styled("div")({ padding: "8px", height: "32px", width: "32px", @@ -50,7 +53,7 @@ const StyledCardHeaderAvatarContainer = styled.div({ }); // TODO: use material ui avatar component and implement figma design -const StyledCardHeaderAvatar = styled.div({ +const StyledCardHeaderAvatar = styled("div")({ width: "24px", height: "24px", fontSize: "18px", @@ -58,11 +61,11 @@ const StyledCardHeaderAvatar = styled.div({ }); // TODO: make the divider a core component -const StyledDivider = styled(Divider)({ - color: "#A3A4B0", +const StyledDivider = styled(Divider)(({ theme }: { theme: Theme }) => ({ + color: theme.palette.secondary[400], height: "24px", alignSelf: "center", -}); +})); const StyledGridItem = styled(Grid)({ textAlign: "center", @@ -81,46 +84,49 @@ interface CardHeaderProps { title: React.ReactNode; } -const CardHeader = ({ actions, avatar, children, title, summary = [] }: CardHeaderProps) => ( - - - - {avatar} - - - - {title} +const CardHeader = ({ actions, avatar, children, title, summary = [] }: CardHeaderProps) => { + const theme = useTheme(); + return ( + + + + {avatar} + + + + {title} + + {summary.map((section: CardHeaderSummaryProps, idx: number) => ( + // eslint-disable-next-line react/no-array-index-key + + + + {section.title} + {section.subheader && ( + + {section.subheader} + + )} + + + ))} - {summary.map((section: CardHeaderSummaryProps, idx: number) => ( - // eslint-disable-next-line react/no-array-index-key - - - - {section.title} - {section.subheader && ( - - {section.subheader} - - )} - - - ))} - - {actions} - - {children} - -); + {actions} + + {children} + + ); +}; // Material UI Spacing system supports many props https://material-ui.com/system/spacing/#api // We can add more to this list as use cases arise interface SpacingProps extends Pick {} -const BaseCardContent = styled.div` +const BaseCardContent = styled("div")` ${spacing} `; -const StyledCardContentContainer = styled.div((props: { maxHeight: number | "none" }) => ({ +const StyledCardContentContainer = styled("div")((props: { maxHeight: number | "none" }) => ({ "> .MuiPaper-root": { border: "0", borderRadius: "0", @@ -133,24 +139,24 @@ const BaseCardActionArea = styled(CardActionArea)` ${spacing} `; -const StyledCardActionArea = styled(BaseCardActionArea)({ +const StyledCardActionArea = styled(BaseCardActionArea)(({ theme }: { theme: Theme }) => ({ ":hover": { - backgroundColor: "#F5F6FD", + backgroundColor: theme.palette.primary[100], }, ":active": { - backgroundColor: "#D7DAF6", + backgroundColor: theme.palette.primary[300], }, -}); +})); -const StyledExpandButton = styled(IconButton)({ +const StyledExpandButton = styled(IconButton)(({ theme }: { theme: Theme }) => ({ width: "32px", height: "32px", - color: "#3548D4", + color: theme.palette.primary[600], ":hover": { backgroundColor: "transparent", }, -}); +})); interface CollapsibleState { /** The text to show in the collapse action area when the card content is collapsed/not collapsed. @@ -189,6 +195,7 @@ const CardContent = ({ maxHeight = "none", ...props }: CardContentProps) => { + const theme = useTheme(); const ref = React.useRef(null); const [showExpand, setShowExpand] = React.useState(false); const [expanded, setExpanded] = React.useState(true); @@ -224,7 +231,7 @@ const CardContent = ({ setExpanded(!expanded)}> - + {expanded ? collapseAction?.open.title : collapseAction?.closed.title} @@ -240,7 +247,7 @@ const CardContent = ({ ); }; -const StyledLandingCard = styled(Card)({ +const StyledLandingCard = styled(Card)(({ theme }: { theme: Theme }) => ({ border: "none", height: "214px", maxHeight: "100%", @@ -256,9 +263,9 @@ const StyledLandingCard = styled(Card)({ fontWeight: "bold", fontSize: "12px", lineHeight: "36px", - color: "rgba(13, 16, 48, 0.6)", + color: alpha(theme.palette.secondary[900], 0.6), }, -}); +})); const TruncatedText = styled(Typography)({ display: "-webkit-box", @@ -278,10 +285,10 @@ const IconAvatar = styled(Avatar)({ marginRight: "8px", }); -const StyledAvatar = styled(IconAvatar)({ - color: "rgba(13, 16, 48, 0.38)", - backgroundColor: "rgba(13, 16, 48, 0.12)", -}); +const StyledAvatar = styled(IconAvatar)(({ theme }: { theme: Theme }) => ({ + color: alpha(theme.palette.secondary[900], 0.38), + backgroundColor: alpha(theme.palette.secondary[900], 0.12), +})); export interface LandingCardProps extends Pick { group: string; @@ -298,6 +305,7 @@ export const LandingCard = ({ onClick, ...props }: LandingCardProps) => { + const theme = useTheme(); const validIcon = icon && icon.length > 0; return ( @@ -313,7 +321,7 @@ export const LandingCard = ({
    {title} - + {description}
    diff --git a/frontend/packages/core/src/chip.tsx b/frontend/packages/core/src/chip.tsx index 8e7ba53660..3d5a130d08 100644 --- a/frontend/packages/core/src/chip.tsx +++ b/frontend/packages/core/src/chip.tsx @@ -1,6 +1,6 @@ import * as React from "react"; -import type { ChipProps as MuiChipProps } from "@mui/material"; -import { Chip as MuiChip } from "@mui/material"; +import type { ChipProps as MuiChipProps, Theme } from "@mui/material"; +import { alpha, Chip as MuiChip } from "@mui/material"; import styled from "./styled"; @@ -32,44 +32,6 @@ export interface ChipProps filled?: boolean; } -const CHIP_COLOR_MAP = { - error: { - background: "#F9EAE7", - label: "#C2302E", - borderColor: "#C2302E", - }, - warn: { - background: "#FEF8E8", - label: "#D87313", - borderColor: "#D87313", - }, - attention: { - background: "#E2E2E6", - label: "#0D1030", - borderColor: "##0D103061", - }, - neutral: { - background: "#F8F8F9", - label: "#0D1030", - borderColor: "rgba(13, 16, 48, 0.1)", - }, - active: { - background: "#EBEDFA", - label: "#3548D4", - borderColor: "#3548D4", - }, - pending: { - background: "#FFFEE8", - label: "#B09027", - borderColor: "#B09027", - }, - success: { - background: "#E9F6EC", - label: "#40A05A", - borderColor: "#40A05A", - }, -}; - const StyledChip = styled(MuiChip)<{ $filled: ChipProps["filled"]; $variant: ChipProps["variant"]; @@ -86,14 +48,53 @@ const StyledChip = styled(MuiChip)<{ padding: "7px 12px", }, }, - props => ({ - height: props.size === "small" ? "24px" : "32px", - background: props.$filled - ? CHIP_COLOR_MAP[props.$variant].borderColor - : CHIP_COLOR_MAP[props.$variant].background, - color: props.$filled ? "#FFFFFF" : CHIP_COLOR_MAP[props.$variant].label, - borderColor: CHIP_COLOR_MAP[props.$variant].borderColor, - }) + props => ({ theme }: { theme: Theme }) => { + const CHIP_COLOR_MAP = { + error: { + background: theme.palette.error[50], + label: theme.palette.error[600], + borderColor: theme.palette.error[600], + }, + warn: { + background: theme.palette.warning[50], + label: theme.palette.warning[600], + borderColor: theme.palette.warning[600], + }, + attention: { + background: theme.palette.secondary[200], + label: theme.palette.secondary[900], + borderColor: alpha(theme.palette.secondary[900], 0.6), + }, + neutral: { + background: theme.palette.secondary[50], + label: theme.palette.secondary[900], + borderColor: alpha(theme.palette.secondary[300], 0.6), + }, + active: { + background: theme.palette.primary[200], + label: theme.palette.primary[600], + borderColor: theme.palette.primary[600], + }, + pending: { + background: theme.palette.warning[50], + label: theme.palette.warning[500], + borderColor: theme.palette.warning[500], + }, + success: { + background: theme.palette.success[50], + label: theme.palette.success[500], + borderColor: theme.palette.success[500], + }, + }; + return { + height: props.size === "small" ? "24px" : "32px", + background: props.$filled + ? CHIP_COLOR_MAP[props.$variant].borderColor + : CHIP_COLOR_MAP[props.$variant].background, + color: props.$filled ? theme.palette.contrastColor : CHIP_COLOR_MAP[props.$variant].label, + borderColor: CHIP_COLOR_MAP[props.$variant].borderColor, + }; + } ); const Chip = ({ variant, filled = false, size = "medium", ...props }: ChipProps) => ( diff --git a/frontend/packages/core/src/confirmation.tsx b/frontend/packages/core/src/confirmation.tsx index 612d1ba930..ca217e9f7b 100644 --- a/frontend/packages/core/src/confirmation.tsx +++ b/frontend/packages/core/src/confirmation.tsx @@ -2,39 +2,39 @@ import React from "react"; import styled from "@emotion/styled"; import CheckCircleIcon from "@mui/icons-material/CheckCircle"; import ThumbUpIcon from "@mui/icons-material/ThumbUp"; -import { Grid } from "@mui/material"; +import { alpha, Grid, Theme } from "@mui/material"; -const IconContainer = styled(Grid)({ +const IconContainer = styled(Grid)(({ theme }: { theme: Theme }) => ({ paddingTop: "4px", display: "flex", flexDirection: "column", justifyContent: "center", - color: "#2F67F6", + color: theme.palette.primary[600], fontSize: "7rem", -}); +})); const Icon = styled(ThumbUpIcon)({ fontSize: "0.5em", marginBottom: "10px", }); -const TitleContainer = styled(Grid)({ - color: "#1E942E", +const TitleContainer = styled(Grid)(({ theme }: { theme: Theme }) => ({ + color: theme.palette.success[600], display: "flex", alignItems: "center", fontSize: "20px", fontWeight: 500, textTransform: "capitalize", -}); +})); const CheckmarkIcon = styled(CheckCircleIcon)({ marginRight: "8px", }); -const SubtitleContainer = styled.div({ - color: "rgba(13, 16, 48, 0.6)", +const SubtitleContainer = styled.div(({ theme }: { theme: Theme }) => ({ + color: alpha(theme.palette.secondary[900], 0.6), fontSize: "12px", -}); +})); const Confirmation: React.FC<{ action: string }> = ({ action, children }) => ( diff --git a/frontend/packages/core/src/dialog.tsx b/frontend/packages/core/src/dialog.tsx index 55ebd68e4c..66c9793a5a 100644 --- a/frontend/packages/core/src/dialog.tsx +++ b/frontend/packages/core/src/dialog.tsx @@ -1,8 +1,8 @@ import * as React from "react"; -import styled from "@emotion/styled"; import CloseIcon from "@mui/icons-material/Close"; -import type { DialogProps as MuiDialogProps } from "@mui/material"; +import type { DialogProps as MuiDialogProps, Theme } from "@mui/material"; import { + alpha, Dialog as MuiDialog, DialogActions as MuiDialogActions, DialogContent as MuiDialogContent, @@ -11,52 +11,54 @@ import { Paper, } from "@mui/material"; -const DialogPaper = styled(Paper)({ - border: "1px solid rgba(13, 16, 48, 0.1)", - boxShadow: "0px 10px 24px rgba(35, 48, 143, 0.3)", +import styled from "./styled"; + +const DialogPaper = styled(Paper)(({ theme }: { theme: Theme }) => ({ + border: `1px solid ${alpha(theme.palette.secondary[900], 0.1)}`, + boxShadow: `0px 10px 24px ${alpha(theme.palette.primary[700], 0.3)}`, boxSizing: "border-box", - backgroundColor: "#FFFFFF", + backgroundColor: theme.palette.contrastColor, width: "max-content", maxWidth: "75vw", -}); +})); -const DialogTitle = styled(MuiDialogTitle)({ +const DialogTitle = styled(MuiDialogTitle)(({ theme }: { theme: Theme }) => ({ display: "flex", justifyContent: "space-between", fontSize: "20px", padding: "12px 12px 0 32px", fontWeight: 500, - color: "#0D1030", -}); + color: theme.palette.secondary[900], +})); -const DialogTitleText = styled.div({ +const DialogTitleText = styled("div")({ padding: "14px 0 0 0", }); -const IconButton = styled(MuiIconButton)({ +const IconButton = styled(MuiIconButton)(({ theme }: { theme: Theme }) => ({ height: "12px", width: "12px", - color: "#0D1030", -}); + color: theme.palette.secondary[900], +})); -const DialogContent = styled(MuiDialogContent)({ +const DialogContent = styled(MuiDialogContent)(({ theme }: { theme: Theme }) => ({ padding: "16px 32px 32px 32px", fontSize: "16px", fontWeight: 400, - color: "rgba(13, 16, 48, 0.6)", + color: alpha(theme.palette.secondary[900], 0.6), "> *": { margin: "16px 0 0 0", }, overflowWrap: "break-word", -}); +})); -const DialogActions = styled(MuiDialogActions)({ - borderTop: "1px solid rgba(13, 16, 48, 0.12)", +const DialogActions = styled(MuiDialogActions)(({ theme }: { theme: Theme }) => ({ + borderTop: `1px solid ${alpha(theme.palette.secondary[900], 0.12)}`, padding: "0 8px", "> *": { margin: "16px 8px 16px 8px", }, -}); +})); export interface DialogContentProps {} export interface DialogActionsProps {} diff --git a/frontend/packages/core/src/horizontal-rule.tsx b/frontend/packages/core/src/horizontal-rule.tsx index c0a3b7293e..d37b9962f8 100644 --- a/frontend/packages/core/src/horizontal-rule.tsx +++ b/frontend/packages/core/src/horizontal-rule.tsx @@ -1,5 +1,7 @@ import * as React from "react"; import styled from "@emotion/styled"; +import type { Theme } from "@mui/material"; +import { alpha } from "@mui/system"; const HorizontalRuleBase = ({ children, ...props }: HorizontalRuleProps) => (
    @@ -17,7 +19,7 @@ export type HorizontalRuleProps = { children: React.ReactNode; }; -const StyledHorizontalRule = styled(HorizontalRuleBase)({ +const StyledHorizontalRule = styled(HorizontalRuleBase)(({ theme }: { theme: Theme }) => ({ alignItems: "center", display: "flex", flexDirection: "row", @@ -30,19 +32,19 @@ const StyledHorizontalRule = styled(HorizontalRuleBase)({ ".line > span": { display: "block", - borderTop: "1px solid rgba(13, 16, 48, 0.12)", + borderTop: `1px solid ${alpha(theme.palette.secondary[900], 0.12)}`, }, ".content": { padding: "0 16px", fontWeight: "bold", fontSize: "14px", - color: "rgba(13, 16, 48, 0.38)", + color: alpha(theme.palette.secondary[900], 0.38), textTransform: "uppercase", display: "inline-flex", alignItems: "center", }, -}); +})); export const HorizontalRule = ({ children }: HorizontalRuleProps) => ( {children} diff --git a/frontend/packages/core/src/icon.tsx b/frontend/packages/core/src/icon.tsx index 778495c1ba..a7fe4ad9fa 100644 --- a/frontend/packages/core/src/icon.tsx +++ b/frontend/packages/core/src/icon.tsx @@ -1,7 +1,7 @@ import React from "react"; import styled from "@emotion/styled"; import FiberManualRecordTwoToneIcon from "@mui/icons-material/FiberManualRecordTwoTone"; -import { Grid } from "@mui/material"; +import { Grid, useTheme } from "@mui/material"; import type { GridJustification } from "./grid"; @@ -28,6 +28,7 @@ export const StatusIcon: React.FC = ({ align = "left", ...props }) => { + const theme = useTheme(); let justifyContent: GridJustification = "flex-start"; if (align === "right") { justifyContent = "flex-end"; @@ -38,17 +39,17 @@ export const StatusIcon: React.FC = ({ {variant === "neutral" && ( <> - {children} + {children} )} {variant === "success" && ( <> - {children} + {children} )} {variant === "failure" && ( <> - {children} + {children} )} diff --git a/frontend/packages/core/src/index.tsx b/frontend/packages/core/src/index.tsx index 921c11e27d..8735a6b094 100644 --- a/frontend/packages/core/src/index.tsx +++ b/frontend/packages/core/src/index.tsx @@ -59,6 +59,7 @@ export { default as Code } from "./text"; export { default as TimeAgo } from "./timeago"; export { Typography } from "./typography"; export { default as ClutchApp } from "./AppProvider"; +export { useTheme } from "./AppProvider/themes"; export { css as EMOTION_CSS, keyframes as EMOTION_KEYFRAMES } from "@emotion/react"; diff --git a/frontend/packages/core/src/landing.tsx b/frontend/packages/core/src/landing.tsx index 0a5ce86021..888fc21f98 100644 --- a/frontend/packages/core/src/landing.tsx +++ b/frontend/packages/core/src/landing.tsx @@ -1,23 +1,26 @@ import React from "react"; import styled from "@emotion/styled"; -import { Grid } from "@mui/material"; +import { alpha, Grid, Theme } from "@mui/material"; import Typography from "@mui/material/Typography"; import { userId } from "./AppLayout/user"; import { workflowsByTrending } from "./AppLayout/utils"; import { MonsterGraphic } from "./Assets/Graphics"; +import { THEME_VARIANTS } from "./Theme/colors"; import { LandingCard } from "./card"; import { useAppContext } from "./Contexts"; import { useNavigate } from "./navigation"; -const StyledLanding = styled.div({ - backgroundColor: "#f9f9fe", +const StyledLanding = styled.div(({ theme }: { theme: Theme }) => ({ display: "flex", flexDirection: "column", flexGrow: 1, "& .welcome": { display: "flex", - backgroundColor: "white", + backgroundColor: + theme.palette.mode === THEME_VARIANTS.light + ? theme.palette.contrastColor + : theme.palette.background.paper, padding: "32px 80px", }, @@ -33,19 +36,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/link.tsx b/frontend/packages/core/src/link.tsx index 8ef4c1312e..59890e5126 100644 --- a/frontend/packages/core/src/link.tsx +++ b/frontend/packages/core/src/link.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import type { LinkProps as MuiLinkProps } from "@mui/material"; +import type { LinkProps as MuiLinkProps, Theme } from "@mui/material"; import { Link as MuiLink } from "@mui/material"; import styled from "./styled"; @@ -9,15 +9,15 @@ type TextTransform = "none" | "capitalize" | "uppercase" | "lowercase" | "initia const StyledLink = styled(MuiLink)<{ $textTransform: LinkProps["textTransform"]; }>( - { + ({ theme }: { theme: Theme }) => ({ display: "flex", width: "100%", maxWidth: "fit-content", fontSize: "14px", - color: "#3548D4", + color: theme.palette.primary[600], overflow: "hidden", textOverflow: "ellipsis", - }, + }), props => ({ textTransform: props.$textTransform, }) diff --git a/frontend/packages/core/src/loading.tsx b/frontend/packages/core/src/loading.tsx index 9f894ca388..92a0c8d8bb 100644 --- a/frontend/packages/core/src/loading.tsx +++ b/frontend/packages/core/src/loading.tsx @@ -1,11 +1,11 @@ import React from "react"; import styled from "@emotion/styled"; -import { CircularProgress, Grid, Paper } from "@mui/material"; +import { CircularProgress, Grid, Paper, Theme } from "@mui/material"; -const LoadingSpinner = styled(CircularProgress)` - color: #3548d4; - position: absolute; -`; +const LoadingSpinner = styled(CircularProgress)(({ theme }: { theme: Theme }) => ({ + color: theme.palette.primary[600], + position: "absolute", +})); const ContentContainer = styled(Grid)` position: relative; diff --git a/frontend/packages/core/src/not-found.tsx b/frontend/packages/core/src/not-found.tsx index b763adfa0f..dfee720d63 100644 --- a/frontend/packages/core/src/not-found.tsx +++ b/frontend/packages/core/src/not-found.tsx @@ -1,16 +1,16 @@ import React from "react"; import styled from "@emotion/styled"; import ThumbDownIcon from "@mui/icons-material/ThumbDown"; -import { Grid, Typography } from "@mui/material"; +import { Grid, Theme, Typography } from "@mui/material"; const Container = styled(Grid)` minheight: 80vh; `; -const IconContainer = styled(Grid)({ - color: "#02acbe", +const IconContainer = styled(Grid)(({ theme }: { theme: Theme }) => ({ + color: theme.palette.brandColor, fontSize: "7rem", -}); +})); const NotFound: React.FC<{}> = () => ( = ({ heading, summary, expanded, child return ( }> - {heading} + {heading}
    - {summary} + {summary} {children} diff --git a/frontend/packages/core/src/paper.tsx b/frontend/packages/core/src/paper.tsx index eb04b47a23..2a886b2b23 100644 --- a/frontend/packages/core/src/paper.tsx +++ b/frontend/packages/core/src/paper.tsx @@ -1,18 +1,18 @@ import * as React from "react"; import styled from "@emotion/styled"; -import type { PaperProps as MuiPaperProps } from "@mui/material"; -import { Paper as MuiPaper } from "@mui/material"; +import type { PaperProps as MuiPaperProps, Theme } from "@mui/material"; +import { alpha, Paper as MuiPaper } from "@mui/material"; export interface PaperProps extends Pick {} -const StyledPaper = styled(MuiPaper)({ - boxShadow: "0px 4px 6px rgba(53, 72, 212, 0.2)", - border: "1px solid rgba(13, 16, 48, 0.1)", - background: "#FFFFFF", +const StyledPaper = styled(MuiPaper)(({ theme }: { theme: Theme }) => ({ + boxShadow: `0px 4px 6px ${alpha(theme.palette.primary[600], 0.2)}`, + border: `1px solid ${alpha(theme.palette.secondary[900], 0.1)}`, + background: theme.palette.contrastColor, padding: "16px", minWidth: "inherit", minHeight: "inherit", -}); +})); const Paper = ({ children, ...props }: PaperProps) => ( {children} diff --git a/frontend/packages/core/src/popper.tsx b/frontend/packages/core/src/popper.tsx index fd94340452..1e5465ff95 100644 --- a/frontend/packages/core/src/popper.tsx +++ b/frontend/packages/core/src/popper.tsx @@ -4,8 +4,10 @@ import type { ClickAwayListenerProps, ListItemProps, PopperProps as MuiPopperProps, + Theme, } from "@mui/material"; import { + alpha, ClickAwayListener, Collapse, List, @@ -20,35 +22,35 @@ const StyledPopper = styled(MuiPopper)({ paddingTop: "16px", }); -const Paper = styled(MuiPaper)({ +const Paper = styled(MuiPaper)(({ theme }: { theme: Theme }) => ({ minWidth: "fit-content", - border: "1px solid #E7E7EA", - boxShadow: "0px 10px 24px rgba(35, 48, 143, 0.3)", + border: `1px solid ${theme.palette.secondary[200]}`, + boxShadow: `0px 10px 24px ${alpha(theme.palette.primary[700], 0.3)}`, ".MuiListItem-root[id='popperItem']": { - backgroundColor: "#FFFFFF", + backgroundColor: theme.palette.contrastColor, height: "48px", "&:hover": { - backgroundColor: "#F5F6FD", + backgroundColor: theme.palette.primary[100], }, "&:active": { - backgroundColor: "#D7DAF6", + backgroundColor: theme.palette.primary[300], }, "&.Mui-selected": { - backgroundColor: "#FFFFFF", + backgroundColor: theme.palette.contrastColor, "&:hover": { - backgroundColor: "#F5F6FD", + backgroundColor: theme.palette.primary[100], }, "&:active": { - backgroundColor: "#D7DAF6", + backgroundColor: theme.palette.primary[300], }, }, "&:hover, &:active, &.Mui-selected": { ".MuiTypography-root": { - color: "#3548D4", + color: theme.palette.primary[600], }, }, }, -}); +})); const ListItem = styled(MuiListItem)({ padding: "0", @@ -60,15 +62,15 @@ const PopperItemIcon = styled.div({ width: "24px", }); -const ListItemText = styled(MuiListItemText)({ +const ListItemText = styled(MuiListItemText)(({ theme }: { theme: Theme }) => ({ ".MuiTypography-root": { - color: "rgba(13, 16, 48, 0.6)", + color: alpha(theme.palette.secondary[900], 0.6), fontWeight: 500, fontSize: "14px", lineHeight: "18px", padding: "15px 15px", }, -}); +})); export interface PopperItemProps extends Pick { children: React.ReactNode; diff --git a/frontend/packages/core/src/stepper.tsx b/frontend/packages/core/src/stepper.tsx index 1a61023b8b..c8a0dea62e 100644 --- a/frontend/packages/core/src/stepper.tsx +++ b/frontend/packages/core/src/stepper.tsx @@ -3,24 +3,27 @@ import styled from "@emotion/styled"; import MuiCheckIcon from "@mui/icons-material/Check"; import PriorityHighIcon from "@mui/icons-material/PriorityHigh"; import { + alpha, Step as MuiStep, StepConnector as MuiStepConnector, StepLabel as MuiStepLabel, Stepper as MuiStepper, + Theme, + useTheme, } from "@mui/material"; -const StepContainer = styled.div({ +const StepContainer = styled.div(({ theme }: { theme: Theme }) => ({ margin: "0px 2px 30px 2px", ".MuiStepLabel-label": { fontWeight: 500, fontSize: "14px", - color: "rgba(13, 16, 48, 0.38)", + color: alpha(theme.palette.secondary[900], 0.38), }, ".MuiStepLabel-label.Mui-active": { - color: "#0d1030", + color: theme.palette.secondary[900], }, ".MuiStepLabel-label.Mui-completed": { - color: "rgba(13, 16, 48, 0.38)", + color: alpha(theme.palette.secondary[900], 0.38), }, ".MuiStepper-root": { background: "transparent", @@ -67,18 +70,18 @@ const StepContainer = styled.div({ ".MuiStepConnector-line": { height: "5px", border: 0, - backgroundColor: "#E7E7EA", + backgroundColor: theme.palette.secondary[200], borderRadius: "4px", }, ".Mui-active .MuiStepConnector-line": { - backgroundColor: "#3548D4", + backgroundColor: theme.palette.primary[600], }, ".Mui-completed .MuiStepConnector-line": { - backgroundColor: "#3548D4", + backgroundColor: theme.palette.primary[600], }, -}); +})); const Circle = styled.div((props: { background: string; border: string }) => ({ backgroundColor: props.background, @@ -115,30 +118,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: theme.palette.contrastColor, + border: `1px solid ${theme.palette.primary[600]}`, + font: theme.palette.primary[600], + }, + pending: { + background: theme.palette.secondary[200], + border: theme.palette.secondary[200], + font: alpha(theme.palette.secondary[900], 0.38), + }, + success: { + background: theme.palette.primary[600], + border: theme.palette.primary[600], + font: theme.palette.contrastColor, + }, + failed: { + background: theme.palette.error[600], + border: theme.palette.error[600], + font: theme.palette.contrastColor, + }, + }; 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..989e7dacd0 100644 --- a/frontend/packages/core/src/tab.tsx +++ b/frontend/packages/core/src/tab.tsx @@ -1,52 +1,52 @@ import * as React from "react"; import styled from "@emotion/styled"; 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"; +import type { TabProps as MuiTabProps, TabsProps as MuiTabsProps, Theme } from "@mui/material"; +import { alpha, Tab as MuiTab } from "@mui/material"; -const StyledTab = styled(MuiTab)({ +const StyledTab = styled(MuiTab)(({ theme }: { theme: Theme }) => ({ minWidth: "111px", height: "46px", padding: "12px 32px", - color: "rgba(13, 16, 48, 0.6)", - borderBottom: "3px solid #E7E7EA", + color: alpha(theme.palette.secondary[900], 0.6), + borderBottom: `3px solid ${theme.palette.secondary[100]}`, fontSize: "14px", fontWeight: "bold", opacity: "1", textTransform: "none", "&.Mui-selected": { backgroundColor: "unset", - color: "#3548D4", + color: theme.palette.primary[600], border: "0", }, "&:hover": { - color: "rgba(13, 16, 48, 0.6)", - backgroundColor: "#E7E7EA", + color: alpha(theme.palette.secondary[900], 0.6), + backgroundColor: theme.palette.secondary[100], outline: "none", }, "&:focus": { - color: "#3548D4", - backgroundColor: "#EBEDFB", + color: theme.palette.primary[600], + backgroundColor: theme.palette.primary[200], }, "&:focus-within": { - color: "#3548D4", - backgroundColor: "#EBEDFB", + color: theme.palette.primary[600], + backgroundColor: theme.palette.primary[200], }, "&:active": { - color: "rgba(13, 16, 48, 0.6)", - backgroundColor: "#DBDBE0", + color: alpha(theme.palette.secondary[900], 0.6), + backgroundColor: theme.palette.secondary[300], }, ".MuiTab-wrapper": { margin: "auto", }, -}); +})); -const StyledTabs = styled(TabList)({ +const StyledTabs = styled(TabList)(({ theme }: { theme: Theme }) => ({ ".MuiTabs-indicator": { height: "4px", - backgroundColor: "#3548D4", + backgroundColor: theme.palette.primary[600], }, -}); +})); export interface TabProps extends Pick { children?: React.ReactNode; 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..23192b9756 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/__snapshots__/landing.test.tsx.snap b/frontend/packages/core/src/tests/__snapshots__/landing.test.tsx.snap index 795d58d771..8393d9b0a7 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 >
    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/package.json b/frontend/workflows/experimentation/package.json index 2dd0c09424..f41ffdbbbf 100644 --- a/frontend/workflows/experimentation/package.json +++ b/frontend/workflows/experimentation/package.json @@ -1,6 +1,6 @@ { "name": "@clutch-sh/experimentation", - "version": "2.0.0-beta", + "version": "3.0.0-beta", "description": "Clutch Experimentation Workflows", "license": "Apache-2.0", "author": "clutch@lyft.com", @@ -20,8 +20,8 @@ }, "dependencies": { "@clutch-sh/api": "^2.0.0-beta", - "@clutch-sh/core": "^2.0.0-beta", - "@clutch-sh/data-layout": "^2.0.0-beta", + "@clutch-sh/core": "^3.0.0-beta", + "@clutch-sh/data-layout": "^3.0.0-beta", "@emotion/styled": "^11.0.0", "@mui/material": "^5.8.5", "@mui/styles": "^5.8.4", 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..1bb799b7ad 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`] = `