diff --git a/integration_tests/test/dark-mode.spec.ts b/integration_tests/test/dark-mode.spec.ts new file mode 100644 index 0000000..fb0b535 --- /dev/null +++ b/integration_tests/test/dark-mode.spec.ts @@ -0,0 +1,37 @@ +import { test } from "fixtures"; +import { TEST_PROJECT } from "~client/_test/test.data.helper"; +import { mockGetProjects } from "utils/mocks"; + +// Run tests in this file with dark mode preferred +test.use({ colorScheme: "dark" }); + +const project = TEST_PROJECT; + +test.beforeEach(async ({ page }) => { + await mockGetProjects(page, [project]); +}); + +test("dark mode login", async ({ page, vrt }) => { + await page.goto("/login"); + await vrt.trackPage(page, "Login page - Dark mode"); +}); + +test("dark mode projects", async ({ page, vrt }) => { + await page.goto("/projects"); + await vrt.trackPage(page, "Projects list page - Dark mode"); +}); + +test("dark mode profile", async ({ page, vrt }) => { + await page.goto("/profile"); + await vrt.trackPage(page, "User profile page - Dark mode"); +}); + +test("dark mode register", async ({ page, vrt }) => { + await page.goto("/register"); + await vrt.trackPage(page, "Register page - Dark mode"); +}); + +test("dark mode users", async ({ page, vrt }) => { + await page.goto("/users"); + await vrt.trackPage(page, "User list page - Dark mode"); +}); diff --git a/playwright.config.ts b/playwright.config.ts index f4e3b67..d3bf6cc 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -17,6 +17,7 @@ export default defineConfig({ actionTimeout: 5000, navigationTimeout: 5000, trace: "retry-with-trace", + colorScheme: 'light', }, retries: process.env.CI ? 1 : 0, forbidOnly: !!process.env.CI, diff --git a/src/App.tsx b/src/App.tsx index ecf1ebc..7f09bff 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,12 +1,13 @@ import React from "react"; import { SnackbarProvider } from "notistack"; -import { Box } from "@mui/material"; +import { Box, useMediaQuery } from "@mui/material"; +import CssBaseline from '@mui/material/CssBaseline'; import { ThemeProvider, StyledEngineProvider, createTheme, } from "@mui/material/styles"; -import { indigo, purple } from "@mui/material/colors"; +import { grey, purple, lightBlue } from "@mui/material/colors"; import Header from "./components/Header"; import { UserProvider, @@ -18,18 +19,38 @@ import { } from "./contexts"; import Router from "./Router"; -// https://mui.com/material-ui/customization/color/#2014-material-design-color-palettes -const theme = createTheme({ - palette: { - primary: indigo, - secondary: purple, - }, -}); - function App() { + const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); + + // Update the theme only if the mode changes + // https://mui.com/material-ui/customization/color/#2014-material-design-color-palettes + const theme = React.useMemo(() => createTheme({ + palette: { + primary: { + main: lightBlue[600], + light: lightBlue[900], + dark: lightBlue[100], + }, + secondary: { + main: purple[500], + }, + text: { + primary: prefersDarkMode ? grey[100] : grey[900], + secondary: prefersDarkMode ? grey[200] : grey[800], + }, + background: { + default: prefersDarkMode ? grey[800] : grey[200], + paper: prefersDarkMode ? grey[700] : grey[300], + }, + mode: prefersDarkMode ? 'dark' : 'light', + }, + } + ), [prefersDarkMode]); + return ( - + + @@ -37,10 +58,10 @@ function App() { - +
- + diff --git a/src/components/LoginForm.tsx b/src/components/LoginForm.tsx index edc2467..0f50601 100644 --- a/src/components/LoginForm.tsx +++ b/src/components/LoginForm.tsx @@ -78,7 +78,7 @@ const LoginForm = () => { diff --git a/src/components/TestDetailsDialog/DrawArea.tsx b/src/components/TestDetailsDialog/DrawArea.tsx index fe37c38..d8efe99 100644 --- a/src/components/TestDetailsDialog/DrawArea.tsx +++ b/src/components/TestDetailsDialog/DrawArea.tsx @@ -3,22 +3,14 @@ import React, { FunctionComponent, useCallback } from "react"; import { Stage, Layer, Image } from "react-konva"; import Rectangle, { MIN_RECT_SIDE_PIXEL } from "../Rectangle"; import { IgnoreArea } from "../../types/ignoreArea"; -import { Grid, CircularProgress, type Theme } from "@mui/material"; +import { Grid, CircularProgress } from "@mui/material"; import { NoImagePlaceholder } from "./NoImageAvailable"; import Konva from "konva"; import { makeStyles } from "@mui/styles"; -const useStyles = makeStyles((theme: Theme) => ({ +const useStyles = makeStyles(() => ({ canvasContainer: { overflow: "auto", - backgroundColor: "white", - }, - imageDetailsContainer: { - position: "absolute", - backgroundColor: "white", - zIndex: 1, - padding: theme.spacing(1), - height: "48px", }, progressContainer: { minHeight: "300px", diff --git a/src/components/TestDetailsDialog/ImageDetails.tsx b/src/components/TestDetailsDialog/ImageDetails.tsx index d33be35..5666219 100644 --- a/src/components/TestDetailsDialog/ImageDetails.tsx +++ b/src/components/TestDetailsDialog/ImageDetails.tsx @@ -9,7 +9,6 @@ const useStyles = makeStyles(() => ({ container: { display: "flex", alignItems: "center", - color: "darkslategrey", }, branchName: { cursor: "pointer", @@ -45,7 +44,7 @@ const ImageDetails: React.FunctionComponent = ({ imageName && ( {image ? `(${image?.width} x ${image?.height})` : "Loading..."} @@ -55,7 +54,7 @@ const ImageDetails: React.FunctionComponent = ({ }; return ( - + {type === "Baseline" ? "Baseline" : "Checkpoint"} {imageSize()} diff --git a/src/components/TestDetailsDialog/TestDetailsModal.tsx b/src/components/TestDetailsDialog/TestDetailsModal.tsx index 4d6238b..389986f 100644 --- a/src/components/TestDetailsDialog/TestDetailsModal.tsx +++ b/src/components/TestDetailsDialog/TestDetailsModal.tsx @@ -38,6 +38,7 @@ import { NavigateNext, NavigateBefore, } from "@mui/icons-material"; +import { useTheme } from '@mui/material/styles'; import { TestRunDetails } from "./TestRunDetails"; import useImage from "use-image"; import { routes } from "../../constants"; @@ -53,50 +54,6 @@ import ImageDetails, { ImageDetailsProps } from "./ImageDetails"; import { calculateScale } from "../../_helpers/scale.helper"; import TestStatusChip from "../TestStatusChip"; -const useStyles = makeStyles(() => ({ - header: { - position: "relative", - textAlign: "left", - background: "#efefef", - paddingLeft: 8, - paddingBottom: 8, - }, - footer: { - background: "#efefef", - }, - scaleActions: { - display: "flex", - alignItems: "center", - }, - testRunActions: { - display: "flex", - alignItems: "center", - alignContent: "center", - }, - testRunName: { - fontWeight: 300, - }, - closeIcon: { - position: "absolute", - right: "8px", - }, - testRunDetails: { - paddingLeft: 8, - }, - drawAreaContainer: { - width: "100%", - height: "100%", - }, - drawAreaItem: { - padding: "0 4px", - height: "100%", - }, - imageToolbar: { - paddingLeft: 5, - height: 52, - }, -})); - const defaultStagePos = { x: 0, y: 0, @@ -121,6 +78,51 @@ const TestDetailsModal: React.FunctionComponent = ({ handleNext, handleClose, }) => { + + const theme = useTheme(); + const useStyles = makeStyles(() => ({ + header: { + position: "relative", + textAlign: "left", + background: theme.palette.divider, + paddingLeft: 8, + paddingBottom: 8, + }, + footer: { + background: theme.palette.divider, + }, + scaleActions: { + display: "flex", + alignItems: "center", + }, + testRunActions: { + display: "flex", + alignItems: "center", + alignContent: "center", + }, + testRunName: { + fontWeight: 300, + }, + closeIcon: { + position: "absolute", + right: "8px", + }, + testRunDetails: { + paddingLeft: 8, + }, + drawAreaContainer: { + width: "100%", + height: "100%", + }, + drawAreaItem: { + padding: "0 4px", + height: "100%", + }, + imageToolbar: { + paddingLeft: 5, + height: 52, + }, + })); const classes = useStyles(); const { enqueueSnackbar } = useSnackbar(); const testRunDispatch = useTestRunDispatch(); diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 7f929f4..d9d425c 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -181,7 +181,7 @@ const ProfilePage = () => {