Skip to content

Commit 31af5a6

Browse files
TrevorBurnhamjust-boris
authored andcommitted
perf: Remove ResizeObserver polyfill
All target browsers now support native ResizeObserver API, making the polyfill unnecessary. This change reduces bundle size and simplifies the codebase. Changes: - Remove @juggle/resize-observer dependency from package.json - Replace polyfill imports with native ResizeObserver API - Update useResizeObserver to use getBoundingClientRect() for initial sync measurement - Add convertElementToEntry() helper since native ResizeObserverEntry cannot be constructed - Remove __mocks__/@juggle/resize-observer.ts mock file - Add comprehensive Jest mocks for ResizeObserver and getBoundingClientRect in test environment - Extract ResizeObserver test mocks to separate reusable module
1 parent 9b10439 commit 31af5a6

File tree

8 files changed

+87
-58
lines changed

8 files changed

+87
-58
lines changed

__mocks__/@juggle/resize-observer.ts

Lines changed: 0 additions & 42 deletions
This file was deleted.

jest/resize-observer-mock.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
/**
5+
* Mock ResizeObserver for Jest tests
6+
*
7+
* This module provides a mock implementation of the native ResizeObserver API
8+
* for use in Jest test environments where ResizeObserver may not be available.
9+
*/
10+
11+
const mockObserve = jest.fn();
12+
const mockUnobserve = jest.fn();
13+
const mockDisconnect = jest.fn();
14+
15+
// Create the ResizeObserver mock constructor
16+
global.ResizeObserver = jest.fn().mockImplementation(() => ({
17+
observe: mockObserve,
18+
unobserve: mockUnobserve,
19+
disconnect: mockDisconnect,
20+
}));
21+
22+
// Mock the prototype methods for backward compatibility with tests that access prototype
23+
Object.defineProperty(global.ResizeObserver, 'prototype', {
24+
value: {
25+
observe: mockObserve,
26+
unobserve: mockUnobserve,
27+
disconnect: mockDisconnect,
28+
},
29+
writable: false,
30+
});
31+
32+
/**
33+
* Mock getBoundingClientRect to return dimensions based on CSS styles
34+
*
35+
* In JSDOM test environments, getBoundingClientRect() returns all zeros because
36+
* JSDOM doesn't perform actual layout calculations. This mock extracts width/height
37+
* from computed CSS styles to provide realistic dimensions for ResizeObserver tests.
38+
*
39+
* Only mock in browser environments (not SSR tests where Element is undefined).
40+
*/
41+
if (typeof Element !== 'undefined') {
42+
Element.prototype.getBoundingClientRect = jest.fn(function () {
43+
const style = window.getComputedStyle(this);
44+
const width = parseFloat(style.width) || 0;
45+
const height = parseFloat(style.height) || 0;
46+
47+
return {
48+
width,
49+
height,
50+
top: 0,
51+
left: 0,
52+
bottom: height,
53+
right: width,
54+
x: 0,
55+
y: 0,
56+
toJSON: () => ({}),
57+
};
58+
});
59+
}
60+
61+
// Export the mock functions for test access if needed
62+
module.exports = {
63+
mockObserve,
64+
mockUnobserve,
65+
mockDisconnect,
66+
};

jest/unit-setup.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@
44
require('@testing-library/jest-dom');
55
const { cleanup } = require('@testing-library/react');
66
afterEach(cleanup);
7+
8+
// Import ResizeObserver and getBoundingClientRect mocks
9+
require('./resize-observer-mock');

package-lock.json

Lines changed: 0 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
"prepare": "husky"
4141
},
4242
"dependencies": {
43-
"@juggle/resize-observer": "^3.3.1",
4443
"tslib": "^2.3.1"
4544
},
4645
"devDependencies": {

src/container-queries/__tests__/use-container-query.test.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import React from 'react';
55
import { render } from '@testing-library/react';
66
import useContainerQuery from '../use-container-query';
7-
import { ResizeObserver } from '@juggle/resize-observer';
87
import { ContainerQueryEntry } from '../interfaces';
98

109
function TestComponent({ mapFn = () => '' }: { mapFn?: (entry: ContainerQueryEntry) => string }) {

src/internal/container-queries/__tests__/use-resize-observer.test.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import React, { useState, useRef } from 'react';
55
import { render } from '@testing-library/react';
66
import { useResizeObserver } from '../use-resize-observer';
7-
import { ResizeObserver } from '@juggle/resize-observer';
87
import { ContainerQueryEntry } from '../interfaces';
98

109
function TestComponent({ mapFn = () => '' }: { mapFn?: (entry: ContainerQueryEntry) => string }) {

src/internal/container-queries/use-resize-observer.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import { unstable_batchedUpdates } from 'react-dom';
5-
import { ResizeObserver, ResizeObserverEntry } from '@juggle/resize-observer';
65
import { useEffect, useLayoutEffect } from 'react';
76
import { ContainerQueryEntry, ElementReference } from './interfaces';
87
import { useStableCallback } from '../stable-callback';
@@ -42,7 +41,8 @@ export function useResizeObserver(elementRef: ElementReference, onObserve: (entr
4241
() => {
4342
const element = typeof elementRef === 'function' ? elementRef() : elementRef?.current;
4443
if (element) {
45-
onObserve(convertResizeObserverEntry(new ResizeObserverEntry(element)));
44+
const rect = element.getBoundingClientRect();
45+
onObserve(convertElementToEntry(element, rect));
4646
}
4747
},
4848
// This effect is only needed for the first render to provide a synchronous update.
@@ -80,3 +80,19 @@ function convertResizeObserverEntry(entry: ResizeObserverEntry): ContainerQueryE
8080
borderBoxHeight: entry.borderBoxSize[0].blockSize,
8181
};
8282
}
83+
84+
function convertElementToEntry(element: Element, rect: DOMRect): ContainerQueryEntry {
85+
const computedStyle = window.getComputedStyle(element);
86+
const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;
87+
const paddingRight = parseFloat(computedStyle.paddingRight) || 0;
88+
const paddingTop = parseFloat(computedStyle.paddingTop) || 0;
89+
const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0;
90+
91+
return {
92+
target: element,
93+
contentBoxWidth: rect.width - paddingLeft - paddingRight,
94+
contentBoxHeight: rect.height - paddingTop - paddingBottom,
95+
borderBoxWidth: rect.width,
96+
borderBoxHeight: rect.height,
97+
};
98+
}

0 commit comments

Comments
 (0)