Skip to content

fix(table-core): keep columnFiltersMeta when filtering from leaf rows - #6564

Open
dylanpulver wants to merge 1 commit into
TanStack:mainfrom
dylanpulver:fix/sub-row-column-filters-meta
Open

fix(table-core): keep columnFiltersMeta when filtering from leaf rows#6564
dylanpulver wants to merge 1 commit into
TanStack:mainfrom
dylanpulver:fix/sub-row-column-filters-meta

Conversation

@dylanpulver

@dylanpulver dylanpulver commented Aug 20, 2026

Copy link
Copy Markdown

🎯 Changes

Fixes #6074

When filterFromLeafRows is enabled, filterRowModelFromLeafs rebuilds every row with
constructRow and copies columnFilters onto the copy, but not columnFiltersMeta:

newRow.columnFilters = row.columnFilters
// columnFiltersMeta never copied

columnFilteringFeature.initRowInstanceData initialises both maps as empty on a freshly constructed
row, so the meta is not left undefined — it is silently reset to {} on every row in the filtered
row model. That wipes the rank metadata a filter function records through its addMeta callback,
which is exactly what the fuzzy filtering guide tells users to sort on:

if (rowA.columnFiltersMeta[columnId]) {
  dir = compareItems(
    rowA.columnFiltersMeta[columnId].itemRank!,
    rowB.columnFiltersMeta[columnId].itemRank!,
  )
}

The guard makes the failure silent: the meta is {}, the branch is skipped, and rank-aware sorting
quietly degrades to the fallback comparator instead of throwing. As the issue notes, this hits
top-level rows with no sub-rows too, since the leaf-up path clones unconditionally.

This copies columnFiltersMeta across alongside columnFilters.

Why a straight per-row copy, rather than inheriting or aggregating. _createFilteredRowModel
tags every row of the pre-filtered model in a flat pre-pass (createFilteredRowModel.ts, the
flatRows loop), running the filter functions against each row's own values before filterRows is
called. So a row's meta is already computed independently at every depth — the leaf-up path is
losing data that exists, not data that needs deriving. Two alternatives were considered and
rejected:

  • Inheriting the parent's meta would stamp a parent's match rank onto its sub-rows and produce
    wrong sort order within a group rather than a visible break.
  • Aggregating sub-row ranks into the parent (e.g. max rank over the subtree) would overwrite the
    parent's own score. The issue author explicitly argued against baking that into the generic
    feature, noting a sortingFn already has row.subRows if it wants that behaviour.

Nothing inside table-core reads columnFiltersMeta — it is a pure user-facing output surface
(createFacetedRowModel and filterRowsImpl branch on row.columnFilters, never on the meta), so
restoring it cannot change any filtering, faceting, or pagination result.

There is a related pre-existing asymmetry I deliberately left alone: filterRowModelFromRoot clones
matching parent rows that have subRows and copies neither columnFilters nor columnFiltersMeta,
so those cloned parents lose both under the default option. That path has an in-core consumer
(row.columnFilters), so changing it is a broader behavioural decision than this bug report covers.
Happy to follow up separately if maintainers want the two paths brought in line.

Worth noting perf-todo.md already records this gap at filterRowsUtils.ts:65 as pre-existing
behaviour that "must be a deliberate, documented decision either way" — this PR makes it deliberate.

Tests (packages/table-core/tests/implementation/features/column-filtering/createFilteredRowModel.test.ts,
added to the existing columnFiltersMeta describe block, using a rank-scoring addMeta filter in
the shape of the fuzzy docs):

  • nested rows with filterFromLeafRows: true — meta survives on a retained sub-row, and each row
    keeps its own score: the retained non-matching parent keeps rank 0 rather than inheriting its
    matching child's rank. That assertion is what pins per-row semantics over inherited ones.
  • flat top-level rows with filterFromLeafRows: true — covers the "even when there are no sub-rows"
    case from the issue.
  • flat top-level rows on the default root-down path — pins existing behaviour so this change is
    provably confined to the leaf-up path.

