Skip to content

feat(useResizeObserver): observe a chosen box model - #1695

Merged
xobotyi merged 2 commits into
masterfrom
feat/resize-observer-box
Jul 26, 2026
Merged

feat(useResizeObserver): observe a chosen box model#1695
xobotyi merged 2 commits into
masterfrom
feat/resize-observer-box

Conversation

@xobotyi

@xobotyi xobotyi commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Closes the correctness gap flagged when #1603 landed: borderBoxMeasurer reads border box sizes, but the hook underneath only ever observed the content box.

Problem

A ResizeObserver notifies on changes to the box it was asked to observe. The shared singleton called observer.observe(target) — content box, the spec default. So:

  • padding or border growing around an unchanged content box → no callback, and useMeasure(true, borderBoxMeasurer) keeps returning the previous size
  • neither box is a superset of the other: padding growth inside a fixed border box changes the content box while the border box stays put, so switching the default would just move the blind spot

Change

useResizeObserver(target, callback, enabled, box?) and useMeasure(enabled, measurer, box?) — both optional, both defaulting to 'content-box', so existing calls behave exactly as before.

Observers are now kept one per box model:

const observerSingletons = new Map<ResizeObserverBoxOptions, ResizeObserverSingleton>();

One instance cannot serve two box models, and elements may be observed by several observers, so this stays within the same "one observer, many subscribers" design — just keyed.

Usage for the border-box case the measurer was written for:

const [measures, ref] = useMeasure(true, borderBoxMeasurer, 'border-box');

The pairing is documented on both hooks' JSDoc, including the failure mode when the two disagree.

Tests

  • useResizeObserver forwards {box} to observe and allocates a separate observer per box model
  • useMeasure threads its box argument through — mutation-checked: dropping the argument fails with expected "vi.fn()" to be called with arguments: [ <div></div>, { box: 'border-box' } ]
  • the existing unsubscribe test now asserts the explicit {box: 'content-box'} call

Verification

vp fmt, tsc --noEmit, vp lint, vp test --run (117 files / 532 tests), and the build all pass. Emitted signatures confirm the additions are optional:

export declare function useResizeObserver<T extends Element>(target: RefObject<T | null> | T | null, callback: UseResizeObserverCallback, enabled?: boolean, box?: ResizeObserverBoxOptions): void;
export declare function useMeasure<T extends Element>(enabled?: boolean, measurer?: Measurer, box?: ResizeObserverBoxOptions): [Measures | undefined, RefObject<T | null>];

xobotyi added 2 commits July 26, 2026 01:26
A ResizeObserver only reports changes of the box it was asked to observe,
and the shared observer always asked for the content box. Border and
padding growth around an unchanged content box therefore raised no
callback -- the gap that makes `borderBoxMeasurer` report stale sizes.

An optional fourth argument selects the box; observers are now kept one
per box model, since a single instance cannot serve two of them.
`borderBoxMeasurer` read border box sizes while the hook still observed
content box changes, so a padding-only change left the returned measures
stale. The third argument picks the observed box.
@codecov

codecov Bot commented Jul 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.50000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 83.25%. Comparing base (acdc7e5) to head (40600cc).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/useResizeObserver/index.ts 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1695      +/-   ##
==========================================
+ Coverage   83.19%   83.25%   +0.05%     
==========================================
  Files          62       62              
  Lines         839      842       +3     
  Branches      151      151              
==========================================
+ Hits          698      701       +3     
  Misses         16       16              
  Partials      125      125              

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

🚀 New features to boost your workflow:
  • 📦 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

Choose a reason for hiding this comment

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

Pull request overview

Adds explicit box-model selection to useResizeObserver (and threads it through useMeasure) to close a correctness gap where a measurer could read border-box sizes but the underlying observer only triggered on content-box changes.

Changes:

  • Extend useResizeObserver(target, callback, enabled, box?) to forward { box } to ResizeObserver.observe and maintain one singleton observer per box model.
  • Extend useMeasure(enabled, measurer, box?) to pass the chosen box through to useResizeObserver.
  • Update/add DOM tests to assert { box: ... } is forwarded and that separate observers are allocated per box model.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/useResizeObserver/index.ts Adds box parameter support and keys observer singletons by box model.
src/useResizeObserver/index.dom.test.ts Updates existing expectations and adds tests for box forwarding + per-box singleton behavior.
src/useMeasure/index.ts Adds optional box param and passes it through to useResizeObserver.
src/useMeasure/index.dom.test.ts Adds a test asserting useMeasure forwards the requested box to observation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@xobotyi
xobotyi merged commit 704c008 into master Jul 26, 2026
7 checks passed
@xobotyi
xobotyi deleted the feat/resize-observer-box branch July 26, 2026 07:50
@xobotyi

xobotyi commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

🎉 This PR is included in version 26.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

2 participants