fix(ui): review round 2 — reveal floor, failed next page, count states, row Play
- DataTable: the reveal floor holds only in the view it was made for (Channels never clears its follow, so every later sort mounted rows up to it) - Feed + Plex browse: a failed next page stops the read-ahead and shows a row that retries, instead of re-requesting it once a round-trip - the YouTube Load more click uses nextPageOnClick; a contract pins NEXT_PAGE to the read-aheads and the click sites to nextPageOnClick - ItemCount: a failed count says so; a stale one says updating to a screen reader - the narrow-picture Play lift applies only where a hover row is there to clear - PlayerModal: one description ladder for the popover and the panel, both tested - plexImage contract reads every img src, any attribute order - Tabs comment names who persists the sub-tab
This commit is contained in:
@@ -29,4 +29,34 @@ describe("an infinite list's read-ahead", () => {
|
||||
it("the feed hands the grid the query's isFetching", () => {
|
||||
expect(source("Feed.tsx")).toMatch(/isFetching=\{query\.isFetching\}/);
|
||||
});
|
||||
|
||||
// Retry is off, so a failed page settles at once; a read-ahead that re-runs on the settle would
|
||||
// request it again once a round-trip while the end is in view (E31 review 2).
|
||||
it("stands down at a failed next page", () => {
|
||||
expect(source("Feed.tsx")).toMatch(
|
||||
/hasNextPage=\{!!hasNextPage && !query\.isFetchNextPageError\}/,
|
||||
);
|
||||
expect(source("PlexBrowse.tsx")).toMatch(
|
||||
/const hasNextPage = browseQ\.hasNextPage && !isFetchNextPageError;/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// A click joins an in-flight next page but cancels a background refetch (nextPageOnClick): through
|
||||
// NEXT_PAGE it would join the refetch and load nothing. NEXT_PAGE is only for the read-aheads, so a
|
||||
// click site that goes back to it fails here (E31 review 2: the YouTube "Load more" was missed).
|
||||
describe("a click that loads the next page", () => {
|
||||
const READ_AHEADS = new Set(["ui/VirtualGrid.tsx", "PlexBrowse.tsx"]);
|
||||
it("uses nextPageOnClick; NEXT_PAGE only in the read-aheads", () => {
|
||||
const users = Object.entries(files)
|
||||
.filter(([, src]) => /fetchNextPage\(NEXT_PAGE\)/.test(src as string))
|
||||
.map(([path]) => path.replace("../components/", ""));
|
||||
expect(users.sort()).toEqual([...READ_AHEADS].sort());
|
||||
for (const [file, sites] of [
|
||||
["AdminLogs.tsx", 2],
|
||||
["Feed.tsx", 2],
|
||||
["PlexBrowse.tsx", 1],
|
||||
] as const)
|
||||
expect(source(file).match(/fetchNextPage\(nextPageOnClick\(/g)?.length, file).toBe(sites);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,8 +47,10 @@ it("names each backdrop cut by the width the proxy cuts it at", () => {
|
||||
|
||||
// Every small Plex picture asks for its card-size cut through cardImage (E31 S4e, swept in S5: the
|
||||
// playlist rows, the search box and the collection picker still fetched the 400px poster for a 32px
|
||||
// picture). A bare `src={…thumb}` is allowed only where the full cut is the point or the URL is not
|
||||
// the proxy's; the SET is pinned, so a new bare one fails here.
|
||||
// picture). A plain `src={…}` is allowed only where the full cut is the point or the URL is not the
|
||||
// proxy's; the SET is pinned, so a new one fails here. Every <img>'s src counts, wherever it sits
|
||||
// among the attributes and whatever its variable is called: the first cut of this test read only a
|
||||
// leading `src={…thumb…}` and missed two of the three sites it was written for (E31 review 2).
|
||||
const plexFiles = import.meta.glob(
|
||||
[
|
||||
"../components/{Plex,plex}*.tsx",
|
||||
@@ -58,7 +60,10 @@ const plexFiles = import.meta.glob(
|
||||
{ query: "?raw", import: "default", eager: true },
|
||||
);
|
||||
const ALLOWED = new Set([
|
||||
"plexCards.tsx pic.src", // cardImage's own src (srcSet/sizes beside it): the card cut
|
||||
"plexCards.tsx c.thumb", // cast photos: the person-image route, not the image proxy
|
||||
"PlexBrowse.tsx p.photo", // the person filter chip's headshot: the person-image route
|
||||
"plexViews.tsx d.photo", // the person page's headshot: the same
|
||||
"PlexInfo.tsx c.thumb", // the same cast photos on the detail page
|
||||
"PlexInfo.tsx detail.thumb", // the detail page's own poster: the full cut is the point
|
||||
"plexDetailUi.tsx thumb", // the show/season header poster: the same
|
||||
@@ -69,7 +74,7 @@ it("leaves no small Plex picture on the full cut", () => {
|
||||
expect(Object.keys(plexFiles).length).toBeGreaterThan(5);
|
||||
const bare = new Set<string>();
|
||||
for (const [path, src] of Object.entries(plexFiles))
|
||||
for (const m of (src as string).matchAll(/<img\s+src=\{([^}]*thumb[^}]*)\}/g))
|
||||
for (const m of (src as string).matchAll(/<img\b[^>]*?\bsrc=\{([^}]*)\}/g))
|
||||
bare.add(`${path.split("/").pop()} ${m[1]!.trim()}`);
|
||||
expect([...bare].filter((b) => !ALLOWED.has(b))).toEqual([]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user