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:
@@ -91,3 +91,65 @@ describe.each(Object.keys(FEED_VIEW_SPEC) as FeedView[])("the %s view's actions"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// E28 S3h, the owner's pick B (2026-09-26): the compact row had a leading action column that sat
|
||||
// empty at rest. Now the title leads, the "…" is always at the row's end, and on hover the quick
|
||||
// toggles float in beside it over the date, which steps aside.
|
||||
describe("the compact row (rowsCompact)", () => {
|
||||
it("leads with the title and ends with the '…'; the toggles reveal beside it over a yielding date", () => {
|
||||
renderCard("rowsCompact");
|
||||
const row = screen.getByTestId("video-card");
|
||||
expect(row.firstElementChild!.textContent).toBe("Cicada 3301");
|
||||
const more = screen.getByRole("button", { name: "card.menu.more" });
|
||||
// the layer that floats left of the "…" (right-full) and shows on hover only
|
||||
const toggles = screen
|
||||
.getByRole("button", { name: "card.markWatched" })
|
||||
.closest<HTMLElement>(".right-full");
|
||||
expect(toggles!.className).toContain("reveal-hover");
|
||||
// the toggles and the "…" share the row's last slot (the resume bar is drawn after it)
|
||||
const slot = more.parentElement!;
|
||||
expect(slot.contains(toggles)).toBe(true);
|
||||
const inFlow = [...row.children].filter((c) => !c.className.includes("absolute"));
|
||||
expect(inFlow[inFlow.length - 1]).toBe(slot);
|
||||
expect(row.querySelector(".w-48")!.className).toContain("group-hover:opacity-0");
|
||||
});
|
||||
});
|
||||
|
||||
// On a shelf (Home's strips) the card is one title line over the channel alone, so every card on
|
||||
// the strip is the same height (E28 S3h, the owner's shelf density).
|
||||
describe("the card on a shelf", () => {
|
||||
function renderSmall(shelf: boolean) {
|
||||
render(
|
||||
<QueryClientProvider client={new QueryClient()}>
|
||||
<VideoCard
|
||||
video={video}
|
||||
view="cardsSmall"
|
||||
width={0}
|
||||
shelf={shelf}
|
||||
onState={() => {}}
|
||||
onToggleSave={() => {}}
|
||||
/>
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
return screen.getByTestId("video-card");
|
||||
}
|
||||
const titleOf = (card: HTMLElement) =>
|
||||
[...card.querySelectorAll("*")].find(
|
||||
(e) => e.childElementCount === 0 && e.textContent === "Cicada 3301",
|
||||
)!;
|
||||
|
||||
it("cuts the title to one line and drops the views · age line", () => {
|
||||
const card = renderSmall(true);
|
||||
const title = titleOf(card).closest(".font-medium")!;
|
||||
expect(title.className).toContain("truncate");
|
||||
expect(title.className).not.toContain("line-clamp-2");
|
||||
expect(card.querySelector("span[title]")).toBeNull();
|
||||
expect(screen.getByRole("link", { name: "LEMMiNO" })).toBeTruthy();
|
||||
});
|
||||
|
||||
it("off a shelf keeps two title lines and the age", () => {
|
||||
const card = renderSmall(false);
|
||||
expect(titleOf(card).closest(".font-medium")!.className).toContain("line-clamp-2");
|
||||
expect(card.querySelector("span[title]")).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user