Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/great-pugs-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tanstack/table-core": patch
---

Fix `getFilteredRowModel().flatRows` listing sub-rows before their parent in both `filterFromLeafRows` and `filterFromRoot` modes. This restores the parent-first order used by the core, sorted, and paginated row models, and aligns the flattened result with the filtered `rows` tree.
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,25 @@ function filterRowModelFromLeafs<
newRow.columnFilters = row.columnFilters

if (row.subRows.length && depth < maxDepth) {
// Descendants are filtered first, so in the flat list a parent must
// be inserted before its surviving children (pre-order) to match the
// readable `rows` tree and the core/sorted/paginated row models.
const flatIndex = newFilteredFlatRows.length

newRow.subRows = recurseFilterRows(row.subRows, depth + 1)
row = newRow

if (filterRow(row) && !newRow.subRows.length) {
newFilteredFlatRows.splice(flatIndex, 0, row)
filteredRows.push(row)
newFilteredRowsById[row.id] = row
newFilteredFlatRows.push(row)
continue
}

if (filterRow(row) || newRow.subRows.length) {
newFilteredFlatRows.splice(flatIndex, 0, row)
filteredRows.push(row)
newFilteredRowsById[row.id] = row
newFilteredFlatRows.push(row)
continue
}
} else {
Expand Down Expand Up @@ -127,6 +132,11 @@ function filterRowModelFromRoot<
const pass = filterRow(row)

if (pass) {
// Take this row's slot before descending, so a parent stays ahead of
// its own sub-rows in flatRows (pre-order).
const flatIndex = newFilteredFlatRows.length
newFilteredFlatRows.push(row)

if (row.subRows.length && depth < maxDepth) {
const newRow = constructRow(
table,
Expand All @@ -139,10 +149,10 @@ function filterRowModelFromRoot<
)
newRow.subRows = recurseFilterRows(row.subRows, depth + 1)
row = newRow
newFilteredFlatRows[flatIndex] = row
}

filteredRows.push(row)
newFilteredFlatRows.push(row)
newFilteredRowsById[row.id] = row

// When maxLeafRowFilterDepth stops the recursion, the kept row's
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,20 @@ describe('createFilteredRowModel', () => {
expect(model.rowsById[keepA.id]).toBe(keepA)
expect(model.rowsById[keepA.subRows[0]!.id]).toBe(keepA.subRows[0])
})

it('flattens each parent ahead of its own sub-rows (from root)', () => {
const table = makeNestedTable()
const model = table.getFilteredRowModel()

// Pre-order: a parent precedes its own surviving sub-rows in flatRows,
// matching the readable `rows` tree.
expect(rowNames(model.flatRows)).toEqual([
'keep-a',
'keep-a1',
'keep-c',
'keep-d',
])
})
})

describe('filterFromLeafRows', () => {
Expand Down Expand Up @@ -270,6 +284,23 @@ describe('createFilteredRowModel', () => {
expect(rowNames(keepA.subRows)).toEqual(['keep-a1'])
expect(keepA.subRows[0]!.subRows).toEqual([])
})

it('flattens each parent ahead of its own sub-rows (from leaf)', () => {
const table = makeNestedTable({ filterFromLeafRows: true })
const { rows, flatRows } = table.getFilteredRowModel()

// drop-b is retained because keep-b1 matches, and its ancestor chain
// must appear before its descendants in flatRows (pre-order).
expect(rowNames(rows)).toEqual(['keep-a', 'drop-b', 'keep-c', 'keep-d'])
expect(rowNames(flatRows)).toEqual([
'keep-a',
'keep-a1',
'drop-b',
'keep-b1',
'keep-c',
'keep-d',
])
})
})

describe('maxLeafRowFilterDepth', () => {
Expand Down Expand Up @@ -324,13 +355,12 @@ describe('createFilteredRowModel', () => {
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. The
// pre-existing flatRows order pushes recursed children before their
// parent.
// depth-2 subtree of keep-a1 is kept as-is and joins flatRows. Each
// parent flattens ahead of its own sub-rows (pre-order).
expect(rowNames(model.flatRows)).toEqual([
'keep-a',
'keep-a1',
'drop-a1a',
'keep-a',
'keep-c',
'keep-d',
])
Expand Down