diff --git a/packages/@mantine/hooks/src/use-resize-observer/UseResizeObserver.story.tsx b/packages/@mantine/hooks/src/use-resize-observer/UseResizeObserver.story.tsx new file mode 100644 index 0000000000..f90218c861 --- /dev/null +++ b/packages/@mantine/hooks/src/use-resize-observer/UseResizeObserver.story.tsx @@ -0,0 +1,66 @@ +import { useResizeObserver } from './use-resize-observer'; + +export default { title: 'use-resize-observer' }; + +export function Usage() { + const [ref, rect] = useResizeObserver(); + return ( +
+
+
+ 1 +
+
+ 2 +
+
+ 3 +
+
+ 4 +
+
+
+
Width: {rect?.width}
+
Height: {rect?.height}
+
+
+ ); +} diff --git a/packages/@mantine/hooks/src/use-resize-observer/use-resize-observer.test.tsx b/packages/@mantine/hooks/src/use-resize-observer/use-resize-observer.test.tsx new file mode 100644 index 0000000000..cf8a0d36ff --- /dev/null +++ b/packages/@mantine/hooks/src/use-resize-observer/use-resize-observer.test.tsx @@ -0,0 +1,92 @@ +import { renderHook } from '@testing-library/react'; +import { useElementSize, useResizeObserver } from './use-resize-observer'; + +class MockResizeObserver { + observe = jest.fn(); + disconnect = jest.fn(); +} + +let mockObserverInstance: MockResizeObserver; + +const originalResizeObserver = global.ResizeObserver; +const originalRAF = window.requestAnimationFrame; +const originalCAF = window.cancelAnimationFrame; + +describe('@mantine/hooks/use-resize-observer', () => { + beforeEach(() => { + global.ResizeObserver = jest.fn().mockImplementation(() => { + mockObserverInstance = new MockResizeObserver(); + return mockObserverInstance; + }) as any; + + window.requestAnimationFrame = (callback) => window.setTimeout(callback, 0) as any; + window.cancelAnimationFrame = jest.fn(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + afterAll(() => { + global.ResizeObserver = originalResizeObserver; + window.requestAnimationFrame = originalRAF; + window.cancelAnimationFrame = originalCAF; + }); + + it('returns reference and default dimensions', () => { + const { result } = renderHook(() => useResizeObserver()); + const [ref, rect] = result.current; + + expect(ref.current).toBe(null); + expect(rect).toEqual({ + x: 0, + y: 0, + width: 0, + height: 0, + top: 0, + left: 0, + bottom: 0, + right: 0, + }); + }); + + it('calls observe on mount and disconnect on unmount', () => { + const { unmount } = renderHook(() => useResizeObserver()); + + expect(global.ResizeObserver).toHaveBeenCalled(); + + unmount(); + + expect(mockObserverInstance.disconnect).toHaveBeenCalled(); + }); +}); + +describe('@mantine/hooks/use-element-size', () => { + beforeEach(() => { + global.ResizeObserver = jest.fn().mockImplementation(() => { + mockObserverInstance = new MockResizeObserver(); + return mockObserverInstance; + }) as any; + + window.requestAnimationFrame = (callback) => window.setTimeout(callback, 0); + window.cancelAnimationFrame = jest.fn(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + afterAll(() => { + global.ResizeObserver = originalResizeObserver; + window.requestAnimationFrame = originalRAF; + window.cancelAnimationFrame = originalCAF; + }); + + it('returns reference and default dimensions', () => { + const { result } = renderHook(() => useElementSize()); + + expect(result.current.ref.current).toBe(null); + expect(result.current.width).toBe(0); + expect(result.current.height).toBe(0); + }); +}); diff --git a/packages/@mantine/hooks/src/use-resize-observer/use-resize-observer.ts b/packages/@mantine/hooks/src/use-resize-observer/use-resize-observer.ts index 236fc3d4c1..017998c7fa 100644 --- a/packages/@mantine/hooks/src/use-resize-observer/use-resize-observer.ts +++ b/packages/@mantine/hooks/src/use-resize-observer/use-resize-observer.ts @@ -30,7 +30,24 @@ export function useResizeObserver(options?: ResizeO frameID.current = requestAnimationFrame(() => { if (ref.current) { - setRect(entry.contentRect); + const boxSize = entry.borderBoxSize?.[0] || entry.contentBoxSize?.[0]; + if (boxSize) { + const width = boxSize.inlineSize; + const height = boxSize.blockSize; + + setRect({ + width, + height, + x: entry.contentRect.x, + y: entry.contentRect.y, + top: entry.contentRect.top, + left: entry.contentRect.left, + bottom: entry.contentRect.bottom, + right: entry.contentRect.right, + }); + } else { + setRect(entry.contentRect); + } } }); }