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

[React Native] Add getInspectorDataForViewAtPoint #18233

Merged
merged 11 commits into from
Mar 11, 2020
70 changes: 46 additions & 24 deletions packages/react-native-renderer/src/ReactNativeFiberInspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import type {Fiber} from 'react-reconciler/src/ReactFiber';
import type {TouchedViewDataAtPoint} from './ReactNativeTypes';
import type {TouchedViewDataAtPoint, InspectorData} from './ReactNativeTypes';

import {
findCurrentHostFiber,
Expand Down Expand Up @@ -82,16 +82,42 @@ if (__DEV__) {
const createHierarchy = function(fiberHierarchy) {
return fiberHierarchy.map(fiber => ({
name: getComponentName(fiber.type),
getInspectorData: findNodeHandle => ({
measure: callback =>
UIManager.measure(getHostNode(fiber, findNodeHandle), callback),
props: getHostProps(fiber),
source: fiber._debugSource,
}),
getInspectorData: findNodeHandle => {
return {
props: getHostProps(fiber),
source: fiber._debugSource,
measure: callback => {
// If this is Fabric, we'll find a ShadowNode and use that to measure.
const hostFiber = findCurrentHostFiber(fiber);
const shadowNode =
hostFiber != null &&
hostFiber.stateNode !== null &&
hostFiber.stateNode.node;

if (shadowNode) {
nativeFabricUIManager.measure(shadowNode, function(
x,
y,
width,
height,
pageX,
pageY,
) {
callback(x, y, width, height, pageX, pageY);
});
} else {
return UIManager.measure(
getHostNode(fiber, findNodeHandle),
callback,
);
}
},
};
},
}));
};

const getInspectorDataForInstance = function(closestInstance, frame): Object {
const getInspectorDataForInstance = function(closestInstance): InspectorData {
// Handle case where user clicks outside of ReactNative
if (!closestInstance) {
return {
Expand All @@ -112,7 +138,6 @@ if (__DEV__) {

return {
hierarchy,
frame,
props,
selection,
source,
Expand Down Expand Up @@ -156,35 +181,31 @@ if (__DEV__) {
callback: (viewData: TouchedViewDataAtPoint) => mixed,
): void {
let closestInstance = null;
let frame = {
left: 0,
top: 0,
width: 0,
height: 0,
};

if (inspectedView._internalInstanceHandle != null) {
// For Fabric we can look up the instance handle directly and measure it.
nativeFabricUIManager.findNodeAtPoint(
inspectedView._internalInstanceHandle.stateNode.node,
locationX,
locationY,
shadowNode => {
if (shadowNode == null) {
callback(getInspectorDataForInstance(closestInstance, frame));
internalInstanceHandle => {
if (internalInstanceHandle == null) {
callback({
pointerY: locationY,
frame: {left: 0, top: 0, width: 0, height: 0},
...getInspectorDataForInstance(closestInstance),
});
}

closestInstance =
shadowNode.stateNode.canonical._internalInstanceHandle;
internalInstanceHandle.stateNode.canonical._internalInstanceHandle;
nativeFabricUIManager.measure(
shadowNode.stateNode.node,
internalInstanceHandle.stateNode.node,
(x, y, width, height, pageX, pageY) => {
const inspectorData = getInspectorDataForInstance(
closestInstance,
);
callback({
...inspectorData,
pointerY: locationY,
frame: {left: pageX, top: pageY, width, height},
...getInspectorDataForInstance(closestInstance),
});
},
);
Expand All @@ -201,6 +222,7 @@ if (__DEV__) {
);
callback({
...inspectorData,
pointerY: locationY,
frame: {left, top, width, height},
touchedViewTag: nativeViewTag,
});
Expand Down
48 changes: 35 additions & 13 deletions packages/react-native-renderer/src/ReactNativeTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,22 +100,44 @@ type SecretInternalsType = {
...
};

type InspectorDataProps = $ReadOnly<{
[propName: string]: string,
...,
}>;

type InspectorDataSource = $ReadOnly<{|
fileName?: string,
lineNumber?: number,
|}>;

type InspectorDataGetter = (
(componentOrHandle: any) => ?number,
) => $ReadOnly<{|
measure: Function,
props: InspectorDataProps,
source: InspectorDataSource,
|}>;

export type InspectorData = $ReadOnly<{
rickhanlonii marked this conversation as resolved.
Show resolved Hide resolved
hierarchy: Array<{|
name: ?string,
getInspectorData: InspectorDataGetter,
|}>,
selection: ?number,
props: InspectorDataProps,
source: ?InspectorDataSource,
}>;

export type TouchedViewDataAtPoint = $ReadOnly<{
hierarchy?: ?Array<{|name: string|}>,
pointerY: number,
touchedViewTag?: ?number,
props: $ReadOnly<{[propName: string]: string, ...}>,
selection: number,
source: $ReadOnly<{|
fileName?: string,
lineNumber?: number,
|}>,
frame?: ?$ReadOnly<{|
top?: ?number,
left?: ?number,
width?: ?number,
height: ?number,
touchedViewTag?: number,
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is this used for? I thought we're getting rid of all these tags so seems odd to add new users of it and expose more implementation details about to change to future work. Shouldn't there be another mechanism?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is to support the current devtools behavior in Paper where tapping in the app will open the element in the tree. I was thinking we would add this now to prevent the regression, while we figure out the correct solution for Fabric and Paper moving forward.

https://github.com/facebook/react-native/blob/84b7b8d07645b528c784e0bacecf1293797e0ec7/Libraries/Inspector/Inspector.js#L209-L212

What do you think?

frame: $ReadOnly<{|
top: number,
left: number,
width: number,
height: number,
|}>,
...InspectorData,
}>;

/**
Expand Down
2 changes: 1 addition & 1 deletion scripts/error-codes/codes.json
Original file line number Diff line number Diff line change
Expand Up @@ -347,5 +347,5 @@
"346": "An event responder context was used outside of an event cycle.",
"347": "Maps are not valid as a React child (found: %s). Consider converting children to an array of keyed ReactElements instead.",
"348": "ensureListeningTo(): received a container that was not an element node. This is likely a bug in React.",
"349": "getInspectorDataForViewAtPoint() is not available in production"
"349": "getInspectorDataForViewAtPoint() is not available in production."
}