From 0aa7e9bd9c6e0167882cf786aca6443325d986ca Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Tue, 11 Aug 2026 05:34:17 +0500 Subject: [PATCH] fix(table-core): keep depth-truncated sub-rows in the leaf-up filter path --- .changeset/leaf-up-truncated-sub-rows.md | 5 ++ .../column-filtering/filterRowsUtils.ts | 9 ++++ .../createFilteredRowModel.test.ts | 49 +++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 .changeset/leaf-up-truncated-sub-rows.md diff --git a/.changeset/leaf-up-truncated-sub-rows.md b/.changeset/leaf-up-truncated-sub-rows.md new file mode 100644 index 0000000000..02efd21ef7 --- /dev/null +++ b/.changeset/leaf-up-truncated-sub-rows.md @@ -0,0 +1,5 @@ +--- +'@tanstack/table-core': patch +--- + +Fix `filterFromLeafRows` discarding the sub-rows of rows kept past `maxLeafRowFilterDepth`. The leaf-up filter path rebuilt those rows without their unfiltered subtree, so the truncated descendants vanished from `subRows`, `flatRows` and `rowsById` alike. They are now carried over and flattened, the way the root-down path already keeps them. diff --git a/packages/table-core/src/features/column-filtering/filterRowsUtils.ts b/packages/table-core/src/features/column-filtering/filterRowsUtils.ts index e8f3e09336..fead4e27a9 100644 --- a/packages/table-core/src/features/column-filtering/filterRowsUtils.ts +++ b/packages/table-core/src/features/column-filtering/filterRowsUtils.ts @@ -82,11 +82,20 @@ function filterRowModelFromLeafs< continue } } else { + // Past maxLeafRowFilterDepth the subtree is never filtered, so it stays + // visible through row.subRows and its rows must enter flatRows and + // rowsById as well, like the root-down path already does + newRow.subRows = row.subRows row = newRow if (filterRow(row)) { filteredRows.push(row) newFilteredRowsById[row.id] = row newFilteredFlatRows.push(row) + addSubRowsToFlatArrays( + row.subRows, + newFilteredFlatRows, + newFilteredRowsById, + ) } } } diff --git a/packages/table-core/tests/implementation/features/column-filtering/createFilteredRowModel.test.ts b/packages/table-core/tests/implementation/features/column-filtering/createFilteredRowModel.test.ts index 67637123f7..5716ad7bd0 100644 --- a/packages/table-core/tests/implementation/features/column-filtering/createFilteredRowModel.test.ts +++ b/packages/table-core/tests/implementation/features/column-filtering/createFilteredRowModel.test.ts @@ -319,6 +319,55 @@ describe('createFilteredRowModel', () => { expect(model.rowsById[keepA1.id]).toBe(keepA1) }) + it('should include unfiltered descendants of kept rows in flatRows and rowsById (from leaf, depth 0)', () => { + const table = makeNestedTable({ + filterFromLeafRows: true, + maxLeafRowFilterDepth: 0, + }) + const model = table.getFilteredRowModel() + + // The subtrees of the kept rows are never filtered at depth 0, so they + // stay visible through subRows and belong in the flat arrays too, the + // same way the root-down path keeps them + const keepA = model.rows[0]! + expect(rowNames(keepA.subRows)).toEqual(['keep-a1', 'drop-a2']) + expect(rowNames(model.flatRows)).toEqual([ + 'keep-a', + 'keep-a1', + 'drop-a1a', + 'drop-a2', + 'keep-c', + 'keep-d', + 'drop-d1', + ]) + + const keepA1 = keepA.subRows[0]! + expect(model.rowsById[keepA1.id]).toBe(keepA1) + }) + + it('should include kept-as-is grandchildren in flatRows when maxLeafRowFilterDepth is 1 (from leaf)', () => { + const table = makeNestedTable({ + filterFromLeafRows: true, + maxLeafRowFilterDepth: 1, + }) + const model = table.getFilteredRowModel() + + // Depth-1 children are still filtered (drop-a2 removed), while the + // depth-2 subtree of keep-a1 is kept as-is and joins flatRows + const keepA = model.rows[0]! + expect(rowNames(keepA.subRows)).toEqual(['keep-a1']) + expect(rowNames(keepA.subRows[0]!.subRows)).toEqual(['drop-a1a']) + expect(rowNames(model.flatRows)).toEqual([ + 'keep-a1', + 'drop-a1a', + 'keep-a', + 'keep-b1', + 'drop-b', + 'keep-c', + 'keep-d', + ]) + }) + it('should include kept-as-is grandchildren in flatRows when maxLeafRowFilterDepth is 1 (from root)', () => { const table = makeNestedTable({ maxLeafRowFilterDepth: 1 }) const model = table.getFilteredRowModel()