Files
siftlode/frontend/src/components/PlexViewToggle.dom.test.tsx
T
peter dddaeb9a38 fix(plex): make the header Discover/Browse toggle clickable
The floating header wrapper is pointer-events-none (so clicks fall through its empty
area to the content below); every interactive child must re-enable pointer events.
PlexViewToggle rendered its SegmentedToggle without the pointer-events-auto wrapper the
sibling PlexScopeToggle has, so the whole Discover/Browse switch was click-dead — the
buttons rendered and were correctly wired, but no real mouse click ever reached them.

Add `className="pointer-events-auto shrink-0"` to the toggle and a regression test that
asserts the root re-enables pointer events (plus the render/onChange/hide-on-deeper-view
contract). Verified live on localdev: real clicks now switch Discover⟷Browse (?v=browse)
in both directions; a header-wide hit-test audit shows no other dead controls.

Gate: tsc/lint/knip/prettier clean, 418 vitest.
2026-08-23 02:23:09 +02:00

66 lines
2.7 KiB
TypeScript

// @vitest-environment jsdom
//
// Contract of the header Discover ⟷ Browse toggle. The floating header wrapper is pointer-events-none,
// so the toggle MUST re-enable pointer events on its root or it is click-dead (the bug this test guards
// against). Also: it only renders on the two title-browse views, marks the active one, and drives
// PlexBrowse's sub-view through the bridge. i18n is key-passthrough; the bridge is mocked.
import { describe, it, expect, afterEach, beforeEach, vi } from "vitest";
import { render, screen, fireEvent, cleanup } 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 }) };
});
const controls = { kind: "discover" as string, open: vi.fn() };
vi.mock("./PlexViewBridge", () => ({
usePlexViewControls: () => controls,
}));
import PlexViewToggle from "./PlexViewToggle";
beforeEach(() => {
controls.kind = "discover";
controls.open.mockReset();
});
afterEach(cleanup);
describe("PlexViewToggle", () => {
it("re-enables pointer events on its root (the header wrapper is pointer-events-none)", () => {
const { container } = render(<PlexViewToggle />);
// Without this class the whole toggle is click-dead inside the pointer-events-none header.
expect(container.querySelector(".pointer-events-auto")).not.toBeNull();
});
it("renders the two options with the current view pressed", () => {
render(<PlexViewToggle />);
const btns = [
...screen.getByRole("group", { name: "plex.view.label" }).querySelectorAll("button"),
];
expect(btns.map((b) => b.textContent)).toEqual(["plex.discover.tab", "plex.view.browse"]);
const pressed = btns
.filter((b) => b.getAttribute("aria-pressed") === "true")
.map((b) => b.textContent);
expect(pressed).toEqual(["plex.discover.tab"]);
});
it("opens the grid view when Browse is picked", () => {
render(<PlexViewToggle />);
fireEvent.click(screen.getByRole("button", { name: "plex.view.browse" }));
expect(controls.open).toHaveBeenCalledWith({ kind: "grid" });
});
it("opens the discover view when Discover is picked from the grid", () => {
controls.kind = "grid";
render(<PlexViewToggle />);
fireEvent.click(screen.getByRole("button", { name: "plex.discover.tab" }));
expect(controls.open).toHaveBeenCalledWith({ kind: "discover" });
});
it("renders nothing on a deeper view (no Discover/Browse choice there)", () => {
controls.kind = "playlists";
const { container } = render(<PlexViewToggle />);
expect(container.firstChild).toBeNull();
});
});