diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 964f47b..59d663d 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -14,40 +14,12 @@ if (typeof window !== "undefined") { } const preview: Preview = { - decorators: [ - (Story, context) => { - const theme = context.globals.theme === "light" ? "light" : "dark"; - - if (typeof document !== "undefined") { - document.documentElement.setAttribute("data-ui-theme", theme); - } - - return Story(); - }, - ], - globalTypes: { - theme: { - description: "Dashboard component theme", - defaultValue: "dark", - toolbar: { - title: "Theme", - icon: "circlehollow", - items: [ - { value: "dark", title: "Dark" }, - { value: "light", title: "Light" }, - ], - dynamicTitle: true, - }, - }, - }, parameters: { backgrounds: { default: "canvas", values: [ { name: "canvas", value: "#020302" }, { name: "raised", value: "#0b0d0c" }, - { name: "light canvas", value: "#f3f5ed" }, - { name: "light raised", value: "#eef1e7" }, ], }, controls: { diff --git a/index.html b/index.html index 75dae71..5e47684 100644 --- a/index.html +++ b/index.html @@ -5,15 +5,6 @@ Dimension Lab -
diff --git a/playwright.config.ts b/playwright.config.ts index 3593452..2785c33 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -2,8 +2,6 @@ import { defineConfig, devices } from "@playwright/test"; const port = Number(process.env.PLAYWRIGHT_PORT || 4173); const baseURL = `http://127.0.0.1:${port}`; -const storybookPort = Number(process.env.PLAYWRIGHT_STORYBOOK_PORT || 6007); -const storybookURL = `http://127.0.0.1:${storybookPort}`; const databaseUrl = process.env.PLAYWRIGHT_DATABASE_URL || `file:./data/playwright-${process.pid}-${Date.now()}.sqlite`; @@ -19,20 +17,12 @@ export default defineConfig({ trace: "retain-on-failure", screenshot: "only-on-failure", }, - webServer: [ - { - command: `DISABLE_LIVE_DATASOURCES=1 bun run build && DISABLE_LIVE_DATASOURCES=1 DATABASE_URL=${databaseUrl} HOST=127.0.0.1 PORT=${port} bun build/index.js`, - url: baseURL, - reuseExistingServer: false, - timeout: 120_000, - }, - { - command: `bun run build-storybook && STORYBOOK_STATIC_PORT=${storybookPort} bun tests/e2e/storybook-server.ts`, - url: storybookURL, - reuseExistingServer: false, - timeout: 120_000, - }, - ], + webServer: { + command: `DISABLE_LIVE_DATASOURCES=1 bun run build && DISABLE_LIVE_DATASOURCES=1 DATABASE_URL=${databaseUrl} HOST=127.0.0.1 PORT=${port} bun build/index.js`, + url: baseURL, + reuseExistingServer: false, + timeout: 120_000, + }, projects: [ { name: "chromium-desktop", diff --git a/src/App.test.tsx b/src/App.test.tsx index b7abbc7..c394ee9 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -18,24 +18,4 @@ describe("React app dashboard state view", () => { expect(html).toContain("Loading Dashboard"); expect(html).toContain("Fetching active model"); }); - - test("renders an accessible theme toggle with the active theme", () => { - const html = renderToString( - undefined} - />, - ); - - expect(html).toContain('data-ui-theme-toggle="true"'); - expect(html).toContain('aria-label="Switch to light theme"'); - expect(html).not.toContain("aria-pressed"); - expect(html).toContain("Dark"); - }); }); diff --git a/src/App.tsx b/src/App.tsx index 99cdc5e..81ec0c3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,11 +3,7 @@ import type { DashboardRuntimeState } from "$lib/server/dashboard"; import { DashboardFrame, SystemState, - ThemeToggle, dashboardDocumentToUiDashboard, - persistUiTheme, - resolveInitialUiTheme, - type UiTheme, type UiSeverity, } from "$lib/ui"; @@ -20,23 +16,13 @@ const loadingDashboardState: DashboardRuntimeState = { export function AppStateView({ dashboard, - onThemeChange, - theme, }: { dashboard: DashboardRuntimeState; - onThemeChange?: (theme: UiTheme) => void; - theme?: UiTheme; }) { - const themeToggle = - theme && onThemeChange ? ( - - ) : null; - if (dashboard.state === "ready") { return ( ); } @@ -46,9 +32,6 @@ export function AppStateView({ return (
- {themeToggle ? ( -
{themeToggle}
- ) : null} (loadingDashboardState); - const [theme, setTheme] = useState(() => { - if (typeof window === "undefined") return "dark"; - - return resolveInitialUiTheme(getThemeStorage()); - }); useEffect(() => { const metadata = resolveDocumentMetadata(dashboard); @@ -111,11 +89,6 @@ export default function App() { description.content = metadata.description; }, [dashboard]); - useEffect(() => { - document.documentElement.dataset.uiTheme = theme; - persistUiTheme(theme, getThemeStorage()); - }, [theme]); - useEffect(() => { let cancelled = false; let refreshTimer: number | undefined; @@ -153,13 +126,7 @@ export default function App() { }; }, []); - return ( - - ); + return ; } function stateSeverity(state: DashboardRuntimeState["state"]): UiSeverity { @@ -173,11 +140,3 @@ function stateIcon(state: DashboardRuntimeState["state"]): string { if (state === "loading") return "mdi:progress-clock"; return "mdi:tray"; } - -function getThemeStorage(): Storage | undefined { - try { - return window.localStorage; - } catch { - return undefined; - } -} diff --git a/src/app.css b/src/app.css index e4fe069..10a9ee9 100644 --- a/src/app.css +++ b/src/app.css @@ -107,8 +107,8 @@ body { min-height: 100vh; margin: 0; background: - linear-gradient(var(--ui-color-grid-line) 1px, transparent 1px), - linear-gradient(90deg, var(--ui-color-grid-line) 1px, transparent 1px), + linear-gradient(rgba(255, 255, 255, 0.035) 1px, transparent 1px), + linear-gradient(90deg, rgba(255, 255, 255, 0.035) 1px, transparent 1px), var(--ui-color-canvas); background-size: 48px 48px, 48px 48px, auto; color: var(--ui-color-text); @@ -142,17 +142,13 @@ a { padding: var(--ui-space-4); } -.state-shell__actions { - justify-self: center; -} - .state-shell ul { display: grid; max-width: 56rem; gap: var(--ui-space-2); margin: 0; border: var(--ui-border); - background: var(--ui-color-surface-module); + background: rgba(6, 8, 7, 0.82); color: var(--ui-color-muted); font-size: 0.76rem; list-style-position: inside; diff --git a/src/lib/ui/components/DashboardFrame.tsx b/src/lib/ui/components/DashboardFrame.tsx index 6a3a35b..e8b8fe2 100644 --- a/src/lib/ui/components/DashboardFrame.tsx +++ b/src/lib/ui/components/DashboardFrame.tsx @@ -1,5 +1,4 @@ import { useId } from "react"; -import type { ReactNode } from "react"; import type { UiDashboardPreview } from "../types"; import { ModuleCard } from "./ModuleCard"; import { ServicePanel } from "./ServicePanel"; @@ -7,31 +6,21 @@ import { StatusStrip } from "./StatusStrip"; import { TelemetryGrid } from "./TelemetryGrid"; export interface DashboardFrameProps { - actions?: ReactNode; dashboard: UiDashboardPreview; titleId?: string; } -export function DashboardFrame({ - actions, - dashboard, - titleId, -}: DashboardFrameProps) { +export function DashboardFrame({ dashboard, titleId }: DashboardFrameProps) { const generatedTitleId = useId(); const resolvedTitleId = titleId || `${generatedTitleId}-title`; return (
-
-
- {dashboard.eyebrow ?

{dashboard.eyebrow}

: null} -

{dashboard.title}

- {dashboard.subtitle ? {dashboard.subtitle} : null} -
- {actions ? ( -
{actions}
- ) : null} +
+ {dashboard.eyebrow ?

{dashboard.eyebrow}

: null} +

{dashboard.title}

+ {dashboard.subtitle ? {dashboard.subtitle} : null}
{dashboard.modules.length ? (
diff --git a/src/lib/ui/components/ThemeToggle.tsx b/src/lib/ui/components/ThemeToggle.tsx deleted file mode 100644 index 1c8fb1a..0000000 --- a/src/lib/ui/components/ThemeToggle.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { UiTheme } from "../theme"; -import { getNextUiTheme } from "../theme"; -import { IconGlyph } from "./IconGlyph"; - -export interface ThemeToggleProps { - theme: UiTheme; - onThemeChange: (theme: UiTheme) => void; -} - -export function ThemeToggle({ theme, onThemeChange }: ThemeToggleProps) { - const nextTheme = getNextUiTheme(theme); - const label = `Switch to ${nextTheme} theme`; - - return ( - - ); -} diff --git a/src/lib/ui/components/styles.css b/src/lib/ui/components/styles.css index 086c0c4..d8111f4 100644 --- a/src/lib/ui/components/styles.css +++ b/src/lib/ui/components/styles.css @@ -8,8 +8,8 @@ overflow: hidden; padding: clamp(0.42rem, 0.65vw, 0.62rem); background: - linear-gradient(90deg, transparent 0 49%, var(--ui-color-frame-spine) 50%, transparent 51%), - var(--ui-color-frame-wash); + linear-gradient(90deg, transparent 0 49%, rgba(255, 255, 255, 0.08) 50%, transparent 51%), + rgba(0, 0, 0, 0.18); } .dashboard-frame__header, @@ -26,22 +26,6 @@ padding-bottom: 0.34rem; } -.dashboard-frame__title { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: var(--ui-space-3); - align-items: start; - min-width: 0; -} - -.dashboard-frame__title-copy { - min-width: 0; -} - -.dashboard-frame__actions { - justify-self: end; -} - .dashboard-header { grid-template-columns: minmax(0, 1fr) auto; padding: var(--ui-space-3); @@ -115,7 +99,7 @@ .panel, .system-state { border: var(--ui-border); - background: var(--ui-color-surface-panel); + background: rgba(3, 4, 3, 0.86); color: var(--ui-color-text); } @@ -123,11 +107,7 @@ display: grid; min-height: 5.15rem; gap: 0.25rem; - background: linear-gradient( - 180deg, - var(--ui-color-card-gradient-start), - var(--ui-color-card-gradient-end) - ); + background: linear-gradient(180deg, rgba(13, 15, 14, 0.82), rgba(2, 3, 2, 0.92)); padding: 0.45rem 0.55rem 0.42rem; } @@ -232,7 +212,7 @@ .progress-meter { height: 0.25rem; border: var(--ui-border); - background: var(--ui-color-surface-inset); + background: #030403; } .telemetry-card__bar span, @@ -291,7 +271,7 @@ align-items: start; min-width: 0; min-height: 4.15rem; - background: var(--ui-color-surface-module); + background: rgba(6, 8, 7, 0.82); padding: 0.5rem; } @@ -317,7 +297,7 @@ .module-card[data-severity="ok"] .icon-glyph { background: var(--ui-color-accent); - color: var(--ui-color-accent-contrast); + color: #050605; } .module-card[data-severity="warning"] { @@ -368,7 +348,7 @@ .service-row:where(a):hover { border-bottom-color: var(--ui-color-accent); - background: var(--ui-color-hover); + background: rgba(244, 244, 244, 0.035); } .service-row__main { @@ -418,7 +398,7 @@ grid-template-columns: auto minmax(0, 1fr); min-height: 1.9rem; align-items: center; - background: var(--ui-color-surface-footer); + background: rgba(2, 3, 2, 0.92); color: inherit; text-decoration: none; } @@ -446,7 +426,7 @@ .footer-cell[data-severity="ok"] strong { background: var(--ui-color-accent); - color: var(--ui-color-accent-contrast); + color: #060706; } .footer-cell[data-severity="warning"] strong { @@ -559,48 +539,18 @@ width: 2.5rem; height: 2.5rem; place-items: center; - background: var(--ui-color-surface-footer); + background: rgba(2, 3, 2, 0.92); color: var(--ui-color-muted); padding: 0; } -.theme-toggle { - display: inline-grid; - grid-auto-flow: column; - gap: var(--ui-space-2); - align-items: center; - justify-content: center; - min-width: 6.5rem; - min-height: 2.15rem; - border: var(--ui-border); - background: var(--ui-color-surface-raised); - color: var(--ui-color-text); - cursor: pointer; - font: inherit; - font-size: 0.62rem; - font-weight: 850; - letter-spacing: 0; - padding: 0 var(--ui-space-3); - text-transform: uppercase; -} - -.theme-toggle:hover { - border-color: var(--ui-color-accent); - color: var(--ui-color-accent); -} - -.theme-toggle span { - line-height: 1; - white-space: nowrap; -} - .icon-glyph { display: inline-grid; width: 1.55rem; height: 1.55rem; place-items: center; border: var(--ui-border); - background: var(--ui-color-surface-icon); + background: #050605; color: currentColor; line-height: 1; } @@ -667,7 +617,6 @@ } .dashboard-frame__header, - .dashboard-frame__title, .dashboard-header, .dashboard-frame__modules, .dashboard-frame__panels { diff --git a/src/lib/ui/index.ts b/src/lib/ui/index.ts index 9eb5448..864306c 100644 --- a/src/lib/ui/index.ts +++ b/src/lib/ui/index.ts @@ -26,17 +26,9 @@ export { SystemState } from "./components/SystemState"; export { TelemetryCard } from "./components/TelemetryCard"; export { TelemetryGrid } from "./components/TelemetryGrid"; export { TelemetryStrip } from "./components/TelemetryStrip"; -export { ThemeToggle } from "./components/ThemeToggle"; export { WeatherModule } from "./components/WeatherModule"; export { dashboardPreviewFixtures } from "./fixtures"; export { dashboardDocumentToUiDashboard } from "./model-renderer"; -export { - getNextUiTheme, - isUiTheme, - persistUiTheme, - resolveInitialUiTheme, - UI_THEME_STORAGE_KEY, -} from "./theme"; export type { UiDashboardPreview, UiLink, @@ -48,4 +40,3 @@ export type { UiStatusItem, UiTelemetryCard, } from "./types"; -export type { UiTheme } from "./theme"; diff --git a/src/lib/ui/stories/ThemeToggle.stories.tsx b/src/lib/ui/stories/ThemeToggle.stories.tsx deleted file mode 100644 index 33e494a..0000000 --- a/src/lib/ui/stories/ThemeToggle.stories.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react-vite"; -import { ThemeToggle } from "../components/ThemeToggle"; - -const meta = { - title: "UI/ThemeToggle", - component: ThemeToggle, - args: { - onThemeChange: () => undefined, - theme: "dark", - }, -} satisfies Meta; - -export default meta; - -type Story = StoryObj; - -export const Dark: Story = {}; - -export const Light: Story = { - args: { - theme: "light", - }, - globals: { - theme: "light", - }, -}; diff --git a/src/lib/ui/storybook.test.ts b/src/lib/ui/storybook.test.ts index aa76cad..f1303c7 100644 --- a/src/lib/ui/storybook.test.ts +++ b/src/lib/ui/storybook.test.ts @@ -36,7 +36,6 @@ const requiredStoryFiles = [ "TelemetryCard.stories.tsx", "TelemetryGrid.stories.tsx", "TelemetryStrip.stories.tsx", - "ThemeToggle.stories.tsx", "WeatherModule.stories.tsx", ] as const; @@ -75,13 +74,6 @@ describe("Storybook inventory", () => { expect(dashboardFrame).not.toContain('./styles.css'); }); - test("configures Storybook theme switching for reusable components", () => { - const previewSource = readFileSync(join(root, ".storybook/preview.ts"), "utf8"); - - expect(previewSource).toContain("globalTypes"); - expect(previewSource).toContain("data-ui-theme"); - }); - test("keeps component and story files paired as the UI inventory changes", () => { const componentStoryFiles = readdirSync(componentsDir) .filter((filename) => filename.endsWith(".tsx") && !filename.endsWith(".test.tsx")) diff --git a/src/lib/ui/theme.test.ts b/src/lib/ui/theme.test.ts deleted file mode 100644 index 8ed3f04..0000000 --- a/src/lib/ui/theme.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { describe, expect, test } from "vitest"; -import { - getNextUiTheme, - isUiTheme, - persistUiTheme, - resolveInitialUiTheme, - UI_THEME_STORAGE_KEY, -} from "./theme"; - -describe("UI theme preference", () => { - test("defaults to dark when no stored preference exists", () => { - const storage = new Map(); - - expect(resolveInitialUiTheme(storage)).toBe("dark"); - }); - - test("restores a valid stored preference", () => { - const storage = new Map([[UI_THEME_STORAGE_KEY, "light"]]); - - expect(resolveInitialUiTheme(storage)).toBe("light"); - }); - - test("ignores invalid stored preferences", () => { - const storage = new Map([[UI_THEME_STORAGE_KEY, "solarized"]]); - - expect(resolveInitialUiTheme(storage)).toBe("dark"); - }); - - test("falls back when stored preferences cannot be read", () => { - const storage = { - getItem() { - throw new Error("storage blocked"); - }, - }; - - expect(resolveInitialUiTheme(storage)).toBe("dark"); - }); - - test("ignores persistence failures", () => { - const storage = { - setItem() { - throw new Error("quota exceeded"); - }, - }; - - expect(() => persistUiTheme("light", storage)).not.toThrow(); - }); - - test("detects and toggles supported themes", () => { - expect(isUiTheme("light")).toBe(true); - expect(isUiTheme("dark")).toBe(true); - expect(isUiTheme("contrast")).toBe(false); - expect(getNextUiTheme("dark")).toBe("light"); - expect(getNextUiTheme("light")).toBe("dark"); - }); -}); diff --git a/src/lib/ui/theme.ts b/src/lib/ui/theme.ts deleted file mode 100644 index 9f48df5..0000000 --- a/src/lib/ui/theme.ts +++ /dev/null @@ -1,68 +0,0 @@ -export const UI_THEME_STORAGE_KEY = "dashboard-ui-theme"; - -export type UiTheme = "dark" | "light"; - -type ReadableThemeStorage = - | { getItem(key: string): string | null } - | { get(key: string): string | undefined }; - -type WritableThemeStorage = - | { setItem(key: string, value: string): void } - | { set(key: string, value: string): unknown }; - -export function isUiTheme(value: unknown): value is UiTheme { - return value === "dark" || value === "light"; -} - -export function getNextUiTheme(theme: UiTheme): UiTheme { - return theme === "dark" ? "light" : "dark"; -} - -export function resolveInitialUiTheme( - storage?: ReadableThemeStorage | null, - fallback: UiTheme = "dark", -): UiTheme { - const stored = safeReadStoredTheme(storage); - - return isUiTheme(stored) ? stored : fallback; -} - -export function persistUiTheme( - theme: UiTheme, - storage?: WritableThemeStorage | null, -): void { - if (!storage) return; - - try { - if ("setItem" in storage) { - storage.setItem(UI_THEME_STORAGE_KEY, theme); - return; - } - - storage.set(UI_THEME_STORAGE_KEY, theme); - } catch { - // Browser storage may be blocked or quota-constrained. - } -} - -function safeReadStoredTheme( - storage?: ReadableThemeStorage | null, -): string | undefined { - try { - return readStoredTheme(storage); - } catch { - return undefined; - } -} - -function readStoredTheme( - storage?: ReadableThemeStorage | null, -): string | undefined { - if (!storage) return undefined; - - if ("getItem" in storage) { - return storage.getItem(UI_THEME_STORAGE_KEY) ?? undefined; - } - - return storage.get(UI_THEME_STORAGE_KEY); -} diff --git a/src/lib/ui/tokens.css b/src/lib/ui/tokens.css index 3e70132..56c2f7e 100644 --- a/src/lib/ui/tokens.css +++ b/src/lib/ui/tokens.css @@ -1,27 +1,14 @@ -:root, -[data-ui-theme="dark"] { +:root { color-scheme: dark; --ui-color-canvas: #020302; --ui-color-surface: #060706; --ui-color-surface-raised: #0b0d0c; - --ui-color-surface-panel: rgba(3, 4, 3, 0.86); - --ui-color-surface-module: rgba(6, 8, 7, 0.82); - --ui-color-surface-footer: rgba(2, 3, 2, 0.92); - --ui-color-surface-inset: #030403; - --ui-color-surface-icon: #050605; --ui-color-line: rgba(244, 244, 244, 0.16); --ui-color-line-strong: rgba(244, 244, 244, 0.32); - --ui-color-grid-line: rgba(255, 255, 255, 0.035); - --ui-color-frame-spine: rgba(255, 255, 255, 0.08); - --ui-color-frame-wash: rgba(0, 0, 0, 0.18); - --ui-color-card-gradient-start: rgba(13, 15, 14, 0.82); - --ui-color-card-gradient-end: rgba(2, 3, 2, 0.92); - --ui-color-hover: rgba(244, 244, 244, 0.035); --ui-color-text: #f3f4ed; --ui-color-muted: #8d948c; --ui-color-dim: #555b55; --ui-color-accent: #d7ff00; - --ui-color-accent-contrast: #050605; --ui-color-ok: #bfff00; --ui-color-warning: #ffb020; --ui-color-danger: #ff1744; @@ -46,37 +33,6 @@ --ui-focus-ring: 0 0 0 2px var(--ui-color-canvas), 0 0 0 4px var(--ui-color-accent); } -[data-ui-theme="light"] { - color-scheme: light; - --ui-color-canvas: #f3f5ed; - --ui-color-surface: #ffffff; - --ui-color-surface-raised: #eef1e7; - --ui-color-surface-panel: rgba(255, 255, 255, 0.9); - --ui-color-surface-module: rgba(255, 255, 255, 0.84); - --ui-color-surface-footer: rgba(248, 250, 241, 0.94); - --ui-color-surface-inset: #e4e8db; - --ui-color-surface-icon: #f8faf1; - --ui-color-line: rgba(19, 25, 19, 0.18); - --ui-color-line-strong: rgba(19, 25, 19, 0.34); - --ui-color-grid-line: rgba(19, 25, 19, 0.055); - --ui-color-frame-spine: rgba(19, 25, 19, 0.1); - --ui-color-frame-wash: rgba(255, 255, 255, 0.48); - --ui-color-card-gradient-start: rgba(255, 255, 255, 0.92); - --ui-color-card-gradient-end: rgba(230, 235, 221, 0.88); - --ui-color-hover: rgba(19, 25, 19, 0.055); - --ui-color-text: #11170f; - --ui-color-muted: #5d665a; - --ui-color-dim: #7d8779; - --ui-color-accent: #516f00; - --ui-color-accent-contrast: #ffffff; - --ui-color-ok: #4c7400; - --ui-color-warning: #a45b00; - --ui-color-danger: #bd173f; - --ui-color-stale: #596975; - --ui-color-unavailable: #6c7479; - --ui-shadow-hard: 0 0 0 1px rgba(81, 111, 0, 0.18) inset; -} - html { background: var(--ui-color-canvas); } @@ -86,8 +42,8 @@ body { min-height: 100vh; margin: 0; background: - linear-gradient(var(--ui-color-grid-line) 1px, transparent 1px), - linear-gradient(90deg, var(--ui-color-grid-line) 1px, transparent 1px), + linear-gradient(rgba(255, 255, 255, 0.035) 1px, transparent 1px), + linear-gradient(90deg, rgba(255, 255, 255, 0.035) 1px, transparent 1px), var(--ui-color-canvas); background-size: 48px 48px, 48px 48px, auto; color: var(--ui-color-text); diff --git a/tests/e2e/dashboard.spec.ts b/tests/e2e/dashboard.spec.ts index 7110900..b432d14 100644 --- a/tests/e2e/dashboard.spec.ts +++ b/tests/e2e/dashboard.spec.ts @@ -209,16 +209,6 @@ test.describe("dashboard page QA gate", () => { await waitForDashboardReady(page); await expect(page.getByRole("main")).toHaveCount(1); - await page.keyboard.press("Tab"); - const themeToggle = page.getByRole("button", { - name: "Switch to light theme", - }); - await expect(themeToggle).toBeFocused(); - const themeFocusBoxShadow = await themeToggle.evaluate((element) => { - return window.getComputedStyle(element).boxShadow; - }); - expect(themeFocusBoxShadow).not.toBe("none"); - for (const serviceId of linkedServiceIds) { await page.keyboard.press("Tab"); @@ -240,24 +230,6 @@ test.describe("dashboard page QA gate", () => { expect(results.violations).toEqual([]); }); - test("passes automated accessibility checks in light mode", async ({ - page, - }, testInfo) => { - test.skip(testInfo.project.name !== "chromium-desktop"); - - await page.goto("/"); - await waitForDashboardReady(page); - await page.getByRole("button", { name: "Switch to light theme" }).click(); - await expect(page.locator("html")).toHaveAttribute("data-ui-theme", "light"); - - const results = await new AxeBuilder({ page }).analyze(); - expect(results.violations).toEqual([]); - - await expect(page).toHaveScreenshot("dashboard-light-desktop.png", { - fullPage: true, - }); - }); - test("honors reduced-motion preferences", async ({ page }) => { await page.emulateMedia({ reducedMotion: "reduce" }); @@ -283,29 +255,6 @@ test.describe("dashboard page QA gate", () => { 0.01, ); }); - - test("toggles the dashboard between dark and light themes", async ({ page }) => { - await page.goto("/"); - await waitForDashboardReady(page); - - await expect(page.locator("html")).toHaveAttribute("data-ui-theme", "dark"); - - const switchToLight = page.getByRole("button", { - name: "Switch to light theme", - }); - await expect(switchToLight).toBeVisible(); - await switchToLight.click(); - - await expect(page.locator("html")).toHaveAttribute("data-ui-theme", "light"); - await expect( - page.getByRole("button", { name: "Switch to dark theme" }), - ).toBeVisible(); - - await page.reload(); - await waitForDashboardReady(page); - - await expect(page.locator("html")).toHaveAttribute("data-ui-theme", "light"); - }); }); async function waitForDashboardReady(page: Page): Promise { diff --git a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png b/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png index 848033e..fa3b3bf 100644 Binary files a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png and b/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png differ diff --git a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png b/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png deleted file mode 100644 index 5b04c30..0000000 Binary files a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png and /dev/null differ diff --git a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png b/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png index 1f6baf4..a199b15 100644 Binary files a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png and b/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png differ diff --git a/tests/e2e/storybook-server.ts b/tests/e2e/storybook-server.ts deleted file mode 100644 index e6e3f63..0000000 --- a/tests/e2e/storybook-server.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { existsSync } from "node:fs"; -import { resolve, sep } from "node:path"; - -const root = resolve(process.cwd(), "storybook-static"); -const port = Number(process.env.STORYBOOK_STATIC_PORT || 6007); - -if (!existsSync(resolve(root, "iframe.html"))) { - throw new Error("storybook-static is missing. Run bun run build-storybook first."); -} - -Bun.serve({ - hostname: "127.0.0.1", - port, - async fetch(request) { - const url = new URL(request.url); - const pathname = decodeURIComponent(url.pathname); - const relativePath = pathname === "/" ? "/index.html" : pathname; - const filePath = resolve(root, `.${relativePath}`); - - if (!filePath.startsWith(`${root}${sep}`)) { - return new Response("Forbidden", { status: 403 }); - } - - const file = Bun.file(filePath); - if (!(await file.exists())) { - return new Response("Not found", { status: 404 }); - } - - return new Response(file); - }, -}); - -console.log(`Storybook static listening on http://127.0.0.1:${port}`); diff --git a/tests/e2e/storybook.spec.ts b/tests/e2e/storybook.spec.ts deleted file mode 100644 index 7873a02..0000000 --- a/tests/e2e/storybook.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { expect, test } from "@playwright/test"; - -const storybookPort = Number(process.env.PLAYWRIGHT_STORYBOOK_PORT || 6007); -const storybookURL = `http://127.0.0.1:${storybookPort}`; - -test.describe("Storybook theme QA", () => { - test("renders the ThemeToggle light story on the light canvas", async ({ - page, - }, testInfo) => { - test.skip(testInfo.project.name !== "chromium-desktop"); - - const consoleMessages: string[] = []; - page.on("console", (message) => { - if (["error", "warning"].includes(message.type())) { - consoleMessages.push(message.text()); - } - }); - - const url = `${storybookURL}/iframe.html?id=ui-themetoggle--light&viewMode=story`; - await page.goto(url, { waitUntil: "networkidle" }); - - await expect(page.locator("html")).toHaveAttribute("data-ui-theme", "light"); - await expect( - page.getByRole("button", { name: "Switch to dark theme" }), - ).toBeVisible(); - await expect(page.locator("body")).toHaveCSS( - "background-color", - "rgb(243, 245, 237)", - ); - expect( - consoleMessages.filter((message) => - message.includes("Global args/argTypes can only be set globally"), - ), - ).toEqual([]); - }); -});