Skip to content

refactor: share sticky header internals between Table and Container - #5212

Open
gethinwebster wants to merge 1 commit into
mainfrom
dev-v3-gethinw-sticky-header-shared-internals
Open

gethinwebster wants to merge 1 commit into
mainfrom
dev-v3-gethinw-sticky-header-shared-internals

Conversation

@gethinwebster

@gethinwebster gethinwebster commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Description

Moves the sticky header logic that Table and Container use into one shared internal module, src/internal/components/sticky-header/. This lets the table components (TableRoot and friends) reuse it for their sticky header in a follow-up PR, instead of having a second copy.

What moves, with no change to public APIs:

  • From Table: the code that tucks the real header up under its sticky copy and scrolls focused rows clear of it (use-sticky-header-copy.ts, sticky-scrolling.ts).
  • From Table's column widths: watching each header cell for size changes (use-cells-resize-observer.ts).
  • From Container: the sticky offset calculation and the stuck state (use-sticky-position.ts, use-header-stuck.ts).
  • The styles that hide the sticky copy's scrollbar, now a shared mixin.

Table, Container and Cards now call the shared code and are otherwise unchanged. One small difference: Table used to re-collect the header cells it watches only when the visible columns changed. It now re-collects them after each render, so it also picks up cells that remount for other reasons.

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

How has this been tested?

  • Existing Container, Table and Cards unit tests pass without changes. This includes the column-width test that covers header cells resizing with their content.
  • New unit tests for the shared cell resize observer. One of them covers a cell remounting between renders, and it fails against the old behaviour.
  • New unit tests for the shared sticky header copy hook's scrollToTop and scrollToRow, which had no coverage in the existing Table either.
  • The sticky offset tests now render the shared useStickyPosition hook and check the top it applies, so they also cover the scrolling-parent and app layout <main> checks.
  • Window resizing was profiled against main: React renders, commits and per-component re-render counts per resize are identical.
  • Documenter and test-utils snapshots are unchanged.
  • Compiled CSS for Table, Container and Cards matches main once class-name hashes are normalised. The only difference is where the hidden-scrollbar rule sits.
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

  • 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 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.82%. Comparing base (cc35fe2) to head (9d1b40a).
⚠️ Report is 11 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5212      +/-   ##
==========================================
+ Coverage   97.75%   97.82%   +0.06%     
==========================================
  Files        1003     1006       +3     
  Lines       32534    32602      +68     
  Branches    12082    12111      +29     
==========================================
+ Hits        31805    31893      +88     
+ Misses        684      667      -17     
+ Partials       45       42       -3     

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

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

No active review issues remain for the shared sticky-header extraction.

No open issues 🎉

What changed in this PR

Extracts sticky-header internals into a shared module for Table, Container, and Cards, preparing for reuse by atomic table components without changing public APIs.

Changes:

  • Centralizes sticky positioning, stuck-state tracking, header synchronization, scrolling helpers, and scrollbar styles.
  • Extracts cell resize observation and refreshes observed cells after each render to handle remounts.
  • Adds resize-observer unit tests and relocates sticky-offset tests.
File Description
src/​table/​use-sticky-header.ts Delegates to shared header synchronization.
src/​table/​use-column-widths.tsx Uses the shared cell resize observer.
src/​table/​styles.scss Uses the shared hidden-scrollbar mixin.
src/​internal/​components/​sticky-header/​use-sticky-position.ts Houses sticky positioning and offset calculation.
src/​internal/​components/​sticky-header/​use-sticky-header-sync.ts Houses header overlap and focus-scrolling logic.
src/​internal/​components/​sticky-header/​use-header-stuck.ts Extracts stuck-state tracking.
src/​internal/​components/​sticky-header/​use-cells-resize-observer.ts Tracks cell replacements and resize notifications.
src/​internal/​components/​sticky-header/​sticky-scrolling.ts Relocates scrolling helpers and removes stale parameter documentation.
src/​internal/​components/​sticky-header/​mixins.scss Defines shared hidden-scrollbar styles.
src/​internal/​components/​sticky-header/​__tests__/​use-cells-resize-observer.test.tsx Tests resizing, replacement cells, callback updates, and cleanup.
src/​internal/​components/​sticky-header/​__tests__/​sticky-offset.test.ts Relocates offset tests and updates imports.
src/​container/​use-sticky-header.ts Delegates to shared sticky positioning.
src/​cards/​index.tsx Imports scrolling helpers from the shared module.

Review effort: Balanced

__additionalOffset?: boolean;
}

export function computeOffset({

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.

Nit: why is this function exported? It doesn't seem to be used anywhere else

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.

good catch, it was only used in tests. I've un-exported it and updated tests

Comment thread src/table/use-sticky-header.ts Outdated
useLayoutEffect(() => {
syncColumnHeaderWidths();
) =>
useStickyHeaderSync({

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.

This part of the refactor does not look very necessary. useStickyHeader now only calls useStickyHeaderSync, and useStickyHeaderSync does not seem to be called anywhere else.

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.

updated by renaming this and importing it directly rather than having this file that just imports and re-exports it. this hook will also be imported in the atomic table components when we implement sticky header there (that's what this PR is unblocking)

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 shared sticky-header refactor has no active review issues blocking approval.

No open issues 🎉

Review effort: Balanced

This branch was successfully deployed

3 active deployments
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