The first two fail on main (expected undefined to deeply equal { itemRank: { rank: 10 } }) and
pass with the fix; the third passes both before and after.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Context you should know before posting

  • Two earlier attempts exist, both closed unmerged, neither rejected on the merits.
  • Both prior PRs shipped the one-line change with zero tests. That is the most plausible reason
    neither converted. Consider mentioning the tests early in the description.
  • Two open PRs edit this exact filefix(table-core): keep depth-truncated sub-rows in the leaf-up filter path #6541 (lazerg, depth-truncated sub-rows in the leaf-up
    path) and fix(table-core): flatten filtered parent rows ahead of their sub-rows #6545 (waterWang, flatten filtered parents ahead of sub-rows). Neither touches the
    meta copy, but whichever lands first may cause a trivial conflict on the surrounding lines.
  • The file path moved between v8 and current main: it is now
    packages/table-core/src/features/column-filtering/filterRowsUtils.ts (was src/utils/). The
    issue and both prior PRs reference the old path.
  • CONTRIBUTING asks for feat-* branch names; this branch is fix/sub-row-column-filters-meta per
    the task spec. Rename before pushing if you want to match their stated preference.
  • CONTRIBUTING also says "Document your changes in the appropriate documentation website markdown
    pages." No doc change was made — this restores documented behaviour rather than changing an API,
    so there is nothing in the fuzzy-filtering guides to amend. Flagging it in case you disagree.

Local verification actually run

Run from packages/table-core unless noted:

Command Result
npx vitest run (full table-core suite) 63 files, 1319 tests passed
npx vitest run …/createFilteredRowModel.test.ts 36 passed (33 pre-existing + 3 new)
same file with the src fix stashed 2 failed / 34 passed — new leaf-path tests are genuinely red without the fix
npx tsc clean
npx tsc -p tests/tsconfig.declaration-emit.json clean
npx eslint ./src (the repo's test:eslint target) clean
npx eslint on the changed test file clean
npx prettier --check on all changed files (repo root) clean
pnpm run test:pr (repo root) see report — confirm before ticking that checklist box

Files changed

  • packages/table-core/src/features/column-filtering/filterRowsUtils.ts — +1 line of code, +5 lines
    of comment explaining why the meta is copied rather than inherited or aggregated
  • packages/table-core/tests/implementation/features/column-filtering/createFilteredRowModel.test.ts
    — +93 lines, three tests plus a shared rank filter fixture
  • .changeset/quick-dryers-attend.md — new, @tanstack/table-core: patch

Commit: 9915384fix(table-core): keep columnFiltersMeta when filtering from leaf rows,
authored as Dylan Pulver <dylanpulver@users.noreply.github.com>, no trailers.

Summary by CodeRabbit

  • Bug Fixes

    • Preserved column filter metadata when filtering hierarchical data from leaf rows.
    • Ensured metadata added by filter callbacks remains available on parent rows, sub-rows, and top-level rows.
    • Improved consistency between leaf-first and root-down filtering results.
  • Tests

    • Added coverage for metadata retention across matching and non-matching hierarchical filter scenarios.

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1f055108-0d3f-4233-a9e5-f7ca313bb117

📥 Commits

Reviewing files that changed from the base of the PR and between 7dd6d48 and 9915384.

📒 Files selected for processing (3)
  • .changeset/quick-dryers-attend.md
  • packages/table-core/src/features/column-filtering/filterRowsUtils.ts
  • packages/table-core/tests/implementation/features/column-filtering/createFilteredRowModel.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Leaf-first filtering now preserves each row’s columnFiltersMeta during row reconstruction. New rank-based tests cover nested rows, top-level rows, and both filtering directions. A patch changeset documents the fix.

Changes

Column filter metadata preservation

Layer / File(s) Summary
Preserve metadata during row reconstruction
packages/table-core/src/features/column-filtering/filterRowsUtils.ts
Leaf-first filtering copies columnFiltersMeta alongside columnFilters when reconstructing rows.
Validate metadata retention
packages/table-core/tests/implementation/features/column-filtering/createFilteredRowModel.test.ts, .changeset/quick-dryers-attend.md
Rank-based tests cover nested and top-level rows during leaf-first and root-down filtering. The changeset documents the patch fix.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 99153

This localized fix preserves filter-ranking metadata when filtering from leaf rows and is covered by targeted tests; no actionable merge-blocking risk remains after normal checks and review.

Possibly related PRs

  • TanStack/table#6503: Both changes modify hierarchical filtering logic in filterRowsUtils.ts.
  • TanStack/table#6557: This change extends the same filtering fix by preserving columnFiltersMeta and adding tests.

Suggested reviewers: kevinvandy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the table-core fix and the specific metadata preserved during leaf-row filtering.
Description check ✅ Passed The description includes the change, motivation, tests, checklist, release impact, and changeset details required by the template.
Linked Issues check ✅ Passed The implementation copies each row’s columnFiltersMeta during leaf-up filtering and adds tests covering the requirements in issue [#6074].
Out of Scope Changes check ✅ Passed The changed implementation, tests, and patch changeset directly support issue [#6074] without unrelated code changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

filterFromLeafRows causes columnFiltersMeta to get wiped

1 participant