Skip to content

Commit 73b893c

Browse files
rubennortefacebook-github-bot
authored andcommitted
Add tests for event dispatching bubbling to document and documentElement
Summary: Changelog: [internal] TSIA Differential Revision: D102604056
1 parent 0c153e2 commit 73b893c

1 file changed

Lines changed: 316 additions & 0 deletions

File tree

packages/react-native/src/private/renderer/core/__tests__/EventTargetDispatching-itest.js

Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,322 @@ const {isOSS} = Fantom.getConstants();
964964
},
965965
);
966966

967+
(ReactNativeFeatureFlags.enableNativeEventTargetEventDispatching()
968+
? describe
969+
: describe.skip)('bubbling to document element and document', () => {
970+
it('event bubbles from child up to the document element', () => {
971+
const root = Fantom.createRoot();
972+
const childRef = React.createRef<React.ElementRef<typeof View>>();
973+
const documentElementHandler = jest.fn();
974+
975+
Fantom.runTask(() => {
976+
root.render(<View ref={childRef} />);
977+
});
978+
979+
asEventTarget(root.document.documentElement).addEventListener(
980+
'pointerup',
981+
documentElementHandler,
982+
);
983+
984+
Fantom.dispatchNativeEvent(
985+
childRef,
986+
'onPointerUp',
987+
{x: 0, y: 0},
988+
{
989+
category: Fantom.NativeEventCategory.Discrete,
990+
},
991+
);
992+
993+
expect(documentElementHandler).toHaveBeenCalledTimes(1);
994+
});
995+
996+
it('event bubbles from child up to the document', () => {
997+
const root = Fantom.createRoot();
998+
const childRef = React.createRef<React.ElementRef<typeof View>>();
999+
const documentHandler = jest.fn();
1000+
1001+
Fantom.runTask(() => {
1002+
root.render(<View ref={childRef} />);
1003+
});
1004+
1005+
asEventTarget(root.document).addEventListener(
1006+
'pointerup',
1007+
documentHandler,
1008+
);
1009+
1010+
Fantom.dispatchNativeEvent(
1011+
childRef,
1012+
'onPointerUp',
1013+
{x: 0, y: 0},
1014+
{
1015+
category: Fantom.NativeEventCategory.Discrete,
1016+
},
1017+
);
1018+
1019+
expect(documentHandler).toHaveBeenCalledTimes(1);
1020+
});
1021+
1022+
it('event bubbles from a deeply nested child up to document element and document', () => {
1023+
const root = Fantom.createRoot();
1024+
const childRef = React.createRef<React.ElementRef<typeof View>>();
1025+
const documentElementHandler = jest.fn();
1026+
const documentHandler = jest.fn();
1027+
1028+
Fantom.runTask(() => {
1029+
root.render(
1030+
<View>
1031+
<View>
1032+
<View>
1033+
<View ref={childRef} />
1034+
</View>
1035+
</View>
1036+
</View>,
1037+
);
1038+
});
1039+
1040+
asEventTarget(root.document.documentElement).addEventListener(
1041+
'pointerup',
1042+
documentElementHandler,
1043+
);
1044+
asEventTarget(root.document).addEventListener(
1045+
'pointerup',
1046+
documentHandler,
1047+
);
1048+
1049+
Fantom.dispatchNativeEvent(
1050+
childRef,
1051+
'onPointerUp',
1052+
{x: 0, y: 0},
1053+
{
1054+
category: Fantom.NativeEventCategory.Discrete,
1055+
},
1056+
);
1057+
1058+
expect(documentElementHandler).toHaveBeenCalledTimes(1);
1059+
expect(documentHandler).toHaveBeenCalledTimes(1);
1060+
});
1061+
1062+
it('capture phase on document fires before capture phase on document element', () => {
1063+
const root = Fantom.createRoot();
1064+
const childRef = React.createRef<React.ElementRef<typeof View>>();
1065+
const order: Array<string> = [];
1066+
1067+
Fantom.runTask(() => {
1068+
root.render(
1069+
<View
1070+
ref={childRef}
1071+
onPointerUpCapture={() => {
1072+
order.push('child-capture');
1073+
}}
1074+
onPointerUp={() => {
1075+
order.push('child-bubble');
1076+
}}
1077+
/>,
1078+
);
1079+
});
1080+
1081+
asEventTarget(root.document).addEventListener(
1082+
'pointerup',
1083+
() => {
1084+
order.push('document-capture');
1085+
},
1086+
{capture: true},
1087+
);
1088+
asEventTarget(root.document.documentElement).addEventListener(
1089+
'pointerup',
1090+
() => {
1091+
order.push('documentElement-capture');
1092+
},
1093+
{capture: true},
1094+
);
1095+
asEventTarget(root.document.documentElement).addEventListener(
1096+
'pointerup',
1097+
() => {
1098+
order.push('documentElement-bubble');
1099+
},
1100+
);
1101+
asEventTarget(root.document).addEventListener('pointerup', () => {
1102+
order.push('document-bubble');
1103+
});
1104+
1105+
Fantom.dispatchNativeEvent(
1106+
childRef,
1107+
'onPointerUp',
1108+
{x: 0, y: 0},
1109+
{
1110+
category: Fantom.NativeEventCategory.Discrete,
1111+
},
1112+
);
1113+
1114+
expect(order).toEqual([
1115+
'document-capture',
1116+
'documentElement-capture',
1117+
'child-capture',
1118+
'child-bubble',
1119+
'documentElement-bubble',
1120+
'document-bubble',
1121+
]);
1122+
});
1123+
1124+
it('event.target points to the original child and event.currentTarget transitions through document element and document', () => {
1125+
const root = Fantom.createRoot();
1126+
const childRef = React.createRef<React.ElementRef<typeof View>>();
1127+
const targets: Array<{target: unknown, currentTarget: unknown}> = [];
1128+
1129+
Fantom.runTask(() => {
1130+
root.render(<View ref={childRef} />);
1131+
});
1132+
1133+
asEventTarget(root.document.documentElement).addEventListener(
1134+
'pointerup',
1135+
(e: $FlowFixMe) => {
1136+
targets.push({target: e.target, currentTarget: e.currentTarget});
1137+
},
1138+
);
1139+
asEventTarget(root.document).addEventListener(
1140+
'pointerup',
1141+
(e: $FlowFixMe) => {
1142+
targets.push({target: e.target, currentTarget: e.currentTarget});
1143+
},
1144+
);
1145+
1146+
Fantom.dispatchNativeEvent(
1147+
childRef,
1148+
'onPointerUp',
1149+
{x: 0, y: 0},
1150+
{
1151+
category: Fantom.NativeEventCategory.Discrete,
1152+
},
1153+
);
1154+
1155+
expect(targets).toHaveLength(2);
1156+
1157+
// event.target is always the original target element
1158+
expect(targets[0].target).toBe(childRef.current);
1159+
expect(targets[1].target).toBe(childRef.current);
1160+
1161+
// event.currentTarget changes at each propagation step
1162+
expect(targets[0].currentTarget).toBe(root.document.documentElement);
1163+
expect(targets[1].currentTarget).toBe(root.document);
1164+
});
1165+
1166+
it('stopPropagation on document element prevents document handler from firing', () => {
1167+
const root = Fantom.createRoot();
1168+
const childRef = React.createRef<React.ElementRef<typeof View>>();
1169+
const documentHandler = jest.fn();
1170+
const documentElementHandler = jest.fn((e: $FlowFixMe) => {
1171+
e.stopPropagation();
1172+
});
1173+
1174+
Fantom.runTask(() => {
1175+
root.render(<View ref={childRef} />);
1176+
});
1177+
1178+
asEventTarget(root.document.documentElement).addEventListener(
1179+
'pointerup',
1180+
documentElementHandler,
1181+
);
1182+
asEventTarget(root.document).addEventListener(
1183+
'pointerup',
1184+
documentHandler,
1185+
);
1186+
1187+
Fantom.dispatchNativeEvent(
1188+
childRef,
1189+
'onPointerUp',
1190+
{x: 0, y: 0},
1191+
{
1192+
category: Fantom.NativeEventCategory.Discrete,
1193+
},
1194+
);
1195+
1196+
expect(documentElementHandler).toHaveBeenCalledTimes(1);
1197+
expect(documentHandler).toHaveBeenCalledTimes(0);
1198+
});
1199+
1200+
it('removeEventListener on document element stops events from being received', () => {
1201+
const root = Fantom.createRoot();
1202+
const childRef = React.createRef<React.ElementRef<typeof View>>();
1203+
const documentElementHandler = jest.fn();
1204+
1205+
Fantom.runTask(() => {
1206+
root.render(<View ref={childRef} />);
1207+
});
1208+
1209+
asEventTarget(root.document.documentElement).addEventListener(
1210+
'pointerup',
1211+
documentElementHandler,
1212+
);
1213+
1214+
Fantom.dispatchNativeEvent(
1215+
childRef,
1216+
'onPointerUp',
1217+
{x: 0, y: 0},
1218+
{
1219+
category: Fantom.NativeEventCategory.Discrete,
1220+
},
1221+
);
1222+
1223+
expect(documentElementHandler).toHaveBeenCalledTimes(1);
1224+
1225+
asEventTarget(root.document.documentElement).removeEventListener(
1226+
'pointerup',
1227+
documentElementHandler,
1228+
);
1229+
1230+
Fantom.dispatchNativeEvent(
1231+
childRef,
1232+
'onPointerUp',
1233+
{x: 0, y: 0},
1234+
{
1235+
category: Fantom.NativeEventCategory.Discrete,
1236+
},
1237+
);
1238+
1239+
expect(documentElementHandler).toHaveBeenCalledTimes(1);
1240+
});
1241+
1242+
it('direct (non-bubbling) events do not reach the document element or document', () => {
1243+
const root = Fantom.createRoot();
1244+
const childRef = React.createRef<React.ElementRef<typeof View>>();
1245+
const childHandler = jest.fn();
1246+
const documentElementHandler = jest.fn();
1247+
const documentHandler = jest.fn();
1248+
1249+
Fantom.runTask(() => {
1250+
root.render(<View ref={childRef} onLayout={childHandler} />);
1251+
});
1252+
1253+
asEventTarget(root.document.documentElement).addEventListener(
1254+
'layout',
1255+
documentElementHandler,
1256+
);
1257+
asEventTarget(root.document).addEventListener(
1258+
'layout',
1259+
documentHandler,
1260+
);
1261+
1262+
const childCallsBefore = childHandler.mock.calls.length;
1263+
1264+
Fantom.dispatchNativeEvent(
1265+
childRef,
1266+
'onLayout',
1267+
{layout: {x: 0, y: 0, width: 100, height: 50}},
1268+
{
1269+
category: Fantom.NativeEventCategory.Discrete,
1270+
},
1271+
);
1272+
1273+
// Child handler fires
1274+
expect(
1275+
childHandler.mock.calls.length - childCallsBefore,
1276+
).toBeGreaterThan(0);
1277+
// Non-bubbling events don't reach the document element or the document
1278+
expect(documentElementHandler).toHaveBeenCalledTimes(0);
1279+
expect(documentHandler).toHaveBeenCalledTimes(0);
1280+
});
1281+
});
1282+
9671283
it('stopPropagation in capture phase prevents all bubble-phase handlers', () => {
9681284
const root = Fantom.createRoot();
9691285
const childRef = React.createRef<React.ElementRef<typeof View>>();

0 commit comments

Comments
 (0)