- Its section switch is `ui/Tabs` (new `tone="media"` paint for the dark chrome) with a wired tabpanel; the panel opens with the focus on the selected tab. - On/off rows are `CheckRow` (the whole row, 44 on touch), the "no background" checkbox too. - Every slider and the four colour wells carry the name their row shows. - The clock's date-style buttons reach the touch target.
82 lines
3.7 KiB
TypeScript
82 lines
3.7 KiB
TypeScript
// @vitest-environment jsdom
|
|
//
|
|
// E28 review D5 + L5 + D6: the Plex player's settings panel. Its section switch was a row of
|
|
// `aria-pressed` buttons (now an ARIA tab list on `ui/Tabs`), its rows sat under the 44 touch target,
|
|
// its on/off rows were a hand-laid label (now `CheckRow`), and its sliders and colour wells had no
|
|
// accessible name — the visible label beside them was not tied to them.
|
|
import { useRef, useState } from "react";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
|
|
vi.mock("react-i18next", async (importOriginal) => ({
|
|
...(await importOriginal<typeof import("react-i18next")>()),
|
|
useTranslation: () => ({ t: (k: string) => k }),
|
|
}));
|
|
|
|
import { PlexPlayerSettings } from "./PlexPlayerSettings";
|
|
import { DEFAULT_PREFS, type PlexPlayerPrefs } from "../lib/plexPlayerPrefs";
|
|
|
|
afterEach(cleanup);
|
|
|
|
type Tab = "sync" | "playback" | "subtitle" | "clock";
|
|
const TABS: { key: Tab; label: string }[] = [
|
|
{ key: "sync", label: "Sync" },
|
|
{ key: "playback", label: "Playback" },
|
|
{ key: "subtitle", label: "Subtitles" },
|
|
{ key: "clock", label: "Clock" },
|
|
];
|
|
|
|
function Harness({ start = "sync" as Tab, prefs: initial = {} as Partial<PlexPlayerPrefs> }) {
|
|
const [tab, setTab] = useState<Tab>(start);
|
|
const [prefs, setPrefs] = useState<PlexPlayerPrefs>({ ...DEFAULT_PREFS, ...initial });
|
|
const menuRef = useRef<HTMLDivElement>(null);
|
|
const back = useRef<HTMLElement>(null);
|
|
return (
|
|
<PlexPlayerSettings
|
|
menuRef={menuRef}
|
|
returnFocusTo={back}
|
|
settingsTabs={TABS}
|
|
activeTab={tab}
|
|
setSettingsTab={setTab}
|
|
prefs={prefs}
|
|
patchPrefs={(p) => setPrefs((cur) => ({ ...cur, ...p }))}
|
|
changeAudioOffset={() => {}}
|
|
now={new Date(2026, 8, 27, 12, 0)}
|
|
/>
|
|
);
|
|
}
|
|
|
|
const tab = (name: string) => screen.getByRole("tab", { name });
|
|
|
|
describe("the Plex player's settings panel", () => {
|
|
it("switches its sections as a tab list, the focus on the selected tab", () => {
|
|
render(<Harness start="subtitle" />);
|
|
expect(screen.getByRole("tablist")).toBeTruthy();
|
|
expect(document.activeElement).toBe(tab("Subtitles"));
|
|
fireEvent.keyDown(document.activeElement!, { key: "ArrowRight" });
|
|
expect(tab("Clock").getAttribute("aria-selected")).toBe("true");
|
|
expect(screen.getByRole("tabpanel", { name: "Clock" })).toBeTruthy();
|
|
});
|
|
|
|
it("gives every slider and colour well the name its row shows", () => {
|
|
render(<Harness start="subtitle" prefs={{ subBg: "#000000", subShadow: 2 }} />);
|
|
for (const name of ["plex.player.subSize", "plex.player.subPos", "plex.player.subShadow"])
|
|
expect(screen.getByRole("slider", { name })).toBeTruthy();
|
|
for (const name of ["plex.player.subColor", "plex.player.subBg", "plex.player.subShadowColor"])
|
|
expect(screen.getByLabelText(name).getAttribute("type")).toBe("color");
|
|
fireEvent.click(tab("Clock"));
|
|
expect(screen.getByLabelText("plex.player.clockColor").getAttribute("type")).toBe("color");
|
|
});
|
|
|
|
it("lays its on/off rows on CheckRow and its buttons at the touch target", () => {
|
|
render(<Harness start="clock" prefs={{ clockDate: true }} />);
|
|
const row = screen.getByRole("checkbox", { name: "plex.player.clockShow" }).closest("label")!;
|
|
expect(row.className).toContain("pointer-coarse:min-h-(--touch-target)");
|
|
for (const b of screen.getAllByRole("button")) expect(b.className).toContain("touch-target");
|
|
for (const t of screen.getAllByRole("tab")) expect(t.className).toContain("touch-target");
|
|
fireEvent.click(tab("Subtitles"));
|
|
const none = screen.getByRole("checkbox", { name: "plex.player.subBgNone" }).closest("label")!;
|
|
expect(none.className).toContain("pointer-coarse:min-h-(--touch-target)");
|
|
});
|
|
});
|