Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement unstable_getBoundingClientRect in RN Fabric refs #26137

Merged
merged 1 commit into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions packages/react-native-renderer/src/ReactFabricHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const {
unstable_DiscreteEventPriority: FabricDiscretePriority,
unstable_getCurrentEventPriority: fabricGetCurrentEventPriority,
setNativeProps,
getBoundingClientRect: fabricGetBoundingClientRect,
} = nativeFabricUIManager;

const {get: getViewConfigForType} = ReactNativeViewConfigRegistry;
Expand Down Expand Up @@ -210,6 +211,20 @@ class ReactFabricHostComponent {
}
}

unstable_getBoundingClientRect(): DOMRect {
const {stateNode} = this._internalInstanceHandle;
if (stateNode != null) {
const rect = fabricGetBoundingClientRect(stateNode.node);

if (rect) {
return new DOMRect(rect[0], rect[1], rect[2], rect[3]);
}
}

// Empty rect if any of the above failed
return new DOMRect(0, 0, 0, 0);
}

setNativeProps(nativeProps: Object) {
if (__DEV__) {
warnForStyleProps(nativeProps, this.viewConfig.validAttributes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,19 @@ const RCTFabricUIManager = {

callback(10, 10, 100, 100);
}),
getBoundingClientRect: jest.fn(function getBoundingClientRect(node) {
if (typeof node !== 'object') {
throw new Error(
`Expected node to be an object, was passed "${typeof node}"`,
);
}

if (typeof node.viewName !== 'string') {
throw new Error('Expected node to be a host node.');
}

return [10, 10, 100, 100];
}),
measureLayout: jest.fn(function measureLayout(
node,
relativeNode,
Expand Down Expand Up @@ -181,3 +194,19 @@ const RCTFabricUIManager = {
};

global.nativeFabricUIManager = RCTFabricUIManager;

// DOMRect isn't provided by jsdom, but it's used by `ReactFabricHostComponent`.
// This is a basic implementation for testing.
global.DOMRect = class DOMRect {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}

toJSON() {
const {x, y, width, height} = this;
return {x, y, width, height};
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,33 @@ describe('measureLayout', () => {
});
});

describe('unstable_getBoundingClientRect', () => {
test('component.unstable_getBoundingClientRect() returns DOMRect', () => {
const [[fooRef]] = mockRenderKeys([['foo']]);

const rect = fooRef.unstable_getBoundingClientRect();

expect(nativeFabricUIManager.getBoundingClientRect).toHaveBeenCalledTimes(
1,
);
expect(rect.toJSON()).toMatchObject({
x: 10,
y: 10,
width: 100,
height: 100,
});
Comment on lines +212 to +217
Copy link
Contributor

Choose a reason for hiding this comment

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

Note that on web this is expected to have top/left/right/bottom values too: https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this is only the mock I defined for the React tests because jsdom doesn't provide DOMRect. In React Native, the implementation is spec-compliant (see https://github.com/facebook/react-native/blob/main/Libraries/DOM/Geometry/DOMRect.js :) )

});

test('unmounted.unstable_getBoundingClientRect() returns empty DOMRect', () => {
const [[fooRef]] = mockRenderKeys([['foo'], null]);

const rect = fooRef.unstable_getBoundingClientRect();

expect(nativeFabricUIManager.getBoundingClientRect).not.toHaveBeenCalled();
expect(rect.toJSON()).toMatchObject({x: 0, y: 0, width: 0, height: 0});
});
});

describe('setNativeProps', () => {
test('setNativeProps(...) invokes setNativeProps on Fabric UIManager', () => {
const {
Expand Down
8 changes: 8 additions & 0 deletions scripts/flow/react-native-host-hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,14 @@ declare var nativeFabricUIManager: {
onFail: () => void,
onSuccess: __MeasureLayoutOnSuccessCallback,
) => void,
getBoundingClientRect: (
node: Node,
) => [
/* x:*/ number,
/* y:*/ number,
/* width:*/ number,
/* height:*/ number,
],
findNodeAtPoint: (
node: Node,
locationX: number,
Expand Down