mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[react-interactions] Event testing library improvements (#17614)
Introduces a state machine around pointer events to produce more accurate mock touch events. This allows multi-touch unit tests to be written entirely in terms of mock pointer interactions, while producing the expected 'changedTouches', 'targetTouches', and 'touches' fields for mock touch events.
This commit is contained in:
+34
-24
@@ -7,13 +7,23 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import {createEventTarget} from 'react-interactions/events/src/dom/testing-library';
|
||||
import {createEventTarget} from 'react-interactions/events/src/dom/event-testing-library';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
let FocusContain;
|
||||
let tabbableScopeQuery;
|
||||
|
||||
function tabNext(target) {
|
||||
target.keydown({key: 'Tab'});
|
||||
target.keyup({key: 'Tab'});
|
||||
}
|
||||
|
||||
function tabPrevious(target) {
|
||||
target.keydown({key: 'Tab', shiftKey: true});
|
||||
target.keyup({key: 'Tab', shiftKey: true});
|
||||
}
|
||||
|
||||
describe('FocusContain', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
@@ -59,13 +69,13 @@ describe('FocusContain', () => {
|
||||
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(document.activeElement).toBe(inputRef.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(buttonRef.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(divRef.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button2Ref.current);
|
||||
createEventTarget(document.activeElement).tabPrevious();
|
||||
tabPrevious(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(divRef.current);
|
||||
});
|
||||
|
||||
@@ -90,15 +100,15 @@ describe('FocusContain', () => {
|
||||
|
||||
ReactDOM.render(<Test />, container);
|
||||
buttonRef.current.focus();
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button2Ref.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(buttonRef.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button2Ref.current);
|
||||
createEventTarget(document.activeElement).tabPrevious();
|
||||
tabPrevious(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(buttonRef.current);
|
||||
createEventTarget(document.activeElement).tabPrevious();
|
||||
tabPrevious(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button2Ref.current);
|
||||
// Focus should be restored to the contained area
|
||||
const rAF = window.requestAnimationFrame;
|
||||
@@ -132,15 +142,15 @@ describe('FocusContain', () => {
|
||||
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(document.activeElement).toBe(buttonRef.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button2Ref.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button3Ref.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button4Ref.current);
|
||||
createEventTarget(document.activeElement).tabPrevious();
|
||||
tabPrevious(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button3Ref.current);
|
||||
createEventTarget(document.activeElement).tabPrevious();
|
||||
tabPrevious(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button2Ref.current);
|
||||
});
|
||||
|
||||
@@ -167,13 +177,13 @@ describe('FocusContain', () => {
|
||||
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(document.activeElement).toBe(button2Ref.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button3Ref.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button2Ref.current);
|
||||
createEventTarget(document.activeElement).tabPrevious();
|
||||
tabPrevious(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button3Ref.current);
|
||||
createEventTarget(document.activeElement).tabPrevious();
|
||||
tabPrevious(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button2Ref.current);
|
||||
});
|
||||
|
||||
@@ -213,15 +223,15 @@ describe('FocusContain', () => {
|
||||
ReactDOM.render(<Test />, container);
|
||||
buttonRef.current.focus();
|
||||
expect(document.activeElement).toBe(buttonRef.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button2Ref.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button3Ref.current);
|
||||
createEventTarget(document.activeElement).tabNext();
|
||||
tabNext(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button4Ref.current);
|
||||
createEventTarget(document.activeElement).tabPrevious();
|
||||
tabPrevious(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button3Ref.current);
|
||||
createEventTarget(document.activeElement).tabPrevious();
|
||||
tabPrevious(createEventTarget(document.activeElement));
|
||||
expect(document.activeElement).toBe(button2Ref.current);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import {createEventTarget} from 'react-interactions/events/src/dom/testing-library';
|
||||
import {createEventTarget} from 'react-interactions/events/src/dom/event-testing-library';
|
||||
import {emulateBrowserTab} from '../shared/emulateBrowserTab';
|
||||
|
||||
let React;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import {createEventTarget} from 'react-interactions/events/src/dom/testing-library';
|
||||
import {createEventTarget} from 'react-interactions/events/src/dom/event-testing-library';
|
||||
import {emulateBrowserTab} from '../shared/emulateBrowserTab';
|
||||
|
||||
let React;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import {createEventTarget} from 'react-interactions/events/src/dom/testing-library';
|
||||
import {createEventTarget} from 'react-interactions/events/src/dom/event-testing-library';
|
||||
|
||||
// This function is used by the a11y modules for testing
|
||||
export function emulateBrowserTab(backwards: boolean): void {
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
createEventTarget,
|
||||
platform,
|
||||
setPointerEvent,
|
||||
} from '../testing-library';
|
||||
} from '../event-testing-library';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import {createEventTarget, setPointerEvent, platform} from '../testing-library';
|
||||
import {
|
||||
createEventTarget,
|
||||
setPointerEvent,
|
||||
platform,
|
||||
} from '../event-testing-library';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import {createEventTarget, setPointerEvent} from '../testing-library';
|
||||
import {createEventTarget, setPointerEvent} from '../event-testing-library';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import {createEventTarget, setPointerEvent} from '../testing-library';
|
||||
import {createEventTarget, setPointerEvent} from '../event-testing-library';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
@@ -14,7 +14,7 @@ let ReactFeatureFlags;
|
||||
let ReactDOM;
|
||||
let useKeyboard;
|
||||
|
||||
import {createEventTarget} from '../testing-library';
|
||||
import {createEventTarget} from '../event-testing-library';
|
||||
|
||||
function initializeModules(hasPointerEvents) {
|
||||
jest.resetModules();
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import {createEventTarget} from '../testing-library';
|
||||
import {createEventTarget} from '../event-testing-library';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
@@ -14,8 +14,9 @@ import {
|
||||
buttonsType,
|
||||
createEventTarget,
|
||||
describeWithPointerEvent,
|
||||
resetActivePointers,
|
||||
setPointerEvent,
|
||||
} from '../testing-library';
|
||||
} from '../event-testing-library';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
@@ -47,6 +48,7 @@ describeWithPointerEvent('Press responder', hasPointerEvents => {
|
||||
ReactDOM.render(null, container);
|
||||
document.body.removeChild(container);
|
||||
container = null;
|
||||
resetActivePointers();
|
||||
});
|
||||
|
||||
describe('disabled', () => {
|
||||
|
||||
@@ -13,8 +13,9 @@ import {
|
||||
buttonType,
|
||||
buttonsType,
|
||||
createEventTarget,
|
||||
resetActivePointers,
|
||||
setPointerEvent,
|
||||
} from '../testing-library';
|
||||
} from '../event-testing-library';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
@@ -59,6 +60,7 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
|
||||
ReactDOM.render(null, container);
|
||||
document.body.removeChild(container);
|
||||
container = null;
|
||||
resetActivePointers();
|
||||
});
|
||||
|
||||
describe('disabled', () => {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import {createEventTarget, setPointerEvent} from '../testing-library';
|
||||
import {createEventTarget, setPointerEvent} from '../event-testing-library';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
@@ -16,7 +16,8 @@ import {
|
||||
describeWithPointerEvent,
|
||||
setPointerEvent,
|
||||
testWithPointerType,
|
||||
} from '../testing-library';
|
||||
resetActivePointers,
|
||||
} from '../event-testing-library';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
@@ -82,6 +83,7 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
|
||||
ReactDOM.render(null, container);
|
||||
document.body.removeChild(container);
|
||||
container = null;
|
||||
resetActivePointers();
|
||||
});
|
||||
|
||||
test('supports repeated use', () => {
|
||||
@@ -295,12 +297,7 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
|
||||
const buttons = buttonsType.primary;
|
||||
target.pointerdown({button, buttons, pointerId: 1, pointerType});
|
||||
expect(onTapStart).toHaveBeenCalledTimes(1);
|
||||
if (hasPointerEvents) {
|
||||
target.pointerdown({button, buttons, pointerId: 2, pointerType});
|
||||
} else {
|
||||
// TouchEvents
|
||||
target.pointerdown([{pointerId: 1}, {pointerId: 2}]);
|
||||
}
|
||||
target.pointerdown({button, buttons, pointerId: 2, pointerType});
|
||||
expect(onTapStart).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -313,28 +310,28 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
|
||||
buttons: buttonsType.secondary,
|
||||
pointerType,
|
||||
});
|
||||
target.pointerup();
|
||||
target.pointerup({pointerType});
|
||||
// middle-click
|
||||
target.pointerdown({
|
||||
button: buttonType.auxiliary,
|
||||
buttons: buttonsType.auxiliary,
|
||||
pointerType,
|
||||
});
|
||||
target.pointerup();
|
||||
target.pointerup({pointerType});
|
||||
// virtual middle-click with misleading 'buttons' value
|
||||
target.pointerdown({
|
||||
button: buttonType.auxiliary,
|
||||
buttons: 0,
|
||||
pointerType,
|
||||
});
|
||||
target.pointerup();
|
||||
target.pointerup({pointerType});
|
||||
// pen eraser
|
||||
target.pointerdown({
|
||||
button: buttonType.eraser,
|
||||
buttons: buttonsType.eraser,
|
||||
pointerType,
|
||||
});
|
||||
target.pointerup();
|
||||
target.pointerup({pointerType});
|
||||
}
|
||||
// alt-click
|
||||
target.pointerdown({
|
||||
@@ -343,7 +340,7 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
|
||||
altKey: true,
|
||||
pointerType,
|
||||
});
|
||||
target.pointerup();
|
||||
target.pointerup({pointerType});
|
||||
// ctrl-click
|
||||
target.pointerdown({
|
||||
button: buttonType.primary,
|
||||
@@ -351,7 +348,7 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
|
||||
ctrlKey: true,
|
||||
pointerType,
|
||||
});
|
||||
target.pointerup();
|
||||
target.pointerup({pointerType});
|
||||
// meta-click
|
||||
target.pointerdown({
|
||||
button: buttonType.primary,
|
||||
@@ -359,7 +356,7 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
|
||||
metaKey: true,
|
||||
pointerType,
|
||||
});
|
||||
target.pointerup();
|
||||
target.pointerup({pointerType});
|
||||
// shift-click
|
||||
target.pointerdown({
|
||||
button: buttonType.primary,
|
||||
@@ -367,7 +364,7 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
|
||||
shiftKey: true,
|
||||
pointerType,
|
||||
});
|
||||
target.pointerup();
|
||||
target.pointerup({pointerType});
|
||||
|
||||
expect(onTapStart).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
@@ -565,8 +562,10 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
|
||||
testWithPointerType('requires activation', pointerType => {
|
||||
const target = createEventTarget(ref.current);
|
||||
target.setBoundingClientRect(rect);
|
||||
target.pointerhover({pointerType, ...coordinates});
|
||||
target.pointermove({pointerType, ...coordinates});
|
||||
if (pointerType !== 'touch') {
|
||||
target.pointerhover({pointerType, ...coordinates});
|
||||
target.pointermove({pointerType, ...coordinates});
|
||||
}
|
||||
expect(onTapUpdate).not.toBeCalled();
|
||||
});
|
||||
|
||||
@@ -771,12 +770,7 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
|
||||
const button = buttonType.primary;
|
||||
const buttons = buttonsType.primary;
|
||||
target.pointerdown({button, buttons, pointerId: 1, pointerType});
|
||||
if (hasPointerEvents) {
|
||||
target.pointerdown({button, buttons, pointerId: 2, pointerType});
|
||||
} else {
|
||||
// TouchEvents
|
||||
target.pointerdown([{pointerId: 1}, {pointerId: 2}]);
|
||||
}
|
||||
target.pointerdown({button, buttons, pointerId: 2, pointerType});
|
||||
expect(onTapCancel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export const defaultPointerId = 1;
|
||||
export const defaultPointerSize = 23;
|
||||
export const defaultBrowserChromeSize = 50;
|
||||
|
||||
/**
|
||||
* Button property
|
||||
* This property only guarantees to indicate which buttons are pressed during events caused by pressing or
|
||||
* releasing one or multiple buttons. As such, it is not reliable for events such as 'mouseenter', 'mouseleave',
|
||||
* 'mouseover', 'mouseout' or 'mousemove'. Furthermore, the semantics differ for PointerEvent, where the value
|
||||
* for 'pointermove' will always be -1.
|
||||
*/
|
||||
|
||||
export const buttonType = {
|
||||
// no change since last event
|
||||
none: -1,
|
||||
// left-mouse
|
||||
// touch contact
|
||||
// pen contact
|
||||
primary: 0,
|
||||
// right-mouse
|
||||
// pen barrel button
|
||||
secondary: 2,
|
||||
// middle mouse
|
||||
auxiliary: 1,
|
||||
// back mouse
|
||||
back: 3,
|
||||
// forward mouse
|
||||
forward: 4,
|
||||
// pen eraser
|
||||
eraser: 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* Buttons bitmask
|
||||
*/
|
||||
|
||||
export const buttonsType = {
|
||||
none: 0,
|
||||
// left-mouse
|
||||
// touch contact
|
||||
// pen contact
|
||||
primary: 1,
|
||||
// right-mouse
|
||||
// pen barrel button
|
||||
secondary: 2,
|
||||
// middle mouse
|
||||
auxiliary: 4,
|
||||
// back mouse
|
||||
back: 8,
|
||||
// forward mouse
|
||||
forward: 16,
|
||||
// pen eraser
|
||||
eraser: 32,
|
||||
};
|
||||
-51
@@ -65,54 +65,3 @@ export const platform = {
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Button property
|
||||
* This property only guarantees to indicate which buttons are pressed during events caused by pressing or
|
||||
* releasing one or multiple buttons. As such, it is not reliable for events such as 'mouseenter', 'mouseleave',
|
||||
* 'mouseover', 'mouseout' or 'mousemove'. Furthermore, the semantics differ for PointerEvent, where the value
|
||||
* for 'pointermove' will always be -1.
|
||||
*/
|
||||
|
||||
export const buttonType = {
|
||||
// no change since last event
|
||||
none: -1,
|
||||
// left-mouse
|
||||
// touch contact
|
||||
// pen contact
|
||||
primary: 0,
|
||||
// right-mouse
|
||||
// pen barrel button
|
||||
secondary: 2,
|
||||
// middle mouse
|
||||
auxiliary: 1,
|
||||
// back mouse
|
||||
back: 3,
|
||||
// forward mouse
|
||||
forward: 4,
|
||||
// pen eraser
|
||||
eraser: 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* Buttons bitmask
|
||||
*/
|
||||
|
||||
export const buttonsType = {
|
||||
none: 0,
|
||||
// left-mouse
|
||||
// touch contact
|
||||
// pen contact
|
||||
primary: 1,
|
||||
// right-mouse
|
||||
// pen barrel button
|
||||
secondary: 2,
|
||||
// middle mouse
|
||||
auxiliary: 4,
|
||||
// back mouse
|
||||
back: 8,
|
||||
// forward mouse
|
||||
forward: 16,
|
||||
// pen eraser
|
||||
eraser: 32,
|
||||
};
|
||||
+361
@@ -0,0 +1,361 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import {
|
||||
buttonType,
|
||||
buttonsType,
|
||||
defaultPointerId,
|
||||
defaultPointerSize,
|
||||
defaultBrowserChromeSize,
|
||||
} from './constants';
|
||||
import * as domEvents from './domEvents';
|
||||
import {hasPointerEvent, platform} from './domEnvironment';
|
||||
import * as touchStore from './touchStore';
|
||||
|
||||
/**
|
||||
* Converts a PointerEvent payload to a Touch
|
||||
*/
|
||||
function createTouch(target, payload) {
|
||||
const {
|
||||
height = defaultPointerSize,
|
||||
pageX,
|
||||
pageY,
|
||||
pointerId,
|
||||
pressure = 1,
|
||||
twist = 0,
|
||||
width = defaultPointerSize,
|
||||
x = 0,
|
||||
y = 0,
|
||||
} = payload;
|
||||
|
||||
return {
|
||||
clientX: x,
|
||||
clientY: y,
|
||||
force: pressure,
|
||||
identifier: pointerId,
|
||||
pageX: pageX || x,
|
||||
pageY: pageY || y,
|
||||
radiusX: width / 2,
|
||||
radiusY: height / 2,
|
||||
rotationAngle: twist,
|
||||
target,
|
||||
screenX: x,
|
||||
screenY: y + defaultBrowserChromeSize,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a PointerEvent to a TouchEvent
|
||||
*/
|
||||
function createTouchEventPayload(target, touch, payload) {
|
||||
const {
|
||||
altKey = false,
|
||||
ctrlKey = false,
|
||||
metaKey = false,
|
||||
preventDefault,
|
||||
shiftKey = false,
|
||||
timeStamp,
|
||||
} = payload;
|
||||
|
||||
return {
|
||||
altKey,
|
||||
changedTouches: [touch],
|
||||
ctrlKey,
|
||||
metaKey,
|
||||
preventDefault,
|
||||
shiftKey,
|
||||
targetTouches: touchStore.getTargetTouches(target),
|
||||
timeStamp,
|
||||
touches: touchStore.getTouches(),
|
||||
};
|
||||
}
|
||||
|
||||
function getPointerType(payload) {
|
||||
let pointerType = 'mouse';
|
||||
if (payload != null && payload.pointerType != null) {
|
||||
pointerType = payload.pointerType;
|
||||
}
|
||||
return pointerType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pointer events sequences.
|
||||
*
|
||||
* Creates representative browser event sequences for high-level gestures based on pointers.
|
||||
* This allows unit tests to be written in terms of simple pointer interactions while testing
|
||||
* that the responses to those interactions account for the complex sequence of events that
|
||||
* browsers produce as a result.
|
||||
*
|
||||
* Every time a new pointer touches the surface a 'touchstart' event should be dispatched.
|
||||
* - 'changedTouches' contains the new touch.
|
||||
* - 'targetTouches' contains all the active pointers for the target.
|
||||
* - 'touches' contains all the active pointers on the surface.
|
||||
*
|
||||
* Every time an existing pointer moves a 'touchmove' event should be dispatched.
|
||||
* - 'changedTouches' contains the updated touch.
|
||||
*
|
||||
* Every time an existing pointer leaves the surface a 'touchend' event should be dispatched.
|
||||
* - 'changedTouches' contains the released touch.
|
||||
* - 'targetTouches' contains any of the remaining active pointers for the target.
|
||||
*/
|
||||
|
||||
export function contextmenu(
|
||||
target,
|
||||
defaultPayload,
|
||||
{pointerType = 'mouse', modified} = {},
|
||||
) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
|
||||
const payload = {
|
||||
pointerId: defaultPointerId,
|
||||
pointerType,
|
||||
...defaultPayload,
|
||||
};
|
||||
|
||||
const preventDefault = payload.preventDefault;
|
||||
|
||||
if (pointerType === 'touch') {
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(
|
||||
domEvents.pointerdown({
|
||||
...payload,
|
||||
button: buttonType.primary,
|
||||
buttons: buttonsType.primary,
|
||||
}),
|
||||
);
|
||||
}
|
||||
const touch = createTouch(target, payload);
|
||||
touchStore.addTouch(touch);
|
||||
const touchEventPayload = createTouchEventPayload(target, touch, payload);
|
||||
dispatch(domEvents.touchstart(touchEventPayload));
|
||||
dispatch(
|
||||
domEvents.contextmenu({
|
||||
button: buttonType.primary,
|
||||
buttons: buttonsType.none,
|
||||
preventDefault,
|
||||
}),
|
||||
);
|
||||
touchStore.removeTouch(touch);
|
||||
} else if (pointerType === 'mouse') {
|
||||
if (modified === true) {
|
||||
const button = buttonType.primary;
|
||||
const buttons = buttonsType.primary;
|
||||
const ctrlKey = true;
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(
|
||||
domEvents.pointerdown({button, buttons, ctrlKey, pointerType}),
|
||||
);
|
||||
}
|
||||
dispatch(domEvents.mousedown({button, buttons, ctrlKey}));
|
||||
if (platform.get() === 'mac') {
|
||||
dispatch(
|
||||
domEvents.contextmenu({button, buttons, ctrlKey, preventDefault}),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const button = buttonType.secondary;
|
||||
const buttons = buttonsType.secondary;
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerdown({button, buttons, pointerType}));
|
||||
}
|
||||
dispatch(domEvents.mousedown({button, buttons}));
|
||||
dispatch(domEvents.contextmenu({button, buttons, preventDefault}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function pointercancel(target, defaultPayload) {
|
||||
const dispatchEvent = arg => target.dispatchEvent(arg);
|
||||
const pointerType = getPointerType(defaultPayload);
|
||||
|
||||
const payload = {
|
||||
pointerId: defaultPointerId,
|
||||
pointerType,
|
||||
...defaultPayload,
|
||||
};
|
||||
|
||||
if (hasPointerEvent()) {
|
||||
dispatchEvent(domEvents.pointercancel(payload));
|
||||
} else {
|
||||
if (pointerType === 'mouse') {
|
||||
dispatchEvent(domEvents.dragstart(payload));
|
||||
} else {
|
||||
const touch = createTouch(target, payload);
|
||||
touchStore.removeTouch(touch);
|
||||
const touchEventPayload = createTouchEventPayload(target, touch, payload);
|
||||
dispatchEvent(domEvents.touchcancel(touchEventPayload));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function pointerdown(target, defaultPayload) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
const pointerType = getPointerType(defaultPayload);
|
||||
|
||||
const payload = {
|
||||
button: buttonType.primary,
|
||||
buttons: buttonsType.primary,
|
||||
pointerId: defaultPointerId,
|
||||
pointerType,
|
||||
...defaultPayload,
|
||||
};
|
||||
|
||||
if (pointerType === 'mouse') {
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerover(payload));
|
||||
dispatch(domEvents.pointerenter(payload));
|
||||
}
|
||||
dispatch(domEvents.mouseover(payload));
|
||||
dispatch(domEvents.mouseenter(payload));
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerdown(payload));
|
||||
}
|
||||
dispatch(domEvents.mousedown(payload));
|
||||
if (document.activeElement !== target) {
|
||||
dispatch(domEvents.focus());
|
||||
}
|
||||
} else {
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerover(payload));
|
||||
dispatch(domEvents.pointerenter(payload));
|
||||
dispatch(domEvents.pointerdown(payload));
|
||||
}
|
||||
const touch = createTouch(target, payload);
|
||||
touchStore.addTouch(touch);
|
||||
const touchEventPayload = createTouchEventPayload(target, touch, payload);
|
||||
dispatch(domEvents.touchstart(touchEventPayload));
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.gotpointercapture(payload));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function pointerenter(target, defaultPayload) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
|
||||
const payload = {
|
||||
pointerId: defaultPointerId,
|
||||
...defaultPayload,
|
||||
};
|
||||
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerover(payload));
|
||||
dispatch(domEvents.pointerenter(payload));
|
||||
}
|
||||
dispatch(domEvents.mouseover(payload));
|
||||
dispatch(domEvents.mouseenter(payload));
|
||||
}
|
||||
|
||||
export function pointerexit(target, defaultPayload) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
|
||||
const payload = {
|
||||
pointerId: defaultPointerId,
|
||||
...defaultPayload,
|
||||
};
|
||||
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerout(payload));
|
||||
dispatch(domEvents.pointerleave(payload));
|
||||
}
|
||||
dispatch(domEvents.mouseout(payload));
|
||||
dispatch(domEvents.mouseleave(payload));
|
||||
}
|
||||
|
||||
export function pointerhover(target, defaultPayload) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
|
||||
const payload = {
|
||||
pointerId: defaultPointerId,
|
||||
...defaultPayload,
|
||||
};
|
||||
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointermove(payload));
|
||||
}
|
||||
dispatch(domEvents.mousemove(payload));
|
||||
}
|
||||
|
||||
export function pointermove(target, defaultPayload) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
const pointerType = getPointerType(defaultPayload);
|
||||
|
||||
const payload = {
|
||||
pointerId: defaultPointerId,
|
||||
pointerType,
|
||||
...defaultPayload,
|
||||
};
|
||||
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(
|
||||
domEvents.pointermove({
|
||||
pressure: pointerType === 'touch' ? 1 : 0.5,
|
||||
...payload,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
if (pointerType === 'mouse') {
|
||||
dispatch(domEvents.mousemove(payload));
|
||||
} else {
|
||||
const touch = createTouch(target, payload);
|
||||
touchStore.updateTouch(touch);
|
||||
const touchEventPayload = createTouchEventPayload(target, touch, payload);
|
||||
dispatch(domEvents.touchmove(touchEventPayload));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function pointerup(target, defaultPayload) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
const pointerType = getPointerType(defaultPayload);
|
||||
|
||||
const payload = {
|
||||
pointerId: defaultPointerId,
|
||||
pointerType,
|
||||
...defaultPayload,
|
||||
};
|
||||
|
||||
if (pointerType === 'mouse') {
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerup(payload));
|
||||
}
|
||||
dispatch(domEvents.mouseup(payload));
|
||||
dispatch(domEvents.click(payload));
|
||||
} else {
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerup(payload));
|
||||
dispatch(domEvents.lostpointercapture(payload));
|
||||
dispatch(domEvents.pointerout(payload));
|
||||
dispatch(domEvents.pointerleave(payload));
|
||||
}
|
||||
const touch = createTouch(target, payload);
|
||||
touchStore.removeTouch(touch);
|
||||
const touchEventPayload = createTouchEventPayload(target, touch, payload);
|
||||
dispatch(domEvents.touchend(touchEventPayload));
|
||||
dispatch(domEvents.mouseover(payload));
|
||||
dispatch(domEvents.mousemove(payload));
|
||||
dispatch(domEvents.mousedown(payload));
|
||||
if (document.activeElement !== target) {
|
||||
dispatch(domEvents.focus());
|
||||
}
|
||||
dispatch(domEvents.mouseup(payload));
|
||||
dispatch(domEvents.click(payload));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should be called after each test to ensure the touchStore is cleared
|
||||
* in cases where the mock pointers weren't released before the test completed
|
||||
* (e.g., a test failed or ran a partial gesture).
|
||||
*/
|
||||
export function resetActivePointers() {
|
||||
touchStore.clear();
|
||||
}
|
||||
+8
-74
@@ -9,7 +9,12 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import {buttonType, buttonsType} from './domEnvironment';
|
||||
import {
|
||||
buttonType,
|
||||
buttonsType,
|
||||
defaultPointerSize,
|
||||
defaultBrowserChromeSize,
|
||||
} from './constants';
|
||||
|
||||
/**
|
||||
* Native event object mocks for higher-level events.
|
||||
@@ -26,9 +31,6 @@ import {buttonType, buttonsType} from './domEnvironment';
|
||||
* 3. PointerEvent and TouchEvent fields are normalized (e.g., 'rotationAngle' -> 'twist')
|
||||
*/
|
||||
|
||||
const defaultPointerSize = 23;
|
||||
const defaultBrowserChromeSize = 50;
|
||||
|
||||
function emptyFunction() {}
|
||||
|
||||
function createEvent(type, data = {}) {
|
||||
@@ -77,7 +79,7 @@ function createPointerEvent(
|
||||
offsetY = 0,
|
||||
pageX,
|
||||
pageY,
|
||||
pointerId = 1,
|
||||
pointerId,
|
||||
pressure = 0,
|
||||
preventDefault = emptyFunction,
|
||||
pointerType = 'mouse',
|
||||
@@ -215,80 +217,12 @@ function createMouseEvent(
|
||||
}
|
||||
|
||||
function createTouchEvent(type, payload) {
|
||||
const touchesPayload = Array.isArray(payload) ? payload : [payload];
|
||||
const firstTouch = touchesPayload[0];
|
||||
let altKey = false;
|
||||
let ctrlKey = false;
|
||||
let metaKey = false;
|
||||
let preventDefault = emptyFunction;
|
||||
let shiftKey = false;
|
||||
let timeStamp;
|
||||
|
||||
if (firstTouch != null) {
|
||||
if (firstTouch.altKey != null) {
|
||||
altKey = firstTouch.altKey;
|
||||
}
|
||||
if (firstTouch.ctrlKey != null) {
|
||||
ctrlKey = firstTouch.ctrlKey;
|
||||
}
|
||||
if (firstTouch.metaKey != null) {
|
||||
metaKey = firstTouch.metaKey;
|
||||
}
|
||||
if (firstTouch.preventDefault != null) {
|
||||
preventDefault = firstTouch.preventDefault;
|
||||
}
|
||||
if (firstTouch.shiftKey != null) {
|
||||
shiftKey = firstTouch.shiftKey;
|
||||
}
|
||||
if (firstTouch.timeStamp != null) {
|
||||
timeStamp = firstTouch.timeStamp;
|
||||
}
|
||||
}
|
||||
|
||||
const touches = touchesPayload.map(
|
||||
({
|
||||
height = defaultPointerSize,
|
||||
pageX,
|
||||
pageY,
|
||||
pointerId = 1,
|
||||
pressure = 1,
|
||||
twist = 0,
|
||||
width = defaultPointerSize,
|
||||
x = 0,
|
||||
y = 0,
|
||||
} = {}) => {
|
||||
return {
|
||||
clientX: x,
|
||||
clientY: y,
|
||||
force: pressure,
|
||||
identifier: pointerId,
|
||||
pageX: pageX || x,
|
||||
pageY: pageY || y,
|
||||
radiusX: width / 2,
|
||||
radiusY: height / 2,
|
||||
rotationAngle: twist,
|
||||
screenX: x,
|
||||
screenY: y + defaultBrowserChromeSize,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const activeTouches = type !== 'touchend' ? touches : [];
|
||||
|
||||
return createEvent(type, {
|
||||
altKey,
|
||||
changedTouches: touches,
|
||||
ctrlKey,
|
||||
...payload,
|
||||
detail: 0,
|
||||
metaKey,
|
||||
preventDefault,
|
||||
shiftKey,
|
||||
sourceCapabilities: {
|
||||
firesTouchEvents: true,
|
||||
},
|
||||
targetTouches: activeTouches,
|
||||
timeStamp,
|
||||
touches: activeTouches,
|
||||
});
|
||||
}
|
||||
|
||||
+5
-55
@@ -9,15 +9,11 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import {buttonType, buttonsType} from './constants';
|
||||
import * as domEvents from './domEvents';
|
||||
import * as domEventSequences from './domEventSequences';
|
||||
import {
|
||||
buttonType,
|
||||
buttonsType,
|
||||
hasPointerEvent,
|
||||
setPointerEvent,
|
||||
platform,
|
||||
} from './domEnvironment';
|
||||
import {hasPointerEvent, setPointerEvent, platform} from './domEnvironment';
|
||||
import {describeWithPointerEvent, testWithPointerType} from './testHelpers';
|
||||
|
||||
const createEventTarget = node => ({
|
||||
node,
|
||||
@@ -52,32 +48,6 @@ const createEventTarget = node => ({
|
||||
virtualclick(payload) {
|
||||
node.dispatchEvent(domEvents.virtualclick(payload));
|
||||
},
|
||||
tabNext() {
|
||||
node.dispatchEvent(
|
||||
domEvents.keydown({
|
||||
key: 'Tab',
|
||||
}),
|
||||
);
|
||||
node.dispatchEvent(
|
||||
domEvents.keyup({
|
||||
key: 'Tab',
|
||||
}),
|
||||
);
|
||||
},
|
||||
tabPrevious() {
|
||||
node.dispatchEvent(
|
||||
domEvents.keydown({
|
||||
key: 'Tab',
|
||||
shiftKey: true,
|
||||
}),
|
||||
);
|
||||
node.dispatchEvent(
|
||||
domEvents.keyup({
|
||||
key: 'Tab',
|
||||
shiftKey: true,
|
||||
}),
|
||||
);
|
||||
},
|
||||
/**
|
||||
* PointerEvent abstraction.
|
||||
* Dispatches the expected sequence of PointerEvents, MouseEvents, and
|
||||
@@ -137,28 +107,7 @@ const createEventTarget = node => ({
|
||||
},
|
||||
});
|
||||
|
||||
function describeWithPointerEvent(message, describeFn) {
|
||||
const pointerEvent = 'PointerEvent';
|
||||
const fallback = 'MouseEvent/TouchEvent';
|
||||
describe.each`
|
||||
value | name
|
||||
${true} | ${pointerEvent}
|
||||
${false} | ${fallback}
|
||||
`(`${message}: $name`, entry => {
|
||||
const hasPointerEvents = entry.value;
|
||||
setPointerEvent(hasPointerEvents);
|
||||
describeFn(hasPointerEvents);
|
||||
});
|
||||
}
|
||||
|
||||
function testWithPointerType(message, testFn) {
|
||||
const table = hasPointerEvent()
|
||||
? ['mouse', 'touch', 'pen']
|
||||
: ['mouse', 'touch'];
|
||||
test.each(table)(`${message}: %s`, pointerType => {
|
||||
testFn(pointerType);
|
||||
});
|
||||
}
|
||||
const resetActivePointers = domEventSequences.resetActivePointers;
|
||||
|
||||
export {
|
||||
buttonType,
|
||||
@@ -167,6 +116,7 @@ export {
|
||||
describeWithPointerEvent,
|
||||
platform,
|
||||
hasPointerEvent,
|
||||
resetActivePointers,
|
||||
setPointerEvent,
|
||||
testWithPointerType,
|
||||
};
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import {hasPointerEvent, setPointerEvent} from './domEnvironment';
|
||||
|
||||
export function describeWithPointerEvent(message, describeFn) {
|
||||
const pointerEvent = 'PointerEvent';
|
||||
const fallback = 'MouseEvent/TouchEvent';
|
||||
describe.each`
|
||||
value | name
|
||||
${true} | ${pointerEvent}
|
||||
${false} | ${fallback}
|
||||
`(`${message}: $name`, entry => {
|
||||
const hasPointerEvents = entry.value;
|
||||
setPointerEvent(hasPointerEvents);
|
||||
describeFn(hasPointerEvents);
|
||||
});
|
||||
}
|
||||
|
||||
export function testWithPointerType(message, testFn) {
|
||||
const table = hasPointerEvent()
|
||||
? ['mouse', 'touch', 'pen']
|
||||
: ['mouse', 'touch'];
|
||||
test.each(table)(`${message}: %s`, pointerType => {
|
||||
testFn(pointerType);
|
||||
});
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Touch events state machine.
|
||||
*
|
||||
* Keeps track of the active pointers and allows them to be reflected in touch events.
|
||||
*/
|
||||
|
||||
const activeTouches = new Map();
|
||||
|
||||
export function addTouch(touch) {
|
||||
const identifier = touch.identifier;
|
||||
const target = touch.target;
|
||||
if (!activeTouches.has(target)) {
|
||||
activeTouches.set(target, new Map());
|
||||
}
|
||||
if (activeTouches.get(target).get(identifier)) {
|
||||
// Do not allow existing touches to be overwritten
|
||||
// eslint-disable-next-line react-internal/no-production-logging
|
||||
console.error(
|
||||
'Touch with identifier %s already exists. Did not record touch start.',
|
||||
identifier,
|
||||
);
|
||||
} else {
|
||||
activeTouches.get(target).set(identifier, touch);
|
||||
}
|
||||
}
|
||||
|
||||
export function updateTouch(touch) {
|
||||
const identifier = touch.identifier;
|
||||
const target = touch.target;
|
||||
if (activeTouches.get(target) != null) {
|
||||
activeTouches.get(target).set(identifier, touch);
|
||||
} else {
|
||||
// eslint-disable-next-line react-internal/no-production-logging
|
||||
console.error(
|
||||
'Touch with identifier %s does not exist. Cannot record touch move without a touch start.',
|
||||
identifier,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function removeTouch(touch) {
|
||||
const identifier = touch.identifier;
|
||||
const target = touch.target;
|
||||
if (activeTouches.get(target) != null) {
|
||||
if (activeTouches.get(target).has(identifier)) {
|
||||
activeTouches.get(target).delete(identifier);
|
||||
} else {
|
||||
// eslint-disable-next-line react-internal/no-production-logging
|
||||
console.error(
|
||||
'Touch with identifier %s does not exist. Cannot record touch end without a touch start.',
|
||||
identifier,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getTouches() {
|
||||
const touches = [];
|
||||
activeTouches.forEach((_, target) => {
|
||||
touches.push(...getTargetTouches(target));
|
||||
});
|
||||
return touches;
|
||||
}
|
||||
|
||||
export function getTargetTouches(target) {
|
||||
if (activeTouches.get(target) != null) {
|
||||
return Array.from(activeTouches.get(target).values());
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export function clear() {
|
||||
activeTouches.clear();
|
||||
}
|
||||
-216
@@ -1,216 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as domEvents from './domEvents';
|
||||
import {
|
||||
buttonType,
|
||||
buttonsType,
|
||||
hasPointerEvent,
|
||||
platform,
|
||||
} from './domEnvironment';
|
||||
|
||||
function emptyFunction() {}
|
||||
|
||||
function getPointerType(payload) {
|
||||
let pointerType = 'mouse';
|
||||
if (payload != null && payload.pointerType != null) {
|
||||
pointerType = payload.pointerType;
|
||||
} else if (Array.isArray(payload)) {
|
||||
pointerType = 'touch';
|
||||
}
|
||||
return pointerType;
|
||||
}
|
||||
|
||||
export function contextmenu(
|
||||
target,
|
||||
{preventDefault = emptyFunction} = {},
|
||||
{pointerType = 'mouse', modified} = {},
|
||||
) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
if (pointerType === 'touch') {
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(
|
||||
domEvents.pointerdown({
|
||||
button: buttonType.primary,
|
||||
buttons: buttonsType.primary,
|
||||
pointerType,
|
||||
}),
|
||||
);
|
||||
}
|
||||
dispatch(domEvents.touchstart());
|
||||
dispatch(
|
||||
domEvents.contextmenu({
|
||||
button: buttonType.primary,
|
||||
buttons: buttonsType.none,
|
||||
preventDefault,
|
||||
}),
|
||||
);
|
||||
} else if (pointerType === 'mouse') {
|
||||
if (modified === true) {
|
||||
const button = buttonType.primary;
|
||||
const buttons = buttonsType.primary;
|
||||
const ctrlKey = true;
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(
|
||||
domEvents.pointerdown({button, buttons, ctrlKey, pointerType}),
|
||||
);
|
||||
}
|
||||
dispatch(domEvents.mousedown({button, buttons, ctrlKey}));
|
||||
if (platform.get() === 'mac') {
|
||||
dispatch(
|
||||
domEvents.contextmenu({button, buttons, ctrlKey, preventDefault}),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const button = buttonType.secondary;
|
||||
const buttons = buttonsType.secondary;
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerdown({button, buttons, pointerType}));
|
||||
}
|
||||
dispatch(domEvents.mousedown({button, buttons}));
|
||||
dispatch(domEvents.contextmenu({button, buttons, preventDefault}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function pointercancel(target, payload) {
|
||||
const dispatchEvent = arg => target.dispatchEvent(arg);
|
||||
const pointerType = getPointerType(payload);
|
||||
if (hasPointerEvent()) {
|
||||
dispatchEvent(domEvents.pointercancel(payload));
|
||||
} else {
|
||||
if (pointerType === 'mouse') {
|
||||
dispatchEvent(domEvents.dragstart(payload));
|
||||
} else {
|
||||
dispatchEvent(domEvents.touchcancel(payload));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function pointerdown(target, defaultPayload) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
const pointerType = getPointerType(defaultPayload);
|
||||
|
||||
if (Array.isArray(defaultPayload)) {
|
||||
// Arrays are for multi-touch only
|
||||
dispatch(domEvents.touchstart(defaultPayload));
|
||||
} else {
|
||||
const payload = {
|
||||
button: buttonType.primary,
|
||||
buttons: buttonsType.primary,
|
||||
...defaultPayload,
|
||||
};
|
||||
if (pointerType === 'mouse') {
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerover(payload));
|
||||
dispatch(domEvents.pointerenter(payload));
|
||||
}
|
||||
dispatch(domEvents.mouseover(payload));
|
||||
dispatch(domEvents.mouseenter(payload));
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerdown(payload));
|
||||
}
|
||||
dispatch(domEvents.mousedown(payload));
|
||||
if (document.activeElement !== target) {
|
||||
dispatch(domEvents.focus());
|
||||
}
|
||||
} else {
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerover(payload));
|
||||
dispatch(domEvents.pointerenter(payload));
|
||||
dispatch(domEvents.pointerdown(payload));
|
||||
}
|
||||
dispatch(domEvents.touchstart(payload));
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.gotpointercapture(payload));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function pointerenter(target, payload) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerover(payload));
|
||||
dispatch(domEvents.pointerenter(payload));
|
||||
}
|
||||
dispatch(domEvents.mouseover(payload));
|
||||
dispatch(domEvents.mouseenter(payload));
|
||||
}
|
||||
|
||||
export function pointerexit(target, payload) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerout(payload));
|
||||
dispatch(domEvents.pointerleave(payload));
|
||||
}
|
||||
dispatch(domEvents.mouseout(payload));
|
||||
dispatch(domEvents.mouseleave(payload));
|
||||
}
|
||||
|
||||
export function pointerhover(target, payload) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointermove(payload));
|
||||
}
|
||||
dispatch(domEvents.mousemove(payload));
|
||||
}
|
||||
|
||||
export function pointermove(target, payload) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
const pointerType = getPointerType(payload);
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(
|
||||
domEvents.pointermove({
|
||||
pressure: pointerType === 'touch' ? 1 : 0.5,
|
||||
...payload,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
if (pointerType === 'mouse') {
|
||||
dispatch(domEvents.mousemove(payload));
|
||||
} else {
|
||||
dispatch(domEvents.touchmove(payload));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function pointerup(target, payload) {
|
||||
const dispatch = arg => target.dispatchEvent(arg);
|
||||
const pointerType = getPointerType(payload);
|
||||
|
||||
if (Array.isArray(payload)) {
|
||||
// Arrays are for multi-touch only
|
||||
dispatch(domEvents.touchend(payload));
|
||||
} else if (pointerType === 'mouse') {
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerup(payload));
|
||||
}
|
||||
dispatch(domEvents.mouseup(payload));
|
||||
dispatch(domEvents.click(payload));
|
||||
} else {
|
||||
if (hasPointerEvent()) {
|
||||
dispatch(domEvents.pointerup(payload));
|
||||
dispatch(domEvents.lostpointercapture(payload));
|
||||
dispatch(domEvents.pointerout(payload));
|
||||
dispatch(domEvents.pointerleave(payload));
|
||||
}
|
||||
dispatch(domEvents.touchend(payload));
|
||||
dispatch(domEvents.mouseover(payload));
|
||||
dispatch(domEvents.mousemove(payload));
|
||||
dispatch(domEvents.mousedown(payload));
|
||||
if (document.activeElement !== target) {
|
||||
dispatch(domEvents.focus());
|
||||
}
|
||||
dispatch(domEvents.mouseup(payload));
|
||||
dispatch(domEvents.click(payload));
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import {createEventTarget} from 'react-interactions/events/src/dom/testing-library';
|
||||
import {createEventTarget} from 'react-interactions/events/src/dom/event-testing-library';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
Reference in New Issue
Block a user