fix(ui): the page's end scrolls out from under Back to top

The button floats over the bottom-right corner, and at the end of a long table it covered the closing
pager's '100' and 'All'. While it is up, PageScroller ends the page a button's height later (only
then, so a full-height layout gets no scrollbar). Its shown state is a shared store read through a
use… wrapper: a store's .use() called straight from a component is memoized by the React Compiler
and never re-renders (contract: store.contract.test.ts).
This commit is contained in:
2026-09-27 16:28:18 +02:00
parent 51f881681b
commit 86884e368e
5 changed files with 73 additions and 6 deletions
+25
View File
@@ -0,0 +1,25 @@
import { readdirSync, readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
// A store's `.use()` is a hook the React Compiler cannot see: its name is a method's, not `use…`'s.
// Called straight from a COMPONENT, the compiled render memoized its result and never re-rendered on
// a change — Back to top stayed hidden (E28 UAT, measured under vitest's compiler build). Inside a
// function named `use…` it is compiled as a hook and works, so that is the only place it may be read.
describe("a store's use()", () => {
it("is read only inside a function named use…", () => {
const root = new URL("../", import.meta.url);
const offenders: string[] = [];
for (const f of readdirSync(root, { recursive: true, encoding: "utf8" })) {
if (!/\.tsx?$/.test(f) || f.includes(".test.")) continue;
const src = readFileSync(new URL(f.replace(/\\/g, "/"), root), "utf8");
for (const m of src.matchAll(/\b\w+\.use\(\)/g)) {
const before = src.slice(0, m.index);
const owners = [...before.matchAll(/(?:function\s+(\w+)|const\s+(\w+)\s*=\s*\()/g)];
const owner = owners.at(-1);
const name = owner?.[1] ?? owner?.[2] ?? "";
if (!name.startsWith("use")) offenders.push(`${f}: ${m[0]} in ${name || "?"}`);
}
}
expect(offenders).toEqual([]);
});
});