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:
2026-10-03 02:25:15 +02:00
parent c4c442bc5b
commit b40440b825
17 changed files with 360 additions and 63 deletions
@@ -124,4 +124,32 @@ describe("DataTable — a long page mounts in steps", () => {
draw({ key: "r90", seq: 1 });
expect(host.querySelector("tbody")?.textContent).toContain("r 90");
});
// E31 review 2: the reveal floor held for the page's lifetime. Channels never clears its `follow`, so
// after one priority pick every later sort or search mounted every row up to that channel at once.
it("lets a revealed row go once the reader moves to another view", () => {
host = document.createElement("div");
document.body.appendChild(host);
root = createRoot(host);
const rows = many(200);
const draw = (reveal: { key: string; seq: number } | null, narrowedBy: string) =>
flushSync(() =>
root!.render(
<DataTable
rows={rows}
columns={plain}
rowKey={(x) => x.id}
pageSize={0}
reveal={reveal}
narrowedBy={narrowedBy}
/>,
),
);
draw(null, "");
const follow = { key: "r150", seq: 1 };
draw(follow, "");
expect(host.querySelector("tbody")?.textContent).toContain("r 150");
draw(follow, "s"); // a search: a new view, the same (never cleared) reveal
expect(bodyRows()).toBe(FIRST_ROWS);
});
});