Skip to content

Commit d6f5236

Browse files
sammy-SCfacebook-github-bot
authored andcommitted
Add getDefinedEventHandlers API
Summary: Add Fantom.getDefinedEventHandlers(element) which returns the names of event handlers registered on a component's shadow node. Changelog: [Internal] Differential Revision: D94936370
1 parent dd6980c commit d6f5236

5 files changed

Lines changed: 223 additions & 3 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
8+
* @format
9+
*/
10+
11+
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
12+
13+
import * as Fantom from '@react-native/fantom';
14+
import * as React from 'react';
15+
import {createRef} from 'react';
16+
import {View} from 'react-native';
17+
18+
describe('Fantom.getDefinedEventHandlers', () => {
19+
it('returns event handler names registered on a view', () => {
20+
const root = Fantom.createRoot();
21+
const ref = createRef<React.ElementRef<typeof View>>();
22+
23+
Fantom.runTask(() => {
24+
root.render(
25+
<View ref={ref} onLayout={() => {}} onTouchStart={() => {}} />,
26+
);
27+
});
28+
29+
const handlers = Fantom.getDefinedEventHandlers(ref);
30+
expect(handlers).toContain('onLayout');
31+
expect(handlers).toContain('onTouchStart');
32+
});
33+
34+
it('returns an empty array when no event handlers are registered', () => {
35+
const root = Fantom.createRoot();
36+
const ref = createRef<React.ElementRef<typeof View>>();
37+
38+
Fantom.runTask(() => {
39+
root.render(<View ref={ref} />);
40+
});
41+
42+
expect(Fantom.getDefinedEventHandlers(ref)).toEqual([]);
43+
});
44+
45+
it('returns multiple event handler names when multiple are registered', () => {
46+
const root = Fantom.createRoot();
47+
const ref = createRef<React.ElementRef<typeof View>>();
48+
49+
Fantom.runTask(() => {
50+
root.render(
51+
<View
52+
ref={ref}
53+
onLayout={() => {}}
54+
onTouchStart={() => {}}
55+
onTouchEnd={() => {}}
56+
onTouchCancel={() => {}}
57+
onClick={() => {}}
58+
/>,
59+
);
60+
});
61+
62+
const handlers = Fantom.getDefinedEventHandlers(ref);
63+
expect(handlers).toContain('onLayout');
64+
expect(handlers).toContain('onTouchStart');
65+
expect(handlers).toContain('onTouchEnd');
66+
expect(handlers).toContain('onTouchCancel');
67+
expect(handlers).toContain('onClick');
68+
});
69+
});

