feat(design): players on touch and the C5 control set, restart on the card (E28 S3f2)
Plex player: a finger's tap on the picture only shows or hides the controls, the hide timer starts with playback, a double-tap on a side third skips 10 s, the seek bar is a 2.75rem touch-none hit area with its head shown on touch. The row is play/pause, Back by position, Next only with a next item, mute, time, tracks, a fixed ⋯ (settings, media info, stats, shortcuts, download) and fullscreen; Stop, Restart and the ±10 s buttons are gone. A started card shows the video card's Continue + Restart pair (one shared MediaResumeButtons, beside the picture's button), and its menu lists the restart; the player opens that item at 0 (fromStart). YouTube modal: the description opens from an ⓘ, the queue arrows sit inside the screen.
This commit is contained in:
@@ -401,3 +401,45 @@ describe("PlayerModal — the watch toggle after a queue advance", () => {
|
||||
expect(screen.queryAllByTitle("player.watchedUnmark").length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
// C3: the description used to open on hovering the title, which a finger never does. It opens from
|
||||
// an ⓘ beside the title now, by click or tap, and a hover over the title opens nothing.
|
||||
describe("PlayerModal — the description", () => {
|
||||
it("opens from the ⓘ, not from hovering the title", async () => {
|
||||
vi.spyOn(api, "videoDetail").mockResolvedValue({
|
||||
description: "A long description",
|
||||
} as Awaited<ReturnType<typeof api.videoDetail>>);
|
||||
const A = vid("A", 0);
|
||||
renderModal(
|
||||
<PlayerModal video={A} queue={[A]} startIndex={0} onClose={() => {}} onState={() => {}} />,
|
||||
);
|
||||
fireEvent.mouseEnter(screen.getByRole("heading", { name: "Video A" }));
|
||||
fireEvent.pointerEnter(screen.getByRole("heading", { name: "Video A" }));
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
expect(screen.queryByText("A long description")).toBeNull();
|
||||
const info = screen.getByRole("button", { name: "player.description" });
|
||||
fireEvent.click(info);
|
||||
expect(await screen.findByText("A long description")).toBeTruthy();
|
||||
expect(info.getAttribute("aria-expanded")).toBe("true");
|
||||
});
|
||||
});
|
||||
|
||||
// C3: the queue's side arrows sit in a gutter of the card's row, not outside it, so no window cuts
|
||||
// them off (outside the card, a 1024 window cut them and a portrait tablet lost them).
|
||||
describe("PlayerModal — the side arrows", () => {
|
||||
it("stand inside the row's gutter", () => {
|
||||
vi.spyOn(api, "videoDetail").mockReturnValue(new Promise(() => {}));
|
||||
const A = vid("A", 0);
|
||||
const B = vid("B", 0);
|
||||
renderModal(
|
||||
<PlayerModal video={A} queue={[A, B]} startIndex={0} onClose={() => {}} onState={() => {}} />,
|
||||
);
|
||||
const arrow = [...document.querySelectorAll<HTMLElement>("[aria-label='player.next']")].find(
|
||||
(b) => b.className.includes("w-12"),
|
||||
)!;
|
||||
expect(arrow).toBeTruthy();
|
||||
expect(arrow.className).toMatch(/\bright-0\b/);
|
||||
expect(arrow.className).not.toMatch(/left-full/);
|
||||
expect(arrow.parentElement!.className).toMatch(/sm:px-14/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user