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 clientTop/clientLeft in ReadOnlyElement #39308

Closed
wants to merge 3 commits into from
Closed
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
52 changes: 47 additions & 5 deletions packages/react-native/Libraries/DOM/Nodes/ReadOnlyElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,55 @@ export default class ReadOnlyElement extends ReadOnlyNode {
}

get clientHeight(): number {
throw new TypeError('Unimplemented');
const node = getShadowNode(this);

if (node != null) {
const innerSize = nullthrows(getFabricUIManager()).getInnerSize(node);
if (innerSize != null) {
return innerSize[1];
}
}

return 0;
}

get clientLeft(): number {
throw new TypeError('Unimplemented');
const node = getShadowNode(this);

if (node != null) {
const borderSize = nullthrows(getFabricUIManager()).getBorderSize(node);
if (borderSize != null) {
return borderSize[3];
}
}

return 0;
}

get clientTop(): number {
throw new TypeError('Unimplemented');
const node = getShadowNode(this);

if (node != null) {
const borderSize = nullthrows(getFabricUIManager()).getBorderSize(node);
if (borderSize != null) {
return borderSize[0];
}
}

return 0;
}

get clientWidth(): number {
throw new TypeError('Unimplemented');
const node = getShadowNode(this);

if (node != null) {
const innerSize = nullthrows(getFabricUIManager()).getInnerSize(node);
if (innerSize != null) {
return innerSize[0];
}
}

return 0;
}

