From 3d9edf41411be4f0615e40a11aa8e0263f6d968c Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Fri, 6 Feb 2026 18:45:56 -0600 Subject: [PATCH] fix(CQ-WEB-11+12): Fix accessibility labels + SSR window check CQ-WEB-11: Add aria-label attributes to search input, date inputs, and id/htmlFor associations for status and priority filter checkboxes in FilterBar component to improve screen reader accessibility. CQ-WEB-12: Guard all browser-specific API usage in ReactFlowEditor behind typeof window checks. Move isDark detection into useState + useEffect to prevent SSR/hydration mismatches. Co-Authored-By: Claude Opus 4.6 --- .../src/components/filters/FilterBar.test.tsx | 66 +++++++++++++++++++ apps/web/src/components/filters/FilterBar.tsx | 9 +++ .../components/mindmap/ReactFlowEditor.tsx | 13 +++- 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/filters/FilterBar.test.tsx b/apps/web/src/components/filters/FilterBar.test.tsx index 53a659b..8b077c4 100644 --- a/apps/web/src/components/filters/FilterBar.test.tsx +++ b/apps/web/src/components/filters/FilterBar.test.tsx @@ -132,4 +132,70 @@ describe("FilterBar", (): void => { // Should show 3 active filters (2 statuses + 1 priority) expect(screen.getByText(/3/)).toBeInTheDocument(); }); + + describe("accessibility (CQ-WEB-11)", (): void => { + it("should have aria-label on search input", (): void => { + render(); + const searchInput = screen.getByRole("textbox", { name: /search tasks/i }); + expect(searchInput).toBeInTheDocument(); + }); + + it("should have aria-label on date inputs", (): void => { + render(); + expect(screen.getByLabelText(/filter from date/i)).toBeInTheDocument(); + expect(screen.getByLabelText(/filter to date/i)).toBeInTheDocument(); + }); + + it("should have aria-labels on status filter buttons", (): void => { + render(); + expect(screen.getByRole("button", { name: /status filter/i })).toBeInTheDocument(); + }); + + it("should have aria-labels on priority filter buttons", (): void => { + render(); + expect(screen.getByRole("button", { name: /priority filter/i })).toBeInTheDocument(); + }); + + it("should have id and htmlFor associations on status checkboxes", async (): Promise => { + const user = userEvent.setup(); + render(); + + // Open status dropdown + await user.click(screen.getByRole("button", { name: /status filter/i })); + + // Verify specific status checkboxes have proper id attributes + const notStartedCheckbox = screen.getByLabelText(/filter by status: not started/i); + expect(notStartedCheckbox).toHaveAttribute("id", "status-filter-NOT_STARTED"); + + const inProgressCheckbox = screen.getByLabelText(/filter by status: in progress/i); + expect(inProgressCheckbox).toHaveAttribute("id", "status-filter-IN_PROGRESS"); + + const completedCheckbox = screen.getByLabelText(/filter by status: completed/i); + expect(completedCheckbox).toHaveAttribute("id", "status-filter-COMPLETED"); + }); + + it("should have id and htmlFor associations on priority checkboxes", async (): Promise => { + const user = userEvent.setup(); + render(); + + // Open priority dropdown + await user.click(screen.getByRole("button", { name: /priority filter/i })); + + // Verify specific priority checkboxes have proper id attributes + const lowCheckbox = screen.getByLabelText(/filter by priority: low/i); + expect(lowCheckbox).toHaveAttribute("id", "priority-filter-LOW"); + + const mediumCheckbox = screen.getByLabelText(/filter by priority: medium/i); + expect(mediumCheckbox).toHaveAttribute("id", "priority-filter-MEDIUM"); + + const highCheckbox = screen.getByLabelText(/filter by priority: high/i); + expect(highCheckbox).toHaveAttribute("id", "priority-filter-HIGH"); + }); + + it("should have aria-label on clear filters button", (): void => { + const filtersWithSearch = { search: "test" }; + render(); + expect(screen.getByRole("button", { name: /clear filters/i })).toBeInTheDocument(); + }); + }); }); diff --git a/apps/web/src/components/filters/FilterBar.tsx b/apps/web/src/components/filters/FilterBar.tsx index 981060d..ccb541c 100644 --- a/apps/web/src/components/filters/FilterBar.tsx +++ b/apps/web/src/components/filters/FilterBar.tsx @@ -112,6 +112,7 @@ export function FilterBar({ { setSearchValue(e.target.value); @@ -141,14 +142,17 @@ export function FilterBar({ {Object.values(TaskStatus).map((status) => (