Skip to content

fix: Keep sticky header cols in sync with grouped table columns - #5194

Open
NathanZlion wants to merge 6 commits into
mainfrom
dev-v3-natidere-table-column-groups-fix
Open

NathanZlion wants to merge 6 commits into
mainfrom
dev-v3-natidere-table-column-groups-fix

Conversation

@NathanZlion

@NathanZlion NathanZlion commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Description

This PR fixes two bugs in table that are related to column groups with resizable columns.

First one is the sticky header being out of line with the column widths when on wide screens. This is a reported bug. When the container is wider than the summed column widths, the last column stretches to fill it. The sticky header's widths were set once at render and never updated, so the header drifted away from the body columns. The sticky s now register through setCol and are updated in syncStickyColumnWidths, together with the sticky header cells.
file-f0dbd3fe80706ea4d1cea566551d1375
Before

file-e5312845f3e5fc3a2643c716169db065 After

Second one is the Group resizer stopping at the wrong minimum. This was caused by a hardcoded 120px for the right most instead of just looking into it's actual min width. this means tracker stopped shrinking but the columns would keep shrinking and columns would shrink upto the correct min width. it's just that the tracker wasn't in sync after some point. Fix is to make the header use the new function getColumnMinWidth which uses the same rule updateWidths already applies and this fixed the issue.

file-b64bdef1749134409e27b0754652aef5 Before file-d9a085b0b1f65f1c407d8059dcc77549 After

Related links, issue #, if available: n/a

How has this been tested?

Review checklist

The following items are to be evaluated by the author(s) and the reviewer(s).

Correctness

  • Changes include appropriate documentation updates.
  • Changes are backward-compatible if not indicated, see CONTRIBUTING.md.
  • Changes do not include unsupported browser features, see CONTRIBUTING.md.
  • Changes were manually tested for accessibility, see accessibility guidelines.

Security

Testing

Added two unit tests in resizable-columns.test.tsx, one per bug. Each fails without its fix and passes with it:

  • The group resizer's aria-valuemin equals the other column's width plus the rightmost column's minWidth.
  • When the column-widths container resizes, the sticky header

Checked manually in Chrome at 1280, 1920 and 2560px, with and without groups, the sticky header, selection and expandable rows. The sticky header lines up with the body columns, and the group resize tracker stays on the column edge when shrinking with the resize buttons.

You can check it out at table/column-groups page also, in a wide screen.

  • Changes are covered with new/existing unit tests?
  • Changes are covered with new/existing integration tests?

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@codecov

codecov Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.74%. Comparing base (60601ee) to head (d5912d3).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #5194   +/-   ##
=======================================
  Coverage   97.73%   97.74%           
=======================================
  Files         996      998    +2     
  Lines       32226    32312   +86     
  Branches    11910    11949   +39     
=======================================
+ Hits        31497    31582   +85     
+ Misses        723      685   -38     
- Partials        6       45   +39     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 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.

Explicit widths let the grouped sticky header alignment fix be checked by widening the window after load, and a low minWidth on Memory lets the group resize minimum fix be checked by shrinking the Performance group.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Sticky synchronization introduces unnecessary synchronous layout reads for tables without sticky headers.

1 open issue
What changed in this PR

This PR fixes grouped, resizable table columns so sticky headers track body widths and group resize limits respect the last column’s minimum width.

Changes:

  • Register sticky <col> elements for width synchronization.
  • Share minimum-width logic between column resizing and group headers.
  • Add regression tests and update the demo with explicit widths.
File Description
src/​table/​use-column-widths.tsx Synchronizes sticky columns and exposes shared minimum-width logic.
src/​table/​header-cell/​group-header-cell.tsx Uses the last child’s minimum width for group resizing.
src/​table/​column-groups/​col-group.tsx Registers sticky and primary column elements.
src/​table/​__tests__/​resizable-columns.test.tsx Adds regression coverage for both fixes.
src/​table/​__tests__/​columns-width.test.tsx Updates context default checks.
pages/​table/​column-groups.page.tsx Configures widths for manual verification.

Review effort: Balanced

Comment thread src/table/use-column-widths.tsx Outdated
@NathanZlion
NathanZlion requested review from a team and pan-kot and removed request for a team October 27, 2026 09:35
syncStickyColumnWidths measured every primary column before checking for a sticky cell or <col>, adding layout reads on resize even without a sticky header. Only measure when there is something to update.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused changes address both reported defects, preserve existing sizing rules, and include targeted regression coverage with no unresolved findings.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

const lastChild = childColumnIds[childColumnIds.length - 1];
const groupMinWidth = childColumnIds.reduce<number>((sum, id) => {
if (id === lastChild) {
return sum + DEFAULT_COLUMN_WIDTH;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the bug 2, this used to use the 120 default width every time. That's what made the tracker and the column edge drift apart. using the actual min width instead.

const cellsRef = useRef(new Map<PropertyKey, HTMLElement>());
const stickyCellsRef = useRef(new Map<PropertyKey, HTMLElement>());
const colsRef = useRef(new Map<PropertyKey, HTMLElement>());
const stickyColsRef = useRef(new Map<PropertyKey, HTMLElement>());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this different from stickyCellsRef we already have?

@NathanZlion NathanZlion Oct 27, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stickyCellsRef holds the sticky header cells, and this holds the sticky s. When s exist, the browser takes column widths from them, not from the cells. We were only updating the cells, so the s kept their first-render width, and that's the bug.

we're using col for grouped ones: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/col

https://github.com/cloudscape-design/components/blob/37fbce9334fcf726c6679780682a0c898b347958/src/table/column-groups/col-group.tsx

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, when we have grouped columns the widths measurement should be done differently - with respect to the groups. When grouping is not used - then technically stickyCellsRef and stickyColsRef should be identical. If thats the case, case we remove the stickyCellsRef then?

If we cannot do this optimisation and we have to keep both - let's add code comments to explain why is that needed.

@NathanZlion NathanZlion Oct 28, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, I'll add a comment. They're not quite the identical, the sticky table only renders a <colgroup> when there are column groups, one of the ways we used back then to make column groups work well with the resizing feature and all. so without groups stickyColsRef stays empty and the cells are what carry the widths.

Comment thread src/table/column-groups/col-group.tsx Outdated
{hasSelection && (
<col
style={sticky && selectionColumnId ? { width: getColumnStyles(true, selectionColumnId).width } : undefined}
ref={sticky && selectionColumnId ? node => setCol(true, selectionColumnId, node) : undefined}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we set this column only if it is sticky? Should it be:

ref={selectionColumnId ? node => setCol(sticky, selectionColumnId, node) : undefined}

Also, isn't selectionColumnId guaranteed? Can we remove the && selectionColumnId check? Otherwise, there is a potential issue possible where the column reference is stored, but then not removed if selectionColumnId gets removed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we set this column only if it is sticky?
Only the sticky one needs it. The sticky header is a separate table, so its cols have to be told the width the main table actually rendered. The main table's checkbox column already gets its 54px from its own cell, so its col never needed a ref (it didn't have one before this change either). With setCol(sticky, ...)

selectionColumnId
yeah this one is for typescript since selectionColumnId is optional. I realize now we can make selectionColumnId a required field. Will address in next commtit.

This branch was successfully deployed

3 active deployments
dev-pages-react18 — d5912d3f Deployed Oct 28, 2026 by NathanZlion via deploy (React 18) / deploy #3029
dev-pages-react16 — d5912d3f Deployed Oct 28, 2026 by NathanZlion via deploy (React 16) / deploy #3029
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.

3 participants