fix(ui): a read-ahead waits out a background refetch, and a revealed row is mounted before it is focused

NEXT_PAGE joins ANY fetch in flight: a read-ahead during an invalidation
or a poll joined that refetch and appended no page, and nothing asked
again until the reader moved the range (the channel page during a
backfill). The read-ahead now waits while the list fetches anything
(mayReadAhead, re-run on isFetching); a Load older click cancels a poll
instead (nextPageOnClick).

A page turn to a revealed row restarted the progressive mount at 30
rows; Channels focuses that row in the next commit, and at index 40 of
a 50-row page it was not there, so focus fell to <body>. The mount now
reaches through the revealed row.
This commit is contained in:
2026-10-02 23:42:31 +02:00
parent ac53a3b757
commit 15ec67b029
11 changed files with 165 additions and 28 deletions
@@ -100,4 +100,28 @@ describe("DataTable — a long page mounts in steps", () => {
expect(bodyRows()).toBe(FIRST_ROWS);
await waitFor(() => expect(bodyRows()).toBe(150));
});
// E31 S5: Channels turns the page to the row a priority pick moved and focuses it in the commit
// that follows. That row was at index 40 of a 50-row page: not mounted yet, focus fell to <body>.
it("mounts a revealed row in the render that turns to its page", () => {
host = document.createElement("div");
document.body.appendChild(host);
root = createRoot(host);
const rows = many(100);
const draw = (reveal: { key: string; seq: number } | null) =>
flushSync(() =>
root!.render(
<DataTable
rows={rows}
columns={plain}
rowKey={(x) => x.id}
pageSize={50}
reveal={reveal}
/>,
),
);
draw(null);
draw({ key: "r90", seq: 1 });
expect(host.querySelector("tbody")?.textContent).toContain("r 90");
});
});