aad2c292 fix(table-core): correct expanded/paginated state contents and sorting toggle defaults (#6501)
Two independent clusters of default-behavior bugs from the beta triage.
Expanded and paginated state contents:
Expand-all materialized every id in `rowsById`, including leaf rows that
can never expand, so a serialized `ExpandedState` was polluted with dead
keys and `getExpandedDepth` was skewed by them. Materialization now writes
only ids where `row.getCanExpand()` is true, chosen over `subRows.length`
so `getRowCanExpand` lazy-load overrides stay expandable.
`getExpandedDepth` filters the same way, and `getIsAllRowsExpanded` now
only considers expandable rows so the materialized map still round-trips
as "all expanded" (a map of stale ids with no expandable rows is false).
Paginated `flatRows` pushed each row and then recursed into its subRows,
but page rows already contain expanded descendants inline, so those rows
appeared twice. The rebuild now dedupes by row id. Collapsed descendants
stay included, consistent with every other row model, where `flatRows`
ignores expansion state.
`row.toggleExpanded(bool)` and `table.toggleAllRowsExpanded(bool)` fired
`onExpandedChange` even when the requested state already matched, so
controlled consumers got spurious callbacks. Both now early-return.
Neither toggle consulted `row.getCanExpand()`, unlike their row-selection
equivalents, so a non-expandable row could be written into expanded state
imperatively. The expand direction is now guarded; collapsing is always
allowed so stale expanded ids can still be cleaned up.
Sorting toggle defaults:
`column.toggleSorting()` called `column.getNextSortingOrder(column)` with
no `multi` argument, so `enableMultiRemove` was dead on every path. The
argument is now forwarded as `multi && column.getCanMultiSort()`, matching
the multi-mode condition the updater itself uses. The public
`getNextSortingOrder` type now accepts the `multi` argument it always
supported at runtime.
`column.getAutoSortDir()` sampled only `flatRows[0]`, so a leading null or
a manual-sorting data swap flipped the inferred first direction mid-cycle
and silently dropped a state from the toggle cycle. It now samples the
first 10 rows for a non-nullish value, matching `getAutoSortFn`.
Docs: `sortUndefined: false` was documented as "considered tied" in all
framework sorting guides; undefined values are actually passed straight to
the sorting function with no special handling. Corrected, and the missing
`'first'`/`'last'` bullets were added to the generated reference.
Also fixes three crashes in the MRT examples surfaced while testing the
sorting changes. The MUI and Mantine sort labels read `sorting` off
`state`, which does not carry it, and crashed on `sorting.length`; they now
read it through `table.Subscribe` on `table.atoms.sorting`, which also
makes the badge reactive. The row-action edit handler passed a shallow row
copy to `setEditingRow`, breaking the row identity the edit modal needs.
`mrtRefsFeature` seeded the keyed `editInputRefs`/`filterInputRefs` bags
with `null` instead of `{}`, crashing on first keyed write. New e2e
coverage pins all three plus the sort-direction indicator.
Closes #6115
Closes #5833
Closes #6136
Closes #4939
Closes #4946
Closes #5147
Closes #5832
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> 8c9df9c2 fix(table-core): wire expansion auto-reset into the core row model and guard first runs (#6499)
Auto-resets are `onAfterUpdate` hooks on the row model stage memos. Two
related defects made them fire in the wrong places.
Expansion auto-reset was only wired from the grouped row model, so
`autoResetExpanded` silently did nothing for tables that use expansion
without grouping (#5801). It is now wired from `createCoreRowModel`
alongside the existing pageIndex, sorting, and cell-selection resets, so
a data reference change resets expansion regardless of which features are
installed. `table_autoResetExpanded` gained the same feature guard the
sorting and cell-selection resets already use, since the core row model
runs on tables without the expanding feature.
Auto-resets also fired on the very first computation of every stage
(#5968). `memo` seeds its dependency list to `[]`, so the initial run
always compares as changed, and merely reading a row model on mount
scheduled resets. For uncontrolled tables this was self-cancelling (the
reset targets `initialState`), which is why it went unnoticed, but it
wiped a seeded `initialState.pagination.pageIndex` and pushed unsolicited
`onExpandedChange` / `onPaginationChange` calls at controlled consumers on
mount. v7 and v8 suppressed the first run with a `registered` flag; the
v9 rewrite dropped it.
A new `skipFirstRun` util restores that suppression. It is applied in the
row model factories, which already run once per table, so each table gets
its own flag. The grouped row model tracked previous inputs already and
only needed its first-run condition inverted. The worker bridge reports
every stage as changed in its first response, so it carries an equivalent
`hasAppliedResults` flag.
Landing order matters: wiring expansion into the core row model without
the first-run guard would have extended the mount-time wipe from grouped
tables to every table using expansion, so both halves ship together.
- fix: wire `table_autoResetExpanded` into `createCoreRowModel` (#5801)
- fix: skip auto-resets on the first computation of each stage (#5968)
- add `skipFirstRun` util; apply to core, filtered, and sorted row models
- invert the grouped row model's first-run condition
- guard `table_autoResetExpanded` when the expanding feature is absent
- skip auto-resets on the worker bridge's first applied result
- tests: expansion reset without the grouping feature; first-run guard
suite covering seeded initial state and controlled-state consumers
Examples: migrate the basic examples to `createColumnHelper` across all
frameworks, document more table options inline as commented-out defaults,
and replace the `filterFns.between` workaround in the React expanding
example with the `filterFn_between` individual export that already
existed.
Closes #5801
Closes #5968
Supersedes #6443
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> 0c57cc8b fix: honor selection rules in select-all paths, add deselectParents option (#6495)
* fix: honor selection rules in select-all paths, add deselectParents option
Three coordinated row selection fixes for the v9 beta window:
- toggleAllRowsSelected and the getIsAllRowsSelected/getIsAllPageRowsSelected
getters now honor enableSubRowSelection: sub-rows whose ancestors block
descent are skipped by select-all and excluded from the all-selected
computation, matching what toggleAllPageRowsSelected already did (#5116)
- deselecting all rows now preserves the selection of rows whose
enableRowSelection resolves false; deselectAll: true and
resetRowSelection(true) remain the full-clear escapes (#5398)
- new opt-in deselectParents flag on ToggleSelectedOptions removes ancestor
row ids from the selection when a row is deselected, covering the
select-parent-then-deselect-child sequence without changing default state
semantics (#6049)
Eligibility walks are deduped with a per-pass ancestor cache (the
enableSubRowSelection predicate runs once per unique parent), and the
all-selected scans check the cheap isRowSelected lookup first so selected
rows skip capability checks entirely.
Includes 20 new tests, updated row-selection guides for all 10 frameworks
(the canonical checkbox snippet now shows the nested-data pattern), SKILL
updates, and refreshed triage notes.
Fixes #5116
Fixes #5398
Fixes #6049
Fixes #4878
Fixes #5416
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(table-core): lock in deselectParents pruning contract for non-selectable ancestors
Review feedback suggested gating pruneAncestorRowIds on row_getCanSelect. That
would reintroduce the stale-parent state the opt-in exists to remove: a
skipped non-selectable ancestor keeps reporting getIsSelected() over a
deselected child. The enableRowSelection deselect guard is deliberately scoped
to the bulk select-all paths only; targeted deselection paths, including
pruning, may clear any id. Encode the decision in JSDoc, an implementation
comment, and a regression test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>