Skip to content

refactor(react)!: autosizeColumns becomes setAllColumnsAutoWidth, and one default column width - #542

Merged
blove merged 5 commits into
mainfrom
blove/auto-width-naming-cleanup
Aug 31, 2026
Merged

refactor(react)!: autosizeColumns becomes setAllColumnsAutoWidth, and one default column width#542
blove merged 5 commits into
mainfrom
blove/auto-width-naming-cleanup

Conversation

@blove

@blove blove commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

The rename

The grid handle's autosizeColumns() never sized anything to content. It put every column into the auto-width set — the mode bit meaning "the grid manages this column's width". Nothing in the column-width path measures a cell, anywhere (verified in a browser during the SP5 arc). The name promised a feature the library does not have.

It is now grid.setAllColumnsAutoWidth(auto: boolean), symmetric with the per-column setColumnAutoWidth(columnId, auto) that shipped with the tool panel, and — unlike the old name — able to express the other direction: false freezes every column at the engine's stored width. The surface prop autosize?: boolean | AutosizeOptions becomes allColumnsAutoWidth?: boolean, and AutosizeOptions is deleted from @pretable/core and @pretable/react: every field on it (averageCharWidth, cellPaddingPx, min/max) tuned a measurement pass that does not exist.

Also fixed in passing: double-clicking a column's resize handle was wired to an empty function body. It now calls setColumnAutoWidth(columnId, true) — the pointer shortcut for handing that column's width back to the grid, which is what the docs claimed all along.

One default column width

An undeclared-width column was drawn by renderer-dom at 140px but stored by grid-core at 160px. Turning auto width off on a never-resized column therefore jumped it 140 → 160 for no reason a user could see — a jump SP5 had to document as expected behavior.

Both numbers now come from one home, packages/layout-core/src/column-defaults.ts (DEFAULT_COLUMN_WIDTH_PX = 140, DEFAULT_WRAPPED_COLUMN_WIDTH_PX = 220), and @pretable/react seeds the engine's stored width through renderer-dom's own resolveColumnWidth. The stored width and the drawn width of a never-resized column are now the same number by construction, so the freeze moves no pixel.

Why 140 and not 160. 140 is what every undeclared-width column has always actually painted at. Moving the renderer to 160 instead would have repainted every example, bench scenario, and docs fixture that leaves widths undeclared; moving the engine to 140 repaints nothing.

Dependency direction. grid-core already declares @pretable-internal/layout-core: workspace:*, so the new import edge is one the manifest already allowed — no duplicated constants, no as any bridge. pnpm build, pnpm consumer:check and pnpm react:compat all accept it.

The phantom docs

grid/column-layout.mdx had an "Autosize" section teaching grid.autosizeColumn(columnId, options?) and grid.autosizeColumns(options?) as fits to measured content width, plus an AutosizeOptions paragraph about sample size and padding. No handle has ever carried either method. Its "Reset" section documented grid.resetColumnLayout(), which has also never existed — the only reset the library ships is the tool panel's Reset columns button.

The section is now "Auto width", written in the mode-bit register tool-panel.mdx already uses, and the Reset section points at what actually exists. The SP5 jump paragraph in tool-panel.mdx now records the jump's absence.

The guard finding

Three phantom grid. methods survived every existing docs guard, because the guards' vocabulary stops short of them: the import sweep reads import { X } from "@pretable/*", and the prose sweep matches Pretable[A-Z]…, i.e. type names. A lowercase method call in a sentence or a fence is neither.

