From b3b3904cf335467e718b1a4bb3d26f2bee40e2b8 Mon Sep 17 00:00:00 2001 From: ilia Date: Wed, 5 Aug 2026 16:57:55 -0400 Subject: [PATCH] Fix invisible admin sidebar links; add e2e nav smoke. --- admin-frontend/src/components/Layout.tsx | 4 +- docs/ADMIN_UX_REVIEW.md | 1 + e2e/tests/admin.sidebar-nav.spec.ts | 60 ++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 e2e/tests/admin.sidebar-nav.spec.ts diff --git a/admin-frontend/src/components/Layout.tsx b/admin-frontend/src/components/Layout.tsx index 9fdd921..0d2fe2c 100644 --- a/admin-frontend/src/components/Layout.tsx +++ b/admin-frontend/src/components/Layout.tsx @@ -97,8 +97,8 @@ export default function Layout() { }} className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-colors ${ isActive - ? 'bg-sidebar-active text-sidebar-accent-foreground ring-1 ring-gold/50' - : 'text-sidebar-foreground/85 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground' + ? 'bg-sidebar-active text-sidebar-accent-foreground ring-1 ring-[color:var(--gold)]' + : 'text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground' } ${extraClasses}`} > {item.label} diff --git a/docs/ADMIN_UX_REVIEW.md b/docs/ADMIN_UX_REVIEW.md index cb8d2ed..9ec8816 100644 --- a/docs/ADMIN_UX_REVIEW.md +++ b/docs/ADMIN_UX_REVIEW.md @@ -21,6 +21,7 @@ Snapshot after Chabad Blue chrome parity (navy sidebar, theme toggle, shared tok | P2 | iOS hamburger only — no desktop collapse | Optional collapsed icon rail for large monitors | | P3 | Viewer filter comboboxes lack accessible names (axe `button-name`) | Label each Select trigger; then restore full-page axe on gallery home | | Done | Admin Home was a SaaS marketing splash (orange/blue hero, emoji placeholders) | Quiet ops home: welcome + workflow links + recent processed strip | +| Done | Sidebar primary links invisible (Tailwind `/85` on CSS-var colors → transparent) | Solid `text-sidebar-foreground`; e2e asserts Scan/Process/People visible | | P3 | Emoji leftovers in Help / some toasts (remove over time) | Plain language + lucide/SVG icons over time | ## Accessibility status diff --git a/e2e/tests/admin.sidebar-nav.spec.ts b/e2e/tests/admin.sidebar-nav.spec.ts new file mode 100644 index 0000000..e698f43 --- /dev/null +++ b/e2e/tests/admin.sidebar-nav.spec.ts @@ -0,0 +1,60 @@ +import { z } from 'zod'; +import { test, expect } from '../fixtures'; +import { DEFAULT_ADMIN_BASE_URL } from '../env-defaults'; + +const TokenResponse = z.object({ + access_token: z.string().min(1), + refresh_token: z.string().min(1), +}); + +/** + * Admin sidebar primary nav must be visible after Chabad chrome (CSS-var + * opacity modifiers made inactive links transparent — #104). + */ +test.describe('admin sidebar nav @smoke', () => { + const adminBaseUrl = process.env.PLAYKIT_ADMIN_BASE_URL || DEFAULT_ADMIN_BASE_URL; + + test('primary workflow links are visible in the sidebar', async ({ page, api }) => { + const username = process.env.E2E_API_USERNAME || ''; + const password = process.env.E2E_API_PASSWORD || ''; + test.skip(!username || !password, 'E2E_API_USERNAME/PASSWORD required for admin UI'); + + const tokens = await api.post>('/api/v1/auth/login', { + body: { username, password }, + expectedStatus: 200, + schema: TokenResponse, + }); + + await page.goto(`${adminBaseUrl}/login`); + await page.evaluate( + ({ access, refresh }) => { + localStorage.setItem('access_token', access); + localStorage.setItem('refresh_token', refresh); + }, + { + access: tokens.data.access_token, + refresh: tokens.data.refresh_token, + }, + ); + await page.goto(`${adminBaseUrl}/`); + await expect(page.getByRole('button', { name: /Log\s*out/i })).toBeVisible({ + timeout: 30_000, + }); + + const nav = page.getByRole('navigation', { name: 'Primary' }); + await expect(nav).toBeVisible(); + + for (const label of ['Scan', 'Process', 'Search photos', 'People', 'Tag photos']) { + const link = nav.getByRole('link', { name: label, exact: true }); + await expect(link, `sidebar missing visible link: ${label}`).toBeVisible(); + // Guard against transparent text (CSS-var + /opacity bug) + await expect(link).toHaveCSS('opacity', '1'); + const color = await link.evaluate((el) => getComputedStyle(el).color); + expect(color, `${label} must not be transparent`).not.toBe('rgba(0, 0, 0, 0)'); + expect(color).not.toMatch(/rgba?\(\s*0,\s*0,\s*0,\s*0\s*\)/); + } + + await expect(nav.getByRole('button', { name: /Maintenance/i })).toBeVisible(); + await expect(nav.getByRole('link', { name: 'Help', exact: true })).toBeVisible(); + }); +});