Refactor event object creation for the experimental event API (#15295)
* Refactor event object creation for the experimental event API
Dominic Gannaway committed
Apr 2, 2019 at 20:03 UTC
9307932fe5b59c2d5ce8d04823b94ec0e48b206e
9 files changed
+348
-147
packages/events/EventTypes.js
+14
-14
@@ -7,29 +7,29 @@
7
* @flow
8
*/
9
10
-import SyntheticEvent from 'events/SyntheticEvent';
10
import type {AnyNativeEvent} from 'events/PluginModuleType';
11
import type {ReactEventResponderEventType} from 'shared/ReactTypes';
12
13
export type EventResponderContext = {
14
event: AnyNativeEvent,
16
- eventTarget: EventTarget,
15
+ eventTarget: Element | Document,
16
eventType: string,
17
isPassive: () => boolean,
18
isPassiveSupported: () => boolean,
20
- dispatchEvent: (
21
- name: string,
22
- listener: (e: SyntheticEvent) => void | null,
23
- pressTarget: EventTarget | null,
24
- discrete: boolean,
25
- extraProperties?: Object,
19
+ dispatchEvent: <E>(
20
+ eventObject: E,
21
+ {
22
+ capture?: boolean,
23
+ discrete?: boolean,
24
+ stopPropagation?: boolean,
25
+ },
26
) => void,
27
isTargetWithinElement: (
28
- childTarget: EventTarget,
29
- parentTarget: EventTarget,
28
+ childTarget: Element | Document,
29
+ parentTarget: Element | Document,
30
) => boolean,
31
- isTargetOwned: EventTarget => boolean,
32
- isTargetWithinEventComponent: EventTarget => boolean,
31
+ isTargetOwned: (Element | Document) => boolean,
32
+ isTargetWithinEventComponent: (Element | Document) => boolean,
33
isPositionWithinTouchHitTarget: (x: number, y: number) => boolean,
34
addRootEventTypes: (
35
rootEventTypes: Array<ReactEventResponderEventType>,
@@ -37,6 +37,6 @@ export type EventResponderContext = {
37
removeRootEventTypes: (
38
rootEventTypes: Array<ReactEventResponderEventType>,
39
) => void,
40
- requestOwnership: (target: EventTarget | null) => boolean,
41
- releaseOwnership: (target: EventTarget | null) => boolean,
40
+ requestOwnership: (target: Element | Document | null) => boolean,
41
+ releaseOwnership: (target: Element | Document | null) => boolean,
42
};
packages/react-dom/src/events/DOMEventResponderSystem.js
+132
-58
@@ -18,15 +18,13 @@ import type {
18
ReactEventResponderEventType,
19
} from 'shared/ReactTypes';
20
import type {DOMTopLevelEventType} from 'events/TopLevelEventTypes';
21
-import SyntheticEvent from 'events/SyntheticEvent';
22
-import {runEventsInBatch} from 'events/EventBatching';
23
-import {interactiveUpdates} from 'events/ReactGenericBatching';
24
-import {executeDispatch} from 'events/EventPluginUtils';
21
+import {batchedUpdates, interactiveUpdates} from 'events/ReactGenericBatching';
22
import type {Fiber} from 'react-reconciler/src/ReactFiber';
26
-
23
import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
24
25
import {enableEventAPI} from 'shared/ReactFeatureFlags';
26
+import {invokeGuardedCallbackAndCatchFirstError} from 'shared/ReactErrorUtils';
27
+import warning from 'shared/warning';
28
29
let listenToResponderEventTypesImpl;
30
@@ -36,6 +34,8 @@ export function setListenToResponderEventTypes(
34
listenToResponderEventTypesImpl = _listenToResponderEventTypesImpl;
35
}
36
37
+const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
38
+
39
const rootEventTypesToEventComponents: Map<
40
DOMTopLevelEventType | string,
41
Set<Fiber>,
@@ -45,12 +45,76 @@ const targetEventTypeCached: Map<
45
Set<DOMTopLevelEventType>,
46
> = new Map();
47
const targetOwnership: Map<EventTarget, Fiber> = new Map();
48
+const eventsWithStopPropagation:
49
+ | WeakSet
50
+ | Set<$Shape<PartialEventObject>> = new PossiblyWeakSet();
51
+
52
+type PartialEventObject = {
53
+ listener: ($Shape<PartialEventObject>) => void,
54
+ target: Element | Document,
55
+ type: string,
56
+};
57
+type EventQueue = {
58
+ bubble: null | Array<$Shape<PartialEventObject>>,
59
+ capture: null | Array<$Shape<PartialEventObject>>,
60
+ discrete: boolean,
61
+ phase: EventQueuePhase,
62
+};
63
+type EventQueuePhase = 0 | 1;
64
+
65
+const DURING_EVENT_PHASE = 0;
66
+const AFTER_EVENT_PHASE = 1;
67
49
-type EventListener = (event: SyntheticEvent) => void;
68
+function createEventQueue(phase: EventQueuePhase): EventQueue {
69
+ return {
70
+ bubble: null,
71
+ capture: null,
72
+ discrete: false,
73
+ phase,
74
+ };
75
+}
76
+
77
+function processEvent(event: $Shape<PartialEventObject>): void {
78
+ const type = event.type;
79
+ const listener = event.listener;
80
+ invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
81
+}
82
51
-function copyEventProperties(eventData, syntheticEvent) {
52
- for (let propName in eventData) {
53
- syntheticEvent[propName] = eventData[propName];
83
+function processEvents(
84
+ bubble: null | Array<$Shape<PartialEventObject>>,
85
+ capture: null | Array<$Shape<PartialEventObject>>,
86
+): void {
87
+ let i, length;
88
+
89
+ if (capture !== null) {
90
+ for (i = capture.length; i-- > 0; ) {
91
+ const event = capture[i];
92
+ processEvent(capture[i]);
93
+ if (eventsWithStopPropagation.has(event)) {
94
+ return;
95
+ }
96
+ }
97
+ }
98
+ if (bubble !== null) {
99
+ for (i = 0, length = bubble.length; i < length; ++i) {
100
+ const event = bubble[i];
101
+ processEvent(event);
102
+ if (eventsWithStopPropagation.has(event)) {
103
+ return;
104
+ }
105
+ }
106
+ }
107
+}
108
+
109
+function processEventQueue(eventQueue: EventQueue): void {
110
+ const {bubble, capture, discrete} = eventQueue;
111
+
112
+ if (discrete) {
113
+ interactiveUpdates(() => {
114
+ processEvents(bubble, capture);
115
+ });
116
+ } else {
117
+ processEvents(bubble, capture);
118
}
119
}
120
@@ -70,6 +134,7 @@ function DOMEventResponderContext(
134
this._discreteEvents = null;
135
this._nonDiscreteEvents = null;
136
this._isBatching = true;
137
+ this._eventQueue = createEventQueue(DURING_EVENT_PHASE);
138
}
139
140
DOMEventResponderContext.prototype.isPassive = function(): boolean {
@@ -81,49 +146,66 @@ DOMEventResponderContext.prototype.isPassiveSupported = function(): boolean {
146
};
147
148
DOMEventResponderContext.prototype.dispatchEvent = function(
84
- eventName: string,
85
- eventListener: EventListener,
86
- eventTarget: AnyNativeEvent,
87
- discrete: boolean,
88
- extraProperties?: Object,
149
+ possibleEventObject: Object,
150
+ {
151
+ capture,
152
+ discrete,
153
+ stopPropagation,
154
+ }: {
155
+ capture?: boolean,
156
+ discrete?: boolean,
157
+ stopPropagation?: boolean,
158
+ },
159
): void {
90
- const eventTargetFiber = getClosestInstanceFromNode(eventTarget);
91
- const syntheticEvent = SyntheticEvent.getPooled(
92
- null,
93
- eventTargetFiber,
94
- this.event,
95
- eventTarget,
96
- );
97
- if (extraProperties !== undefined) {
98
- copyEventProperties(extraProperties, syntheticEvent);
160
+ const eventQueue = this._eventQueue;
161
+ const {listener, target, type} = possibleEventObject;
162
+
163
+ if (listener == null || target == null || type == null) {
164
+ throw new Error(
165
+ 'context.dispatchEvent: "listener", "target" and "type" fields on event object are required.',
166
+ );
167
}
100
- syntheticEvent.type = eventName;
101
- syntheticEvent._dispatchInstances = [eventTargetFiber];
102
- syntheticEvent._dispatchListeners = [eventListener];
103
-
104
- if (this._isBatching) {
105
- let events;
106
- if (discrete) {
107
- events = this._discreteEvents;
108
- if (events === null) {
109
- events = this._discreteEvents = [];
110
- }
111
- } else {
112
- events = this._nonDiscreteEvents;
113
- if (events === null) {
114
- events = this._nonDiscreteEvents = [];
115
- }
168
+ if (__DEV__) {
169
+ possibleEventObject.preventDefault = () => {
170
+ // Update this warning when we have a story around dealing with preventDefault
171
+ warning(
172
+ false,
173
+ 'preventDefault() is no longer available on event objects created from event responder modules.',
174
+ );
175
+ };
176
+ possibleEventObject.stopPropagation = () => {
177
+ // Update this warning when we have a story around dealing with stopPropgation
178
+ warning(
179
+ false,
180
+ 'stopPropagation() is no longer available on event objects created from event responder modules.',
181
+ );
182
+ };
183
+ }
184
+ const eventObject = ((possibleEventObject: any): $Shape<PartialEventObject>);
185
+ let events;
186
+
187
+ if (capture) {
188
+ events = eventQueue.capture;
189
+ if (events === null) {
190
+ events = eventQueue.capture = [];
191
}
117
- events.push(syntheticEvent);
192
} else {
119
- if (discrete) {
120
- interactiveUpdates(() => {
121
- executeDispatch(syntheticEvent, eventListener, eventTargetFiber);
122
- });
123
- } else {
124
- executeDispatch(syntheticEvent, eventListener, eventTargetFiber);
193
+ events = eventQueue.bubble;
194
+ if (events === null) {
195
+ events = eventQueue.bubble = [];
196
}
197
}
198
+ if (discrete) {
199
+ eventQueue.discrete = true;
200
+ }
201
+ events.push(eventObject);
202
+
203
+ if (stopPropagation) {
204
+ eventsWithStopPropagation.add(eventObject);
205
+ }
206
+ if (eventQueue.phase === AFTER_EVENT_PHASE) {
207
+ batchedUpdates(processEventQueue, eventQueue);
208
+ }
209
};
210
211
DOMEventResponderContext.prototype.isTargetWithinEventComponent = function(
@@ -318,17 +400,9 @@ export function runResponderEventsInBatch(
400
);
401
}
402
}
321
- // Run batched events
322
- const discreteEvents = context._discreteEvents;
323
- if (discreteEvents !== null) {
324
- interactiveUpdates(() => {
325
- runEventsInBatch(discreteEvents);
326
- });
327
- }
328
- const nonDiscreteEvents = context._nonDiscreteEvents;
329
- if (nonDiscreteEvents !== null) {
330
- runEventsInBatch(nonDiscreteEvents);
331
- }
332
- context._isBatching = false;
403
+ processEventQueue(context._eventQueue);
404
+ // In order to capture and process async events from responder modules
405
+ // we create a new event queue.
406
+ context._eventQueue = createEventQueue(AFTER_EVENT_PHASE);
407
}
408
}
packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
+6
-6
@@ -228,12 +228,12 @@ describe('DOMEventResponderSystem', () => {
228
['click'],
229
(context, props) => {
230
if (props.onMagicClick) {
231
- context.dispatchEvent(
232
- 'magicclick',
233
- props.onMagicClick,
234
- context.eventTarget,
235
- false,
236
- );
231
+ const event = {
232
+ listener: props.onMagicClick,
233
+ target: context.eventTarget,
234
+ type: 'magicclick',
235
+ };
236
+ context.dispatchEvent(event, {discrete: true});
237
}
238
},
239
);
packages/react-events/src/Drag.js
+41
-12
@@ -14,7 +14,7 @@ const targetEventTypes = ['pointerdown', 'pointercancel'];
14
const rootEventTypes = ['pointerup', {name: 'pointermove', passive: false}];
15
16
type DragState = {
17
- dragTarget: null | EventTarget,
17
+ dragTarget: null | Element | Document,
18
isPointerDown: boolean,
19
isDragging: boolean,
20
startX: number,
@@ -33,18 +33,45 @@ if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
33
});
34
}
35
36
+type EventData = {
37
+ diffX: number,
38
+ diffY: number,
39
+};
40
+type DragEventType = 'dragend' | 'dragchange' | 'dragmove';
41
+
42
+type DragEvent = {|
43
+ listener: DragEvent => void,
44
+ target: Element | Document,
45
+ type: DragEventType,
46
+ diffX?: number,
47
+ diffY?: number,
48
+|};
49
+
50
+function createDragEvent(
51
+ type: DragEventType,
52
+ target: Element | Document,
53
+ listener: DragEvent => void,
54
+ eventData?: EventData,
55
+): DragEvent {
56
+ return {
57
+ listener,
58
+ target,
59
+ type,
60
+ ...eventData,
61
+ };
62
+}
63
+
64
function dispatchDragEvent(
65
context: EventResponderContext,
38
- name: string,
39
- listener: (e: Object) => void,
66
+ name: DragEventType,
67
+ listener: DragEvent => void,
68
state: DragState,
69
discrete: boolean,
42
- eventData?: {
43
- diffX: number,
44
- diffY: number,
45
- },
70
+ eventData?: EventData,
71
): void {
47
- context.dispatchEvent(name, listener, state.dragTarget, discrete, eventData);
72
+ const target = ((state.dragTarget: any): Element | Document);
73
+ const syntheticEvent = createDragEvent(name, target, listener, eventData);
74
+ context.dispatchEvent(syntheticEvent, {discrete});
75
}
76
77
const DragResponder = {
@@ -112,10 +139,11 @@ const DragResponder = {
139
const dragChangeEventListener = () => {
140
props.onDragChange(true);
141
};
115
- context.dispatchEvent(
142
+ dispatchDragEvent(
143
+ context,
144
'dragchange',
145
dragChangeEventListener,
118
- state.dragTarget,
146
+ state,
147
true,
148
);
149
}
@@ -160,10 +188,11 @@ const DragResponder = {
188
const dragChangeEventListener = () => {
189
props.onDragChange(false);
190
};
163
- context.dispatchEvent(
191
+ dispatchDragEvent(
192
+ context,
193
'dragchange',
194
dragChangeEventListener,
166
- state.dragTarget,
195
+ state,
196
true,
197
);
198
}
packages/react-events/src/Focus.js
+34
-8
@@ -19,24 +19,49 @@ type FocusState = {
19
isFocused: boolean,
20
};
21
22
+type FocusEventType = 'focus' | 'blur' | 'focuschange';
23
+
24
+type FocusEvent = {|
25
+ listener: FocusEvent => void,
26
+ target: Element | Document,
27
+ type: FocusEventType,
28
+|};
29
+
30
+function createFocusEvent(
31
+ type: FocusEventType,
32
+ target: Element | Document,
33
+ listener: FocusEvent => void,
34
+): FocusEvent {
35
+ return {
36
+ listener,
37
+ target,
38
+ type,
39
+ };
40
+}
41
+
42
function dispatchFocusInEvents(context: EventResponderContext, props: Object) {
43
const {event, eventTarget} = context;
44
if (context.isTargetWithinEventComponent((event: any).relatedTarget)) {
45
return;
46
}
47
if (props.onFocus) {
28
- context.dispatchEvent('focus', props.onFocus, eventTarget, true);
48
+ const syntheticEvent = createFocusEvent(
49
+ 'focus',
50
+ eventTarget,
51
+ props.onFocus,
52
+ );
53
+ context.dispatchEvent(syntheticEvent, {discrete: true});
54
}
55
if (props.onFocusChange) {
56
const focusChangeEventListener = () => {
57
props.onFocusChange(true);
58
};
34
- context.dispatchEvent(
59
+ const syntheticEvent = createFocusEvent(
60
'focuschange',
36
- focusChangeEventListener,
61
eventTarget,
38
- true,
62
+ focusChangeEventListener,
63
);
64
+ context.dispatchEvent(syntheticEvent, {discrete: true});
65
}
66
}
67
@@ -46,18 +71,19 @@ function dispatchFocusOutEvents(context: EventResponderContext, props: Object) {
71
return;
72
}
73
if (props.onBlur) {
49
- context.dispatchEvent('blur', props.onBlur, eventTarget, true);
74
+ const syntheticEvent = createFocusEvent('blur', eventTarget, props.onBlur);
75
+ context.dispatchEvent(syntheticEvent, {discrete: true});
76
}
77
if (props.onFocusChange) {
78
const focusChangeEventListener = () => {
79
props.onFocusChange(false);
80
};
55
- context.dispatchEvent(
81
+ const syntheticEvent = createFocusEvent(
82
'focuschange',
57
- focusChangeEventListener,
83
eventTarget,
59
- true,
84
+ focusChangeEventListener,
85
);
86
+ context.dispatchEvent(syntheticEvent, {discrete: true});
87
}
88
}
89
packages/react-events/src/Hover.js
+38
-8
@@ -23,6 +23,26 @@ type HoverState = {
23
isTouched: boolean,
24
};
25
26
+type HoverEventType = 'hoverstart' | 'hoverend' | 'hoverchange';
27
+
28
+type HoverEvent = {|
29
+ listener: HoverEvent => void,
30
+ target: Element | Document,
31
+ type: HoverEventType,
32
+|};
33
+
34
+function createHoverEvent(
35
+ type: HoverEventType,
36
+ target: Element | Document,
37
+ listener: HoverEvent => void,
38
+): HoverEvent {
39
+ return {
40
+ listener,
41
+ target,
42
+ type,
43
+ };
44
+}
45
+
46
// In the case we don't have PointerEvents (Safari), we listen to touch events
47
// too
48
if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
@@ -39,18 +59,23 @@ function dispatchHoverStartEvents(
59
return;
60
}
61
if (props.onHoverStart) {
42
- context.dispatchEvent('hoverstart', props.onHoverStart, eventTarget, true);
62
+ const syntheticEvent = createHoverEvent(
63
+ 'hoverstart',
64
+ eventTarget,
65
+ props.onHoverStart,
66
+ );
67
+ context.dispatchEvent(syntheticEvent, {discrete: true});
68
}
69
if (props.onHoverChange) {
70
const hoverChangeEventListener = () => {
71
props.onHoverChange(true);
72
};
48
- context.dispatchEvent(
73
+ const syntheticEvent = createHoverEvent(
74
'hoverchange',
50
- hoverChangeEventListener,
75
eventTarget,
52
- true,
76
+ hoverChangeEventListener,
77
);
78
+ context.dispatchEvent(syntheticEvent, {discrete: true});
79
}
80
}
81
@@ -60,18 +85,23 @@ function dispatchHoverEndEvents(context: EventResponderContext, props: Object) {
85
return;
86
}
87
if (props.onHoverEnd) {
63
- context.dispatchEvent('hoverend', props.onHoverEnd, eventTarget, true);
88
+ const syntheticEvent = createHoverEvent(
89
+ 'hoverend',
90
+ eventTarget,
91
+ props.onHoverEnd,
92
+ );
93
+ context.dispatchEvent(syntheticEvent, {discrete: true});
94
}
95
if (props.onHoverChange) {
96
const hoverChangeEventListener = () => {
97
props.onHoverChange(false);
98
};
69
- context.dispatchEvent(
99
+ const syntheticEvent = createHoverEvent(
100
'hoverchange',
71
- hoverChangeEventListener,
101
eventTarget,
73
- true,
102
+ hoverChangeEventListener,
103
);
104
+ context.dispatchEvent(syntheticEvent, {discrete: true});
105
}
106
}
107
packages/react-events/src/Press.js
+44
-31
@@ -36,13 +36,13 @@ type PressProps = {
36
delayLongPress: number,
37
delayPressEnd: number,
38
delayPressStart: number,
39
- onLongPress: (e: Object) => void,
39
+ onLongPress: (e: PressEvent) => void,
40
onLongPressChange: boolean => void,
41
onLongPressShouldCancelPress: () => boolean,
42
- onPress: (e: Object) => void,
42
+ onPress: (e: PressEvent) => void,
43
onPressChange: boolean => void,
44
- onPressEnd: (e: Object) => void,
45
- onPressStart: (e: Object) => void,
44
+ onPressEnd: (e: PressEvent) => void,
45
+ onPressStart: (e: PressEvent) => void,
46
pressRententionOffset: Object,
47
};
48
@@ -52,17 +52,45 @@ type PressState = {
52
isLongPressed: boolean,
53
isPressed: boolean,
54
longPressTimeout: null | TimeoutID,
55
- pressTarget: null | EventTarget,
55
+ pressTarget: null | Element | Document,
56
shouldSkipMouseAfterTouch: boolean,
57
};
58
59
+type PressEventType =
60
+ | 'press'
61
+ | 'pressstart'
62
+ | 'pressend'
63
+ | 'presschange'
64
+ | 'longpress'
65
+ | 'longpresschange';
66
+
67
+type PressEvent = {|
68
+ listener: PressEvent => void,
69
+ target: Element | Document,
70
+ type: PressEventType,
71
+|};
72
+
73
+function createPressEvent(
74
+ type: PressEventType,
75
+ target: Element | Document,
76
+ listener: PressEvent => void,
77
+): PressEvent {
78
+ return {
79
+ listener,
80
+ target,
81
+ type,
82
+ };
83
+}
84
+
85
function dispatchPressEvent(
86
context: EventResponderContext,
87
state: PressState,
62
- name: string,
88
+ name: PressEventType,
89
listener: (e: Object) => void,
90
): void {
65
- context.dispatchEvent(name, listener, state.pressTarget, true);
91
+ const target = ((state.pressTarget: any): Element | Document);
92
+ const syntheticEvent = createPressEvent(name, target, listener);
93
+ context.dispatchEvent(syntheticEvent, {discrete: true});
94
}
95
96
function dispatchPressStartEvents(
@@ -97,9 +125,10 @@ function dispatchPressStartEvents(
125
if (props.onLongPress) {
126
const longPressEventListener = e => {
127
props.onLongPress(e);
100
- if (e.nativeEvent.defaultPrevented) {
101
- state.defaultPrevented = true;
102
- }
128
+ // TODO address this again at some point
129
+ // if (e.nativeEvent.defaultPrevented) {
130
+ // state.defaultPrevented = true;
131
+ // }
132
};
133
dispatchPressEvent(context, state, 'longpress', longPressEventListener);
134
}
@@ -193,24 +222,7 @@ const PressResponder = {
222
) {
223
return;
224
}
196
- let keyPressEventListener = props.onPress;
197
-
198
- // Wrap listener with prevent default behaviour, unless
199
- // we are dealing with an anchor. Anchor tags are special beacuse
200
- // we need to use the "click" event, to properly allow browser
201
- // heuristics for cancelling link clicks. Furthermore, iOS and
202
- // Android can show previous of anchor tags that requires working
203
- // with click rather than touch events (and mouse down/up).
204
- if (!isAnchorTagElement(eventTarget)) {
205
- keyPressEventListener = e => {
206
- if (!e.isDefaultPrevented() && !e.nativeEvent.defaultPrevented) {
207
- e.preventDefault();
208
- state.defaultPrevented = true;
209
- props.onPress(e);
210
- }
211
- };
212
- }
213
- dispatchPressEvent(context, state, 'press', keyPressEventListener);
225
+ dispatchPressEvent(context, state, 'press', props.onPress);
226
break;
227
}
228
@@ -332,9 +344,10 @@ const PressResponder = {
344
) {
345
const pressEventListener = e => {
346
props.onPress(e);
335
- if (e.nativeEvent.defaultPrevented) {
336
- state.defaultPrevented = true;
337
- }
347
+ // TODO address this again at some point
348
+ // if (e.nativeEvent.defaultPrevented) {
349
+ // state.defaultPrevented = true;
350
+ // }
351
};
352
dispatchPressEvent(context, state, 'press', pressEventListener);
353
}
packages/react-events/src/Swipe.js
+35
-8
@@ -23,18 +23,45 @@ if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
23
});
24
}
25
26
+type EventData = {
27
+ diffX: number,
28
+ diffY: number,
29
+};
30
+type SwipeEventType = 'swipeleft' | 'swiperight' | 'swipeend' | 'swipemove';
31
+
32
+type SwipeEvent = {|
33
+ listener: SwipeEvent => void,
34
+ target: Element | Document,
35
+ type: SwipeEventType,
36
+ diffX?: number,
37
+ diffY?: number,
38
+|};
39
+
40
+function createSwipeEvent(
41
+ type: SwipeEventType,
42
+ target: Element | Document,
43
+ listener: SwipeEvent => void,
44
+ eventData?: EventData,
45
+): SwipeEvent {
46
+ return {
47
+ listener,
48
+ target,
49
+ type,
50
+ ...eventData,
51
+ };
52
+}
53
+
54
function dispatchSwipeEvent(
55
context: EventResponderContext,
28
- name: string,
29
- listener: (e: Object) => void,
56
+ name: SwipeEventType,
57
+ listener: SwipeEvent => void,
58
state: SwipeState,
59
discrete: boolean,
32
- eventData?: {
33
- diffX: number,
34
- diffY: number,
35
- },
60
+ eventData?: EventData,
61
) {
37
- context.dispatchEvent(name, listener, state.swipeTarget, discrete, eventData);
62
+ const target = ((state.swipeTarget: any): Element | Document);
63
+ const syntheticEvent = createSwipeEvent(name, target, listener, eventData);
64
+ context.dispatchEvent(syntheticEvent, {discrete});
65
}
66
67
type SwipeState = {
@@ -44,7 +71,7 @@ type SwipeState = {
71
startX: number,
72
startY: number,
73
touchId: null | number,
47
- swipeTarget: null | EventTarget,
74
+ swipeTarget: null | Element | Document,
75
x: number,
76
y: number,
77
};
packages/react-events/src/__tests__/Press-test.internal.js
+4
-2
@@ -388,8 +388,10 @@ describe('Event responder: Press', () => {
388
389
events = [];
390
ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'Enter'}));
391
- // Outer press should not occur as inner press will preventDefault
392
- expect(events).toEqual(['keydown', 'inner: onPress']);
391
+ // TODO update this test once we have a form of stopPropagation in
392
+ // the responder system again. This test had to be updated because
393
+ // we have removed stopPropagation() from synthetic events.
394
+ expect(events).toEqual(['keydown', 'inner: onPress', 'outer: onPress']);
395
});
396
});
397
});