fix: sync sticky head on body resize via ResizeObserver#50
Open
melikhov-dev wants to merge 5 commits intomainfrom
Open
fix: sync sticky head on body resize via ResizeObserver#50melikhov-dev wants to merge 5 commits intomainfrom
melikhov-dev wants to merge 5 commits intomainfrom
Conversation
When cell content changes asynchronously (e.g. useEffect + dangerouslySetInnerHTML), only the tbody re-renders. This does not trigger TableHead.componentDidUpdate, and no window.resize is fired, so the sticky head column widths stay stale. Fix: subscribe a ResizeObserver to _box in componentDidMount when stickyHead is enabled. Callbacks are debounced through a single requestAnimationFrame so rapid bursts coalesce into one syncHeadWidths() call. The observer is disconnected and the pending rAF is cancelled in componentWillUnmount. Guarded by typeof ResizeObserver !== 'undefined' for SSR / older browser compatibility. Also adds the first component-level test suite for DataTable, covering: - ResizeObserver is created and observes _box when stickyHead is on - ResizeObserver is not created when stickyHead is off - disconnect() is called on unmount (no leak) - resize events are debounced into one rAF - multiple rapid events coalesce into one rAF - guard resets correctly after each rAF flush - no throw when ResizeObserver is unavailable
@testing-library/react@16 requires @types/react@^18 which conflicts with the project-pinned @types/react@16.14.21, causing npm ci to fail on CI. Switch to the legacy ReactDOM.render / unmountComponentAtNode API (already typed by @types/react-dom@16) and act from react-dom/test-utils. React 18 deprecation warnings for those APIs are suppressed inside the test file.
@types/react was pinned to 16.14.21 while react/react-dom were already on ^18.3.1 (bumped in 5073030 without updating the type packages). Align them so the types match the runtime. This also unblocks @testing-library/react which requires @types/react@^18, so the test file is switched back from the legacy ReactDOM.render API to @testing-library/react render + act.
Previous lock file was generated with --legacy-peer-deps on Node 22, causing npm ci to fail on CI (Node 18) with missing type-fest entry. Regenerated cleanly after aligning @types/react to ^18.
Contributor
|
Preview is ready. |
Restore lock file to main, then re-run npm install on top so the diff only reflects the packages actually added in this PR (@types/react^18, @types/react-dom^18, @testing-library/* and their transitive deps).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a
DataTableis rendered withstickyHead: MOVINGand cell content changes asynchronously (e.g. viauseEffect+dangerouslySetInnerHTML), the sticky header column widths become desynchronised from the body.Root cause:
syncHeadWidths()was only triggered bywindow.resize. An async re-render of<tbody>children doesn't fireTableHead.componentDidUpdate(head props/state didn't change) and doesn't dispatch a resize event, so the sticky head stays at the stale widths from the initial render.The symptom persists until something causes a
window.resize— e.g. toggling DevTools or callingwindow.dispatchEvent(new Event('resize'))manually.Fix
Subscribe a
ResizeObserverto_boxincomponentDidMountwheneverstickyHeadis enabled. When the observer fires, arequestAnimationFramedebounce coalesces rapid bursts into a singlesyncHeadWidths()call. Both the observer and any pending rAF are cleaned up incomponentWillUnmount.Compatibility: guarded by
typeof ResizeObserver !== 'undefined'— no-op in SSR and older browsers.Tests
Adds the first component-level test suite (
src/test/DataTable.stickyHead.test.tsx, 8 tests):ResizeObserveris created and observes.data-table__boxwhenstickyHeadis enabledResizeObserveris not created whenstickyHeadis disableddisconnect()is called on unmount (no leak)rAFrAFrAFflushResizeObserveris unavailableAlso adds required test infrastructure:
jest-environment-jsdom,@testing-library/react,@types/jest, SCSS mock, and updatesjest.config.jsto pick up.test.tsxfiles.