feat(plex): the person page in two columns; a bio on its way holds its place (E30 S6)
The biography sat at reading width in the left half of a full-width panel with nothing on the right. Now the person (headshot, facts, the bio clamped to eight lines with More / Less when it is cut) is on the left and "Known for" on the right; with fewer than four titles the rail takes only its posters' width. One column where the panel is narrow (200% zoom, the largest text size). While the server fetches a bio (`enriching`), the page shows a paragraph skeleton (a new "text" shape of LoadingState) and asks again every 1.5 s for up to 20 s. The view is keyed by the person's name, so another person starts fresh.
This commit is contained in:
@@ -186,3 +186,68 @@ describe("PlexPersonView — the reader's language (E30)", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("PlexPersonView — a bio on its way (E30 S6)", () => {
|
||||
const view = (
|
||||
<PlexPersonView
|
||||
name="Jane Doe"
|
||||
onBack={() => {}}
|
||||
onOpenCard={() => {}}
|
||||
onRestartCard={() => {}}
|
||||
onInfoCard={() => {}}
|
||||
onFilter={() => {}}
|
||||
/>
|
||||
);
|
||||
const person = (over: object) => ({
|
||||
name: "Jane Doe",
|
||||
photo: null,
|
||||
also_known_as: [],
|
||||
known_for: [],
|
||||
movies: [card("m1", "Movie One")],
|
||||
shows: [],
|
||||
...over,
|
||||
});
|
||||
|
||||
it("holds the bio's place and asks again until it lands", async () => {
|
||||
plexPerson.mockReset();
|
||||
plexPerson
|
||||
.mockResolvedValueOnce(person({ biography: null, enriching: true }))
|
||||
.mockResolvedValue(person({ biography: "Landed bio." }));
|
||||
wrap(view);
|
||||
await screen.findByText("Movie One");
|
||||
expect(screen.getByRole("status").textContent).toContain("plex.person.bioLoading");
|
||||
await screen.findByText("Landed bio.", undefined, { timeout: 3000 });
|
||||
expect(screen.queryByRole("status")).toBeNull();
|
||||
expect(plexPerson).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("does not ask again for a person whose bio is not coming", async () => {
|
||||
plexPerson.mockReset();
|
||||
plexPerson.mockResolvedValue(person({ biography: null, enriching: false }));
|
||||
wrap(view);
|
||||
await screen.findByText("Movie One");
|
||||
await new Promise((r) => setTimeout(r, 1800));
|
||||
expect(plexPerson).toHaveBeenCalledTimes(1);
|
||||
expect(screen.queryByRole("status")).toBeNull();
|
||||
});
|
||||
|
||||
it("offers More only when the bio is cut, and Less once open", async () => {
|
||||
const tall = vi.spyOn(HTMLElement.prototype, "scrollHeight", "get").mockReturnValue(400);
|
||||
const box = vi.spyOn(HTMLElement.prototype, "clientHeight", "get").mockReturnValue(160);
|
||||
try {
|
||||
plexPerson.mockReset();
|
||||
plexPerson.mockResolvedValue(person({ biography: "A long life.\n\nMany films." }));
|
||||
wrap(view);
|
||||
fireEvent.click(await screen.findByText("plex.person.more"));
|
||||
expect(screen.getByText("plex.person.less").getAttribute("aria-expanded")).toBe("true");
|
||||
} finally {
|
||||
tall.mockRestore();
|
||||
box.mockRestore();
|
||||
}
|
||||
cleanup();
|
||||
plexPerson.mockResolvedValue(person({ biography: "Short." }));
|
||||
wrap(view);
|
||||
await screen.findByText("Short.");
|
||||
expect(screen.queryByText("plex.person.more")).toBeNull(); // jsdom: nothing is cut
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user