packages/react-native/src/private/testing/fantom/specs/NativeFantom.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ interface Spec extends TurboModule {
9999
width: number,
100100
) => void;
101101
takeMountingManagerLogs: (surfaceId: RootTag) => Array<string>;
102+
getDefinedEventHandlers: (
103+
shadowNode: unknown /* ShadowNode */,
104+
) => Array<string>;
102105
getDirectManipulationProps: (
103106
shadowNode: unknown /* ShadowNode */,
104107
) => Readonly<{

private/react-native-fantom/src/index.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,20 @@ export function unstable_getFabricUpdateProps(nodeOrRef: NodeOrRef): Readonly<{
237237
return NativeFantom.getFabricUpdateProps(shadowNode);
238238
}
239239

240+
/**
241+
* Returns the names of event handlers registered on a component's shadow node.
242+
*
243+
* @param nodeOrRef - The node or ref for which to retrieve event handlers.
244+
* @returns Array of event handler prop names (e.g. `['onLayout', 'onTouchStart']`)
245+
*/
246+
export function getDefinedEventHandlers(
247+
nodeOrRef: NodeOrRef,
248+
): ReadonlyArray<string> {
249+
const node = getNode(nodeOrRef);
250+
const shadowNode = getNativeNodeReference(node);
251+
return NativeFantom.getDefinedEventHandlers(shadowNode);
252+
}
253+
240254
/**
241255
* Simulates running a task on the UI thread and forces side effect to drain
242256
* the event queue, scheduling events to be dispatched to JavaScript.

private/react-native-fantom/tester/src/NativeFantom.cpp

Lines changed: 133 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <react/debug/flags.h>
1414
#include <react/renderer/components/modal/ModalHostViewShadowNode.h>
1515
#include <react/renderer/components/scrollview/ScrollViewShadowNode.h>
16+
#include <react/renderer/components/view/BaseViewProps.h>
17+
#include <react/renderer/components/view/primitives.h>
1618
#include <react/renderer/uimanager/UIManagerBinding.h>
1719
#include <fstream>
1820
#include <iostream>
@@ -86,7 +88,8 @@ std::string NativeFantom::getRenderedOutput(
8688
SurfaceId surfaceId,
8789
NativeFantomGetRenderedOutputRenderFormatOptions options) {
8890
RenderFormatOptions formatOptions{
89-
options.includeRoot, options.includeLayoutMetrics};
91+
.includeRoot = options.includeRoot,
92+
.includeLayoutMetrics = options.includeLayoutMetrics};
9093

9194
auto viewTree = appDelegate_.mountingManager_->getViewTree(surfaceId);
9295
return appDelegate_.mountingManager_->renderer()->render(
@@ -107,6 +110,133 @@ jsi::Object NativeFantom::getDirectManipulationProps(
107110
return facebook::jsi::valueFromDynamic(runtime, props).asObject(runtime);
108111
}
109112

113+
std::vector<std::string> NativeFantom::getDefinedEventHandlers(
114+
jsi::Runtime& /*runtime*/,
115+
const std::shared_ptr<const ShadowNode>& shadowNode) {
116+
std::vector<std::string> result;
117+
result.reserve(36);
118+
119+
const auto* viewProps =
120+
dynamic_cast<const BaseViewProps*>(shadowNode->getProps().get());
121+
if (viewProps == nullptr) {
122+
return result;
123+
}
124+
125+
if (viewProps->onLayout) {
126+
result.emplace_back("onLayout");
127+
}
128+
129+
const auto& events = viewProps->events;
130+
131+
if (events[ViewEvents::Offset::PointerEnter]) {
132+
result.emplace_back("onPointerEnter");
133+
}
134+
if (events[ViewEvents::Offset::PointerMove]) {
135+
result.emplace_back("onPointerMove");
136+
}
137+
if (events[ViewEvents::Offset::PointerLeave]) {
138+
result.emplace_back("onPointerLeave");
139+
}
140+
if (events[ViewEvents::Offset::MoveShouldSetResponder]) {
141+
result.emplace_back("onMoveShouldSetResponder");
142+
}
143+
if (events[ViewEvents::Offset::MoveShouldSetResponderCapture]) {
144+
result.emplace_back("onMoveShouldSetResponderCapture");
145+
}
146+
if (events[ViewEvents::Offset::StartShouldSetResponder]) {
147+
result.emplace_back("onStartShouldSetResponder");
148+
}
149+
if (events[ViewEvents::Offset::StartShouldSetResponderCapture]) {
150+
result.emplace_back("onStartShouldSetResponderCapture");
151+
}
152+
if (events[ViewEvents::Offset::ResponderGrant]) {
153+
result.emplace_back("onResponderGrant");
154+
}
155+
if (events[ViewEvents::Offset::ResponderReject]) {
156+
result.emplace_back("onResponderReject");
157+
}
158+
if (events[ViewEvents::Offset::ResponderStart]) {
159+
result.emplace_back("onResponderStart");
160+
}
161+
if (events[ViewEvents::Offset::ResponderEnd]) {
162+
result.emplace_back("onResponderEnd");
163+
}
164+
if (events[ViewEvents::Offset::ResponderRelease]) {
165+
result.emplace_back("onResponderRelease");
166+
}
167+
if (events[ViewEvents::Offset::ResponderMove]) {
168+
result.emplace_back("onResponderMove");
169+
}
170+
if (events[ViewEvents::Offset::ResponderTerminate]) {
171+
result.emplace_back("onResponderTerminate");
172+
}
173+
if (events[ViewEvents::Offset::ResponderTerminationRequest]) {
174+
result.emplace_back("onResponderTerminationRequest");
175+
}
176+
if (events[ViewEvents::Offset::ShouldBlockNativeResponder]) {
177+
result.emplace_back("onShouldBlockNativeResponder");
178+
}
179+
if (events[ViewEvents::Offset::TouchStart]) {
180+
result.emplace_back("onTouchStart");
181+
}
182+
if (events[ViewEvents::Offset::TouchMove]) {
183+
result.emplace_back("onTouchMove");
184+
}
185+
if (events[ViewEvents::Offset::TouchEnd]) {
186+
result.emplace_back("onTouchEnd");
187+
}
188+
if (events[ViewEvents::Offset::TouchCancel]) {
189+
result.emplace_back("onTouchCancel");
190+
}
191+
if (events[ViewEvents::Offset::PointerEnterCapture]) {
192+
result.emplace_back("onPointerEnterCapture");
193+
}
194+
if (events[ViewEvents::Offset::PointerLeaveCapture]) {
195+
result.emplace_back("onPointerLeaveCapture");
196+
}
197+
if (events[ViewEvents::Offset::PointerMoveCapture]) {
198+
result.emplace_back("onPointerMoveCapture");
199+
}
200+
if (events[ViewEvents::Offset::PointerOver]) {
201+
result.emplace_back("onPointerOver");
202+
}
203+
if (events[ViewEvents::Offset::PointerOut]) {
204+
result.emplace_back("onPointerOut");
205+
}
206+
if (events[ViewEvents::Offset::PointerOverCapture]) {
207+
result.emplace_back("onPointerOverCapture");
208+
}
209+
if (events[ViewEvents::Offset::PointerOutCapture]) {
210+
result.emplace_back("onPointerOutCapture");
211+
}
212+
if (events[ViewEvents::Offset::Click]) {
213+
result.emplace_back("onClick");
214+
}
215+
if (events[ViewEvents::Offset::ClickCapture]) {
216+
result.emplace_back("onClickCapture");
217+
}
218+
if (events[ViewEvents::Offset::GotPointerCapture]) {
219+
result.emplace_back("onGotPointerCapture");
220+
}
221+
if (events[ViewEvents::Offset::LostPointerCapture]) {
222+
result.emplace_back("onLostPointerCapture");
223+
}
224+
if (events[ViewEvents::Offset::PointerDown]) {
225+
result.emplace_back("onPointerDown");
226+
}
227+
if (events[ViewEvents::Offset::PointerDownCapture]) {
228+
result.emplace_back("onPointerDownCapture");
229+
}
230+
if (events[ViewEvents::Offset::PointerUp]) {
231+
result.emplace_back("onPointerUp");
232+
}
233+
if (events[ViewEvents::Offset::PointerUpCapture]) {
234+
result.emplace_back("onPointerUpCapture");
235+
}
236+
237+
return result;
238+
}
239+
110240
jsi::Object NativeFantom::getFabricUpdateProps(
111241
jsi::Runtime& runtime,
112242
const std::shared_ptr<const ShadowNode>& shadowNode) {
@@ -124,10 +254,10 @@ void NativeFantom::enqueueNativeEvent(
124254
std::optional<bool> isUnique) {
125255
if (isUnique.value_or(false)) {
126256
shadowNode->getEventEmitter()->dispatchUniqueEvent(
127-
std::move(type), payload.value_or(folly::dynamic::object()));
257+
type, payload.value_or(folly::dynamic::object()));
128258
} else {
129259
shadowNode->getEventEmitter()->dispatchEvent(
130-
std::move(type),
260+
type,
131261
payload.value_or(folly::dynamic::object()),
132262
category.value_or(RawEvent::Category::Unspecified));
133263
}

private/react-native-fantom/tester/src/NativeFantom.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ class NativeFantom : public NativeFantomCxxSpec<NativeFantom> {
114114

115115
jsi::Object getDirectManipulationProps(jsi::Runtime &runtime, const std::shared_ptr<const ShadowNode> &shadowNode);
116116

117+
std::vector<std::string> getDefinedEventHandlers(
118+
jsi::Runtime &runtime,
119+
const std::shared_ptr<const ShadowNode> &shadowNode);
120+
117121
jsi::Object getFabricUpdateProps(jsi::Runtime &runtime, const std::shared_ptr<const ShadowNode> &shadowNode);
118122

119123
void enqueueModalSizeUpdate(

0 commit comments

Comments
 (0)