Extended honestly, in docs-api-surface.test.ts: a new sweep matches grid.<name>( across every docs page and requires the name to be declared as a member of some exported interface or object type alias, pooled across every reported package. It deliberately does not ask which handle the surrounding prose holds — a page may be writing about PretableReactGrid, PretableSurfaceGrid, or the headless PretableGridUiCore, and deciding that from prose is a reader's job, not a regex's. Erring wide is the conservative direction: the guard is an authority on whether a method is real, not on where it lives. Scoping to a grid. receiver plus a following ( holds the false-positive rate at zero on the current corpus (pretable/grid.css, 31 hits, and gridRef.current are not calls). It fails closed twice — an empty member vocabulary would pass everything, an empty corpus would check nothing.

It found two more real defects on its first run, beyond the ones this PR set out to fix: grid/editing.mdx read grid.getSnapshot().editing (the handle's reader is getState), and grid/keyboard.mdx called grid.selectAll() (it is selectAllVisibleRows). Both fixed.

Mutation-proven: renaming a documented call to grid.fitColumnToContent(columnId) fails the guard by name and page; reverting turns it green.

Test plan

  • pnpm buildpnpm apipnpm api:check green. Report moves are exactly the expected ones: AutosizeOptions removed from both public reports, autosizeColumnssetAllColumnsAutoWidth, autosizeallColumnsAutoWidth.
  • pnpm test (root, turbo) green. Package counts: react 116 files / 1644 tests, website guards 104 files / 607 tests, core 15 / 181, grid-core 6 / 169, renderer-dom 9 / 168, layout-core 8 / 157, ui 4 / 99.
  • pnpm lint, pnpm typecheck, pnpm format, pnpm consumer:check, pnpm react:compat — all green.
  • E2E: prod build + next start, tool-panel.spec.ts and grid-tab-wrap-rows.spec.ts with --workers=152 passed, chromium and webkit.
  • Re-verified end-to-end after rebasing onto current origin/main.

Pins that moved

Three, all of them the 160 that no longer exists, each rewritten to state the absence of the jump rather than deleted:

  • packages/core/.../create-grid.test.ts — the engine's stored default for an undeclared column, 160 → 140.
  • packages/grid-core/.../grid-ui-core.test.ts — same, now reading DEFAULT_COLUMN_WIDTH_PX from layout-core rather than re-typing the number, with the constant's value asserted separately.
  • packages/react/.../column-auto-width.test.tsx and tool-panel.test.tsx — these used 140-vs-160 as the observable for auto-set membership, which the unification takes away. Membership is now made observable by writing a width that differs from any default (200), and the old jump's site asserts the freeze is a no-op pixel. A new test pins the double-click gesture.

No e2e pin moved: the SP5 specs measure a declared-width 110 column against the 140 renderer default, and neither number changed.

🤖 Generated with Claude Code

blove and others added 4 commits August 30, 2026 16:03
… one default column width

`autosizeColumns()` never sized anything to content: it put every column
into the auto-width SET — the mode bit meaning "the grid manages this
column's width". Nothing in the column-width path measures a cell, so the
name promised a feature the library does not have. It is now
`setAllColumnsAutoWidth(auto)`, symmetric with the per-column
`setColumnAutoWidth(columnId, auto)` and, unlike the old name, able to
express the other direction — `false` freezes every column at the engine's
stored width. The surface prop `autosize?: boolean | AutosizeOptions`
becomes `allColumnsAutoWidth?: boolean`, and `AutosizeOptions` is deleted
from @pretable/core and @pretable/react: every field on it tuned a
measurement pass that does not exist.

The second half is the constant. An undeclared-width column was DRAWN by
renderer-dom at 140px and STORED by grid-core at 160px, so turning auto
width off on a never-resized column jumped it 140 -> 160. Both now read
`DEFAULT_COLUMN_WIDTH_PX` (140, and 220 wrapped) from the new
layout-core/src/column-defaults.ts, and @pretable/react seeds the engine's
stored width through renderer-dom's own `resolveColumnWidth` — so the
stored width and the drawn width of a never-resized column are the same
number by construction, and the freeze moves no pixel. 140 won because it
is what such columns have always painted at; moving the renderer to 160
would have repainted every example, bench scenario, and docs fixture that
leaves widths undeclared. grid-core already depended on layout-core, so
the new edge is one the manifest already allows.

Also: double-clicking a column's resize handle was wired to an empty
function. It now calls `setColumnAutoWidth(columnId, true)` — the pointer
shortcut for handing that column's width back to the grid.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…er existed

grid/column-layout.mdx's "Autosize" section taught
`grid.autosizeColumn(columnId, options?)` and `grid.autosizeColumns(options?)`
as fits to measured content width, with an `AutosizeOptions` paragraph
about sample size and padding. No handle has ever carried either method,
and the width path measures nothing — a reader who typed the line got a
compile error out of the page that taught it. Its "Reset" section
documented `grid.resetColumnLayout()`, which has also never existed: the
only reset the library ships is the tool panel's Reset columns button.

The section is now "Auto width" and describes the mode bit in the register
tool-panel.mdx already uses — on means the grid manages the width (the
renderer's default, or a flex share), off means manual at the engine's
stored width, and nothing measures cell content anywhere. With the width
defaults unified, turning auto off on a never-resized column no longer
jumps, and the page says so.

Two more phantoms the new guard found on other pages: grid/editing.mdx
read `grid.getSnapshot().editing` (the handle's reader is `getState`) and
grid/keyboard.mdx called `grid.selectAll()` (it is
`selectAllVisibleRows`). Both fixed. The grid index's "not yet documented"
list advertised a content-resizing `autosize` option on `usePretable`;
that entry is gone rather than deferred, because the capability it
promises is not deferred, it is imaginary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three phantom `grid.` methods survived every existing check because the
guards' vocabulary stops short of them: the import sweep reads
`import { X } from "@pretable/*"`, and the prose sweep matches
`Pretable[A-Z]...`, i.e. TYPE names. A lowercase method call in a sentence
or a fence is neither.

The new sweep matches `grid.<name>(` across every docs page and requires
the name to be declared as a member at the top level of some exported
interface or object type alias, pooled across every reported package. It
deliberately does not ask WHICH handle the surrounding prose holds — a
page may be writing about `PretableReactGrid`, `PretableSurfaceGrid`, or
the headless `PretableGridUiCore`, and deciding that from prose is a
reader's job, not a regex's. Erring wide is the conservative direction:
the guard is an authority on whether a method is REAL, not on where it
lives. Scoping to a `grid.` receiver plus a following `(` is what holds
the false-positive rate at zero on the current corpus — `pretable/
grid.css` (31 hits) and `gridRef.current` are not calls.

It fails closed twice: an empty member vocabulary would pass everything
and an empty corpus would check nothing, so both are asserted non-trivial.
Mutation-proven: renaming a documented call to
`grid.fitColumnToContent(columnId)` fails the guard by name and page, and
reverting turns it green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`AutosizeOptions` leaves both public reports, `autosizeColumns` becomes
`setAllColumnsAutoWidth`, and the surface's `autosize` prop becomes
`allColumnsAutoWidth`. `pnpm api:check` is green against the regenerated
reports.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
pretable Ignored Ignored Aug 30, 2026 11:48pm

Request Review

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Vercel preview ready

Preview: https://pretable-g9i0jynvn-cacheplane.vercel.app
Commit: 33618bf8850e6c99e5b20be1c9a4a1fff0cdabc7

Updated automatically by the deploy-preview job.

`setColumnWidth` cleared a column's auto bit unconditionally, and a
consumer with a controlled `state.columnWidths` replays its whole widths
map through `setColumnWidth` on every write-back pass. So ANY re-render of
such a consumer silently took every column in the map back out of the auto
set. Both entry points to the mode bit were defeated in practice: the tool
panel's Auto width toggle and the resize handle's double-click each set the
bit and had it un-set before paint. Reproduced deterministically against
the shape of the column-layout docs example — the very example the new
prose points a reader at.

The bit is now cleared only when the write actually MOVES the stored
width. The comparison reads grid-core's store on both sides rather than
comparing to the argument, because grid-core clamps against the column's
min/max: a request that clamps back onto the current width is not a move
either.

The double-click no longer fires `onColumnWidthsChange`. That callback
reports the ENGINE's stored widths and this gesture moves none of them —
it announced a change that had not happened, and under a controlled
consumer the announcement came straight back as the `setColumnWidth`
replay above.

The controlled round trip is pinned beside the double-click test, and
mutation-checked: reverting the guard fails it with `expected 120 to be
140`, the declared width reasserted over the renderer's default.

The unconditional clear predates this branch — it shipped with the tool
panel's auto-width toggle — so the changeset discloses it as a
user-visible fix rather than a consequence of the rename. Also softens the
new docs guard's failure message: an author whose fence legitimately binds
a local `grid` is now told to rename the variable rather than sent to
regenerate a report.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@blove
blove merged commit d53bc8d into main Aug 31, 2026
21 checks passed
@blove
blove deleted the blove/auto-width-naming-cleanup branch August 31, 2026 00:11
@blove blove mentioned this pull request Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant