From 0041efb85d78937e8443e7f7d4f6d40b4dd646f1 Mon Sep 17 00:00:00 2001 From: sanjibani <18418553+sanjibani@users.noreply.github.com> Date: Thu, 9 Jul 2026 11:35:12 +0530 Subject: [PATCH 1/2] fix(table-core): guard Array.isArray before index access in range filter autoRemove `filterFn_between`, `filterFn_betweenInclusive`, and `filterFn_inNumberRange` shared an `autoRemove` that did `testFalsy(val[0]) && testFalsy(val[1])`. When a scalar value such as `99` was passed instead of a `[min, max]` tuple, `val[0]` and `val[1]` were both `undefined`, so the condition evaluated to `true && true` and the filter was silently auto-removed. Numeric columns default to `inNumberRange` via `column_getAutoFilterFn`, so calling `col.setColumnFilter(99)` on a numeric column discarded the filter before the row scan even ran. Wrap the index checks in `Array.isArray(val) &&`. The leading `testFalsy(val)` still handles `undefined`, `null`, `0`, and `''`. Behavior of the existing array-tuple cases is unchanged because `Array.isArray` is true and short-circuits to the same boolean. Closes #6353 --- packages/table-core/src/fns/filterFns.ts | 9 ++-- .../tests/unit/fns/filterFns.test.ts | 43 +++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/packages/table-core/src/fns/filterFns.ts b/packages/table-core/src/fns/filterFns.ts index 7e85698890..0854c4e302 100644 --- a/packages/table-core/src/fns/filterFns.ts +++ b/packages/table-core/src/fns/filterFns.ts @@ -219,7 +219,8 @@ const filterFn_between = Object.assign( filterFn_lessThan(row, columnId, filterValues[1])), { autoRemove: (val: any) => - testFalsy(val) || (testFalsy(val[0]) && testFalsy(val[1])), + testFalsy(val) || + (Array.isArray(val) && testFalsy(val[0]) && testFalsy(val[1])), }, ) @@ -243,7 +244,8 @@ const filterFn_betweenInclusive = Object.assign( filterFn_lessThanOrEqualTo(row, columnId, filterValues[1])), { autoRemove: (val: any) => - testFalsy(val) || (testFalsy(val[0]) && testFalsy(val[1])), + testFalsy(val) || + (Array.isArray(val) && testFalsy(val[0]) && testFalsy(val[1])), }, ) @@ -287,7 +289,8 @@ export const filterFn_inNumberRange = Object.assign( return [min, max] as const }, autoRemove: (val: any) => - testFalsy(val) || (testFalsy(val[0]) && testFalsy(val[1])), + testFalsy(val) || + (Array.isArray(val) && testFalsy(val[0]) && testFalsy(val[1])), }, ) diff --git a/packages/table-core/tests/unit/fns/filterFns.test.ts b/packages/table-core/tests/unit/fns/filterFns.test.ts index 1b6f74a092..f55c97d926 100644 --- a/packages/table-core/tests/unit/fns/filterFns.test.ts +++ b/packages/table-core/tests/unit/fns/filterFns.test.ts @@ -537,6 +537,12 @@ describe('Filter Functions', () => { expect(autoRemove([undefined, 10])).toBe(false) expect(autoRemove([5, undefined])).toBe(false) }) + it('should NOT auto-remove a scalar value passed instead of a range tuple', () => { + // Regression test for #6353 - without the Array.isArray guard, + // `val[0]` and `val[1]` are undefined for a scalar and the index + // checks incorrectly auto-remove the filter. + expect(autoRemove(99 as any)).toBe(false) + }) }) describe('filterFns.betweenInclusive.autoRemove', () => { @@ -561,6 +567,43 @@ describe('Filter Functions', () => { expect(autoRemove([undefined, 10])).toBe(false) expect(autoRemove([5, undefined])).toBe(false) }) + it('should NOT auto-remove a scalar value passed instead of a range tuple', () => { + // Regression test for #6353 - same Array.isArray guard needed + // here as in `between` and `inNumberRange`. + expect(autoRemove(99 as any)).toBe(false) + }) + }) + + describe('filterFns.inNumberRange.autoRemove', () => { + const autoRemove = filterFns.inNumberRange.autoRemove! + + it('should auto-remove when both endpoints are undefined', () => { + expect(autoRemove([undefined, undefined])).toBe(true) + }) + it('should auto-remove when both endpoints are null', () => { + expect(autoRemove([null, null])).toBe(true) + }) + it('should auto-remove when both endpoints are empty strings', () => { + expect(autoRemove(['', ''])).toBe(true) + }) + it('should NOT auto-remove when both endpoints are valid numbers', () => { + expect(autoRemove([5, 10])).toBe(false) + }) + it('should NOT auto-remove when lower bound is 0 (falsy number)', () => { + expect(autoRemove([0, 10])).toBe(false) + }) + it('should NOT auto-remove when only one endpoint is provided', () => { + expect(autoRemove([undefined, 10])).toBe(false) + expect(autoRemove([5, undefined])).toBe(false) + }) + it('should NOT auto-remove a scalar number passed instead of a range tuple', () => { + // Regression test for #6353 - `val[0]` and `val[1]` were both + // undefined for a scalar, so the previous index checks produced + // true && true and the filter was discarded. + expect(autoRemove(99 as any)).toBe(false) + expect(autoRemove(0 as any)).toBe(false) + expect(autoRemove('' as any)).toBe(false) + }) }) }) }) From b098522368ec6a3153ae4e97e0989828ade78035 Mon Sep 17 00:00:00 2001 From: sanjibani <18418553+sanjibani@users.noreply.github.com> Date: Thu, 9 Jul 2026 21:05:31 +0530 Subject: [PATCH 2/2] test(filterFns): assert empty-string scalar IS auto-removed The previous regression test for #6353 bundled the non-empty scalar case (autoRemove(99) and autoRemove(0)) with an empty-string expectation (autoRemove('')). testFalsy treats '' as falsy, so the leading testFalsy(val) branch correctly returns true for '' - the Array.isArray guard added in 0041efb is intentionally additive and not meant to override that behavior. Split the assertion: the non-empty scalar test now stands alone with just autoRemove(99) and autoRemove(0); the empty-string behavior is covered by a separate explicit assertion (expect(autoRemove(''))).toBe(true)) Vitest run: Test Files 1 passed (1) Tests 73 passed (73) Signed-off-by: sanjibani <18418553+sanjibani@users.noreply.github.com> --- .../table-core/tests/unit/fns/filterFns.test.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/table-core/tests/unit/fns/filterFns.test.ts b/packages/table-core/tests/unit/fns/filterFns.test.ts index f55c97d926..2d40ac1b00 100644 --- a/packages/table-core/tests/unit/fns/filterFns.test.ts +++ b/packages/table-core/tests/unit/fns/filterFns.test.ts @@ -596,13 +596,19 @@ describe('Filter Functions', () => { expect(autoRemove([undefined, 10])).toBe(false) expect(autoRemove([5, undefined])).toBe(false) }) - it('should NOT auto-remove a scalar number passed instead of a range tuple', () => { + it('should NOT auto-remove a non-empty scalar number passed instead of a range tuple', () => { // Regression test for #6353 - `val[0]` and `val[1]` were both - // undefined for a scalar, so the previous index checks produced - // true && true and the filter was discarded. + // undefined for a non-array scalar, so the previous index checks + // produced true && true and the filter was discarded. expect(autoRemove(99 as any)).toBe(false) expect(autoRemove(0 as any)).toBe(false) - expect(autoRemove('' as any)).toBe(false) + }) + it('should auto-remove an empty-string scalar (matches existing testFalsy behavior)', () => { + // `testFalsy` treats '' as falsy, and the leading `testFalsy(val)` + // branch short-circuits to true for an empty string. The new + // Array.isArray guard is intentionally additive and only kicks in + // when the value is not already caught by `testFalsy`. + expect(autoRemove('' as any)).toBe(true) }) }) })