Skip to content

Commit 7644b8d

Browse files
committed
fix(tables): drain past the cap so exclusions can't shrink a select-all chip
Cursor Bugbot: for a gutter select-all the menu count comes from selectedRowCount (capped), but the chip was built by loading exactly MAX_TABLE_SELECTION_ROWS and filtering exclusions AFTER. Any excluded row inside that prefix left the chip short of the advertised count. Third appearance of the same invariant — label vs payload — this time in the select-all path specifically, which the earlier fixes did not touch. Loads the cap plus the exclusion count, which covers the worst case where every exclusion falls inside the prefix, so the filtered result still reaches the cap whenever the table has the rows. Extracted as drainTargetForChip so the compensation is stated and tested rather than an inline arithmetic detail.
1 parent 714e6d8 commit 7644b8d

3 files changed

Lines changed: 34 additions & 2 deletions

File tree

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-grid.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { useParams } from 'next/navigation'
1212
import { usePostHog } from 'posthog-js/react'
1313
import type { RunLimit, RunMode, TableFindMatch } from '@/lib/api/contracts/tables'
1414
import { attachSelectionContextToClipboard } from '@/lib/copilot/chat/selection-clipboard'
15-
import { MAX_TABLE_SELECTION_ROWS } from '@/lib/copilot/chat/selection-context'
1615
import { captureEvent } from '@/lib/posthog/client'
1716
import type {
1817
ColumnDefinition,
@@ -73,6 +72,7 @@ import {
7372
classifyExecStatusMix,
7473
collectRowSnapshots,
7574
computeNormalizedSelection,
75+
drainTargetForChip,
7676
type ExecStatusMix,
7777
expandToDisplayColumns,
7878
isCellInSelection,
@@ -3840,7 +3840,11 @@ export function TableGrid({
38403840
let sourceRowIds = addToChatRowIds
38413841
if (contextMenuIsSelectAll || isColumnSelectionRef.current) {
38423842
try {
3843-
const { rows: loaded } = await ensureRowsLoadedUpToRef.current(MAX_TABLE_SELECTION_ROWS)
3843+
const excludedCount =
3844+
rowSelectionRef.current.kind === 'all' ? (rowSelectionRef.current.excluded?.size ?? 0) : 0
3845+
const { rows: loaded } = await ensureRowsLoadedUpToRef.current(
3846+
drainTargetForChip(excludedCount)
3847+
)
38443848
// A column selection spans all rows; a gutter select-all filters by the
38453849
// (exclusion-aware) row selection.
38463850
const drained = (

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/utils.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
buildTableSelectionContext,
1313
canWriteRowsWithChip,
1414
chipRowCount,
15+
drainTargetForChip,
1516
selectedColumnIds,
1617
} from './utils'
1718

@@ -131,3 +132,18 @@ describe('chipRowCount', () => {
131132
}
132133
)
133134
})
135+
136+
describe('drainTargetForChip', () => {
137+
it('still yields a full cap when every exclusion lands in the loaded prefix', () => {
138+
// The worst case for a gutter select-all: exclusions are filtered out AFTER
139+
// loading, so loading only the cap would leave the chip short of the count
140+
// the menu already advertised.
141+
const excluded = 30
142+
143+
expect(drainTargetForChip(excluded) - excluded).toBe(MAX_TABLE_SELECTION_ROWS)
144+
})
145+
146+
it('loads exactly the cap when nothing is excluded', () => {
147+
expect(drainTargetForChip(0)).toBe(MAX_TABLE_SELECTION_ROWS)
148+
})
149+
})

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/utils.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,18 @@ export function buildTableSelectionContext(opts: {
404404
}
405405
}
406406

407+
/**
408+
* How many rows to load before building a select-all chip. A gutter select-all
409+
* can carry exclusions anywhere in the table, and they are filtered out AFTER
410+
* loading — so loading only {@link MAX_TABLE_SELECTION_ROWS} yields fewer than
411+
* the cap whenever an excluded row sits in that prefix, leaving the chip short
412+
* of the count the menu advertised. Loading the cap plus the exclusion count
413+
* covers the worst case, where every exclusion falls inside the prefix.
414+
*/
415+
export function drainTargetForChip(excludedCount: number): number {
416+
return MAX_TABLE_SELECTION_ROWS + excludedCount
417+
}
418+
407419
/**
408420
* Rows a chip will actually reference for a selection of `requested` rows —
409421
* {@link buildTableSelectionContext} caps its `rowIds`, so any count shown to

0 commit comments

Comments
 (0)