fix(a11y): chips, grips, the panel tab and Back to top keep the focus somewhere real
- 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.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
import { describe, it, expect, vi, afterEach, beforeAll } from "vitest";
|
||||
import { render, screen, fireEvent, cleanup } from "@testing-library/react";
|
||||
import { FloatingTree } from "@floating-ui/react";
|
||||
import { useState } from "react";
|
||||
|
||||
beforeAll(() => {
|
||||
class Stub {
|
||||
@@ -278,6 +279,28 @@ describe("Combobox", () => {
|
||||
// page is the fixed fallback of 10 rows; the measured page is pinned in lib/listPaging.
|
||||
// E28 review J1 + J2: a failed list said "No matches" (an error dressed as an empty result), and a
|
||||
// picker whose pick was still being written took another pick.
|
||||
// E28 review F5: a chip's × pressed from the keyboard unmounted with the focus on it — to <body>,
|
||||
// out of the picker. The focus goes to the next chip's × (else the previous one, else the field).
|
||||
describe("Combobox · removing a chip from the keyboard", () => {
|
||||
function Harness({ initial }: { initial: string[] }) {
|
||||
const [sel, setSel] = useState(initial);
|
||||
return <Combobox label="Genre" options={OPTIONS} selected={sel} onChange={setSel} />;
|
||||
}
|
||||
const x = (name: string) =>
|
||||
screen
|
||||
.getAllByRole("button", { name: "common.removeNamed" })
|
||||
.find((b) => b.closest("span")?.textContent?.includes(name))!;
|
||||
|
||||
it("hands the focus to the neighbour chip, then to the field", () => {
|
||||
wrap(<Harness initial={["Drama", "Action"]} />);
|
||||
x("Drama").focus();
|
||||
fireEvent.click(x("Drama"), { detail: 0 });
|
||||
expect(document.activeElement).toBe(x("Action"));
|
||||
fireEvent.click(x("Action"), { detail: 0 });
|
||||
expect(document.activeElement).toBe(screen.getByRole("combobox"));
|
||||
});
|
||||
});
|
||||
|
||||
describe("Combobox · honest states", () => {
|
||||
it("says the list failed to load, not that nothing matched", () => {
|
||||
wrap(
|
||||
|
||||
Reference in New Issue
Block a user