feat(design): reorder without a drag everywhere a grip drags (WCAG 2.5.7, E28 S3e4a)
One rule, two forms: useOrderItems (Top / Up / Down / Bottom rows of a row menu) and OrderButtons (four buttons with the place, or a compact Up / Down pair). A move that would go nowhere is shown off. - YouTube playlist rows: the four moves now come from useOrderItems. - Plex playlist rows: a block among the blocks, an episode within its show, off while a write is in flight; drag and menu share moveBlock/moveEpisode. - Saved views editor: the selected view's four buttons (owner: A), and the grip's keyboard pick-up (its instructions promised one it did not have). - Side panel groups in edit mode: an Up / Down pair (owner: A); a group the stored order does not hold yet moves too; the name wraps there. - The grip's touch-action: none is now test-held.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
// @vitest-environment jsdom
|
||||
//
|
||||
// A side panel's groups in edit mode move without a drag too (WCAG 2.5.7): each carries an Up / Down
|
||||
// pair, named by its group, and a move lands in the stored order.
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { render, screen, fireEvent, cleanup, within } from "@testing-library/react";
|
||||
|
||||
vi.mock("react-i18next", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("react-i18next")>();
|
||||
return { ...actual, useTranslation: () => ({ t: (key: string) => key }) };
|
||||
});
|
||||
|
||||
import PanelGroups, { type PanelItem } from "./PanelGroups";
|
||||
import type { PanelLayout } from "../../lib/panelLayout";
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
const items: PanelItem[] = ["a", "b", "c"].map((id) => ({
|
||||
id,
|
||||
title: `Group ${id}`,
|
||||
body: <p>{id} body</p>,
|
||||
}));
|
||||
const layout = (order: string[]): PanelLayout => ({ order, collapsed: {}, hidden: {} });
|
||||
|
||||
function mount(order: string[], editing = true) {
|
||||
const setLayout = vi.fn();
|
||||
render(
|
||||
<PanelGroups items={items} layout={layout(order)} setLayout={setLayout} editing={editing} />,
|
||||
);
|
||||
const pair = (title: string) => screen.getByRole("group", { name: new RegExp(title) });
|
||||
const btn = (title: string, name: string) =>
|
||||
within(pair(title)).getByRole("button", { name }) as HTMLButtonElement;
|
||||
return { setLayout, btn };
|
||||
}
|
||||
|
||||
describe("PanelGroups — reordering without a drag", () => {
|
||||
it("moves a group up or down, the ends held off", () => {
|
||||
const { setLayout, btn } = mount(["a", "b", "c"]);
|
||||
expect(btn("Group a", "common.order.up").disabled).toBe(true);
|
||||
expect(btn("Group c", "common.order.down").disabled).toBe(true);
|
||||
fireEvent.click(btn("Group a", "common.order.down"));
|
||||
expect(setLayout).toHaveBeenLastCalledWith(expect.objectContaining({ order: ["b", "a", "c"] }));
|
||||
fireEvent.click(btn("Group c", "common.order.up"));
|
||||
expect(setLayout).toHaveBeenLastCalledWith(expect.objectContaining({ order: ["a", "c", "b"] }));
|
||||
});
|
||||
|
||||
it("moves a group the stored order does not hold yet", () => {
|
||||
// "c" became available after the layout was saved: it renders last, and still moves.
|
||||
const { setLayout, btn } = mount(["a", "b"]);
|
||||
fireEvent.click(btn("Group c", "common.order.up"));
|
||||
expect(setLayout).toHaveBeenLastCalledWith(expect.objectContaining({ order: ["a", "c", "b"] }));
|
||||
});
|
||||
|
||||
it("offers no move outside edit mode", () => {
|
||||
mount(["a", "b", "c"], false);
|
||||
expect(screen.queryByRole("button", { name: "common.order.up" })).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user