feat(design): one media card, and Home's hero and shelves as cards (E28 S3h)
- ui/MediaCard + mediaCardStyles: one frame for every video, Plex title and Home tile; the meta line pinned to the bottom edge, the length a badge on the picture, the media Play only where the click plays, the hover row at the picture's bottom-right (the owner's pick C). - Shelf density: on any strip a one-line title over one meta line; mixed Home shelves are landscape; rails turn content-visibility off (a card's reserved height had become the strip's). Home slot 232px, so the Play and the hover row no longer touch; a landscape shelf takes the small Play. - Home hero is a card (hover row, the whole menu, a touch "…"), three quarters wide and centred; channel / playlist tiles get Play, open and "…"; a show tile acts on its next episode, ⓘ opens the show. - Plex preview: a ~0.9 s mouse rest opens a passive panel with the whole title, facts and synopsis (popover `pointerOnly`); the synopsis and title tooltips are gone. - Compact list row B: the title leads, "…" at the end, toggles over the date on hover. - Media-info layout B (collections, crew, cast beside the facts sidebar; an empty column is not drawn); filtering values are chips there and on the show page; "Cast" over the cast-only strips. - Browse tiles hover like a card; prose capped by the `measure` utility.
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
// a Carousel (useHScroll → ResizeObserver), stubbed for jsdom.
|
||||
import { StrictMode } from "react";
|
||||
import { describe, it, expect, afterEach, beforeAll, beforeEach, vi } from "vitest";
|
||||
import { act, render, screen, fireEvent, cleanup, waitFor } from "@testing-library/react";
|
||||
import { act, render, screen, fireEvent, cleanup, waitFor, within } from "@testing-library/react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { qk } from "../lib/queryKeys";
|
||||
|
||||
@@ -785,3 +785,58 @@ describe("Home · keyboard", () => {
|
||||
expect(rails.map((rail) => rail.querySelectorAll("[data-nav-index]").length)).toEqual([2, 2]);
|
||||
});
|
||||
});
|
||||
|
||||
// E28 S3h (the owner, 2026-09-26/27): the hero stands in three quarters of a wide page with its text
|
||||
// centred beside the picture; one fact line, no repeated year, and prose only where there is some,
|
||||
// at a readable measure.
|
||||
describe("Home's hero text", () => {
|
||||
const film = {
|
||||
source: "plex",
|
||||
kind: "movie",
|
||||
id: "m1",
|
||||
title: "A Film",
|
||||
subtitle: "2003",
|
||||
thumb: null,
|
||||
aspect: "poster",
|
||||
duration_seconds: 9240,
|
||||
position_seconds: 0,
|
||||
remaining_seconds: null,
|
||||
status: "new",
|
||||
video: null,
|
||||
plex: {
|
||||
id: "m1",
|
||||
type: "movie",
|
||||
title: "A Film",
|
||||
thumb: "",
|
||||
year: 2003,
|
||||
rating: 7.8,
|
||||
summary: "What the film is about.",
|
||||
},
|
||||
};
|
||||
|
||||
it("a film: the year once, in the fact line with the length and score; its summary at the measure", async () => {
|
||||
discover.mockResolvedValue({ ...payload("v1"), hero: { tier: "surprise", card: film } });
|
||||
mount();
|
||||
const title = await screen.findByRole("heading", { name: "A Film" });
|
||||
const text = title.parentElement!;
|
||||
expect(within(text).getByText(/^2003 · \S/)).toBeTruthy();
|
||||
expect(within(text).getByText("7.8")).toBeTruthy();
|
||||
// the subtitle was the year alone: it is not printed a second time
|
||||
expect(within(text).queryByText("2003")).toBeNull();
|
||||
expect(within(text).getByText("What the film is about.").className).toContain("measure");
|
||||
expect(title.closest("section")!.className).toContain("lg:w-3/4");
|
||||
});
|
||||
|
||||
it("a video: the channel as the subtitle, views and age as the facts, and no summary line", async () => {
|
||||
discover.mockResolvedValue({
|
||||
...payload("v1"),
|
||||
hero: { tier: "surprise", card: ytCard("v9", "A video") },
|
||||
continue_watching: [],
|
||||
});
|
||||
mount();
|
||||
const text = (await screen.findByRole("heading", { name: "A video" })).parentElement!;
|
||||
expect(within(text).getByText("A channel")).toBeTruthy();
|
||||
expect(within(text).getByText(/^10 card\.views · /)).toBeTruthy();
|
||||
expect(text.querySelector("p.measure")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user