get firstElementChild(): ReadOnlyElement | null {
Expand Down Expand Up @@ -137,7 +173,13 @@ export default class ReadOnlyElement extends ReadOnlyNode {
}

get tagName(): string {
throw new TypeError('Unimplemented');
const node = getShadowNode(this);

if (node != null) {
return nullthrows(getFabricUIManager()).getTagName(node);
}

return '';
}

get textContent(): string | null {
Expand Down
16 changes: 16 additions & 0 deletions packages/react-native/Libraries/ReactNative/FabricUIManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ export interface Spec {
+getScrollPosition: (
node: Node,
) => ?[/* scrollLeft: */ number, /* scrollTop: */ number];
+getInnerSize: (node: Node) => ?[/* width: */ number, /* height: */ number];
+getBorderSize: (
node: Node,
) => ?[
/* topWidth: */ number,
/* rightWidth: */ number,
/* bottomWidth: */ number,
/* leftWidth: */ number,
];
+getTagName: (node: Node) => string;

/**
* Support methods for the Pointer Capture APIs.
Expand Down Expand Up @@ -131,6 +141,12 @@ const CACHED_PROPERTIES = [
'getBoundingClientRect',
'getOffset',
'getScrollPosition',
'getInnerSize',
'getBorderSize',
'getTagName',
'hasPointerCapture',
'setPointerCapture',
'releasePointerCapture',
];

// This is exposed as a getter because apps using the legacy renderer AND
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,12 +199,15 @@ const FabricUIManagerMock: IFabricUIManagerMock = {
});
},
),

cloneNode: jest.fn((node: Node): Node => {
return toNode({...fromNode(node)});
}),

cloneNodeWithNewChildren: jest.fn((node: Node): Node => {
return toNode({...fromNode(node), children: []});
}),

cloneNodeWithNewProps: jest.fn((node: Node, newProps: NodeProps): Node => {
return toNode({
...fromNode(node),
Expand All @@ -214,6 +217,7 @@ const FabricUIManagerMock: IFabricUIManagerMock = {
},
});
}),

cloneNodeWithNewChildrenAndProps: jest.fn(
(node: Node, newProps: NodeProps): Node => {
return toNode({
Expand All @@ -226,35 +230,42 @@ const FabricUIManagerMock: IFabricUIManagerMock = {
});
},
),

createChildSet: jest.fn((rootTag: RootTag): NodeSet => {
return [];
}),

appendChild: jest.fn((parentNode: Node, child: Node): Node => {
// Although the signature returns a Node, React expects this to be mutating.
fromNode(parentNode).children.push(child);
return parentNode;
}),

appendChildToSet: jest.fn((childSet: NodeSet, child: Node): void => {
childSet.push(child);
}),

completeRoot: jest.fn((rootTag: RootTag, childSet: NodeSet): void => {
commitHooks.forEach(hook =>
hook.shadowTreeWillCommit(rootTag, roots.get(rootTag), childSet),
);
roots.set(rootTag, childSet);
}),

measure: jest.fn((node: Node, callback: MeasureOnSuccessCallback): void => {
ensureHostNode(node);

callback(10, 10, 100, 100, 0, 0);
}),

measureInWindow: jest.fn(
(node: Node, callback: MeasureInWindowOnSuccessCallback): void => {
ensureHostNode(node);

callback(10, 10, 100, 100);
},
),

measureLayout: jest.fn(
(
node: Node,
Expand All @@ -268,15 +279,19 @@ const FabricUIManagerMock: IFabricUIManagerMock = {
onSuccess(1, 1, 100, 100);
},
),

configureNextLayoutAnimation: jest.fn(
(
config: LayoutAnimationConfig,
callback: () => void, // check what is returned here
errorCallback: () => void,
): void => {},
),

sendAccessibilityEvent: jest.fn((node: Node, eventType: string): void => {}),

findShadowNodeByTag_DEPRECATED: jest.fn((reactTag: number): ?Node => {}),

getBoundingClientRect: jest.fn(
(
node: Node,
Expand Down Expand Up @@ -312,13 +327,19 @@ const FabricUIManagerMock: IFabricUIManagerMock = {
return [x, y, width, height];
},
),

hasPointerCapture: jest.fn((node: Node, pointerId: number): boolean => false),

setPointerCapture: jest.fn((node: Node, pointerId: number): void => {}),

releasePointerCapture: jest.fn((node: Node, pointerId: number): void => {}),

setNativeProps: jest.fn((node: Node, newProps: NodeProps): void => {}),

dispatchCommand: jest.fn(
(node: Node, commandName: string, args: Array<mixed>): void => {},
),

getParentNode: jest.fn((node: Node): ?InternalInstanceHandle => {
const ancestors = getAncestorsInCurrentTree(node);
if (ancestors == null || ancestors.length - 2 < 0) {
Expand All @@ -329,6 +350,7 @@ const FabricUIManagerMock: IFabricUIManagerMock = {
const parentInCurrentTree = fromNode(parentOfParent).children[position];
return fromNode(parentInCurrentTree).instanceHandle;
}),

getChildNodes: jest.fn(
(node: Node): $ReadOnlyArray<InternalInstanceHandle> => {
const nodeInCurrentTree = getNodeInCurrentTree(node);
Expand All @@ -342,9 +364,11 @@ const FabricUIManagerMock: IFabricUIManagerMock = {
);
},
),

isConnected: jest.fn((node: Node): boolean => {
return getNodeInCurrentTree(node) != null;
}),

getTextContent: jest.fn((node: Node): string => {
const nodeInCurrentTree = getNodeInCurrentTree(node);

Expand All @@ -366,6 +390,7 @@ const FabricUIManagerMock: IFabricUIManagerMock = {
}
return result;
}),

compareDocumentPosition: jest.fn((node: Node, otherNode: Node): number => {
/* eslint-disable no-bitwise */
const ReadOnlyNode = require('../../DOM/Nodes/ReadOnlyNode').default;
Expand Down Expand Up @@ -419,6 +444,7 @@ const FabricUIManagerMock: IFabricUIManagerMock = {

return ReadOnlyNode.DOCUMENT_POSITION_FOLLOWING;
}),

getOffset: jest.fn(
(
node: Node,
Expand Down Expand Up @@ -469,6 +495,7 @@ const FabricUIManagerMock: IFabricUIManagerMock = {
];
},
),

getScrollPosition: jest.fn(
(node: Node): ?[/* scrollLeft: */ number, /* scrollTop: */ number] => {
ensureHostNode(node);
Expand Down Expand Up @@ -497,6 +524,81 @@ const FabricUIManagerMock: IFabricUIManagerMock = {
},
),

getInnerSize: jest.fn(
(node: Node): ?[/* width: */ number, /* height: */ number] => {
ensureHostNode(node);

const nodeInCurrentTree = getNodeInCurrentTree(node);
const currentProps =
nodeInCurrentTree != null ? fromNode(nodeInCurrentTree).props : null;
if (currentProps == null) {
return null;
}

const innerSizeForTests: ?{
width: number,
height: number,
...
} =
// $FlowExpectedError[prop-missing]
currentProps.__innerSizeForTests;

if (innerSizeForTests == null) {
return null;
}

const {width, height} = innerSizeForTests;
return [width, height];
},
),

getBorderSize: jest.fn(
(
node: Node,
): ?[
/* topWidth: */ number,
/* rightWidth: */ number,
/* bottomWidth: */ number,
/* leftWidth: */ number,
] => {
ensureHostNode(node);

const nodeInCurrentTree = getNodeInCurrentTree(node);
const currentProps =
nodeInCurrentTree != null ? fromNode(nodeInCurrentTree).props : null;
if (currentProps == null) {
return null;
}

const borderSizeForTests: ?{
topWidth?: number,
rightWidth?: number,
bottomWidth?: number,
leftWidth?: number,
...
} =
// $FlowExpectedError[prop-missing]
currentProps.__borderSizeForTests;

if (borderSizeForTests == null) {
return null;
}

const {
topWidth = 0,
rightWidth = 0,
bottomWidth = 0,
leftWidth = 0,
} = borderSizeForTests;
return [topWidth, rightWidth, bottomWidth, leftWidth];
},
),

getTagName: jest.fn((node: Node): string => {
ensureHostNode(node);
return 'RN:' + fromNode(node).viewName;
}),

__getInstanceHandleFromNode(node: Node): InternalInstanceHandle {
return fromNode(node).instanceHandle;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ struct LayoutMetrics {
frame.size.height - contentInsets.top - contentInsets.bottom}};
}

// Origin: the outer border of the node.
// Size: includes content and padding (but no borders).
Rect getPaddingFrame() const {
return Rect{
Point{borderWidth.left, borderWidth.top},
Size{
frame.size.width - borderWidth.left - borderWidth.right,
frame.size.height - borderWidth.top - borderWidth.bottom}};
}

bool operator==(const LayoutMetrics& rhs) const {
return std::tie(
this->frame,
Expand Down
Loading