Skip to content

Clamp column spans, and add getColSpan() to calculated columns - #4251

Merged
nstepien merged 11 commits into
mainfrom
drag-handle-colspan
Oct 27, 2026
Merged

nstepien merged 11 commits into
mainfrom
drag-handle-colspan

Conversation

@nstepien

@nstepien nstepien commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Calculated columns have a new getColSpan(args) method, which returns the number of columns that a cell spans: the value returned by the column's colSpan, clamped, or 1. Cells, keyboard navigation, the editor, and the drag handle all use it, so they agree on each cell's span.

Spans are clamped instead of ignored. A span is clamped to the start-frozen, unfrozen, or end-frozen columns, whichever the column is part of:

  • A span that crossed the edge of the frozen columns used to be ignored, and the cell spanned one column. The cell now spans as many columns as it can.
  • A span could also go past the last column, if the column was end-frozen, or unfrozen without end-frozen columns. It's now clamped to the last column.

Drag handle fixes:

  • The drag handle called colSpan directly. When the cells ignored a span, the drag handle still spanned the columns that the span would have covered, and showed up at the end of the last one.
  • When scrolling, the drag handle of a start-frozen cell sticks at the end of the cell's first column, so it moved to the middle of cells that span columns. It now sticks at the end of the last column that the cell spans.

API changes:

  • CalculatedColumn has a new getColSpan member.
  • colSpan in RenderCellProps and in the callback of mapViewportColumns is now always a number: 1 instead of undefined for cells that don't span.
  • Cells that don't span still don't render aria-colspan.
  • ColumnFrozen is now false | 'start' | 'end'. Column.frozen still accepts true, as an alias for 'start', and calculated columns normalize it to 'start'.

Tests:

  • New tests in colSpan.test.ts check that spans are clamped to each group of columns in the header row, rows, and summary rows, and to the last column.
  • Two tests in dragFill.test.tsx check that the drag handle straddles the end of a cell whose span is clamped, and the end of a start-frozen cell that spans columns, before and after scrolling.
  • These 4 tests fail on main.

The drag handle fix was split from #4005.

vp run ci passes.

🤖 Generated with Claude Code

Cells validate the span returned by a column's `colSpan` with
`getColSpan()`, but the drag handle called `colSpan` directly. When
`getColSpan()` ignored a span, e.g., one that would cross into the
end-frozen columns, the cell spanned one column, and the drag handle
showed up at the end of the last column the span would have covered.

Split from #4005.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov-commenter

codecov-commenter commented Oct 26, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.22%. Comparing base (81f2df3) to head (2ba85db).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4251      +/-   ##
==========================================
+ Coverage   99.13%   99.22%   +0.09%     
==========================================
  Files          46       44       -2     
  Lines        2207     2207              
  Branches      977      971       -6     
==========================================
+ Hits         2188     2190       +2     
+ Misses         19       17       -2     
Files with missing lines Coverage Δ
src/Cell.tsx 100.00% <100.00%> (ø)
src/DataGrid.tsx 99.26% <100.00%> (+0.18%) ⬆️
src/EditCell.tsx 98.98% <100.00%> (+0.04%) ⬆️
src/GroupCell.tsx 100.00% <100.00%> (ø)
src/HeaderCell.tsx 98.29% <100.00%> (ø)
src/SummaryCell.tsx 100.00% <100.00%> (ø)
src/hooks/useCalculatedColumns.ts 100.00% <100.00%> (ø)
src/hooks/useViewportColumns.ts 100.00% <100.00%> (ø)
src/utils/activePositionUtils.ts 100.00% <100.00%> (ø)
src/utils/index.ts 90.90% <ø> (ø)
... and 2 more

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

nstepien and others added 6 commits October 26, 2026 23:24
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Now that getColSpan() returns 1 for cells that don't span, cells rendered aria-colspan="1": omit it again.
mapViewportColumns passes 1 instead of undefined to its callback for these cells, and calculated columns have getColSpan, so update the tests that checked them.

Add tests for spans clamped to the start-frozen, unfrozen, or end-frozen columns, and to the last column, and document the clamping and getColSpan().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When scrolling, the drag handle of a start-frozen cell sticks at the end of the cell's column, so it moved to the middle of cells that span columns. It now sticks at the end of the last column that the cell spans.

Update the drag handle test for the clamped spans.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nstepien nstepien changed the title Use getColSpan() for the drag handle Clamp column spans, and add getColSpan() to calculated columns Oct 27, 2026
nstepien and others added 4 commits October 27, 2026 01:42
…g demo

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- EditCell computes its span from the edited row with column.getColSpan(), instead of receiving it as a prop.
- getColSpan() accepts undefined arguments, and returns 1 for them, so mapColumnsWithColSpan() doesn't need to check them.
- getCellColSpan() only counts the top summary rows when the row isn't the header row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ColumnFrozen is now false | 'start' | 'end'. Column.frozen still accepts true, as an alias for 'start', and calculated columns normalize it, so isStartFrozen() is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nstepien
nstepien merged commit 9917439 into main Oct 27, 2026
6 checks passed
@nstepien
nstepien deleted the drag-handle-colspan branch October 27, 2026 02:57
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.

2 participants