diff --git a/packages/react-strict-dom/src/native/modules/useStrictDOMElement.js b/packages/react-strict-dom/src/native/modules/useStrictDOMElement.js index 8825366a..5f0d5cd5 100644 --- a/packages/react-strict-dom/src/native/modules/useStrictDOMElement.js +++ b/packages/react-strict-dom/src/native/modules/useStrictDOMElement.js @@ -33,12 +33,16 @@ type Options = { tagName: string }; +// $FlowFixMe[unclear-type] +type Node = any; + +const originalGetBoundingClientRects = new WeakMap DOMRect>(); + export function useStrictDOMElement({ tagName }: Options): CallbackRef { const { scale: viewportScale } = useViewportScale(); const elementCallback = useElementCallback( React.useCallback( - // $FlowFixMe[unclear-type] - (node: any) => { + (node: Node) => { Object.defineProperty(node, 'nodeName', { value: tagName.toUpperCase(), writable: false @@ -86,9 +90,14 @@ export function useStrictDOMElement({ tagName }: Options): CallbackRef { } if (viewportScale !== 1) { - const getBoundingClientRect = node.getBoundingClientRect; + let getBoundingClientRect = originalGetBoundingClientRects.get(node); + if (getBoundingClientRect == null) { + getBoundingClientRect = node.getBoundingClientRect; + originalGetBoundingClientRects.set(node, getBoundingClientRect); + } + const getBoundingRectConst = getBoundingClientRect; node.getBoundingClientRect = function () { - const rect = getBoundingClientRect.call(node); + const rect = getBoundingRectConst.call(node); return new DOMRect( rect.x / viewportScale, diff --git a/packages/react-strict-dom/tests/html-test.native.js b/packages/react-strict-dom/tests/html-test.native.js index e12bbffc..c209aa8d 100644 --- a/packages/react-strict-dom/tests/html-test.native.js +++ b/packages/react-strict-dom/tests/html-test.native.js @@ -9,6 +9,15 @@ import React from 'react'; import { css, html, contexts } from 'react-strict-dom'; import { act, create } from 'react-test-renderer'; +global.DOMRect = class DOMRect { + constructor(x, y, width, height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } +}; + describe('', () => { beforeEach(() => { // avoid console messages for these tests @@ -1632,12 +1641,20 @@ describe('', () => { }); describe('viewport width', () => { - test('lengths are scaled according to viewport width', () => { - const { ViewportProvider } = contexts; - const ReactNative = require('../src/native/react-native'); + const ReactNative = require('../src/native/react-native'); + + beforeEach(() => { jest .spyOn(ReactNative, 'useWindowDimensions') .mockReturnValue({ width: 960 }); + }); + + afterEach(() => { + ReactNative.useWindowDimensions.mockRestore(); + }); + + test('lengths are scaled according to viewport width', () => { + const { ViewportProvider } = contexts; const styles = css.create({ container: { @@ -1667,8 +1684,36 @@ describe('', () => { // scale factor 0.75 expect(root.toJSON()).toMatchSnapshot('scaled lengths'); + }); - ReactNative.useWindowDimensions.mockRestore(); + test('getClientBoundingRect() returns scaled values', () => { + const { ViewportProvider } = contexts; + + function createNodeMock(element) { + const obj = {}; + obj.addEventListener_unstable = () => {}; + obj.blur = () => {}; + obj.focus = () => {}; + obj.removeEventListener_unstable = () => {}; + obj.getBoundingClientRect = () => new DOMRect(21, 21, 99, 99); + return obj; + } + + let scaledDomRect; + act(() => { + create( + + { + scaledDomRect = node.getBoundingClientRect(); + }} + /> + , + { createNodeMock } + ); + }); + + expect(scaledDomRect).toEqual(new DOMRect(28, 28, 132, 132)); }); }); });