diff --git a/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js b/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js index 832ff1caf39..4a004b5ec29 100644 --- a/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js +++ b/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js @@ -336,7 +336,54 @@ const UIManagerJS: UIManagerJSInterface & {[string]: any} = { height: number, ) => void, ): void => { - raiseSoftError('findSubviewIn'); + if (reactTag == null) { + console.error( + `findSubviewIn() noop: Cannot be called with ${String( + reactTag, + )} reactTag`, + ); + return; + } + + const FabricUIManager = nullthrows(getFabricUIManager()); + const shadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag); + + if (!shadowNode) { + console.error( + `findSubviewIn() noop: Cannot find view with reactTag ${reactTag}`, + ); + return; + } + + FabricUIManager.findNodeAtPoint( + shadowNode, + point[0], + point[1], + function (internalInstanceHandle) { + if (internalInstanceHandle == null) { + console.error('findSubviewIn(): Cannot find node at point'); + return; + } + + let instanceHandle: Object = internalInstanceHandle; + let node = instanceHandle.stateNode.node; + + if (!node) { + console.error('findSubviewIn(): Cannot find node at point'); + return; + } + + let nativeViewTag = (instanceHandle.stateNode.canonical + .nativeTag: number); + + FabricUIManager.measure( + node, + function (x, y, width, height, pageX, pageY) { + callback(nativeViewTag, pageX, pageY, width, height); + }, + ); + }, + ); }, viewIsDescendantOf: ( reactTag: ?number, diff --git a/packages/react-native/Libraries/ReactNative/FabricUIManager.js b/packages/react-native/Libraries/ReactNative/FabricUIManager.js index 99c8c438d49..8233a12f3fe 100644 --- a/packages/react-native/Libraries/ReactNative/FabricUIManager.js +++ b/packages/react-native/Libraries/ReactNative/FabricUIManager.js @@ -64,6 +64,12 @@ export interface Spec { commandName: string, args: Array, ) => void; + +findNodeAtPoint: ( + node: Node, + locationX: number, + locationY: number, + callback: (instanceHandle: ?InternalInstanceHandle) => void, + ) => void; /** * Support methods for the DOM-compatible APIs. diff --git a/packages/react-native/Libraries/ReactNative/__mocks__/FabricUIManager.js b/packages/react-native/Libraries/ReactNative/__mocks__/FabricUIManager.js index b2a8c545b35..c51df7240c1 100644 --- a/packages/react-native/Libraries/ReactNative/__mocks__/FabricUIManager.js +++ b/packages/react-native/Libraries/ReactNative/__mocks__/FabricUIManager.js @@ -292,6 +292,15 @@ const FabricUIManagerMock: IFabricUIManagerMock = { findShadowNodeByTag_DEPRECATED: jest.fn((reactTag: number): ?Node => {}), + findNodeAtPoint: jest.fn( + ( + node: Node, + locationX: number, + locationY: number, + callback: (instanceHandle: ?InternalInstanceHandle) => void, + ): void => {}, + ), + getBoundingClientRect: jest.fn( ( node: Node, diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 99104b4063c..7f7729af0e9 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -6564,6 +6564,12 @@ export interface Spec { commandName: string, args: Array ) => void; + +findNodeAtPoint: ( + node: Node, + locationX: number, + locationY: number, + callback: (instanceHandle: ?InternalInstanceHandle) => void + ) => void; +getParentNode: (node: Node) => ?InternalInstanceHandle; +getChildNodes: (node: Node) => $ReadOnlyArray; +isConnected: (node: Node) => boolean;