@samitouri / QOS-React-2 / commits / ebd1f5ddb0

[react-events] Press: improve test coverage (#16397)

1. Run the tests in both an environment without PointerEvent and one with PointerEvent. 2. Improve test coverage to include both mouse and touch pointers. 3. Change 'Press' so that it only listens to either pointer events or fallbacks events.

Nicolas Gallagher committed Aug 15, 2019 at 10:28 UTC ebd1f5ddb0da7e3626b56614e2b5723debadd839
6 files changed +858 -1702
packages/react-events/src/dom/Press.js
+33 -32
@@ -95,10 +95,14 @@ type PressEvent = {|
95 shiftKey: boolean,
96 |};
97
98 +const hasPointerEvents =
99 + typeof window !== 'undefined' && window.PointerEvent !== undefined;
100 +
101 const isMac =
102 typeof window !== 'undefined' && window.navigator != null
103 ? /^Mac/.test(window.navigator.platform)
104 : false;
105 +
106 const DEFAULT_PRESS_RETENTION_OFFSET = {
107 bottom: 20,
108 top: 20,
@@ -106,37 +110,32 @@ const DEFAULT_PRESS_RETENTION_OFFSET = {
110 right: 20,
111 };
112
109 -const targetEventTypes = [
110 - 'keydown_active',
111 - // We need to preventDefault on pointerdown for mouse/pen events
112 - // that are in hit target area but not the element area.
113 - 'pointerdown_active',
114 - 'click_active',
115 -];
116 -const rootEventTypes = [
117 - 'click',
118 - 'keyup',
119 - 'pointerup',
120 - 'pointermove',
121 - 'scroll',
122 - 'pointercancel',
123 - // We listen to this here so stopPropagation can
124 - // block other mouseup events used internally
125 - 'mouseup_active',
126 - 'touchend',
127 -];
128 -
129 -// If PointerEvents is not supported (e.g., Safari), also listen to touch and mouse events.
130 -if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
131 - targetEventTypes.push('touchstart', 'mousedown');
132 - rootEventTypes.push(
133 - 'mousemove',
134 - 'touchmove',
135 - 'touchcancel',
136 - // Used as a 'cancel' signal for mouse interactions
137 - 'dragstart',
138 - );
139 -}
113 +const targetEventTypes = hasPointerEvents
114 + ? [
115 + 'keydown_active',
116 + // We need to preventDefault on pointerdown for mouse/pen events
117 + // that are in hit target area but not the element area.
118 + 'pointerdown_active',
119 + 'click_active',
120 + ]
121 + : ['keydown_active', 'touchstart', 'mousedown', 'click_active'];
122 +
123 +const rootEventTypes = hasPointerEvents
124 + ? ['pointerup', 'pointermove', 'pointercancel', 'click', 'keyup', 'scroll']
125 + : [
126 + 'click',
127 + 'keyup',
128 + 'scroll',
129 + 'mousemove',
130 + 'touchmove',
131 + 'touchcancel',
132 + // Used as a 'cancel' signal for mouse interactions
133 + 'dragstart',
134 + // We listen to this here so stopPropagation can
135 + // block other mouseup events used internally
136 + 'mouseup_active',
137 + 'touchend',
138 + ];
139
140 function isFunction(obj): boolean {
141 return typeof obj === 'function';
@@ -539,7 +538,7 @@ const pressResponderImpl = {
538 }
539
540 state.shouldPreventClick = false;
542 - if (isPointerEvent || isTouchEvent) {
541 + if (isTouchEvent) {
542 state.ignoreEmulatedMouseEvents = true;
543 } else if (isKeyboardEvent) {
544 // Ignore unrelated key events
@@ -676,6 +675,7 @@ const pressResponderImpl = {
675 if (state.isPressWithinResponderRegion) {
676 if (isPressed) {
677 const onPressMove = props.onPressMove;
678 +
679 if (isFunction(onPressMove)) {
680 dispatchEvent(
681 event,
@@ -777,6 +777,7 @@ const pressResponderImpl = {
777 );
778 }
779 }
780 +
781 if (state.isPressWithinResponderRegion && button !== 1) {
782 dispatchEvent(
783 event,
packages/react-events/src/dom/__tests__/Focus-test.internal.js
+12 -12
@@ -15,8 +15,8 @@ import {
15 keydown,
16 setPointerEvent,
17 platform,
18 - dispatchPointerPressDown,
19 - dispatchPointerPressRelease,
18 + dispatchPointerDown,
19 + dispatchPointerUp,
20 } from '../test-utils';
21
22 let React;
@@ -138,8 +138,8 @@ describe.each(table)('Focus responder', hasPointerEvents => {
138
139 it('is called with the correct pointerType: mouse', () => {
140 const target = ref.current;
141 - dispatchPointerPressDown(target, {pointerType: 'mouse'});
142 - dispatchPointerPressRelease(target, {pointerType: 'mouse'});
141 + dispatchPointerDown(target, {pointerType: 'mouse'});
142 + dispatchPointerUp(target, {pointerType: 'mouse'});
143 expect(onFocus).toHaveBeenCalledTimes(1);
144 expect(onFocus).toHaveBeenCalledWith(
145 expect.objectContaining({pointerType: 'mouse'}),
@@ -148,8 +148,8 @@ describe.each(table)('Focus responder', hasPointerEvents => {
148
149 it('is called with the correct pointerType: touch', () => {
150 const target = ref.current;
151 - dispatchPointerPressDown(target, {pointerType: 'touch'});
152 - dispatchPointerPressRelease(target, {pointerType: 'touch'});
151 + dispatchPointerDown(target, {pointerType: 'touch'});
152 + dispatchPointerUp(target, {pointerType: 'touch'});
153 expect(onFocus).toHaveBeenCalledTimes(1);
154 expect(onFocus).toHaveBeenCalledWith(
155 expect.objectContaining({pointerType: 'touch'}),
@@ -159,8 +159,8 @@ describe.each(table)('Focus responder', hasPointerEvents => {
159 if (hasPointerEvents) {
160 it('is called with the correct pointerType: pen', () => {
161 const target = ref.current;
162 - dispatchPointerPressDown(target, {pointerType: 'pen'});
163 - dispatchPointerPressRelease(target, {pointerType: 'pen'});
162 + dispatchPointerDown(target, {pointerType: 'pen'});
163 + dispatchPointerUp(target, {pointerType: 'pen'});
164 expect(onFocus).toHaveBeenCalledTimes(1);
165 expect(onFocus).toHaveBeenCalledWith(
166 expect.objectContaining({pointerType: 'pen'}),
@@ -278,7 +278,7 @@ describe.each(table)('Focus responder', hasPointerEvents => {
278 expect(onFocusVisibleChange).toHaveBeenCalledTimes(1);
279 expect(onFocusVisibleChange).toHaveBeenCalledWith(true);
280 // then use pointer on the target, focus should no longer be visible
281 - dispatchPointerPressDown(target);
281 + dispatchPointerDown(target);
282 expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
283 expect(onFocusVisibleChange).toHaveBeenCalledWith(false);
284 // onFocusVisibleChange should not be called again
@@ -288,9 +288,9 @@ describe.each(table)('Focus responder', hasPointerEvents => {
288
289 it('is not called after "focus" and "blur" events without keyboard', () => {
290 const target = ref.current;
291 - dispatchPointerPressDown(target);
292 - dispatchPointerPressRelease(target);
293 - dispatchPointerPressDown(container);
291 + dispatchPointerDown(target);
292 + dispatchPointerUp(target);
293 + dispatchPointerDown(container);
294 target.dispatchEvent(blur({relatedTarget: container}));
295 expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
296 });
packages/react-events/src/dom/__tests__/FocusWithin-test.internal.js
+6 -6
@@ -14,8 +14,8 @@ import {
14 focus,
15 keydown,
16 setPointerEvent,
17 - dispatchPointerPressDown,
18 - dispatchPointerPressRelease,
17 + dispatchPointerDown,
18 + dispatchPointerUp,
19 } from '../test-utils';
20
21 let React;
@@ -203,7 +203,7 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
203 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
204 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
205 // then use pointer on the next target, focus should no longer be visible
206 - dispatchPointerPressDown(innerTarget2);
206 + dispatchPointerDown(innerTarget2);
207 innerTarget1.dispatchEvent(blur({relatedTarget: innerTarget2}));
208 innerTarget2.dispatchEvent(focus());
209 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
@@ -215,7 +215,7 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
215 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(3);
216 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
217 // then use pointer on the target, focus should no longer be visible
218 - dispatchPointerPressDown(innerTarget1);
218 + dispatchPointerDown(innerTarget1);
219 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
220 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
221 // onFocusVisibleChange should not be called again
@@ -225,8 +225,8 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
225
226 it('is not called after "focus" and "blur" events without keyboard', () => {
227 const innerTarget = innerRef.current;
228 - dispatchPointerPressDown(innerTarget);
229 - dispatchPointerPressRelease(innerTarget);
228 + dispatchPointerDown(innerTarget);
229 + dispatchPointerUp(innerTarget);
230 innerTarget.dispatchEvent(blur({relatedTarget: container}));
231 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(0);
232 });
packages/react-events/src/dom/__tests__/Hover-test.internal.js
+4 -5
@@ -213,7 +213,8 @@ describe.each(table)('Hover responder', hasPointerEvents => {
213
214 const target = ref.current;
215 dispatchPointerHoverEnter(target);
216 - dispatchPointerHoverMove(target, {from: {x: 0, y: 0}, to: {x: 1, y: 1}});
216 + dispatchPointerHoverMove(target, {x: 0, y: 0});
217 + dispatchPointerHoverMove(target, {x: 1, y: 1});
218 expect(onHoverMove).toHaveBeenCalledTimes(2);
219 expect(onHoverMove).toHaveBeenCalledWith(
220 expect.objectContaining({type: 'hovermove'}),
@@ -317,10 +318,8 @@ describe.each(table)('Hover responder', hasPointerEvents => {
318 const target = ref.current;
319
320 dispatchPointerHoverEnter(target, {x: 10, y: 10});
320 - dispatchPointerHoverMove(target, {
321 - from: {x: 10, y: 10},
322 - to: {x: 20, y: 20},
323 - });
321 + dispatchPointerHoverMove(target, {x: 10, y: 10});
322 + dispatchPointerHoverMove(target, {x: 20, y: 20});
323 dispatchPointerHoverExit(target, {x: 20, y: 20});
324
325 expect(eventLog).toEqual([
packages/react-events/src/dom/__tests__/Press-test.internal.js
+646 -1571
@@ -9,6 +9,21 @@
9
10 'use strict';
11
12 +import {
13 + click,
14 + dispatchPointerCancel,
15 + dispatchPointerDown,
16 + dispatchPointerUp,
17 + dispatchPointerHoverMove,
18 + dispatchPointerMove,
19 + keydown,
20 + keyup,
21 + scroll,
22 + pointerdown,
23 + pointerup,
24 + setPointerEvent,
25 +} from '../test-utils';
26 +
27 let React;
28 let ReactFeatureFlags;
29 let ReactDOM;
@@ -16,39 +31,9 @@ let PressResponder;
31 let usePressResponder;
32 let Scheduler;
33
19 -const createEvent = (type, data) => {
20 - const event = document.createEvent('CustomEvent');
21 - event.initCustomEvent(type, true, true);
22 - if (data != null) {
23 - Object.entries(data).forEach(([key, value]) => {
24 - event[key] = value;
25 - });
26 - }
27 - return event;
28 -};
29 -
30 -function createTouchEvent(type, id, data) {
31 - return createEvent(type, {
32 - changedTouches: [
33 - {
34 - ...data,
35 - identifier: id,
36 - },
37 - ],
38 - targetTouches: [
39 - {
40 - ...data,
41 - identifier: id,
42 - },
43 - ],
44 - });
45 -}
46 -
47 -const createKeyboardEvent = (type, data) => {
48 - return createEvent(type, data);
49 -};
50 -
51 -function init() {
34 +function initializeModules(hasPointerEvents) {
35 + jest.resetModules();
36 + setPointerEvent(hasPointerEvents);
37 ReactFeatureFlags = require('shared/ReactFeatureFlags');
38 ReactFeatureFlags.enableFlareAPI = true;
39 React = require('react');
@@ -58,12 +43,23 @@ function init() {
43 Scheduler = require('scheduler');
44 }
45
61 -describe('Event responder: Press', () => {
46 +function removePressMoveStrings(eventString) {
47 + if (eventString === 'onPressMove') {
48 + return false;
49 + }
50 + return true;
51 +}
52 +
53 +const forcePointerEvents = true;
54 +const environmentTable = [[forcePointerEvents], [!forcePointerEvents]];
55 +
56 +const pointerTypesTable = [['mouse'], ['touch']];
57 +
58 +describe.each(environmentTable)('Press responder', hasPointerEvents => {
59 let container;
60
61 beforeEach(() => {
65 - jest.resetModules();
66 - init();
62 + initializeModules(hasPointerEvents);
63 container = document.createElement('div');
64 document.body.appendChild(container);
65 });
@@ -92,11 +88,13 @@ describe('Event responder: Press', () => {
88 return <div ref={ref} listeners={listener} />;
89 };
90 ReactDOM.render(<Component />, container);
91 + document.elementFromPoint = () => ref.current;
92 });
93
97 - it('prevents custom events being dispatched', () => {
98 - ref.current.dispatchEvent(createEvent('pointerdown'));
99 - ref.current.dispatchEvent(createEvent('pointerup'));
94 + it('does not call callbacks', () => {
95 + const target = ref.current;
96 + dispatchPointerDown(target);
97 + dispatchPointerUp(target);
98 expect(onPressStart).not.toBeCalled();
99 expect(onPress).not.toBeCalled();
100 expect(onPressEnd).not.toBeCalled();
@@ -116,27 +114,22 @@ describe('Event responder: Press', () => {
114 return <div ref={ref} listeners={listener} />;
115 };
116 ReactDOM.render(<Component />, container);
117 + document.elementFromPoint = () => ref.current;
118 });
119
121 - it('is called after "pointerdown" event', () => {
122 - ref.current.dispatchEvent(
123 - createEvent('pointerdown', {pointerType: 'pen'}),
124 - );
125 - ref.current.dispatchEvent(
126 - createTouchEvent('touchstart', 0, {
127 - target: ref.current,
128 - }),
129 - );
130 - expect(onPressStart).toHaveBeenCalledTimes(1);
131 - expect(onPressStart).toHaveBeenCalledWith(
132 - expect.objectContaining({pointerType: 'pen', type: 'pressstart'}),
133 - );
134 - });
120 + it.each(pointerTypesTable)(
121 + 'is called after pointer down: %s',
122 + pointerType => {
123 + dispatchPointerDown(ref.current, {pointerType});
124 + expect(onPressStart).toHaveBeenCalledTimes(1);
125 + expect(onPressStart).toHaveBeenCalledWith(
126 + expect.objectContaining({pointerType, type: 'pressstart'}),
127 + );
128 + },
129 + );
130
136 - it('is called after auxillary-button "pointerdown" event', () => {
137 - ref.current.dispatchEvent(
138 - createEvent('pointerdown', {button: 1, pointerType: 'mouse'}),
139 - );
131 + it('is called after auxillary-button pointer down', () => {
132 + dispatchPointerDown(ref.current, {button: 1, pointerType: 'mouse'});
133 expect(onPressStart).toHaveBeenCalledTimes(1);
134 expect(onPressStart).toHaveBeenCalledWith(
135 expect.objectContaining({
@@ -148,64 +141,38 @@ describe('Event responder: Press', () => {
141 });
142
143 it('is not called after "pointermove" following auxillary-button press', () => {
151 - ref.current.getBoundingClientRect = () => ({
144 + const target = ref.current;
145 + target.getBoundingClientRect = () => ({
146 top: 0,
147 left: 0,
148 bottom: 100,
149 right: 100,
150 });
157 - ref.current.dispatchEvent(
158 - createEvent('pointerdown', {
159 - button: 1,
160 - pointerType: 'mouse',
161 - clientX: 50,
162 - clientY: 50,
163 - }),
164 - );
165 - ref.current.dispatchEvent(
166 - createEvent('pointerup', {
167 - button: 1,
168 - pointerType: 'mouse',
169 - clientX: 50,
170 - clientY: 50,
171 - }),
172 - );
173 - container.dispatchEvent(
174 - createEvent('pointermove', {
175 - button: 1,
176 - pointerType: 'mouse',
177 - clientX: 110,
178 - clientY: 110,
179 - }),
180 - );
181 - container.dispatchEvent(
182 - createEvent('pointermove', {
183 - button: 1,
184 - pointerType: 'mouse',
185 - clientX: 50,
186 - clientY: 50,
187 - }),
188 - );
189 - expect(onPressStart).toHaveBeenCalledTimes(1);
190 - });
191 -
192 - it('ignores browser emulated events', () => {
193 - ref.current.dispatchEvent(createEvent('pointerdown'));
194 - ref.current.dispatchEvent(createEvent('touchstart'));
195 - ref.current.dispatchEvent(createEvent('mousedown'));
151 + dispatchPointerDown(target, {
152 + button: 1,
153 + pointerType: 'mouse',
154 + });
155 + dispatchPointerUp(target, {
156 + button: 1,
157 + pointerType: 'mouse',
158 + });
159 + dispatchPointerHoverMove(target, {x: 110, y: 110});
160 + dispatchPointerHoverMove(target, {x: 50, y: 50});
161 expect(onPressStart).toHaveBeenCalledTimes(1);
162 });
163
164 it('ignores any events not caused by primary/auxillary-click or touch/pen contact', () => {
200 - ref.current.dispatchEvent(createEvent('pointerdown', {button: 5}));
201 - ref.current.dispatchEvent(createEvent('mousedown', {button: 2}));
165 + const target = ref.current;
166 + dispatchPointerDown(target, {button: 2});
167 + dispatchPointerDown(target, {button: 5});
168 expect(onPressStart).toHaveBeenCalledTimes(0);
169 });
170
171 it('is called once after "keydown" events for Enter', () => {
206 - ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'Enter'}));
207 - ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'Enter'}));
208 - ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'Enter'}));
172 + const target = ref.current;
173 + target.dispatchEvent(keydown({key: 'Enter'}));
174 + target.dispatchEvent(keydown({key: 'Enter'}));
175 + target.dispatchEvent(keydown({key: 'Enter'}));
176 expect(onPressStart).toHaveBeenCalledTimes(1);
177 expect(onPressStart).toHaveBeenCalledWith(
178 expect.objectContaining({pointerType: 'keyboard', type: 'pressstart'}),
@@ -213,14 +180,11 @@ describe('Event responder: Press', () => {
180 });
181
182 it('is called once after "keydown" events for Spacebar', () => {
183 + const target = ref.current;
184 const preventDefault = jest.fn();
217 - ref.current.dispatchEvent(
218 - createKeyboardEvent('keydown', {key: ' ', preventDefault}),
219 - );
185 + target.dispatchEvent(keydown({key: ' ', preventDefault}));
186 expect(preventDefault).toBeCalled();
221 - ref.current.dispatchEvent(createKeyboardEvent('keypress', {key: ' '}));
222 - ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: ' '}));
223 - ref.current.dispatchEvent(createKeyboardEvent('keypress', {key: ' '}));
187 + target.dispatchEvent(keydown({key: ' ', preventDefault}));
188 expect(onPressStart).toHaveBeenCalledTimes(1);
189 expect(onPressStart).toHaveBeenCalledWith(
190 expect.objectContaining({
@@ -231,34 +195,9 @@ describe('Event responder: Press', () => {
195 });
196
197 it('is not called after "keydown" for other keys', () => {
234 - ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'a'}));
198 + ref.current.dispatchEvent(keydown({key: 'a'}));
199 expect(onPressStart).not.toBeCalled();
200 });
237 -
238 - // No PointerEvent fallbacks
239 - it('is called after "mousedown" event', () => {
240 - ref.current.dispatchEvent(
241 - createEvent('mousedown', {
242 - button: 0,
243 - }),
244 - );
245 - expect(onPressStart).toHaveBeenCalledTimes(1);
246 - expect(onPressStart).toHaveBeenCalledWith(
247 - expect.objectContaining({pointerType: 'mouse', type: 'pressstart'}),
248 - );
249 - });
250 -
251 - it('is called after "touchstart" event', () => {
252 - ref.current.dispatchEvent(
253 - createTouchEvent('touchstart', 0, {
254 - target: ref.current,
255 - }),
256 - );
257 - expect(onPressStart).toHaveBeenCalledTimes(1);
258 - expect(onPressStart).toHaveBeenCalledWith(
259 - expect.objectContaining({pointerType: 'touch', type: 'pressstart'}),
260 - );
261 - });
201 });
202
203 describe('onPressEnd', () => {
@@ -274,36 +213,26 @@ describe('Event responder: Press', () => {
213 return <div ref={ref} listeners={listener} />;
214 };
215 ReactDOM.render(<Component />, container);
216 + document.elementFromPoint = () => ref.current;
217 });
218
279 - it('is called after "pointerup" event', () => {
280 - ref.current.dispatchEvent(
281 - createEvent('pointerdown', {pointerType: 'pen'}),
282 - );
283 - ref.current.dispatchEvent(
284 - createTouchEvent('touchstart', 0, {
285 - target: ref.current,
286 - }),
287 - );
288 - ref.current.dispatchEvent(
289 - createTouchEvent('touchend', 0, {
290 - target: ref.current,
291 - }),
292 - );
293 - ref.current.dispatchEvent(createEvent('pointerup', {pointerType: 'pen'}));
294 - expect(onPressEnd).toHaveBeenCalledTimes(1);
295 - expect(onPressEnd).toHaveBeenCalledWith(
296 - expect.objectContaining({pointerType: 'pen', type: 'pressend'}),
297 - );
298 - });
219 + it.each(pointerTypesTable)(
220 + 'is called after pointer up: %s',
221 + pointerType => {
222 + const target = ref.current;
223 + dispatchPointerDown(target, {pointerType});
224 + dispatchPointerUp(target, {pointerType});
225 + expect(onPressEnd).toHaveBeenCalledTimes(1);
226 + expect(onPressEnd).toHaveBeenCalledWith(
227 + expect.objectContaining({pointerType, type: 'pressend'}),
228 + );
229 + },
230 + );
231
300 - it('is called after auxillary-button "pointerup" event', () => {
301 - ref.current.dispatchEvent(
302 - createEvent('pointerdown', {button: 1, pointerType: 'mouse'}),
303 - );
304 - ref.current.dispatchEvent(
305 - createEvent('pointerup', {button: 1, pointerType: 'mouse'}),
306 - );
232 + it('is called after auxillary-button pointer up', () => {
233 + const target = ref.current;
234 + dispatchPointerDown(target, {button: 1, pointerType: 'mouse'});
235 + dispatchPointerUp(target, {button: 1, pointerType: 'mouse'});
236 expect(onPressEnd).toHaveBeenCalledTimes(1);
237 expect(onPressEnd).toHaveBeenCalledWith(
238 expect.objectContaining({
@@ -314,37 +243,12 @@ describe('Event responder: Press', () => {
243 );
244 });
245
317 - it('ignores browser emulated events', () => {
318 - ref.current.dispatchEvent(
319 - createEvent('pointerdown', {pointerType: 'touch'}),
320 - );
321 - ref.current.dispatchEvent(
322 - createTouchEvent('touchstart', 0, {
323 - target: ref.current,
324 - }),
325 - );
326 - ref.current.dispatchEvent(
327 - createEvent('pointerup', {pointerType: 'touch'}),
328 - );
329 - ref.current.dispatchEvent(
330 - createTouchEvent('touchend', 0, {
331 - target: ref.current,
332 - }),
333 - );
334 - ref.current.dispatchEvent(createEvent('mousedown'));
335 - ref.current.dispatchEvent(createEvent('mouseup'));
336 - ref.current.dispatchEvent(createEvent('click'));
337 - expect(onPressEnd).toHaveBeenCalledTimes(1);
338 - expect(onPressEnd).toHaveBeenCalledWith(
339 - expect.objectContaining({pointerType: 'touch', type: 'pressend'}),
340 - );
341 - });
342 -
246 it('is called after "keyup" event for Enter', () => {
344 - ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'Enter'}));
247 + const target = ref.current;
248 + target.dispatchEvent(keydown({key: 'Enter'}));
249 // click occurs before keyup
346 - ref.current.dispatchEvent(createKeyboardEvent('click'));
347 - ref.current.dispatchEvent(createKeyboardEvent('keyup', {key: 'Enter'}));
250 + target.dispatchEvent(click());
251 + target.dispatchEvent(keyup({key: 'Enter'}));
252 expect(onPressEnd).toHaveBeenCalledTimes(1);
253 expect(onPressEnd).toHaveBeenCalledWith(
254 expect.objectContaining({pointerType: 'keyboard', type: 'pressend'}),
@@ -352,8 +256,9 @@ describe('Event responder: Press', () => {
256 });
257
258 it('is called after "keyup" event for Spacebar', () => {
355 - ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: ' '}));
356 - ref.current.dispatchEvent(createKeyboardEvent('keyup', {key: ' '}));
259 + const target = ref.current;
260 + target.dispatchEvent(keydown({key: ' '}));
261 + target.dispatchEvent(keyup({key: ' '}));
262 expect(onPressEnd).toHaveBeenCalledTimes(1);
263 expect(onPressEnd).toHaveBeenCalledWith(
264 expect.objectContaining({pointerType: 'keyboard', type: 'pressend'}),
@@ -361,15 +266,17 @@ describe('Event responder: Press', () => {
266 });
267
268 it('is not called after "keyup" event for other keys', () => {
364 - ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'Enter'}));
365 - ref.current.dispatchEvent(createKeyboardEvent('keyup', {key: 'a'}));
269 + const target = ref.current;
270 + target.dispatchEvent(keydown({key: 'Enter'}));
271 + target.dispatchEvent(keyup({key: 'a'}));
272 expect(onPressEnd).not.toBeCalled();
273 });
274
275 it('is called with keyboard modifiers', () => {
370 - ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'Enter'}));
371 - ref.current.dispatchEvent(
372 - createKeyboardEvent('keyup', {
276 + const target = ref.current;
277 + target.dispatchEvent(keydown({key: 'Enter'}));
278 + target.dispatchEvent(
279 + keyup({
280 key: 'Enter',
281 metaKey: true,
282 ctrlKey: true,
@@ -388,41 +295,6 @@ describe('Event responder: Press', () => {
295 }),
296 );
297 });
391 -
392 - // No PointerEvent fallbacks
393 - it('is called after "mouseup" event', () => {
394 - ref.current.dispatchEvent(
395 - createEvent('mousedown', {
396 - button: 0,
397 - }),
398 - );
399 - ref.current.dispatchEvent(
400 - createEvent('mouseup', {
401 - button: 0,
402 - }),
403 - );
404 - expect(onPressEnd).toHaveBeenCalledTimes(1);
405 - expect(onPressEnd).toHaveBeenCalledWith(
406 - expect.objectContaining({pointerType: 'mouse', type: 'pressend'}),
407 - );
408 - });
409 - it('is called after "touchend" event', () => {
410 - document.elementFromPoint = () => ref.current;
411 - ref.current.dispatchEvent(
412 - createTouchEvent('touchstart', 0, {
413 - target: ref.current,
414 - }),
415 - );
416 - ref.current.dispatchEvent(
417 - createTouchEvent('touchend', 0, {
418 - target: ref.current,
419 - }),
420 - );
421 - expect(onPressEnd).toHaveBeenCalledTimes(1);
422 - expect(onPressEnd).toHaveBeenCalledWith(
423 - expect.objectContaining({pointerType: 'touch', type: 'pressend'}),
424 - );
425 - });
298 });
299
300 describe('onPressChange', () => {
@@ -438,22 +310,27 @@ describe('Event responder: Press', () => {
310 return <div ref={ref} listeners={listener} />;
311 };
312 ReactDOM.render(<Component />, container);
313 + document.elementFromPoint = () => ref.current;
314 });
315
443 - it('is called after "pointerdown" and "pointerup" events', () => {
444 - ref.current.dispatchEvent(createEvent('pointerdown'));
445 - expect(onPressChange).toHaveBeenCalledTimes(1);
446 - expect(onPressChange).toHaveBeenCalledWith(true);
447 - ref.current.dispatchEvent(createEvent('pointerup'));
448 - expect(onPressChange).toHaveBeenCalledTimes(2);
449 - expect(onPressChange).toHaveBeenCalledWith(false);
450 - });
316 + it.each(pointerTypesTable)(
317 + 'is called after pointer down and up: %s',
318 + pointerType => {
319 + const target = ref.current;
320 + dispatchPointerDown(target, {pointerType});
321 + expect(onPressChange).toHaveBeenCalledTimes(1);
322 + expect(onPressChange).toHaveBeenCalledWith(true);
323 + dispatchPointerUp(target, {pointerType});
324 + expect(onPressChange).toHaveBeenCalledTimes(2);
325 + expect(onPressChange).toHaveBeenCalledWith(false);
326 + },
327 + );
328
329 it('is called after valid "keydown" and "keyup" events', () => {
453 - ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'Enter'}));
330 + ref.current.dispatchEvent(keydown({key: 'Enter'}));
331 expect(onPressChange).toHaveBeenCalledTimes(1);
332 expect(onPressChange).toHaveBeenCalledWith(true);
456 - ref.current.dispatchEvent(createKeyboardEvent('keyup', {key: 'Enter'}));
333 + ref.current.dispatchEvent(keyup({key: 'Enter'}));
334 expect(onPressChange).toHaveBeenCalledTimes(2);
335 expect(onPressChange).toHaveBeenCalledWith(false);
336 });
@@ -478,58 +355,33 @@ describe('Event responder: Press', () => {
355 bottom: 100,
356 right: 100,
357 });
358 + document.elementFromPoint = () => ref.current;
359 });
360
483 - it('is called after "pointerup" event', () => {
484 - ref.current.dispatchEvent(
485 - createEvent('pointerdown', {pointerType: 'pen'}),
486 - );
487 - ref.current.dispatchEvent(
488 - createTouchEvent('touchstart', 0, {
489 - target: ref.current,
490 - clientX: 0,
491 - clientY: 0,
492 - }),
493 - );
494 - ref.current.dispatchEvent(
495 - createTouchEvent('touchend', 0, {
496 - target: ref.current,
497 - clientX: 0,
498 - clientY: 0,
499 - }),
500 - );
501 - ref.current.dispatchEvent(
502 - createEvent('pointerup', {
503 - pointerType: 'pen',
504 - clientX: 0,
505 - clientY: 0,
506 - }),
507 - );
508 - expect(onPress).toHaveBeenCalledTimes(1);
509 - expect(onPress).toHaveBeenCalledWith(
510 - expect.objectContaining({pointerType: 'pen', type: 'press'}),
511 - );
512 - });
361 + it.each(pointerTypesTable)(
362 + 'is called after pointer up: %s',
363 + pointerType => {
364 + const target = ref.current;
365 + dispatchPointerDown(target, {pointerType});
366 + dispatchPointerUp(target, {pointerType, x: 10, y: 10});
367 + expect(onPress).toHaveBeenCalledTimes(1);
368 + expect(onPress).toHaveBeenCalledWith(
369 + expect.objectContaining({pointerType, type: 'press'}),
370 + );
371 + },
372 + );
373
374 it('is not called after auxillary-button press', () => {
515 - const Component = () => {
516 - const listener = usePressResponder({
517 - onPress,
518 - });
519 - return <div ref={ref} listeners={listener} />;
520 - };
521 - ReactDOM.render(<Component />, container);
522 -
523 - ref.current.dispatchEvent(createEvent('pointerdown', {button: 1}));
524 - ref.current.dispatchEvent(
525 - createEvent('pointerup', {button: 1, clientX: 10, clientY: 10}),
526 - );
375 + const target = ref.current;
376 + dispatchPointerDown(target, {button: 1, pointerType: 'mouse'});
377 + dispatchPointerUp(target, {button: 1, pointerType: 'mouse'});
378 expect(onPress).not.toHaveBeenCalled();
379 });
380
381 it('is called after valid "keyup" event', () => {
531 - ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'Enter'}));
532 - ref.current.dispatchEvent(createKeyboardEvent('keyup', {key: 'Enter'}));
382 + const target = ref.current;
383 + target.dispatchEvent(keydown({key: 'Enter'}));
384 + target.dispatchEvent(keyup({key: 'Enter'}));
385 expect(onPress).toHaveBeenCalledTimes(1);
386 expect(onPress).toHaveBeenCalledWith(
387 expect.objectContaining({pointerType: 'keyboard', type: 'press'}),
@@ -545,26 +397,21 @@ describe('Event responder: Press', () => {
397 return <input ref={inputRef} listeners={listener} />;
398 };
399 ReactDOM.render(<Component />, container);
548 - inputRef.current.dispatchEvent(
549 - createKeyboardEvent('keydown', {key: 'Enter'}),
550 - );
551 - inputRef.current.dispatchEvent(
552 - createKeyboardEvent('keyup', {key: 'Enter'}),
553 - );
554 - inputRef.current.dispatchEvent(
555 - createKeyboardEvent('keydown', {key: ' '}),
556 - );
557 - inputRef.current.dispatchEvent(createKeyboardEvent('keyup', {key: ' '}));
400 + const target = inputRef.current;
401 + target.dispatchEvent(keydown({key: 'Enter'}));
402 + target.dispatchEvent(keyup({key: 'Enter'}));
403 + target.dispatchEvent(keydown({key: ' '}));
404 + target.dispatchEvent(keyup({key: ' '}));
405 expect(onPress).not.toBeCalled();
406 });
407
408 it('is called with modifier keys', () => {
562 - ref.current.dispatchEvent(
563 - createEvent('pointerdown', {metaKey: true, pointerType: 'mouse'}),
564 - );
565 - ref.current.dispatchEvent(
566 - createEvent('pointerup', {metaKey: true, pointerType: 'mouse'}),
567 - );
409 + const target = ref.current;
410 + dispatchPointerDown(target, {metaKey: true, pointerType: 'mouse'});
411 + dispatchPointerUp(target, {
412 + metaKey: true,
413 + pointerType: 'mouse',
414 + });
415 expect(onPress).toHaveBeenCalledWith(
416 expect.objectContaining({
417 pointerType: 'mouse',
@@ -596,28 +443,19 @@ describe('Event responder: Press', () => {
443 bottom: 0,
444 top: 0,
445 });
599 - buttonRef.current.dispatchEvent(
600 - createEvent('pointerdown', {pointerType: 'mouse'}),
601 - );
602 - buttonRef.current.dispatchEvent(
603 - createEvent('pointerup', {pointerType: 'mouse'}),
604 - );
446 + const target = buttonRef.current;
447 + dispatchPointerDown(target, {pointerType: 'mouse'});
448 + dispatchPointerUp(target, {pointerType: 'mouse'});
449 expect(onPress).toBeCalled();
450 });
607 -
608 - // No PointerEvent fallbacks
609 - // TODO: jsdom missing APIs
610 - // it('is called after "touchend" event', () => {
611 - // ref.current.dispatchEvent(createEvent('touchstart'));
612 - // ref.current.dispatchEvent(createEvent('touchend'));
613 - // expect(onPress).toHaveBeenCalledTimes(1);
614 - // });
451 });
452
453 describe('onPressMove', () => {
618 - it('is called after "pointermove"', () => {
619 - const onPressMove = jest.fn();
620 - const ref = React.createRef();
454 + let onPressMove, ref;
455 +
456 + beforeEach(() => {
457 + onPressMove = jest.fn();
458 + ref = React.createRef();
459 const Component = () => {
460 const listener = usePressResponder({
461 onPressMove,
@@ -625,102 +463,90 @@ describe('Event responder: Press', () => {
463 return <div ref={ref} listeners={listener} />;
464 };
465 ReactDOM.render(<Component />, container);
628 -
466 ref.current.getBoundingClientRect = () => ({
467 top: 0,
468 left: 0,
469 bottom: 100,
470 right: 100,
471 });
635 - ref.current.dispatchEvent(
636 - createEvent('pointerdown', {pointerType: 'mouse'}),
637 - );
638 - ref.current.dispatchEvent(
639 - createEvent('pointermove', {
640 - pointerType: 'mouse',
641 - clientX: 10,
642 - clientY: 10,
643 - }),
644 - );
645 - expect(onPressMove).toHaveBeenCalledTimes(1);
646 - expect(onPressMove).toHaveBeenCalledWith(
647 - expect.objectContaining({pointerType: 'mouse', type: 'pressmove'}),
648 - );
472 + document.elementFromPoint = () => ref.current;
473 });
474
651 - it('is not called if "pointermove" occurs during keyboard press', () => {
652 - const onPressMove = jest.fn();
653 - const ref = React.createRef();
654 - const Component = () => {
655 - const listener = usePressResponder({
656 - onPressMove,
475 + it.each(pointerTypesTable)(
476 + 'is called after pointer move: %s',
477 + pointerType => {
478 + const target = ref.current;
479 + target.getBoundingClientRect = () => ({
480 + top: 0,
481 + left: 0,
482 + bottom: 100,
483 + right: 100,
484 });
658 - return <div ref={ref} listeners={listener} />;
659 - };
660 - ReactDOM.render(<Component />, container);
485 + dispatchPointerDown(target, {pointerType});
486 + dispatchPointerMove(target, {
487 + pointerType,
488 + x: 10,
489 + y: 10,
490 + });
491 + dispatchPointerMove(target, {
492 + pointerType,
493 + x: 20,
494 + y: 20,
495 + });
496 + expect(onPressMove).toHaveBeenCalledTimes(2);
497 + expect(onPressMove).toHaveBeenCalledWith(
498 + expect.objectContaining({pointerType, type: 'pressmove'}),
499 + );
500 + },
501 + );
502
662 - ref.current.getBoundingClientRect = () => ({
503 + it('is not called if pointer move occurs during keyboard press', () => {
504 + const target = ref.current;
505 + target.getBoundingClientRect = () => ({
506 top: 0,
507 left: 0,
508 bottom: 100,
509 right: 100,
510 });
668 - ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'Enter'}));
669 - ref.current.dispatchEvent(
670 - createEvent('pointermove', {
671 - pointerType: 'mouse',
672 - clientX: 10,
673 - clientY: 10,
674 - }),
675 - );
511 + target.dispatchEvent(keydown({key: 'Enter'}));
512 + dispatchPointerMove(target, {
513 + button: -1,
514 + pointerType: 'mouse',
515 + x: 10,
516 + y: 10,
517 + });
518 expect(onPressMove).not.toBeCalled();
519 });
520 + });
521
679 - it('ignores browser emulated events', () => {
680 - const onPressMove = jest.fn();
681 - const ref = React.createRef();
522 + describe.each(pointerTypesTable)('press with movement: %s', pointerType => {
523 + let events, ref, outerRef;
524 +
525 + beforeEach(() => {
526 + events = [];
527 + ref = React.createRef();
528 + outerRef = React.createRef();
529 + const createEventHandler = msg => () => {
530 + events.push(msg);
531 + };
532 const Component = () => {
533 const listener = usePressResponder({
684 - onPressMove,
534 + onPress: createEventHandler('onPress'),
535 + onPressChange: createEventHandler('onPressChange'),
536 + onPressMove: createEventHandler('onPressMove'),
537 + onPressStart: createEventHandler('onPressStart'),
538 + onPressEnd: createEventHandler('onPressEnd'),
539 });
686 - return <div ref={ref} listeners={listener} />;
540 + return (
541 + <div ref={outerRef}>
542 + <div ref={ref} listeners={listener} />
543 + </div>
544 + );
545 };
546 ReactDOM.render(<Component />, container);
689 -
690 - ref.current.getBoundingClientRect = () => ({
691 - top: 0,
692 - left: 0,
693 - bottom: 100,
694 - right: 100,
695 - });
696 - ref.current.dispatchEvent(
697 - createEvent('pointerdown', {pointerType: 'touch'}),
698 - );
699 - ref.current.dispatchEvent(
700 - createTouchEvent('touchstart', 0, {
701 - target: ref.current,
702 - }),
703 - );
704 - ref.current.dispatchEvent(
705 - createEvent('pointermove', {
706 - pointerType: 'touch',
707 - clientX: 10,
708 - clientY: 10,
709 - }),
710 - );
711 - ref.current.dispatchEvent(
712 - createTouchEvent('touchmove', 0, {
713 - target: ref.current,
714 - clientX: 10,
715 - clientY: 10,
716 - }),
717 - );
718 - ref.current.dispatchEvent(createEvent('mousemove'));
719 - expect(onPressMove).toHaveBeenCalledTimes(1);
547 + document.elementFromPoint = () => ref.current;
548 });
721 - });
549
723 - describe('press with movement (pointer events)', () => {
550 const rectMock = {
551 width: 100,
552 height: 100,
@@ -732,12 +558,12 @@ describe('Event responder: Press', () => {
558 const pressRectOffset = 20;
559 const getBoundingClientRectMock = () => rectMock;
560 const coordinatesInside = {
735 - clientX: rectMock.left - pressRectOffset,
736 - clientY: rectMock.top - pressRectOffset,
561 + x: rectMock.left - pressRectOffset,
562 + y: rectMock.top - pressRectOffset,
563 };
564 const coordinatesOutside = {
739 - clientX: rectMock.left - pressRectOffset - 1,
740 - clientY: rectMock.top - pressRectOffset - 1,
565 + x: rectMock.left - pressRectOffset - 1,
566 + y: rectMock.top - pressRectOffset - 1,
567 };
568
569 describe('within bounds of hit rect', () => {
@@ -749,32 +575,12 @@ describe('Event responder: Press', () => {
575 * └──────────────────┘
576 */
577 it('"onPress*" events are called immediately', () => {
752 - let events = [];
753 - const ref = React.createRef();
754 - const createEventHandler = msg => () => {
755 - events.push(msg);
756 - };
757 -
758 - const Component = () => {
759 - const listener = usePressResponder({
760 - onPress: createEventHandler('onPress'),
761 - onPressChange: createEventHandler('onPressChange'),
762 - onPressMove: createEventHandler('onPressMove'),
763 - onPressStart: createEventHandler('onPressStart'),
764 - onPressEnd: createEventHandler('onPressEnd'),
765 - });
766 - return <div ref={ref} listeners={listener} />;
767 - };
768 - ReactDOM.render(<Component />, container);
769 -
770 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
771 - ref.current.dispatchEvent(createEvent('pointerdown'));
772 - ref.current.dispatchEvent(
773 - createEvent('pointermove', coordinatesInside),
774 - );
775 - ref.current.dispatchEvent(createEvent('pointerup', coordinatesInside));
578 + const target = ref.current;
579 + target.getBoundingClientRect = getBoundingClientRectMock;
580 + dispatchPointerDown(target, {pointerType});
581 + dispatchPointerMove(target, {pointerType, ...coordinatesInside});
582 + dispatchPointerUp(target, {pointerType, ...coordinatesInside});
583 jest.runAllTimers();
777 -
584 expect(events).toEqual([
585 'onPressStart',
586 'onPressChange',
@@ -786,12 +592,41 @@ describe('Event responder: Press', () => {
592 });
593
594 it('"onPress*" events are correctly called with target change', () => {
789 - let events = [];
790 - const outerRef = React.createRef();
791 - const innerRef = React.createRef();
595 + const target = ref.current;
596 + const outer = outerRef.current;
597 + target.getBoundingClientRect = getBoundingClientRectMock;
598 + dispatchPointerDown(target, {pointerType});
599 + dispatchPointerMove(target, {pointerType, ...coordinatesInside});
600 + // TODO: this sequence may differ in the future between PointerEvent and mouse fallback when
601 + // use 'setPointerCapture'.
602 + if (pointerType === 'touch') {
603 + dispatchPointerMove(target, {pointerType, ...coordinatesOutside});
604 + } else {
605 + dispatchPointerMove(outer, {pointerType, ...coordinatesOutside});
606 + }
607 + dispatchPointerMove(target, {pointerType, ...coordinatesInside});
608 + dispatchPointerUp(target, {pointerType, ...coordinatesInside});
609 +
610 + expect(events.filter(removePressMoveStrings)).toEqual([
611 + 'onPressStart',
612 + 'onPressChange',
613 + 'onPressEnd',
614 + 'onPressChange',
615 + 'onPressStart',
616 + 'onPressChange',
617 + 'onPressEnd',
618 + 'onPressChange',
619 + 'onPress',
620 + ]);
621 + });
622 +
623 + it('press retention offset can be configured', () => {
624 + let localEvents = [];
625 + const localRef = React.createRef();
626 const createEventHandler = msg => () => {
793 - events.push(msg);
627 + localEvents.push(msg);
628 };
629 + const pressRetentionOffset = {top: 40, bottom: 40, left: 40, right: 40};
630
631 const Component = () => {
632 const listener = usePressResponder({
@@ -800,72 +635,22 @@ describe('Event responder: Press', () => {
635 onPressMove: createEventHandler('onPressMove'),
636 onPressStart: createEventHandler('onPressStart'),
637 onPressEnd: createEventHandler('onPressEnd'),
638 + pressRetentionOffset,
639 });
804 - return (
805 - <div ref={outerRef}>
806 - <div ref={innerRef} listeners={listener} />
807 - </div>
808 - );
640 + return <div ref={localRef} listeners={listener} />;
641 };
642 ReactDOM.render(<Component />, container);
643
812 - innerRef.current.getBoundingClientRect = getBoundingClientRectMock;
813 - innerRef.current.dispatchEvent(createEvent('pointerdown'));
814 - outerRef.current.dispatchEvent(
815 - createEvent('pointermove', coordinatesOutside),
816 - );
817 - innerRef.current.dispatchEvent(
818 - createEvent('pointermove', coordinatesInside),
819 - );
820 - innerRef.current.dispatchEvent(
821 - createEvent('pointerup', coordinatesInside),
822 - );
823 - jest.runAllTimers();
824 -
825 - expect(events).toEqual([
826 - 'onPressStart',
827 - 'onPressChange',
828 - 'onPressEnd',
829 - 'onPressChange',
830 - 'onPressStart',
831 - 'onPressChange',
832 - 'onPressEnd',
833 - 'onPressChange',
834 - 'onPress',
835 - ]);
836 - });
837 -
838 - it('press retention offset can be configured', () => {
839 - let events = [];
840 - const ref = React.createRef();
841 - const createEventHandler = msg => () => {
842 - events.push(msg);
843 - };
844 - const pressRetentionOffset = {top: 40, bottom: 40, left: 40, right: 40};
845 -
846 - const Component = () => {
847 - const listener = usePressResponder({
848 - onPress: createEventHandler('onPress'),
849 - onPressChange: createEventHandler('onPressChange'),
850 - onPressMove: createEventHandler('onPressMove'),
851 - onPressStart: createEventHandler('onPressStart'),
852 - onPressEnd: createEventHandler('onPressEnd'),
853 - pressRetentionOffset,
854 - });
855 - return <div ref={ref} listeners={listener} />;
856 - };
857 - ReactDOM.render(<Component />, container);
858 -
859 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
860 - ref.current.dispatchEvent(createEvent('pointerdown'));
861 - ref.current.dispatchEvent(
862 - createEvent('pointermove', {
863 - clientX: rectMock.left - pressRetentionOffset.left,
864 - clientY: rectMock.top - pressRetentionOffset.top,
865 - }),
866 - );
867 - ref.current.dispatchEvent(createEvent('pointerup', coordinatesInside));
868 - expect(events).toEqual([
644 + const target = localRef.current;
645 + target.getBoundingClientRect = getBoundingClientRectMock;
646 + dispatchPointerDown(target, {pointerType});
647 + dispatchPointerMove(target, {
648 + pointerType,
649 + x: rectMock.left,
650 + y: rectMock.top,
651 + });
652 + dispatchPointerUp(target, {pointerType, ...coordinatesInside});
653 + expect(localEvents).toEqual([
654 'onPressStart',
655 'onPressChange',
656 'onPressMove',
@@ -876,26 +661,11 @@ describe('Event responder: Press', () => {
661 });
662
663 it('responder region accounts for decrease in element dimensions', () => {
879 - let events = [];
880 - const ref = React.createRef();
881 - const createEventHandler = msg => () => {
882 - events.push(msg);
883 - };
884 -
885 - const Component = () => {
886 - const listener = usePressResponder({
887 - onPress: createEventHandler('onPress'),
888 - onPressStart: createEventHandler('onPressStart'),
889 - onPressEnd: createEventHandler('onPressEnd'),
890 - });
891 - return <div ref={ref} listeners={listener} />;
892 - };
893 - ReactDOM.render(<Component />, container);
894 -
895 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
896 - ref.current.dispatchEvent(createEvent('pointerdown'));
664 + const target = ref.current;
665 + target.getBoundingClientRect = getBoundingClientRectMock;
666 + dispatchPointerDown(target, {pointerType});
667 // emulate smaller dimensions change on activation
898 - ref.current.getBoundingClientRect = () => ({
668 + target.getBoundingClientRect = () => ({
669 width: 80,
670 height: 80,
671 top: 60,
@@ -904,36 +674,28 @@ describe('Event responder: Press', () => {
674 bottom: 490,
675 });
676 const coordinates = {
907 - clientX: rectMock.left,
908 - clientY: rectMock.top,
677 + x: rectMock.left,
678 + y: rectMock.top,
679 };
680 // move to an area within the pre-activation region
911 - ref.current.dispatchEvent(createEvent('pointermove', coordinates));
912 - ref.current.dispatchEvent(createEvent('pointerup', coordinates));
913 - expect(events).toEqual(['onPressStart', 'onPressEnd', 'onPress']);
681 + dispatchPointerMove(target, {pointerType, ...coordinates});
682 + dispatchPointerUp(target, {pointerType, ...coordinates});
683 + expect(events).toEqual([
684 + 'onPressStart',
685 + 'onPressChange',
686 + 'onPressMove',
687 + 'onPressEnd',
688 + 'onPressChange',
689 + 'onPress',
690 + ]);
691 });
692
693 it('responder region accounts for increase in element dimensions', () => {
917 - let events = [];
918 - const ref = React.createRef();
919 - const createEventHandler = msg => () => {
920 - events.push(msg);
921 - };
922 -
923 - const Component = () => {
924 - const listener = usePressResponder({
925 - onPress: createEventHandler('onPress'),
926 - onPressStart: createEventHandler('onPressStart'),
927 - onPressEnd: createEventHandler('onPressEnd'),
928 - });
929 - return <div ref={ref} listeners={listener} />;
930 - };
931 - ReactDOM.render(<Component />, container);
932 -
933 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
934 - ref.current.dispatchEvent(createEvent('pointerdown'));
694 + const target = ref.current;
695 + target.getBoundingClientRect = getBoundingClientRectMock;
696 + dispatchPointerDown(target, {pointerType});
697 // emulate larger dimensions change on activation
936 - ref.current.getBoundingClientRect = () => ({
698 + target.getBoundingClientRect = () => ({
699 width: 200,
700 height: 200,
701 top: 0,
@@ -942,397 +704,12 @@ describe('Event responder: Press', () => {
704 bottom: 550,
705 });
706 const coordinates = {
945 - clientX: rectMock.left - 50,
946 - clientY: rectMock.top - 50,
707 + x: rectMock.left - 50,
708 + y: rectMock.top - 50,
709 };
710 // move to an area within the post-activation region
949 - ref.current.dispatchEvent(createEvent('pointermove', coordinates));
950 - ref.current.dispatchEvent(createEvent('pointerup', coordinates));
951 - expect(events).toEqual(['onPressStart', 'onPressEnd', 'onPress']);
952 - });
953 - });
954 -
955 - describe('beyond bounds of hit rect', () => {
956 - /** ┌──────────────────┐
957 - * │ ┌────────────┐ │
958 - * │ │ VisualRect │ │
959 - * │ └────────────┘ │
960 - * │ HitRect │
961 - * └──────────────────┘
962 - * X <= Move to X and release
963 - */
964 -
965 - it('"onPress" is not called on release', () => {
966 - let events = [];
967 - const ref = React.createRef();
968 - const createEventHandler = msg => () => {
969 - events.push(msg);
970 - };
971 -
972 - const Component = () => {
973 - const listener = usePressResponder({
974 - onPress: createEventHandler('onPress'),
975 - onPressChange: createEventHandler('onPressChange'),
976 - onPressMove: createEventHandler('onPressMove'),
977 - onPressStart: createEventHandler('onPressStart'),
978 - onPressEnd: createEventHandler('onPressEnd'),
979 - });
980 - return <div ref={ref} listeners={listener} />;
981 - };
982 - ReactDOM.render(<Component />, container);
983 -
984 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
985 - ref.current.dispatchEvent(createEvent('pointerdown'));
986 - ref.current.dispatchEvent(
987 - createEvent('pointermove', coordinatesInside),
988 - );
989 - container.dispatchEvent(createEvent('pointermove', coordinatesOutside));
990 - container.dispatchEvent(createEvent('pointerup', coordinatesOutside));
991 - jest.runAllTimers();
992 -
993 - expect(events).toEqual([
994 - 'onPressStart',
995 - 'onPressChange',
996 - 'onPressMove',
997 - 'onPressEnd',
998 - 'onPressChange',
999 - ]);
1000 - });
1001 - });
1002 -
1003 - it('"onPress" is not called on release with mouse', () => {
1004 - let events = [];
1005 - const ref = React.createRef();
1006 - const createEventHandler = msg => () => {
1007 - events.push(msg);
1008 - };
1009 -
1010 - const Component = () => {
1011 - const listener = usePressResponder({
1012 - onPress: createEventHandler('onPress'),
1013 - onPressChange: createEventHandler('onPressChange'),
1014 - onPressMove: createEventHandler('onPressMove'),
1015 - onPressStart: createEventHandler('onPressStart'),
1016 - onPressEnd: createEventHandler('onPressEnd'),
1017 - });
1018 - return <div ref={ref} listeners={listener} />;
1019 - };
1020 - ReactDOM.render(<Component />, container);
1021 -
1022 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
1023 - ref.current.dispatchEvent(
1024 - createEvent('pointerdown', {
1025 - pointerType: 'mouse',
1026 - }),
1027 - );
1028 - ref.current.dispatchEvent(
1029 - createEvent('pointermove', {
1030 - ...coordinatesInside,
1031 - pointerType: 'mouse',
1032 - }),
1033 - );
1034 - container.dispatchEvent(
1035 - createEvent('pointermove', {
1036 - ...coordinatesOutside,
1037 - pointerType: 'mouse',
1038 - }),
1039 - );
1040 - container.dispatchEvent(
1041 - createEvent('pointerup', {
1042 - ...coordinatesOutside,
1043 - pointerType: 'mouse',
1044 - }),
1045 - );
1046 - jest.runAllTimers();
1047 -
1048 - expect(events).toEqual([
1049 - 'onPressStart',
1050 - 'onPressChange',
1051 - 'onPressMove',
1052 - 'onPressEnd',
1053 - 'onPressChange',
1054 - ]);
1055 - });
1056 -
1057 - it('"onPress" is called on re-entry to hit rect for mouse', () => {
1058 - let events = [];
1059 - const ref = React.createRef();
1060 - const createEventHandler = msg => () => {
1061 - events.push(msg);
1062 - };
1063 -
1064 - const Component = () => {
1065 - const listener = usePressResponder({
1066 - onPress: createEventHandler('onPress'),
1067 - onPressChange: createEventHandler('onPressChange'),
1068 - onPressMove: createEventHandler('onPressMove'),
1069 - onPressStart: createEventHandler('onPressStart'),
1070 - onPressEnd: createEventHandler('onPressEnd'),
1071 - });
1072 - return <div ref={ref} listeners={listener} />;
1073 - };
1074 - ReactDOM.render(<Component />, container);
1075 -
1076 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
1077 - ref.current.dispatchEvent(
1078 - createEvent('pointerdown', {
1079 - pointerType: 'mouse',
1080 - }),
1081 - );
1082 - ref.current.dispatchEvent(
1083 - createEvent('pointermove', {
1084 - ...coordinatesInside,
1085 - pointerType: 'mouse',
1086 - }),
1087 - );
1088 - container.dispatchEvent(
1089 - createEvent('pointermove', {
1090 - ...coordinatesOutside,
1091 - pointerType: 'mouse',
1092 - }),
1093 - );
1094 - container.dispatchEvent(
1095 - createEvent('pointermove', {
1096 - ...coordinatesInside,
1097 - pointerType: 'mouse',
1098 - }),
1099 - );
1100 - container.dispatchEvent(
1101 - createEvent('pointerup', {
1102 - ...coordinatesInside,
1103 - pointerType: 'mouse',
1104 - }),
1105 - );
1106 - jest.runAllTimers();
1107 -
1108 - expect(events).toEqual([
1109 - 'onPressStart',
1110 - 'onPressChange',
1111 - 'onPressMove',
1112 - 'onPressEnd',
1113 - 'onPressChange',
1114 - 'onPressStart',
1115 - 'onPressChange',
1116 - 'onPressEnd',
1117 - 'onPressChange',
1118 - 'onPress',
1119 - ]);
1120 - });
1121 -
1122 - it('"onPress" is called on re-entry to hit rect for touch', () => {
1123 - let events = [];
1124 - const ref = React.createRef();
1125 - const createEventHandler = msg => () => {
1126 - events.push(msg);
1127 - };
1128 -
1129 - const Component = () => {
1130 - const listener = usePressResponder({
1131 - onPress: createEventHandler('onPress'),
1132 - onPressChange: createEventHandler('onPressChange'),
1133 - onPressMove: createEventHandler('onPressMove'),
1134 - onPressStart: createEventHandler('onPressStart'),
1135 - onPressEnd: createEventHandler('onPressEnd'),
1136 - });
1137 - return <div ref={ref} listeners={listener} />;
1138 - };
1139 - ReactDOM.render(<Component />, container);
1140 -
1141 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
1142 - ref.current.dispatchEvent(
1143 - createEvent('pointerdown', {
1144 - pointerType: 'touch',
1145 - }),
1146 - );
1147 - ref.current.dispatchEvent(
1148 - createTouchEvent('touchstart', 0, {
1149 - target: ref.current,
1150 - }),
1151 - );
1152 - ref.current.dispatchEvent(
1153 - createEvent('pointermove', {
1154 - ...coordinatesInside,
1155 - pointerType: 'touch',
1156 - }),
1157 - );
1158 - ref.current.dispatchEvent(
1159 - createTouchEvent('touchmove', 0, {
1160 - ...coordinatesInside,
1161 - target: ref.current,
1162 - }),
1163 - );
1164 - container.dispatchEvent(
1165 - createEvent('pointermove', {
1166 - ...coordinatesOutside,
1167 - pointerType: 'touch',
1168 - }),
1169 - );
1170 - ref.current.dispatchEvent(
1171 - createTouchEvent('touchmove', 0, {
1172 - ...coordinatesOutside,
1173 - target: ref.current,
1174 - }),
1175 - );
1176 - container.dispatchEvent(
1177 - createEvent('pointermove', {
1178 - ...coordinatesInside,
1179 - pointerType: 'touch',
1180 - }),
1181 - );
1182 - ref.current.dispatchEvent(
1183 - createTouchEvent('touchmove', 0, {
1184 - ...coordinatesInside,
1185 - target: ref.current,
1186 - }),
1187 - );
1188 - ref.current.dispatchEvent(
1189 - createTouchEvent('touchend', 0, {
1190 - ...coordinatesInside,
1191 - target: ref.current,
1192 - }),
1193 - );
1194 - container.dispatchEvent(
1195 - createEvent('pointerup', {
1196 - ...coordinatesInside,
1197 - pointerType: 'touch',
1198 - }),
1199 - );
1200 - jest.runAllTimers();
1201 -
1202 - expect(events).toEqual([
1203 - 'onPressStart',
1204 - 'onPressChange',
1205 - 'onPressMove',
1206 - 'onPressEnd',
1207 - 'onPressChange',
1208 - 'onPressStart',
1209 - 'onPressChange',
1210 - 'onPressEnd',
1211 - 'onPressChange',
1212 - 'onPress',
1213 - ]);
1214 - });
1215 - });
1216 -
1217 - describe('press with movement (touch events fallback)', () => {
1218 - const rectMock = {
1219 - width: 100,
1220 - height: 100,
1221 - top: 50,
1222 - left: 50,
1223 - right: 150,
1224 - bottom: 150,
1225 - };
1226 - const pressRectOffset = 20;
1227 - const getBoundingClientRectMock = () => rectMock;
1228 - const coordinatesInside = {
1229 - clientX: rectMock.left - pressRectOffset,
1230 - clientY: rectMock.top - pressRectOffset,
1231 - };
1232 - const coordinatesOutside = {
1233 - clientX: rectMock.left - pressRectOffset - 1,
1234 - clientY: rectMock.top - pressRectOffset - 1,
1235 - };
1236 -
1237 - describe('within bounds of hit rect', () => {
1238 - /** ┌──────────────────┐
1239 - * │ ┌────────────┐ │
1240 - * │ │ VisualRect │ │
1241 - * │ └────────────┘ │
1242 - * │ HitRect X │ <= Move to X and release
1243 - * └──────────────────┘
1244 - */
1245 - it('"onPress*" events are called immediately', () => {
1246 - let events = [];
1247 - const ref = React.createRef();
1248 - const createEventHandler = msg => () => {
1249 - events.push(msg);
1250 - };
1251 -
1252 - const Component = () => {
1253 - const listener = usePressResponder({
1254 - onPress: createEventHandler('onPress'),
1255 - onPressChange: createEventHandler('onPressChange'),
1256 - onPressMove: createEventHandler('onPressMove'),
1257 - onPressStart: createEventHandler('onPressStart'),
1258 - onPressEnd: createEventHandler('onPressEnd'),
1259 - });
1260 - return <div ref={ref} listeners={listener} />;
1261 - };
1262 - ReactDOM.render(<Component />, container);
1263 -
1264 - document.elementFromPoint = () => ref.current;
1265 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
1266 - ref.current.dispatchEvent(
1267 - createTouchEvent('touchstart', 0, {
1268 - target: ref.current,
1269 - }),
1270 - );
1271 - ref.current.dispatchEvent(
1272 - createTouchEvent('touchmove', 0, {
1273 - ...coordinatesInside,
1274 - target: ref.current,
1275 - }),
1276 - );
1277 - ref.current.dispatchEvent(
1278 - createTouchEvent('touchend', 0, {
1279 - ...coordinatesInside,
1280 - target: ref.current,
1281 - }),
1282 - );
1283 - jest.runAllTimers();
1284 -
1285 - expect(events).toEqual([
1286 - 'onPressStart',
1287 - 'onPressChange',
1288 - 'onPressMove',
1289 - 'onPressEnd',
1290 - 'onPressChange',
1291 - 'onPress',
1292 - ]);
1293 - });
1294 -
1295 - it('press retention offset can be configured', () => {
1296 - let events = [];
1297 - const ref = React.createRef();
1298 - const createEventHandler = msg => () => {
1299 - events.push(msg);
1300 - };
1301 - const pressRetentionOffset = {top: 40, bottom: 40, left: 40, right: 40};
1302 -
1303 - const Component = () => {
1304 - const listener = usePressResponder({
1305 - onPress: createEventHandler('onPress'),
1306 - onPressChange: createEventHandler('onPressChange'),
1307 - onPressMove: createEventHandler('onPressMove'),
1308 - onPressStart: createEventHandler('onPressStart'),
1309 - onPressEnd: createEventHandler('onPressEnd'),
1310 - pressRetentionOffset,
1311 - });
1312 - return <div ref={ref} listeners={listener} />;
1313 - };
1314 - ReactDOM.render(<Component />, container);
1315 -
1316 - document.elementFromPoint = () => ref.current;
1317 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
1318 - ref.current.dispatchEvent(
1319 - createTouchEvent('touchstart', 0, {
1320 - target: ref.current,
1321 - }),
1322 - );
1323 - ref.current.dispatchEvent(
1324 - createTouchEvent('touchmove', 0, {
1325 - clientX: rectMock.left - pressRetentionOffset.left,
1326 - clientY: rectMock.top - pressRetentionOffset.top,
1327 - target: ref.current,
1328 - }),
1329 - );
1330 - ref.current.dispatchEvent(
1331 - createTouchEvent('touchend', 0, {
1332 - ...coordinatesInside,
1333 - target: ref.current,
1334 - }),
1335 - );
711 + dispatchPointerMove(target, {pointerType, ...coordinates});
712 + dispatchPointerUp(target, {pointerType, ...coordinates});
713 expect(events).toEqual([
714 'onPressStart',
715 'onPressChange',
@@ -1342,112 +719,6 @@ describe('Event responder: Press', () => {
719 'onPress',
720 ]);
721 });
1345 -
1346 - it('responder region accounts for decrease in element dimensions', () => {
1347 - let events = [];
1348 - const ref = React.createRef();
1349 - const createEventHandler = msg => () => {
1350 - events.push(msg);
1351 - };
1352 -
1353 - const Component = () => {
1354 - const listener = usePressResponder({
1355 - onPress: createEventHandler('onPress'),
1356 - onPressStart: createEventHandler('onPressStart'),
1357 - onPressEnd: createEventHandler('onPressEnd'),
1358 - });
1359 - return <div ref={ref} listeners={listener} />;
1360 - };
1361 - ReactDOM.render(<Component />, container);
1362 -
1363 - document.elementFromPoint = () => ref.current;
1364 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
1365 - ref.current.dispatchEvent(
1366 - createTouchEvent('touchstart', 0, {
1367 - target: ref.current,
1368 - }),
1369 - );
1370 - // emulate smaller dimensions change on activation
1371 - ref.current.getBoundingClientRect = () => ({
1372 - width: 80,
1373 - height: 80,
1374 - top: 60,
1375 - left: 60,
1376 - right: 140,
1377 - bottom: 140,
1378 - });
1379 - const coordinates = {
1380 - clientX: rectMock.left,
1381 - clientY: rectMock.top,
1382 - };
1383 - // move to an area within the pre-activation region
1384 - ref.current.dispatchEvent(
1385 - createTouchEvent('touchmove', 0, {
1386 - ...coordinates,
1387 - target: ref.current,
1388 - }),
1389 - );
1390 - ref.current.dispatchEvent(
1391 - createTouchEvent('touchend', 0, {
1392 - ...coordinates,
1393 - target: ref.current,
1394 - }),
1395 - );
1396 - expect(events).toEqual(['onPressStart', 'onPressEnd', 'onPress']);
1397 - });
1398 -
1399 - it('responder region accounts for increase in element dimensions', () => {
1400 - let events = [];
1401 - const ref = React.createRef();
1402 - const createEventHandler = msg => () => {
1403 - events.push(msg);
1404 - };
1405 -
1406 - const Component = () => {
1407 - const listener = usePressResponder({
1408 - onPress: createEventHandler('onPress'),
1409 - onPressStart: createEventHandler('onPressStart'),
1410 - onPressEnd: createEventHandler('onPressEnd'),
1411 - });
1412 - return <div ref={ref} listeners={listener} />;
1413 - };
1414 - ReactDOM.render(<Component />, container);
1415 -
1416 - document.elementFromPoint = () => ref.current;
1417 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
1418 - ref.current.dispatchEvent(
1419 - createTouchEvent('touchstart', 0, {
1420 - target: ref.current,
1421 - }),
1422 - );
1423 - // emulate larger dimensions change on activation
1424 - ref.current.getBoundingClientRect = () => ({
1425 - width: 200,
1426 - height: 200,
1427 - top: 0,
1428 - left: 0,
1429 - right: 200,
1430 - bottom: 200,
1431 - });
1432 - const coordinates = {
1433 - clientX: rectMock.left - 50,
1434 - clientY: rectMock.top - 50,
1435 - };
1436 - // move to an area within the post-activation region
1437 - ref.current.dispatchEvent(
1438 - createTouchEvent('touchmove', 0, {
1439 - ...coordinates,
1440 - target: ref.current,
1441 - }),
1442 - );
1443 - ref.current.dispatchEvent(
1444 - createTouchEvent('touchend', 0, {
1445 - ...coordinates,
1446 - target: ref.current,
1447 - }),
1448 - );
1449 - expect(events).toEqual(['onPressStart', 'onPressEnd', 'onPress']);
1450 - });
722 });
723
724 describe('beyond bounds of hit rect', () => {
@@ -1459,117 +730,40 @@ describe('Event responder: Press', () => {
730 * └──────────────────┘
731 * X <= Move to X and release
732 */
1462 -
733 it('"onPress" is not called on release', () => {
1464 - let events = [];
1465 - const ref = React.createRef();
1466 - const createEventHandler = msg => () => {
1467 - events.push(msg);
1468 - };
1469 -
1470 - const Component = () => {
1471 - const listener = usePressResponder({
1472 - onPress: createEventHandler('onPress'),
1473 - onPressChange: createEventHandler('onPressChange'),
1474 - onPressMove: createEventHandler('onPressMove'),
1475 - onPressStart: createEventHandler('onPressStart'),
1476 - onPressEnd: createEventHandler('onPressEnd'),
1477 - });
1478 - return <div ref={ref} listeners={listener} />;
1479 - };
1480 - ReactDOM.render(<Component />, container);
1481 -
1482 - document.elementFromPoint = () => ref.current;
1483 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
1484 - ref.current.dispatchEvent(
1485 - createTouchEvent('touchstart', 0, {
1486 - target: ref.current,
1487 - }),
1488 - );
1489 - ref.current.dispatchEvent(
1490 - createTouchEvent('touchmove', 0, {
1491 - ...coordinatesInside,
1492 - target: ref.current,
1493 - }),
1494 - );
1495 - document.elementFromPoint = () => container;
1496 - container.dispatchEvent(
1497 - createTouchEvent('touchmove', 0, {
1498 - ...coordinatesOutside,
1499 - target: container,
1500 - }),
1501 - );
1502 - container.dispatchEvent(
1503 - createTouchEvent('touchend', 0, {
1504 - ...coordinatesOutside,
1505 - target: container,
1506 - }),
1507 - );
1508 - jest.runAllTimers();
1509 -
1510 - expect(events).toEqual([
1511 - 'onPressStart',
1512 - 'onPressChange',
1513 - 'onPressMove',
1514 - 'onPressEnd',
1515 - 'onPressChange',
1516 - ]);
1517 - });
1518 - });
1519 -
1520 - it('"onPress" is called on re-entry to hit rect for touch', () => {
1521 - let events = [];
1522 - const ref = React.createRef();
1523 - const createEventHandler = msg => () => {
1524 - events.push(msg);
1525 - };
1526 -
1527 - const Component = () => {
1528 - const listener = usePressResponder({
1529 - onPress: createEventHandler('onPress'),
1530 - onPressChange: createEventHandler('onPressChange'),
1531 - onPressMove: createEventHandler('onPressMove'),
1532 - onPressStart: createEventHandler('onPressStart'),
1533 - onPressEnd: createEventHandler('onPressEnd'),
1534 - });
1535 - return <div ref={ref} listeners={listener} />;
1536 - };
1537 - ReactDOM.render(<Component />, container);
1538 -
1539 - document.elementFromPoint = () => ref.current;
1540 - ref.current.getBoundingClientRect = getBoundingClientRectMock;
1541 - ref.current.dispatchEvent(
1542 - createTouchEvent('touchstart', 0, {
1543 - target: ref.current,
1544 - }),
1545 - );
1546 - ref.current.dispatchEvent(
1547 - createTouchEvent('touchmove', 0, {
1548 - ...coordinatesInside,
1549 - target: ref.current,
1550 - }),
1551 - );
1552 - document.elementFromPoint = () => container;
1553 - container.dispatchEvent(
1554 - createTouchEvent('touchmove', 0, {
1555 - ...coordinatesOutside,
1556 - target: container,
1557 - }),
1558 - );
1559 - ref.current.dispatchEvent(
1560 - createTouchEvent('touchmove', 0, {
1561 - ...coordinatesInside,
1562 - target: ref.current,
1563 - }),
1564 - );
1565 - document.elementFromPoint = () => ref.current;
1566 - ref.current.dispatchEvent(
1567 - createTouchEvent('touchend', 0, {
1568 - ...coordinatesInside,
1569 - target: ref.current,
1570 - }),
1571 - );
1572 - jest.runAllTimers();
734 + const target = ref.current;
735 + target.getBoundingClientRect = getBoundingClientRectMock;
736 + dispatchPointerDown(target, {pointerType});
737 + dispatchPointerMove(target, {pointerType, ...coordinatesInside});
738 + if (pointerType === 'mouse') {
739 + // TODO: use setPointerCapture so this is only true for fallback mouse events.
740 + dispatchPointerMove(container, {pointerType, ...coordinatesOutside});
741 + } else {
742 + dispatchPointerMove(target, {pointerType, ...coordinatesOutside});
743 + }
744 + dispatchPointerUp(container, {pointerType, ...coordinatesOutside});
745 + expect(events.filter(removePressMoveStrings)).toEqual([
746 + 'onPressStart',
747 + 'onPressChange',
748 + 'onPressEnd',
749 + 'onPressChange',
750 + ]);
751 + });
752 + });
753 +
754 + it('"onPress" is called on re-entry to hit rect', () => {
755 + const target = ref.current;
756 + target.getBoundingClientRect = getBoundingClientRectMock;
757 + dispatchPointerDown(target, {pointerType});
758 + dispatchPointerMove(target, {pointerType, ...coordinatesInside});
759 + if (pointerType === 'mouse') {
760 + // TODO: use setPointerCapture so this is only true for fallback mouse events.
761 + dispatchPointerMove(container, {pointerType, ...coordinatesOutside});
762 + } else {
763 + dispatchPointerMove(target, {pointerType, ...coordinatesOutside});
764 + }
765 + dispatchPointerMove(target, {pointerType, ...coordinatesInside});
766 + dispatchPointerUp(target, {pointerType, ...coordinatesInside});
767
768 expect(events).toEqual([
769 'onPressStart',
@@ -1587,71 +781,71 @@ describe('Event responder: Press', () => {
781 });
782
783 describe('nested responders', () => {
1590 - it('dispatch events in the correct order', () => {
1591 - const events = [];
1592 - const ref = React.createRef();
1593 - const createEventHandler = msg => () => {
1594 - events.push(msg);
1595 - };
784 + if (hasPointerEvents) {
785 + it('dispatch events in the correct order', () => {
786 + const events = [];
787 + const ref = React.createRef();
788 + const createEventHandler = msg => () => {
789 + events.push(msg);
790 + };
791
1597 - const Inner = () => {
1598 - const listener = usePressResponder({
1599 - onPress: createEventHandler('inner: onPress'),
1600 - onPressChange: createEventHandler('inner: onPressChange'),
1601 - onPressMove: createEventHandler('inner: onPressMove'),
1602 - onPressStart: createEventHandler('inner: onPressStart'),
1603 - onPressEnd: createEventHandler('inner: onPressEnd'),
1604 - stopPropagation: false,
1605 - });
1606 - return (
1607 - <div
1608 - ref={ref}
1609 - listeners={listener}
1610 - onPointerDown={createEventHandler('pointerdown')}
1611 - onPointerUp={createEventHandler('pointerup')}
1612 - onKeyDown={createEventHandler('keydown')}
1613 - onKeyUp={createEventHandler('keyup')}
1614 - />
1615 - );
1616 - };
792 + const Inner = () => {
793 + const listener = usePressResponder({
794 + onPress: createEventHandler('inner: onPress'),
795 + onPressChange: createEventHandler('inner: onPressChange'),
796 + onPressMove: createEventHandler('inner: onPressMove'),
797 + onPressStart: createEventHandler('inner: onPressStart'),
798 + onPressEnd: createEventHandler('inner: onPressEnd'),
799 + stopPropagation: false,
800 + });
801 + return (
802 + <div
803 + ref={ref}
804 + listeners={listener}
805 + onPointerDown={createEventHandler('pointerdown')}
806 + onPointerUp={createEventHandler('pointerup')}
807 + onKeyDown={createEventHandler('keydown')}
808 + onKeyUp={createEventHandler('keyup')}
809 + />
810 + );
811 + };
812
1618 - const Outer = () => {
1619 - const listener = usePressResponder({
1620 - onPress: createEventHandler('outer: onPress'),
1621 - onPressChange: createEventHandler('outer: onPressChange'),
1622 - onPressMove: createEventHandler('outer: onPressMove'),
1623 - onPressStart: createEventHandler('outer: onPressStart'),
1624 - onPressEnd: createEventHandler('outer: onPressEnd'),
1625 - });
1626 - return (
1627 - <div listeners={listener}>
1628 - <Inner />
1629 - </div>
1630 - );
1631 - };
1632 - ReactDOM.render(<Outer />, container);
813 + const Outer = () => {
814 + const listener = usePressResponder({
815 + onPress: createEventHandler('outer: onPress'),
816 + onPressChange: createEventHandler('outer: onPressChange'),
817 + onPressMove: createEventHandler('outer: onPressMove'),
818 + onPressStart: createEventHandler('outer: onPressStart'),
819 + onPressEnd: createEventHandler('outer: onPressEnd'),
820 + });
821 + return (
822 + <div listeners={listener}>
823 + <Inner />
824 + </div>
825 + );
826 + };
827 + ReactDOM.render(<Outer />, container);
828
1634 - ref.current.getBoundingClientRect = () => ({
1635 - top: 0,
1636 - left: 0,
1637 - bottom: 100,
1638 - right: 100,
829 + const target = ref.current;
830 + target.getBoundingClientRect = () => ({
831 + top: 0,
832 + left: 0,
833 + bottom: 100,
834 + right: 100,
835 + });
836 + dispatchPointerDown(target);
837 + dispatchPointerUp(target);
838 + expect(events).toEqual([
839 + 'inner: onPressStart',
840 + 'inner: onPressChange',
841 + 'pointerdown',
842 + 'inner: onPressEnd',
843 + 'inner: onPressChange',
844 + 'inner: onPress',
845 + 'pointerup',
846 + ]);
847 });
1640 -
1641 - ref.current.dispatchEvent(createEvent('pointerdown'));
1642 - ref.current.dispatchEvent(
1643 - createEvent('pointerup', {clientX: 10, clientY: 10}),
1644 - );
1645 - expect(events).toEqual([
1646 - 'inner: onPressStart',
1647 - 'inner: onPressChange',
1648 - 'pointerdown',
1649 - 'inner: onPressEnd',
1650 - 'inner: onPressChange',
1651 - 'inner: onPress',
1652 - 'pointerup',
1653 - ]);
1654 - });
848 + }
849
850 describe('correctly not propagate', () => {
851 it('for onPress', () => {
@@ -1677,17 +871,15 @@ describe('Event responder: Press', () => {
871 };
872 ReactDOM.render(<Outer />, container);
873
1680 - ref.current.getBoundingClientRect = () => ({
874 + const target = ref.current;
875 + target.getBoundingClientRect = () => ({
876 top: 0,
877 left: 0,
878 bottom: 100,
879 right: 100,
880 });
1686 -
1687 - ref.current.dispatchEvent(createEvent('pointerdown'));
1688 - ref.current.dispatchEvent(
1689 - createEvent('pointerup', {clientX: 10, clientY: 10}),
1690 - );
881 + dispatchPointerDown(target);
882 + dispatchPointerUp(target);
883 expect(fn).toHaveBeenCalledTimes(1);
884 });
885
@@ -1717,10 +909,11 @@ describe('Event responder: Press', () => {
909 };
910 ReactDOM.render(<Outer />, container);
911
1720 - ref.current.dispatchEvent(createEvent('pointerdown'));
912 + const target = ref.current;
913 + dispatchPointerDown(target);
914 expect(fn).toHaveBeenCalledTimes(1);
915 expect(fn2).toHaveBeenCalledTimes(0);
1723 - ref.current.dispatchEvent(createEvent('pointerup'));
916 + dispatchPointerUp(target);
917 expect(fn).toHaveBeenCalledTimes(1);
918 expect(fn2).toHaveBeenCalledTimes(1);
919 });
@@ -1748,16 +941,17 @@ describe('Event responder: Press', () => {
941 };
942 ReactDOM.render(<Outer />, container);
943
1751 - ref.current.dispatchEvent(createEvent('pointerdown'));
944 + const target = ref.current;
945 + dispatchPointerDown(target);
946 expect(fn).toHaveBeenCalledTimes(1);
1753 - ref.current.dispatchEvent(createEvent('pointerup'));
947 + dispatchPointerUp(target);
948 expect(fn).toHaveBeenCalledTimes(2);
949 });
950 });
951 });
952
953 describe('link components', () => {
1760 - it('prevents native behaviour for pointer events by default', () => {
954 + it('prevents native behavior by default', () => {
955 const onPress = jest.fn();
956 const preventDefault = jest.fn();
957 const ref = React.createRef();
@@ -1770,14 +964,9 @@ describe('Event responder: Press', () => {
964 };
965 ReactDOM.render(<Component />, container);
966
1773 - ref.current.dispatchEvent(createEvent('pointerdown'));
1774 - ref.current.dispatchEvent(
1775 - createEvent('pointerup', {
1776 - clientX: 0,
1777 - clientY: 0,
1778 - }),
1779 - );
1780 - ref.current.dispatchEvent(createEvent('click', {preventDefault}));
967 + const target = ref.current;
968 + dispatchPointerDown(target);
969 + dispatchPointerUp(target, {preventDefault});
970 expect(preventDefault).toBeCalled();
971 expect(onPress).toHaveBeenCalledWith(
972 expect.objectContaining({defaultPrevented: true}),
@@ -1797,9 +986,10 @@ describe('Event responder: Press', () => {
986 };
987 ReactDOM.render(<Component />, container);
988
1800 - ref.current.dispatchEvent(createEvent('keydown', {key: 'Enter'}));
1801 - ref.current.dispatchEvent(createEvent('click', {preventDefault}));
1802 - ref.current.dispatchEvent(createEvent('keyup', {key: 'Enter'}));
989 + const target = ref.current;
990 + target.dispatchEvent(keydown({key: 'Enter'}));
991 + target.dispatchEvent(click({preventDefault}));
992 + target.dispatchEvent(keyup({key: 'Enter'}));
993 expect(preventDefault).toBeCalled();
994 expect(onPress).toHaveBeenCalledWith(
995 expect.objectContaining({defaultPrevented: true}),
@@ -1823,14 +1013,9 @@ describe('Event responder: Press', () => {
1013 };
1014 ReactDOM.render(<Component />, container);
1015
1826 - buttonRef.current.dispatchEvent(createEvent('pointerdown'));
1827 - buttonRef.current.dispatchEvent(
1828 - createEvent('pointerup', {
1829 - clientX: 0,
1830 - clientY: 0,
1831 - }),
1832 - );
1833 - buttonRef.current.dispatchEvent(createEvent('click', {preventDefault}));
1016 + const target = buttonRef.current;
1017 + dispatchPointerDown(target);
1018 + dispatchPointerUp(target, {preventDefault});
1019 expect(preventDefault).toBeCalled();
1020 });
1021
@@ -1851,14 +1036,9 @@ describe('Event responder: Press', () => {
1036 };
1037 ReactDOM.render(<Component />, container);
1038
1854 - ref.current.dispatchEvent(createEvent('pointerdown'));
1855 - ref.current.dispatchEvent(
1856 - createEvent('pointerup', {
1857 - clientX: 0,
1858 - clientY: 0,
1859 - }),
1860 - );
1861 - ref.current.dispatchEvent(createEvent('click', {preventDefault}));
1039 + const target = ref.current;
1040 + dispatchPointerDown(target);
1041 + dispatchPointerUp(target, {preventDefault});
1042 expect(preventDefault).toBeCalled();
1043 expect(onPress).toHaveBeenCalledWith(
1044 expect.objectContaining({defaultPrevented: true}),
@@ -1879,19 +1059,9 @@ describe('Event responder: Press', () => {
1059 ReactDOM.render(<Component />, container);
1060
1061 ['metaKey', 'ctrlKey', 'shiftKey'].forEach(modifierKey => {
1882 - ref.current.dispatchEvent(
1883 - createEvent('pointerdown', {[modifierKey]: true}),
1884 - );
1885 - ref.current.dispatchEvent(
1886 - createEvent('pointerup', {
1887 - [modifierKey]: true,
1888 - clientX: 0,
1889 - clientY: 0,
1890 - }),
1891 - );
1892 - ref.current.dispatchEvent(
1893 - createEvent('click', {[modifierKey]: true, preventDefault}),
1894 - );
1062 + const target = ref.current;
1063 + dispatchPointerDown(target, {[modifierKey]: true});
1064 + dispatchPointerUp(target, {[modifierKey]: true, preventDefault});
1065 expect(preventDefault).not.toBeCalled();
1066 expect(onPress).toHaveBeenCalledWith(
1067 expect.objectContaining({defaultPrevented: false}),
@@ -1913,14 +1083,9 @@ describe('Event responder: Press', () => {
1083 };
1084 ReactDOM.render(<Component />, container);
1085
1916 - ref.current.dispatchEvent(createEvent('pointerdown'));
1917 - ref.current.dispatchEvent(
1918 - createEvent('pointerup', {
1919 - clientX: 0,
1920 - clientY: 0,
1921 - }),
1922 - );
1923 - ref.current.dispatchEvent(createEvent('click', {preventDefault}));
1086 + const target = ref.current;
1087 + dispatchPointerDown(target);
1088 + dispatchPointerUp(target, {preventDefault});
1089 expect(preventDefault).not.toBeCalled();
1090 expect(onPress).toHaveBeenCalledWith(
1091 expect.objectContaining({defaultPrevented: false}),
@@ -1941,9 +1106,10 @@ describe('Event responder: Press', () => {
1106 };
1107 ReactDOM.render(<Component />, container);
1108
1944 - ref.current.dispatchEvent(createEvent('keydown', {key: 'Enter'}));
1945 - ref.current.dispatchEvent(createEvent('click', {preventDefault}));
1946 - ref.current.dispatchEvent(createEvent('keyup', {key: 'Enter'}));
1109 + const target = ref.current;
1110 + target.dispatchEvent(keydown({key: 'Enter'}));
1111 + target.dispatchEvent(click({preventDefault}));
1112 + target.dispatchEvent(keyup({key: 'Enter'}));
1113 expect(preventDefault).not.toBeCalled();
1114 expect(onPress).toHaveBeenCalledWith(
1115 expect.objectContaining({defaultPrevented: false}),
@@ -1952,7 +1118,7 @@ describe('Event responder: Press', () => {
1118 });
1119
1120 describe('responder cancellation', () => {
1955 - it('ends on "pointercancel", "touchcancel", "scroll", and "dragstart"', () => {
1121 + it.each(pointerTypesTable)('ends on pointer cancel', pointerType => {
1122 const onPressEnd = jest.fn();
1123 const ref = React.createRef();
1124
@@ -1964,64 +1130,14 @@ describe('Event responder: Press', () => {
1130 };
1131 ReactDOM.render(<Component />, container);
1132
1967 - // Should cancel for non-mouse events
1968 - ref.current.dispatchEvent(
1969 - createEvent('pointerdown', {
1970 - pointerType: 'touch',
1971 - }),
1972 - );
1973 - ref.current.dispatchEvent(createEvent('scroll'));
1974 - expect(onPressEnd).toHaveBeenCalledTimes(1);
1975 -
1976 - onPressEnd.mockReset();
1977 -
1978 - // Should not cancel for mouse events
1979 - ref.current.dispatchEvent(
1980 - createEvent('pointerdown', {
1981 - pointerType: 'mouse',
1982 - }),
1983 - );
1984 - ref.current.dispatchEvent(createEvent('scroll'));
1985 - expect(onPressEnd).toHaveBeenCalledTimes(0);
1986 -
1987 - // When pointer events are supported
1988 - ref.current.dispatchEvent(
1989 - createEvent('pointerdown', {
1990 - pointerType: 'mouse',
1991 - }),
1992 - );
1993 - ref.current.dispatchEvent(
1994 - createEvent('pointercancel', {
1995 - pointerType: 'mouse',
1996 - }),
1997 - );
1998 - expect(onPressEnd).toHaveBeenCalledTimes(1);
1999 -
2000 - onPressEnd.mockReset();
2001 -
2002 - // Touch fallback
2003 - ref.current.dispatchEvent(
2004 - createTouchEvent('touchstart', 0, {
2005 - target: ref.current,
2006 - }),
2007 - );
2008 - ref.current.dispatchEvent(
2009 - createTouchEvent('touchcancel', 0, {
2010 - target: ref.current,
2011 - }),
2012 - );
2013 - expect(onPressEnd).toHaveBeenCalledTimes(1);
2014 -
2015 - onPressEnd.mockReset();
2016 -
2017 - // Mouse fallback
2018 - ref.current.dispatchEvent(createEvent('mousedown'));
2019 - ref.current.dispatchEvent(createEvent('dragstart'));
1133 + const target = ref.current;
1134 + dispatchPointerDown(target, {pointerType});
1135 + dispatchPointerCancel(target, {pointerType});
1136 expect(onPressEnd).toHaveBeenCalledTimes(1);
1137 });
1138 });
1139
2024 - it('does end on "scroll" to document', () => {
1140 + it('does end on "scroll" to document (not mouse)', () => {
1141 const onPressEnd = jest.fn();
1142 const ref = React.createRef();
1143
@@ -2033,12 +1149,13 @@ describe('Event responder: Press', () => {
1149 };
1150 ReactDOM.render(<Component />, container);
1151
2036 - ref.current.dispatchEvent(createEvent('pointerdown'));
2037 - document.dispatchEvent(createEvent('scroll'));
1152 + const target = ref.current;
1153 + dispatchPointerDown(target, {pointerType: 'touch'});
1154 + document.dispatchEvent(scroll());
1155 expect(onPressEnd).toHaveBeenCalledTimes(1);
1156 });
1157
2041 - it('does end on "scroll" to a parent container', () => {
1158 + it('does end on "scroll" to a parent container (not mouse)', () => {
1159 const onPressEnd = jest.fn();
1160 const ref = React.createRef();
1161 const containerRef = React.createRef();
@@ -2055,8 +1172,8 @@ describe('Event responder: Press', () => {
1172 };
1173 ReactDOM.render(<Component />, container);
1174
2058 - ref.current.dispatchEvent(createEvent('pointerdown'));
2059 - containerRef.current.dispatchEvent(createEvent('scroll'));
1175 + dispatchPointerDown(ref.current, {pointerType: 'touch'});
1176 + containerRef.current.dispatchEvent(scroll());
1177 expect(onPressEnd).toHaveBeenCalledTimes(1);
1178 });
1179
@@ -2078,8 +1195,8 @@ describe('Event responder: Press', () => {
1195 };
1196 ReactDOM.render(<Component />, container);
1197
2081 - ref.current.dispatchEvent(createEvent('pointerdown'));
2082 - outsideRef.current.dispatchEvent(createEvent('scroll'));
1198 + dispatchPointerDown(ref.current);
1199 + outsideRef.current.dispatchEvent(scroll());
1200 expect(onPressEnd).not.toBeCalled();
1201 });
1202
@@ -2095,10 +1212,11 @@ describe('Event responder: Press', () => {
1212 };
1213 ReactDOM.render(<Component />, container);
1214
2098 - ref.current.dispatchEvent(createEvent('pointerdown'));
2099 - ref.current.dispatchEvent(createEvent('pointermove'));
2100 - ref.current.dispatchEvent(createEvent('pointerup'));
2101 - ref.current.dispatchEvent(createEvent('pointerdown'));
1215 + const target = ref.current;
1216 + dispatchPointerDown(target);
1217 + dispatchPointerMove(target);
1218 + dispatchPointerUp(target);
1219 + dispatchPointerDown(target);
1220 });
1221
1222 it('should correctly pass through event properties', () => {
@@ -2133,57 +1251,49 @@ describe('Event responder: Press', () => {
1251 };
1252 ReactDOM.render(<Component />, container);
1253
2136 - ref.current.getBoundingClientRect = () => ({
1254 + const target = ref.current;
1255 + target.getBoundingClientRect = () => ({
1256 top: 10,
1257 left: 10,
1258 bottom: 110,
1259 right: 110,
1260 });
2142 -
2143 - ref.current.dispatchEvent(
2144 - createEvent('pointerdown', {
2145 - pointerType: 'mouse',
2146 - pageX: 15,
2147 - pageY: 16,
2148 - screenX: 20,
2149 - screenY: 21,
2150 - clientX: 30,
2151 - clientY: 31,
2152 - }),
2153 - );
2154 - ref.current.dispatchEvent(
2155 - createEvent('pointermove', {
2156 - pointerType: 'mouse',
2157 - pageX: 16,
2158 - pageY: 17,
2159 - screenX: 21,
2160 - screenY: 22,
2161 - clientX: 31,
2162 - clientY: 32,
2163 - }),
2164 - );
2165 - ref.current.dispatchEvent(
2166 - createEvent('pointerup', {
2167 - pointerType: 'mouse',
2168 - pageX: 17,
2169 - pageY: 18,
2170 - screenX: 22,
2171 - screenY: 23,
2172 - clientX: 32,
2173 - clientY: 33,
2174 - }),
2175 - );
2176 - ref.current.dispatchEvent(
2177 - createEvent('pointerdown', {
2178 - pointerType: 'mouse',
2179 - pageX: 18,
2180 - pageY: 19,
2181 - screenX: 23,
2182 - screenY: 24,
2183 - clientX: 33,
2184 - clientY: 34,
2185 - }),
2186 - );
1261 + dispatchPointerDown(target, {
1262 + pointerType: 'mouse',
1263 + pageX: 15,
1264 + pageY: 16,
1265 + screenX: 20,
1266 + screenY: 21,
1267 + clientX: 30,
1268 + clientY: 31,
1269 + });
1270 + dispatchPointerMove(target, {
1271 + pointerType: 'mouse',
1272 + pageX: 16,
1273 + pageY: 17,
1274 + screenX: 21,
1275 + screenY: 22,
1276 + clientX: 31,
1277 + clientY: 32,
1278 + });
1279 + dispatchPointerUp(target, {
1280 + pointerType: 'mouse',
1281 + pageX: 17,
1282 + pageY: 18,
1283 + screenX: 22,
1284 + screenY: 23,
1285 + clientX: 32,
1286 + clientY: 33,
1287 + });
1288 + dispatchPointerDown(target, {
1289 + pointerType: 'mouse',
1290 + pageX: 18,
1291 + pageY: 19,
1292 + screenX: 23,
1293 + screenY: 24,
1294 + clientX: 33,
1295 + clientY: 34,
1296 + });
1297 expect(typeof timeStamps[0] === 'number').toBe(true);
1298 expect(eventLog).toEqual([
1299 {
@@ -2249,175 +1359,85 @@ describe('Event responder: Press', () => {
1359 ]);
1360 });
1361
2252 - function dispatchEventWithTimeStamp(elem, name, timeStamp) {
2253 - const event = createEvent(name, {
2254 - clientX: 0,
2255 - clientY: 0,
2256 - });
2257 - Object.defineProperty(event, 'timeStamp', {
2258 - value: timeStamp,
2259 - });
2260 - elem.dispatchEvent(event);
2261 - }
2262 -
2263 - it('should properly only flush sync once when the event systems are mixed', () => {
2264 - const ref = React.createRef();
2265 - let renderCounts = 0;
2266 -
2267 - function MyComponent() {
2268 - const [, updateCounter] = React.useState(0);
2269 - renderCounts++;
2270 -
2271 - function handlePress() {
2272 - updateCounter(count => count + 1);
2273 - }
2274 -
2275 - const listener = usePressResponder({
2276 - onPress: handlePress,
2277 - });
2278 -
2279 - return (
2280 - <div>
2281 - <button
2282 - ref={ref}
2283 - listeners={listener}
2284 - onClick={() => {
2285 - updateCounter(count => count + 1);
2286 - }}>
2287 - Press me
2288 - </button>
2289 - </div>
2290 - );
2291 - }
2292 -
2293 - const newContainer = document.createElement('div');
2294 - const root = ReactDOM.unstable_createRoot(newContainer);
2295 - document.body.appendChild(newContainer);
2296 - root.render(<MyComponent />);
2297 - Scheduler.unstable_flushAll();
2298 -
2299 - dispatchEventWithTimeStamp(ref.current, 'pointerdown', 100);
2300 - dispatchEventWithTimeStamp(ref.current, 'pointerup', 100);
2301 - dispatchEventWithTimeStamp(ref.current, 'click', 100);
2302 -
2303 - if (__DEV__) {
2304 - expect(renderCounts).toBe(2);
2305 - } else {
2306 - expect(renderCounts).toBe(1);
2307 - }
2308 - Scheduler.unstable_flushAll();
2309 - if (__DEV__) {
2310 - expect(renderCounts).toBe(4);
2311 - } else {
2312 - expect(renderCounts).toBe(2);
2313 - }
2314 -
2315 - dispatchEventWithTimeStamp(ref.current, 'pointerdown', 100);
2316 - dispatchEventWithTimeStamp(ref.current, 'pointerup', 100);
2317 - // Ensure the timeStamp logic works
2318 - dispatchEventWithTimeStamp(ref.current, 'click', 101);
2319 -
2320 - if (__DEV__) {
2321 - expect(renderCounts).toBe(6);
2322 - } else {
2323 - expect(renderCounts).toBe(3);
2324 - }
1362 + if (hasPointerEvents) {
1363 + it('should properly only flush sync once when the event systems are mixed', () => {
1364 + const ref = React.createRef();
1365 + let renderCounts = 0;
1366
2326 - Scheduler.unstable_flushAll();
2327 - document.body.removeChild(newContainer);
2328 - });
1367 + function MyComponent() {
1368 + const [, updateCounter] = React.useState(0);
1369 + renderCounts++;
1370
2330 - it('should properly flush sync when the event systems are mixed with unstable_flushDiscreteUpdates', () => {
2331 - const ref = React.createRef();
2332 - let renderCounts = 0;
1371 + function handlePress() {
1372 + updateCounter(count => count + 1);
1373 + }
1374
2334 - function MyComponent() {
2335 - const [, updateCounter] = React.useState(0);
2336 - renderCounts++;
1375 + const listener = usePressResponder({
1376 + onPress: handlePress,
1377 + });
1378
2338 - function handlePress() {
2339 - updateCounter(count => count + 1);
1379 + return (
1380 + <div>
1381 + <button
1382 + ref={ref}
1383 + listeners={listener}
1384 + onClick={() => {
1385 + updateCounter(count => count + 1);
1386 + }}>
1387 + Press me
1388 + </button>
1389 + </div>
1390 + );
1391 }
1392
2342 - const listener = usePressResponder({
2343 - onPress: handlePress,
2344 - });
2345 -
2346 - return (
2347 - <div>
2348 - <button
2349 - ref={ref}
2350 - listeners={listener}
2351 - onClick={() => {
2352 - // This should flush synchronously
2353 - ReactDOM.unstable_flushDiscreteUpdates();
2354 - updateCounter(count => count + 1);
2355 - }}>
2356 - Press me
2357 - </button>
2358 - </div>
2359 - );
2360 - }
2361 -
2362 - const newContainer = document.createElement('div');
2363 - const root = ReactDOM.unstable_createRoot(newContainer);
2364 - document.body.appendChild(newContainer);
2365 - root.render(<MyComponent />);
2366 - Scheduler.unstable_flushAll();
2367 -
2368 - dispatchEventWithTimeStamp(ref.current, 'pointerdown', 100);
2369 - dispatchEventWithTimeStamp(ref.current, 'pointerup', 100);
2370 - dispatchEventWithTimeStamp(ref.current, 'click', 100);
2371 -
2372 - if (__DEV__) {
2373 - expect(renderCounts).toBe(4);
2374 - } else {
2375 - expect(renderCounts).toBe(2);
2376 - }
2377 - Scheduler.unstable_flushAll();
2378 - if (__DEV__) {
2379 - expect(renderCounts).toBe(6);
2380 - } else {
2381 - expect(renderCounts).toBe(3);
2382 - }
1393 + const newContainer = document.createElement('div');
1394 + const root = ReactDOM.unstable_createRoot(newContainer);
1395 + document.body.appendChild(newContainer);
1396 + root.render(<MyComponent />);
1397 + Scheduler.unstable_flushAll();
1398
2384 - dispatchEventWithTimeStamp(ref.current, 'pointerdown', 100);
2385 - dispatchEventWithTimeStamp(ref.current, 'pointerup', 100);
2386 - // Ensure the timeStamp logic works
2387 - dispatchEventWithTimeStamp(ref.current, 'click', 101);
1399 + const target = ref.current;
1400 + target.dispatchEvent(pointerdown({timeStamp: 100}));
1401 + target.dispatchEvent(pointerup({timeStamp: 100}));
1402 + target.dispatchEvent(click({timeStamp: 100}));
1403
2389 - if (__DEV__) {
2390 - expect(renderCounts).toBe(8);
2391 - } else {
2392 - expect(renderCounts).toBe(4);
2393 - }
1404 + if (__DEV__) {
1405 + expect(renderCounts).toBe(2);
1406 + } else {
1407 + expect(renderCounts).toBe(1);
1408 + }
1409 + Scheduler.unstable_flushAll();
1410 + if (__DEV__) {
1411 + expect(renderCounts).toBe(4);
1412 + } else {
1413 + expect(renderCounts).toBe(2);
1414 + }
1415
2395 - Scheduler.unstable_flushAll();
2396 - document.body.removeChild(newContainer);
2397 - });
1416 + target.dispatchEvent(pointerdown({timeStamp: 100}));
1417 + target.dispatchEvent(pointerup({timeStamp: 100}));
1418 + // Ensure the timeStamp logic works
1419 + target.dispatchEvent(click({timeStamp: 101}));
1420
2399 - it(
2400 - 'should only flush before outermost discrete event handler when mixing ' +
2401 - 'event systems',
2402 - async () => {
2403 - const {useState} = React;
1421 + if (__DEV__) {
1422 + expect(renderCounts).toBe(6);
1423 + } else {
1424 + expect(renderCounts).toBe(3);
1425 + }
1426
2405 - const button = React.createRef();
1427 + Scheduler.unstable_flushAll();
1428 + document.body.removeChild(newContainer);
1429 + });
1430
2407 - const ops = [];
1431 + it('should properly flush sync when the event systems are mixed with unstable_flushDiscreteUpdates', () => {
1432 + const ref = React.createRef();
1433 + let renderCounts = 0;
1434
1435 function MyComponent() {
2410 - const [pressesCount, updatePressesCount] = useState(0);
2411 - const [clicksCount, updateClicksCount] = useState(0);
1436 + const [, updateCounter] = React.useState(0);
1437 + renderCounts++;
1438
1439 function handlePress() {
2414 - // This dispatches a synchronous, discrete event in the legacy event
2415 - // system. However, because it's nested inside the new event system,
2416 - // its updates should not flush until the end of the outer handler.
2417 - button.current.click();
2418 - // Text context should not have changed
2419 - ops.push(newContainer.textContent);
2420 - updatePressesCount(pressesCount + 1);
1440 + updateCounter(count => count + 1);
1441 }
1442
1443 const listener = usePressResponder({
@@ -2427,76 +1447,131 @@ describe('Event responder: Press', () => {
1447 return (
1448 <div>
1449 <button
1450 + ref={ref}
1451 listeners={listener}
2431 - ref={button}
2432 - onClick={() => updateClicksCount(clicksCount + 1)}>
2433 - Presses: {pressesCount}, Clicks: {clicksCount}
1452 + onClick={() => {
1453 + // This should flush synchronously
1454 + ReactDOM.unstable_flushDiscreteUpdates();
1455 + updateCounter(count => count + 1);
1456 + }}>
1457 + Press me
1458 </button>
1459 </div>
1460 );
1461 }
1462
1463 const newContainer = document.createElement('div');
2440 - document.body.appendChild(newContainer);
1464 const root = ReactDOM.unstable_createRoot(newContainer);
2442 -
1465 + document.body.appendChild(newContainer);
1466 root.render(<MyComponent />);
1467 Scheduler.unstable_flushAll();
2445 - expect(newContainer.textContent).toEqual('Presses: 0, Clicks: 0');
1468
2447 - dispatchEventWithTimeStamp(button.current, 'pointerdown', 100);
2448 - dispatchEventWithTimeStamp(button.current, 'pointerup', 100);
2449 - dispatchEventWithTimeStamp(button.current, 'click', 100);
1469 + const target = ref.current;
1470 + target.dispatchEvent(pointerdown({timeStamp: 100}));
1471 + target.dispatchEvent(pointerup({timeStamp: 100}));
1472 + target.dispatchEvent(click({timeStamp: 100}));
1473 +
1474 + if (__DEV__) {
1475 + expect(renderCounts).toBe(4);
1476 + } else {
1477 + expect(renderCounts).toBe(2);
1478 + }
1479 Scheduler.unstable_flushAll();
2451 - expect(newContainer.textContent).toEqual('Presses: 1, Clicks: 1');
1480 + if (__DEV__) {
1481 + expect(renderCounts).toBe(6);
1482 + } else {
1483 + expect(renderCounts).toBe(3);
1484 + }
1485
2453 - expect(ops).toEqual(['Presses: 0, Clicks: 0']);
2454 - },
2455 - );
1486 + target.dispatchEvent(pointerdown({timeStamp: 100}));
1487 + target.dispatchEvent(pointerup({timeStamp: 100}));
1488 + // Ensure the timeStamp logic works
1489 + target.dispatchEvent(click({timeStamp: 101}));
1490
2457 - it('should work correctly with stopPropagation set to true', () => {
2458 - const ref = React.createRef();
2459 - const pointerDownEvent = jest.fn();
1491 + if (__DEV__) {
1492 + expect(renderCounts).toBe(8);
1493 + } else {
1494 + expect(renderCounts).toBe(4);
1495 + }
1496
2461 - const Component = () => {
2462 - const listener = usePressResponder({stopPropagation: true});
1497 + Scheduler.unstable_flushAll();
1498 + document.body.removeChild(newContainer);
1499 + });
1500
2464 - return <div ref={ref} listeners={listener} />;
2465 - };
1501 + it(
1502 + 'should only flush before outermost discrete event handler when mixing ' +
1503 + 'event systems',
1504 + async () => {
1505 + const {useState} = React;
1506
2467 - container.addEventListener('pointerdown', pointerDownEvent);
2468 - ReactDOM.render(<Component />, container);
1507 + const button = React.createRef();
1508
2470 - ref.current.dispatchEvent(
2471 - createEvent('pointerdown', {pointerType: 'mouse', button: 0}),
2472 - );
2473 - container.removeEventListener('pointerdown', pointerDownEvent);
2474 - expect(pointerDownEvent).toHaveBeenCalledTimes(0);
2475 - });
1509 + const ops = [];
1510
2477 - it('has the correct press target when used with event hook', () => {
2478 - const ref = React.createRef();
2479 - const onPress = jest.fn();
2480 - const Component = () => {
2481 - const listener = usePressResponder({onPress});
1511 + function MyComponent() {
1512 + const [pressesCount, updatePressesCount] = useState(0);
1513 + const [clicksCount, updateClicksCount] = useState(0);
1514
2483 - return (
2484 - <div>
2485 - <a href="#" ref={ref} listeners={listener} />
2486 - </div>
2487 - );
2488 - };
2489 - ReactDOM.render(<Component />, container);
1515 + function handlePress() {
1516 + // This dispatches a synchronous, discrete event in the legacy event
1517 + // system. However, because it's nested inside the new event system,
1518 + // its updates should not flush until the end of the outer handler.
1519 + button.current.click();
1520 + // Text context should not have changed
1521 + ops.push(newContainer.textContent);
1522 + updatePressesCount(pressesCount + 1);
1523 + }
1524
2491 - ref.current.dispatchEvent(
2492 - createEvent('pointerdown', {pointerType: 'mouse', button: 0}),
2493 - );
2494 - ref.current.dispatchEvent(
2495 - createEvent('pointerup', {pointerType: 'mouse', button: 0}),
2496 - );
2497 - expect(onPress).toHaveBeenCalledTimes(1);
2498 - expect(onPress).toHaveBeenCalledWith(
2499 - expect.objectContaining({target: ref.current}),
1525 + const listener = usePressResponder({
1526 + onPress: handlePress,
1527 + });
1528 +
1529 + return (
1530 + <div>
1531 + <button
1532 + listeners={listener}
1533 + ref={button}
1534 + onClick={() => updateClicksCount(clicksCount + 1)}>
1535 + Presses: {pressesCount}, Clicks: {clicksCount}
1536 + </button>
1537 + </div>
1538 + );
1539 + }
1540 +
1541 + const newContainer = document.createElement('div');
1542 + document.body.appendChild(newContainer);
1543 + const root = ReactDOM.unstable_createRoot(newContainer);
1544 +
1545 + root.render(<MyComponent />);
1546 + Scheduler.unstable_flushAll();
1547 + expect(newContainer.textContent).toEqual('Presses: 0, Clicks: 0');
1548 +
1549 + const target = button.current;
1550 + target.dispatchEvent(pointerdown({timeStamp: 100}));
1551 + target.dispatchEvent(pointerup({timeStamp: 100}));
1552 + target.dispatchEvent(click({timeStamp: 100}));
1553 +
1554 + Scheduler.unstable_flushAll();
1555 + expect(newContainer.textContent).toEqual('Presses: 1, Clicks: 1');
1556 +
1557 + expect(ops).toEqual(['Presses: 0, Clicks: 0']);
1558 + },
1559 );
2501 - });
1560 +
1561 + it('should work correctly with stopPropagation set to true', () => {
1562 + const ref = React.createRef();
1563 + const pointerDownEvent = jest.fn();
1564 +
1565 + const Component = () => {
1566 + const listener = usePressResponder({stopPropagation: true});
1567 + return <div ref={ref} listeners={listener} />;
1568 + };
1569 +
1570 + container.addEventListener('pointerdown', pointerDownEvent);
1571 + ReactDOM.render(<Component />, container);
1572 + dispatchPointerDown(ref.current);
1573 + container.removeEventListener('pointerdown', pointerDownEvent);
1574 + expect(pointerDownEvent).toHaveBeenCalledTimes(0);
1575 + });
1576 + }
1577 });
packages/react-events/src/dom/test-utils.js
+157 -76
@@ -15,7 +15,7 @@
15 * Change environment support for PointerEvent.
16 */
17
18 -function hasPointerEvent(bool) {
18 +function hasPointerEvent() {
19 return global != null && global.PointerEvent != null;
20 }
21
@@ -57,23 +57,38 @@ const platform = {
57 * Mock native events
58 */
59
60 -function createEvent(type, data) {
60 +function createEvent(type, data = {}) {
61 const event = document.createEvent('CustomEvent');
62 event.initCustomEvent(type, true, true);
63 + event.clientX = data.x || 0;
64 + event.clientY = data.y || 0;
65 + event.x = data.x || 0;
66 + event.y = data.y || 0;
67 if (data != null) {
64 - Object.entries(data).forEach(([key, value]) => {
65 - event[key] = value;
68 + Object.keys(data).forEach(key => {
69 + const value = data[key];
70 + Object.defineProperty(event, key, {value});
71 });
72 }
73 return event;
74 }
75
71 -function createTouchEvent(type, data, id) {
76 +function createTouchEvent(type, data = {}, id) {
77 return createEvent(type, {
78 changedTouches: [
79 {
80 + identifier: id,
81 + clientX: data.x || 0,
82 + clientY: data.y || 0,
83 ...data,
84 + },
85 + ],
86 + targetTouches: [
87 + {
88 identifier: id,
89 + clientX: 0 || data.x,
90 + clientY: 0 || data.y,
91 + ...data,
92 },
93 ],
94 });
@@ -112,17 +127,45 @@ function gotpointercapture(data) {
127 }
128
129 function keydown(data) {
115 - return createKeyboardEvent('keydown', data);
130 + return createEvent('keydown', data);
131 }
132
133 function keyup(data) {
119 - return createKeyboardEvent('keyup', data);
134 + return createEvent('keyup', data);
135 }
136
137 function lostpointercapture(data) {
138 return createEvent('lostpointercapture', data);
139 }
140
141 +function mousedown(data) {
142 + return createEvent('mousedown', data);
143 +}
144 +
145 +function mouseenter(data) {
146 + return createEvent('mouseenter', data);
147 +}
148 +
149 +function mouseleave(data) {
150 + return createEvent('mouseleave', data);
151 +}
152 +
153 +function mousemove(data) {
154 + return createEvent('mousemove', data);
155 +}
156 +
157 +function mouseout(data) {
158 + return createEvent('mouseout', data);
159 +}
160 +
161 +function mouseover(data) {
162 + return createEvent('mouseover', data);
163 +}
164 +
165 +function mouseup(data) {
166 + return createEvent('mouseup', data);
167 +}
168 +
169 function pointercancel(data) {
170 return createEvent('pointercancel', data);
171 }
@@ -155,32 +198,8 @@ function pointerup(data) {
198 return createEvent('pointerup', data);
199 }
200
158 -function mousedown(data) {
159 - return createEvent('mousedown', data);
160 -}
161 -
162 -function mouseenter(data) {
163 - return createEvent('mouseenter', data);
164 -}
165 -
166 -function mouseleave(data) {
167 - return createEvent('mouseleave', data);
168 -}
169 -
170 -function mousemove(data) {
171 - return createEvent('mousemove', data);
172 -}
173 -
174 -function mouseout(data) {
175 - return createEvent('mouseout', data);
176 -}
177 -
178 -function mouseover(data) {
179 - return createEvent('mouseover', data);
180 -}
181 -
182 -function mouseup(data) {
183 - return createEvent('mouseup', data);
201 +function scroll(data) {
202 + return createEvent('scroll', data);
203 }
204
205 function touchcancel(data, id) {
@@ -264,15 +283,15 @@ function dispatchPointerHoverEnter(target, {relatedTarget, x, y} = {}) {
283 dispatch(pointerenter({pointerType, ...event}));
284 }
285 dispatch(mouseover(event));
267 - dispatch(mouseover(event));
286 + dispatch(mouseenter(event));
287 }
288
270 -function dispatchPointerHoverMove(target, {from, to} = {}) {
289 +function dispatchPointerHoverMove(target, {x, y} = {}) {
290 const dispatch = arg => target.dispatchEvent(arg);
291 const button = -1;
292 const pointerId = 1;
293 const pointerType = 'mouse';
275 - function dispatchMove({x, y}) {
294 + function dispatchMove() {
295 const event = {
296 button,
297 clientX: x,
@@ -285,8 +304,7 @@ function dispatchPointerHoverMove(target, {from, to} = {}) {
304 }
305 dispatch(mousemove(event));
306 }
288 - dispatchMove({x: from.x, y: from.y});
289 - dispatchMove({x: to.x, y: to.y});
307 + dispatchMove();
308 }
309
310 function dispatchPointerHoverExit(target, {relatedTarget, x, y} = {}) {
@@ -309,77 +327,135 @@ function dispatchPointerHoverExit(target, {relatedTarget, x, y} = {}) {
327 dispatch(mouseleave(event));
328 }
329
312 -function dispatchPointerCancel(target, options) {
330 +function dispatchPointerCancel(target, {pointerType = 'mouse', ...rest} = {}) {
331 const dispatchEvent = arg => target.dispatchEvent(arg);
314 - dispatchEvent(pointercancel({pointerType: 'mouse'}));
315 - dispatchEvent(dragstart({pointerType: 'mouse'}));
332 + if (hasPointerEvent()) {
333 + dispatchEvent(pointercancel({pointerType, ...rest}));
334 + } else {
335 + if (pointerType === 'mouse') {
336 + dispatchEvent(dragstart({...rest}));
337 + } else {
338 + dispatchEvent(touchcancel({...rest}));
339 + }
340 + }
341 }
342
318 -function dispatchPointerPressDown(
343 +function dispatchPointerDown(
344 target,
320 - {button = 0, pointerType = 'mouse'} = {},
345 + {button = 0, pointerType = 'mouse', ...rest} = {},
346 ) {
347 const dispatch = arg => target.dispatchEvent(arg);
348 const pointerId = 1;
324 - if (pointerType !== 'mouse') {
349 + const pointerEvent = {button, pointerId, pointerType, ...rest};
350 + const mouseEvent = {button, ...rest};
351 + const touch = {...rest};
352 +
353 + if (pointerType === 'mouse') {
354 if (hasPointerEvent()) {
326 - dispatch(pointerover({button, pointerId, pointerType}));
327 - dispatch(pointerenter({button, pointerId, pointerType}));
328 - dispatch(pointerdown({button, pointerId, pointerType}));
355 + dispatch(pointerover(pointerEvent));
356 + dispatch(pointerenter(pointerEvent));
357 }
330 - dispatch(touchstart(null, pointerId));
358 + dispatch(mouseover(mouseEvent));
359 + dispatch(mouseenter(mouseEvent));
360 if (hasPointerEvent()) {
332 - dispatch(gotpointercapture({button, pointerId, pointerType}));
361 + dispatch(pointerdown(pointerEvent));
362 + }
363 + dispatch(mousedown(mouseEvent));
364 + if (document.activeElement !== target) {
365 + dispatch(focus());
366 }
367 } else {
368 if (hasPointerEvent()) {
336 - dispatch(pointerdown({button, pointerId, pointerType}));
369 + dispatch(pointerover(pointerEvent));
370 + dispatch(pointerenter(pointerEvent));
371 + dispatch(pointerdown(pointerEvent));
372 }
338 - dispatch(mousedown({button}));
339 - if (document.activeElement !== target) {
340 - dispatch(focus({button}));
373 + dispatch(touchstart(touch, pointerId));
374 + if (hasPointerEvent()) {
375 + dispatch(gotpointercapture(pointerEvent));
376 }
377 }
378 }
379
345 -function dispatchPointerPressRelease(
380 +function dispatchPointerUp(
381 target,
347 - {button = 0, pointerType = 'mouse'} = {},
382 + {button = 0, pointerType = 'mouse', ...rest} = {},
383 ) {
384 const dispatch = arg => target.dispatchEvent(arg);
385 const pointerId = 1;
351 - if (pointerType !== 'mouse') {
386 + const pointerEvent = {button, pointerId, pointerType, ...rest};
387 + const mouseEvent = {button, ...rest};
388 + const touch = {...rest};
389 +
390 + if (pointerType === 'mouse') {
391 if (hasPointerEvent()) {
353 - dispatch(pointerup({button, pointerId, pointerType}));
354 - dispatch(lostpointercapture({button, pointerId, pointerType}));
355 - dispatch(pointerout({button, pointerId, pointerType}));
356 - dispatch(pointerleave({button, pointerId, pointerType}));
357 - }
358 - dispatch(touchend(null, pointerId));
359 - dispatch(mouseover({button}));
360 - dispatch(mousemove({button}));
361 - dispatch(mousedown({button}));
362 - if (document.activeElement !== target) {
363 - dispatch(focus({button}));
392 + dispatch(pointerup(pointerEvent));
393 }
365 - dispatch(mouseup({button}));
366 - dispatch(click({button}));
394 + dispatch(mouseup(mouseEvent));
395 + dispatch(click(mouseEvent));
396 } else {
397 if (hasPointerEvent()) {
369 - dispatch(pointerup({button, pointerId, pointerType}));
398 + dispatch(pointerup(pointerEvent));
399 + dispatch(lostpointercapture(pointerEvent));
400 + dispatch(pointerout(pointerEvent));
401 + dispatch(pointerleave(pointerEvent));
402 }
371 - dispatch(mouseup({button}));
372 - dispatch(click({button}));
403 + dispatch(touchend(touch, pointerId));
404 + dispatch(mouseover(mouseEvent));
405 + dispatch(mousemove(mouseEvent));
406 + dispatch(mousedown(mouseEvent));
407 + if (document.activeElement !== target) {
408 + dispatch(focus());
409 + }
410 + dispatch(mouseup(mouseEvent));
411 + dispatch(click(mouseEvent));
412 + }
413 +}
414 +
415 +function dispatchPointerMove(
416 + target,
417 + {button = 0, pointerType = 'mouse', ...rest} = {},
418 +) {
419 + const dispatch = arg => target.dispatchEvent(arg);
420 + const pointerId = 1;
421 + const pointerEvent = {
422 + button,
423 + pointerId,
424 + pointerType,
425 + ...rest,
426 + };
427 + const mouseEvent = {
428 + button,
429 + ...rest,
430 + };
431 + const touch = {
432 + ...rest,
433 + };
434 +
435 + if (hasPointerEvent()) {
436 + dispatch(pointermove(pointerEvent));
437 + }
438 + if (pointerType === 'mouse') {
439 + dispatch(mousemove(mouseEvent));
440 + }
441 + if (pointerType === 'touch') {
442 + dispatch(touchmove(touch, pointerId));
443 }
444 }
445
446 function dispatchTouchTap(target) {
377 - dispatchPointerPressDown(target, {pointerType: 'touch'});
378 - dispatchPointerPressRelease(target, {pointerType: 'touch'});
447 + dispatchPointerDown(target, {pointerType: 'touch'});
448 + dispatchPointerUp(target, {pointerType: 'touch'});
449 +}
450 +
451 +function dispatchMouseTap(target) {
452 + dispatchPointerDown(target, {pointerType: 'mouse'});
453 + dispatchPointerUp(target, {pointerType: 'mouse'});
454 }
455
456 module.exports = {
457 blur,
458 + click,
459 focus,
460 createEvent,
461 dispatchLongPressContextMenu,
@@ -389,11 +465,16 @@ module.exports = {
465 dispatchPointerHoverEnter,
466 dispatchPointerHoverExit,
467 dispatchPointerHoverMove,
392 - dispatchPointerPressDown,
393 - dispatchPointerPressRelease,
468 + dispatchPointerMove,
469 + dispatchPointerDown,
470 + dispatchPointerUp,
471 dispatchTouchTap,
472 + dispatchMouseTap,
473 keydown,
474 keyup,
475 + scroll,
476 + pointerdown,
477 + pointerup,
478 platform,
479 hasPointerEvent,
480 setPointerEvent,