From 5ebb4cb50634855ca2637c235a885ff0d0e8d50a Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sun, 27 Sep 2026 15:01:22 +0200 Subject: [PATCH] fix(a11y): chips, grips, the panel tab and Back to top keep the focus somewhere real MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - F5: a chip's × pressed from the keyboard hands the focus to the next chip (else the previous one, else what follows) before it unmounts — in DismissibleChip, so all five chip rows get it (`focusNeighbour` in lib/useFocusKeeper). - F4: a drag grip names what it lifts (`common.order.dragNamed`: panel groups, personal views); the two generic "Drag to reorder" keys are gone. - F6: the side panel's edge tab is out of the Tab order while the card covers it, names its panel, and a keyboard pin moves the focus into the card. - F7: Back to top is no Tab stop while hidden, and a keyboard press leaves the focus on the page. --- .../PersonalViewsModal.dom.test.tsx | 10 ++++- .../src/components/PersonalViewsModal.tsx | 6 ++- .../src/components/ui/BackToTop.dom.test.tsx | 39 +++++++++++++++++++ frontend/src/components/ui/BackToTop.tsx | 15 ++++--- frontend/src/components/ui/Chip.tsx | 10 ++++- .../src/components/ui/Combobox.dom.test.tsx | 23 +++++++++++ frontend/src/components/ui/PanelGroup.tsx | 2 +- .../components/ui/PanelGroups.dom.test.tsx | 16 +++++++- .../ui/SidePanel.drawer.dom.test.tsx | 21 +++++++++- frontend/src/components/ui/SidePanel.tsx | 25 +++++++++--- frontend/src/i18n/locales/en/common.json | 3 +- frontend/src/i18n/locales/en/sidebar.json | 3 +- frontend/src/i18n/locales/en/views.json | 1 - frontend/src/i18n/locales/hu/common.json | 3 +- frontend/src/i18n/locales/hu/sidebar.json | 3 +- frontend/src/i18n/locales/hu/views.json | 1 - frontend/src/lib/useFocusKeeper.ts | 15 +++++++ 17 files changed, 173 insertions(+), 23 deletions(-) create mode 100644 frontend/src/components/ui/BackToTop.dom.test.tsx diff --git a/frontend/src/components/PersonalViewsModal.dom.test.tsx b/frontend/src/components/PersonalViewsModal.dom.test.tsx index dc3ea327..486f2bf6 100644 --- a/frontend/src/components/PersonalViewsModal.dom.test.tsx +++ b/frontend/src/components/PersonalViewsModal.dom.test.tsx @@ -19,7 +19,13 @@ const h = vi.hoisted(() => ({ vi.mock("react-i18next", async (importOriginal) => { const actual = await importOriginal(); - return { ...actual, useTranslation: () => ({ t: (key: string) => key }) }; + return { + ...actual, + useTranslation: () => ({ + t: (key: string, o?: { name?: string }) => + key === "common.order.dragNamed" ? `${key}:${o?.name}` : key, + }), + }; }); // A full feed filter set that differs from the sidebar default ONLY in `tags`, so useActiveFilters @@ -225,7 +231,7 @@ describe("PersonalViewsModal — reordering without a drag (WCAG 2.5.7)", () => it("picks a view up from the keyboard, as its grip's instructions say", async () => { CTX.views = [view(5, "Docs", 0), view(6, "Music", 1)]; mount(); - const grip = (await screen.findAllByRole("button", { name: "views.reorder" }))[0]!; + const grip = await screen.findByRole("button", { name: "common.order.dragNamed:Docs" }); grip.focus(); fireEvent.keyDown(grip, { code: "Space", key: " " }); await waitFor(() => expect(document.body.textContent).toContain("common.dnd.pickedUp")); diff --git a/frontend/src/components/PersonalViewsModal.tsx b/frontend/src/components/PersonalViewsModal.tsx index 691a5622..e4b415a6 100644 --- a/frontend/src/components/PersonalViewsModal.tsx +++ b/frontend/src/components/PersonalViewsModal.tsx @@ -62,7 +62,11 @@ function ViewRow({ selected ? "bg-accent/15" : "state-layer" }`} > - +