perf(pokedex): send the box grid as packed rows with the page

The server load fetched a full combined-data page at a 9999 item page size,
carrying detail text and ownership fields the grid never renders, and the client
re-fetched the same payload after hydration.

Load a trimmed grid row instead and pack it as positional tuples so field names
are not repeated for every one of a thousand-plus entries. Entry detail is
fetched on demand from the new per-entry endpoint when a cell is opened, and the
grid marks itself interactive so other page-start work can queue behind it.
This commit is contained in:
Josh Creek
2026-09-15 17:46:19 +01:00
parent 4c268ba15c
commit acaf760b36
16 changed files with 1382 additions and 444 deletions
+4 -3
View File
@@ -23,7 +23,8 @@ When('I load the Pokédex page directly', async ({ page, state }) => {
// Only requests made while the page first loads matter; the page's 60s reconciliation refetch
// can't fire within this window.
const countEntryRequests = (request: { url(): string }) => {
if (/\/api\/pokedexes\/[^/]+\/combined-data/.test(request.url())) record.entryRequests++;
if (/\/api\/pokedexes\/[^/]+\/(?:grid|combined-data)/.test(request.url()))
record.entryRequests++;
};
page.on('request', countEntryRequests);
@@ -40,8 +41,8 @@ Then('its entries appear within {int} seconds', async ({ page }, seconds: number
expect(entriesMs!).toBeLessThan(seconds * 1000);
});
Then('the browser did not request the entries separately', async ({ page }) => {
// The server load streams the first page of entries with the HTML, so the page must not make
Then('the browser did not request the grid separately', async ({ page }) => {
// The server load includes the compact grid with the HTML, so the page must not make
// the old hydrate-then-fetch round trip.
expect(timings.get(page)?.entryRequests).toBe(0);
});