From a8a25d66ea5ebfd436bbc11941f5d24df6bf64de Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Sat, 28 Jan 2017 10:37:49 -0800 Subject: [PATCH] [fix] measure CSS layout independent of transforms Fix #332 --- src/apis/UIManager/__tests__/index-test.js | 107 --------------------- src/apis/UIManager/index.js | 20 ++-- 2 files changed, 14 insertions(+), 113 deletions(-) diff --git a/src/apis/UIManager/__tests__/index-test.js b/src/apis/UIManager/__tests__/index-test.js index 19afc74c..5e8103a8 100644 --- a/src/apis/UIManager/__tests__/index-test.js +++ b/src/apis/UIManager/__tests__/index-test.js @@ -10,114 +10,7 @@ const createNode = (style = {}) => { return root; }; -let defaultBodyMargin; - describe('apis/UIManager', () => { - beforeEach(() => { - // remove default body margin so we can predict the measured offsets - defaultBodyMargin = document.body.style.margin; - document.body.style.margin = 0; - }); - - afterEach(() => { - document.body.style.margin = defaultBodyMargin; - }); - - describe('measure', () => { - test('provides correct layout to callback', (done) => { - const node = createNode({ height: '5000px', left: '100px', position: 'relative', top: '100px', width: '5000px' }); - document.body.appendChild(node); - - node.getBoundingClientRect = jest.fn(() => ({ width: 5000, height: 5000, top: 100, left: 100 })); - - UIManager.measure(node, (x, y, width, height, pageX, pageY) => { - expect(x).toEqual(100); - expect(y).toEqual(100); - expect(width).toEqual(5000); - expect(height).toEqual(5000); - expect(pageX).toEqual(100); - expect(pageY).toEqual(100); - done(); - document.body.removeChild(node); - }); - }); - - test('provides correct layout to callback', (done) => { - const node = createNode({ height: '5000px', left: '100px', position: 'relative', top: '100px', width: '5000px' }); - document.body.appendChild(node); - - // test values account for scroll position - window.scrollTo(200, 200); - node.getBoundingClientRect = jest.fn(() => ({ width: 5000, height: 5000, top: -100, left: -100 })); - node.parentNode.getBoundingClientRect = jest.fn(() => ({ top: -200, left: -200 })); - - UIManager.measure(node, (x, y, width, height, pageX, pageY) => { - expect(x).toEqual(100); - expect(y).toEqual(100); - expect(width).toEqual(5000); - expect(height).toEqual(5000); - expect(pageX).toEqual(-100); - expect(pageY).toEqual(-100); - done(); - document.body.removeChild(node); - }); - }); - }); - - describe('measureLayout', () => { - test('provides correct layout to onSuccess callback', (done) => { - const node = createNode({ height: '10px', width: '10px' }); - const middle = createNode({ padding: '20px' }); - const context = createNode({ padding: '20px' }); - middle.appendChild(node); - context.appendChild(middle); - document.body.appendChild(context); - - node.getBoundingClientRect = jest.fn(() => ({ - width: 10, - height: 10, - top: 40, - left: 40 - })); - - UIManager.measureLayout(node, context, () => {}, (x, y, width, height) => { - expect(x).toEqual(40); - expect(y).toEqual(40); - expect(width).toEqual(10); - expect(height).toEqual(10); - done(); - document.body.removeChild(context); - }); - }); - }); - - describe('measureInWindow', () => { - test('provides correct layout to callback', (done) => { - const node = createNode({ height: '10px', width: '10px' }); - const middle = createNode({ padding: '20px' }); - const context = createNode({ padding: '20px' }); - middle.appendChild(node); - context.appendChild(middle); - document.body.appendChild(context); - - node.getBoundingClientRect = jest.fn(() => ({ - width: 10, - height: 10, - top: 40, - left: 40 - })); - - UIManager.measureInWindow(node, (x, y, width, height) => { - expect(x).toEqual(40); - expect(y).toEqual(40); - expect(width).toEqual(10); - expect(height).toEqual(10); - done(); - document.body.removeChild(context); - }); - }); - }); - describe('updateView', () => { const componentStub = { _reactInternalInstance: { diff --git a/src/apis/UIManager/index.js b/src/apis/UIManager/index.js index 3e93a7eb..6622cdef 100644 --- a/src/apis/UIManager/index.js +++ b/src/apis/UIManager/index.js @@ -1,11 +1,19 @@ import asap from 'asap'; import CSSPropertyOperations from 'react-dom/lib/CSSPropertyOperations'; -const _measureLayout = (node, relativeToNativeNode, callback) => { +const getRect = (node) => { + const height = node.offsetHeight; + const left = node.offsetLeft; + const top = node.offsetTop; + const width = node.offsetWidth; + return { height, left, top, width }; +}; + +const measureLayout = (node, relativeToNativeNode, callback) => { asap(() => { const relativeNode = relativeToNativeNode || node.parentNode; - const relativeRect = relativeNode.getBoundingClientRect(); - const { height, left, top, width } = node.getBoundingClientRect(); + const relativeRect = getRect(relativeNode); + const { height, left, top, width } = getRect(node); const x = left - relativeRect.left; const y = top - relativeRect.top; callback(x, y, width, height, left, top); @@ -22,17 +30,17 @@ const UIManager = { }, measure(node, callback) { - _measureLayout(node, null, callback); + measureLayout(node, null, callback); }, measureInWindow(node, callback) { - const { height, left, top, width } = node.getBoundingClientRect(); + const { height, left, top, width } = getRect(node); callback(left, top, width, height); }, measureLayout(node, relativeToNativeNode, onFail, onSuccess) { const relativeTo = relativeToNativeNode || node.parentNode; - _measureLayout(node, relativeTo, onSuccess); + measureLayout(node, relativeTo, onSuccess); }, updateView(node, props, component /* only needed to surpress React errors in development */) {