diff --git a/packages/react-events/src/dom/Focus.js b/packages/react-events/src/dom/Focus.js
index 85f9b8455ea82..1e7f5f0d1968e 100644
--- a/packages/react-events/src/dom/Focus.js
+++ b/packages/react-events/src/dom/Focus.js
@@ -33,6 +33,7 @@ type FocusState = {
isFocused: boolean,
isFocusVisible: boolean,
pointerType: PointerType,
+ isEmulatingMouseEvents: boolean,
};
type FocusProps = {
@@ -66,25 +67,12 @@ const isMac =
const targetEventTypes = ['focus', 'blur'];
-const rootEventTypes = [
- 'keydown',
- 'keyup',
- 'pointermove',
- 'pointerdown',
- 'pointerup',
-];
-
-// If PointerEvents is not supported (e.g., Safari), also listen to touch and mouse events.
-if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
- rootEventTypes.push(
- 'mousemove',
- 'mousedown',
- 'mouseup',
- 'touchmove',
- 'touchstart',
- 'touchend',
- );
-}
+const hasPointerEvents =
+ typeof window !== 'undefined' && window.PointerEvent != null;
+
+const rootEventTypes = hasPointerEvents
+ ? ['keydown', 'keyup', 'pointermove', 'pointerdown', 'pointerup']
+ : ['keydown', 'keyup', 'mousedown', 'touchmove', 'touchstart', 'touchend'];
function isFunction(obj): boolean {
return typeof obj === 'function';
@@ -110,13 +98,7 @@ function handleRootPointerEvent(
state: FocusState,
callback: boolean => void,
): void {
- const {type, target} = event;
- // Ignore a Safari quirks where 'mousemove' is dispatched on the 'html'
- // element when the window blurs.
- if (type === 'mousemove' && target.nodeName === 'HTML') {
- return;
- }
-
+ const {type} = event;
isGlobalFocusVisible = false;
// Focus should stop being visible if a pointer is used on the element
@@ -124,7 +106,7 @@ function handleRootPointerEvent(
const focusTarget = state.focusTarget;
if (
focusTarget !== null &&
- context.isTargetWithinNode(event.target, focusTarget) &&
+ context.isTargetWithinResponderScope(focusTarget) &&
(type === 'mousedown' || type === 'touchstart' || type === 'pointerdown')
) {
callback(false);
@@ -140,13 +122,6 @@ function handleRootEvent(
const {type} = event;
switch (type) {
- case 'mousemove':
- case 'mousedown':
- case 'mouseup': {
- state.pointerType = 'mouse';
- handleRootPointerEvent(event, context, state, callback);
- break;
- }
case 'pointermove':
case 'pointerdown':
case 'pointerup': {
@@ -156,27 +131,45 @@ function handleRootEvent(
handleRootPointerEvent(event, context, state, callback);
break;
}
+
+ case 'keydown':
+ case 'keyup': {
+ const nativeEvent = event.nativeEvent;
+ const focusTarget = state.focusTarget;
+ const {key, metaKey, altKey, ctrlKey} = (nativeEvent: any);
+ const validKey =
+ key === 'Enter' ||
+ key === ' ' ||
+ (key === 'Tab' && !(metaKey || (!isMac && altKey) || ctrlKey));
+
+ if (validKey) {
+ state.pointerType = 'keyboard';
+ isGlobalFocusVisible = true;
+ if (
+ focusTarget !== null &&
+ context.isTargetWithinResponderScope(focusTarget)
+ ) {
+ callback(true);
+ }
+ }
+ break;
+ }
+
+ // fallbacks for no PointerEvent support
case 'touchmove':
case 'touchstart':
case 'touchend': {
state.pointerType = 'touch';
+ state.isEmulatingMouseEvents = true;
handleRootPointerEvent(event, context, state, callback);
break;
}
-
- case 'keydown':
- case 'keyup': {
- const nativeEvent = event.nativeEvent;
- if (
- nativeEvent.key === 'Tab' &&
- !(
- nativeEvent.metaKey ||
- (!isMac && nativeEvent.altKey) ||
- nativeEvent.ctrlKey
- )
- ) {
- state.pointerType = 'keyboard';
- isGlobalFocusVisible = true;
+ case 'mousedown': {
+ if (!state.isEmulatingMouseEvents) {
+ state.pointerType = 'mouse';
+ handleRootPointerEvent(event, context, state, callback);
+ } else {
+ state.isEmulatingMouseEvents = false;
}
break;
}
@@ -271,6 +264,7 @@ const focusResponderImpl = {
getInitialState(): FocusState {
return {
focusTarget: null,
+ isEmulatingMouseEvents: false,
isFocused: false,
isFocusVisible: false,
pointerType: '',
@@ -303,6 +297,7 @@ const focusResponderImpl = {
state.isFocusVisible = isGlobalFocusVisible;
dispatchFocusEvents(context, props, state);
}
+ state.isEmulatingMouseEvents = false;
break;
}
case 'blur': {
@@ -311,6 +306,17 @@ const focusResponderImpl = {
state.isFocusVisible = isGlobalFocusVisible;
state.isFocused = false;
}
+ // This covers situations where focus is lost to another document in
+ // the same window (e.g., iframes). Any action that restores focus to
+ // the document (e.g., touch or click) first causes 'focus' to be
+ // dispatched, which means the 'pointerType' we provide is stale
+ // (it reflects the *previous* pointer). We cannot determine the
+ // 'pointerType' in this case, so a blur with no
+ // relatedTarget is used as a signal to reset the 'pointerType'.
+ if (event.nativeEvent.relatedTarget == null) {
+ state.pointerType = '';
+ }
+ state.isEmulatingMouseEvents = false;
break;
}
}
@@ -322,7 +328,7 @@ const focusResponderImpl = {
state: FocusState,
): void {
handleRootEvent(event, context, state, isFocusVisible => {
- if (state.isFocusVisible !== isFocusVisible) {
+ if (state.isFocused && state.isFocusVisible !== isFocusVisible) {
state.isFocusVisible = isFocusVisible;
dispatchFocusVisibleChangeEvent(context, props, isFocusVisible);
}
@@ -402,6 +408,7 @@ const focusWithinResponderImpl = {
getInitialState(): FocusState {
return {
focusTarget: null,
+ isEmulatingMouseEvents: false,
isFocused: false,
isFocusVisible: false,
pointerType: '',
@@ -460,7 +467,7 @@ const focusWithinResponderImpl = {
state: FocusState,
): void {
handleRootEvent(event, context, state, isFocusVisible => {
- if (state.isFocusVisible !== isFocusVisible) {
+ if (state.isFocused && state.isFocusVisible !== isFocusVisible) {
state.isFocusVisible = isFocusVisible;
dispatchFocusWithinVisibleChangeEvent(
context,
diff --git a/packages/react-events/src/dom/__tests__/ContextMenu-test.internal.js b/packages/react-events/src/dom/__tests__/ContextMenu-test.internal.js
index 4e03d897bf82d..bf2fb0f3e3a7a 100644
--- a/packages/react-events/src/dom/__tests__/ContextMenu-test.internal.js
+++ b/packages/react-events/src/dom/__tests__/ContextMenu-test.internal.js
@@ -9,7 +9,13 @@
'use strict';
-import {createEvent, platform, setPointerEvent} from '../test-utils';
+import {
+ dispatchLongPressContextMenu,
+ dispatchRightClickContextMenu,
+ dispatchModifiedClickContextMenu,
+ platform,
+ setPointerEvent,
+} from '../test-utils';
let React;
let ReactFeatureFlags;
@@ -27,44 +33,6 @@ function initializeModules(hasPointerEvents) {
.useContextMenuResponder;
}
-function dispatchContextMenuEvents(ref, options) {
- const preventDefault = options.preventDefault || function() {};
- const variant = (options.variant: 'mouse' | 'touch' | 'modified');
- const dispatchEvent = arg => ref.current.dispatchEvent(arg);
-
- if (variant === 'mouse') {
- // right-click
- dispatchEvent(
- createEvent('pointerdown', {pointerType: 'mouse', button: 2}),
- );
- dispatchEvent(createEvent('mousedown', {button: 2}));
- dispatchEvent(createEvent('contextmenu', {button: 2, preventDefault}));
- } else if (variant === 'modified') {
- // left-click + ctrl
- dispatchEvent(
- createEvent('pointerdown', {pointerType: 'mouse', button: 0}),
- );
- dispatchEvent(createEvent('mousedown', {button: 0}));
- if (platform.get() === 'mac') {
- dispatchEvent(
- createEvent('contextmenu', {button: 0, ctrlKey: true, preventDefault}),
- );
- }
- } else if (variant === 'touch') {
- // long-press
- dispatchEvent(
- createEvent('pointerdown', {pointerType: 'touch', button: 0}),
- );
- dispatchEvent(
- createEvent('touchstart', {
- changedTouches: [],
- targetTouches: [],
- }),
- );
- dispatchEvent(createEvent('contextmenu', {button: 0, preventDefault}));
- }
-}
-
const forcePointerEvents = true;
const table = [[forcePointerEvents], [!forcePointerEvents]];
@@ -94,7 +62,7 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
};
ReactDOM.render(, container);
- dispatchContextMenuEvents(ref, {variant: 'mouse', preventDefault});
+ dispatchRightClickContextMenu(ref.current, {preventDefault});
expect(preventDefault).toHaveBeenCalledTimes(1);
expect(onContextMenu).toHaveBeenCalledTimes(1);
expect(onContextMenu).toHaveBeenCalledWith(
@@ -112,7 +80,7 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
};
ReactDOM.render(, container);
- dispatchContextMenuEvents(ref, {variant: 'touch', preventDefault});
+ dispatchLongPressContextMenu(ref.current, {preventDefault});
expect(preventDefault).toHaveBeenCalledTimes(1);
expect(onContextMenu).toHaveBeenCalledTimes(1);
expect(onContextMenu).toHaveBeenCalledWith(
@@ -132,7 +100,7 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
};
ReactDOM.render(, container);
- dispatchContextMenuEvents(ref, 'mouse');
+ dispatchRightClickContextMenu(ref.current);
expect(onContextMenu).toHaveBeenCalledTimes(0);
});
@@ -149,7 +117,7 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
};
ReactDOM.render(, container);
- dispatchContextMenuEvents(ref, {variant: 'mouse', preventDefault});
+ dispatchRightClickContextMenu(ref.current, {preventDefault});
expect(preventDefault).toHaveBeenCalledTimes(0);
expect(onContextMenu).toHaveBeenCalledTimes(1);
});
@@ -174,7 +142,7 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
};
ReactDOM.render(, container);
- dispatchContextMenuEvents(ref, {variant: 'modified'});
+ dispatchModifiedClickContextMenu(ref.current);
expect(onContextMenu).toHaveBeenCalledTimes(1);
expect(onContextMenu).toHaveBeenCalledWith(
expect.objectContaining({pointerType: 'mouse', type: 'contextmenu'}),
@@ -201,7 +169,7 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
};
ReactDOM.render(, container);
- dispatchContextMenuEvents(ref, {variant: 'modified'});
+ dispatchModifiedClickContextMenu(ref.current);
expect(onContextMenu).toHaveBeenCalledTimes(0);
});
});
diff --git a/packages/react-events/src/dom/__tests__/Focus-test.internal.js b/packages/react-events/src/dom/__tests__/Focus-test.internal.js
index 3fca82bb70523..a432f64091cfb 100644
--- a/packages/react-events/src/dom/__tests__/Focus-test.internal.js
+++ b/packages/react-events/src/dom/__tests__/Focus-test.internal.js
@@ -9,47 +9,41 @@
'use strict';
+import {
+ blur,
+ focus,
+ keydown,
+ setPointerEvent,
+ platform,
+ dispatchPointerPressDown,
+ dispatchPointerPressRelease,
+} from '../test-utils';
+
let React;
let ReactFeatureFlags;
let ReactDOM;
let FocusResponder;
let useFocusResponder;
-const createEvent = (type, data) => {
- const event = document.createEvent('CustomEvent');
- event.initCustomEvent(type, true, true);
- if (data != null) {
- Object.entries(data).forEach(([key, value]) => {
- event[key] = value;
- });
- }
- return event;
-};
-
-const createKeyboardEvent = (type, data) => {
- return new KeyboardEvent(type, {
- bubbles: true,
- cancelable: true,
- ...data,
- });
-};
-
-const modulesInit = () => {
+function initializeModules(hasPointerEvents) {
+ setPointerEvent(hasPointerEvents);
+ jest.resetModules();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.enableFlareAPI = true;
React = require('react');
ReactDOM = require('react-dom');
FocusResponder = require('react-events/focus').FocusResponder;
useFocusResponder = require('react-events/focus').useFocusResponder;
-};
+}
+
+const forcePointerEvents = true;
+const table = [[forcePointerEvents], [!forcePointerEvents]];
-describe('Focus event responder', () => {
+describe.each(table)('Focus responder', hasPointerEvents => {
let container;
beforeEach(() => {
- jest.resetModules();
- modulesInit();
-
+ initializeModules(hasPointerEvents);
container = document.createElement('div');
document.body.appendChild(container);
});
@@ -78,9 +72,10 @@ describe('Focus event responder', () => {
ReactDOM.render(, container);
});
- it('prevents custom events being dispatched', () => {
- ref.current.dispatchEvent(createEvent('focus'));
- ref.current.dispatchEvent(createEvent('blur'));
+ it('does not call callbacks', () => {
+ const dispatch = arg => ref.current.dispatchEvent(arg);
+ dispatch(focus());
+ dispatch(blur());
expect(onFocus).not.toBeCalled();
expect(onBlur).not.toBeCalled();
});
@@ -102,8 +97,9 @@ describe('Focus event responder', () => {
});
it('is called after "blur" event', () => {
- ref.current.dispatchEvent(createEvent('focus'));
- ref.current.dispatchEvent(createEvent('blur'));
+ const dispatch = arg => ref.current.dispatchEvent(arg);
+ dispatch(focus());
+ dispatch(blur());
expect(onBlur).toHaveBeenCalledTimes(1);
});
});
@@ -131,83 +127,52 @@ describe('Focus event responder', () => {
beforeEach(componentInit);
it('is called after "focus" event', () => {
- ref.current.dispatchEvent(createEvent('focus'));
+ ref.current.dispatchEvent(focus());
expect(onFocus).toHaveBeenCalledTimes(1);
});
it('is not called if descendants of target receive focus', () => {
- const target = innerRef.current;
- target.dispatchEvent(createEvent('focus'));
+ innerRef.current.dispatchEvent(focus());
expect(onFocus).not.toBeCalled();
});
- it('is called with the correct pointerType using pointer events', () => {
- // Pointer mouse
- ref.current.dispatchEvent(
- createEvent('pointerdown', {
- pointerType: 'mouse',
- }),
- );
- ref.current.dispatchEvent(createEvent('focus'));
+ it('is called with the correct pointerType: mouse', () => {
+ const target = ref.current;
+ dispatchPointerPressDown(target, {pointerType: 'mouse'});
+ dispatchPointerPressRelease(target, {pointerType: 'mouse'});
expect(onFocus).toHaveBeenCalledTimes(1);
expect(onFocus).toHaveBeenCalledWith(
expect.objectContaining({pointerType: 'mouse'}),
);
- ref.current.dispatchEvent(createEvent('blur'));
-
- // Pointer touch
- ref.current.dispatchEvent(
- createEvent('pointerdown', {
- pointerType: 'touch',
- }),
- );
- ref.current.dispatchEvent(createEvent('focus'));
- expect(onFocus).toHaveBeenCalledTimes(2);
- expect(onFocus).toHaveBeenCalledWith(
- expect.objectContaining({pointerType: 'touch'}),
- );
- ref.current.dispatchEvent(createEvent('blur'));
-
- // Pointer pen
- ref.current.dispatchEvent(
- createEvent('pointerdown', {
- pointerType: 'pen',
- }),
- );
- ref.current.dispatchEvent(createEvent('focus'));
- expect(onFocus).toHaveBeenCalledTimes(3);
- expect(onFocus).toHaveBeenCalledWith(
- expect.objectContaining({pointerType: 'pen'}),
- );
});
- it('is called with the correct pointerType without pointer events', () => {
- // Mouse
- ref.current.dispatchEvent(createEvent('mousedown'));
- ref.current.dispatchEvent(createEvent('focus'));
+ it('is called with the correct pointerType: touch', () => {
+ const target = ref.current;
+ dispatchPointerPressDown(target, {pointerType: 'touch'});
+ dispatchPointerPressRelease(target, {pointerType: 'touch'});
expect(onFocus).toHaveBeenCalledTimes(1);
- expect(onFocus).toHaveBeenCalledWith(
- expect.objectContaining({pointerType: 'mouse'}),
- );
- ref.current.dispatchEvent(createEvent('blur'));
-
- // Touch
- ref.current.dispatchEvent(createEvent('touchstart'));
- ref.current.dispatchEvent(createEvent('focus'));
- expect(onFocus).toHaveBeenCalledTimes(2);
expect(onFocus).toHaveBeenCalledWith(
expect.objectContaining({pointerType: 'touch'}),
);
});
+ if (hasPointerEvents) {
+ it('is called with the correct pointerType: pen', () => {
+ const target = ref.current;
+ dispatchPointerPressDown(target, {pointerType: 'pen'});
+ dispatchPointerPressRelease(target, {pointerType: 'pen'});
+ expect(onFocus).toHaveBeenCalledTimes(1);
+ expect(onFocus).toHaveBeenCalledWith(
+ expect.objectContaining({pointerType: 'pen'}),
+ );
+ });
+ }
+
it('is called with the correct pointerType using a keyboard', () => {
+ const target = ref.current;
// Keyboard tab
- ref.current.dispatchEvent(
- createEvent('keydown', {
- key: 'Tab',
- }),
- );
- ref.current.dispatchEvent(createEvent('focus'));
+ target.dispatchEvent(keydown({key: 'Tab'}));
+ target.dispatchEvent(focus());
expect(onFocus).toHaveBeenCalledTimes(1);
expect(onFocus).toHaveBeenCalledWith(
expect.objectContaining({pointerType: 'keyboard'}),
@@ -215,19 +180,14 @@ describe('Focus event responder', () => {
});
it('is called with the correct pointerType using Tab+altKey on Mac', () => {
+ platform.set('mac');
jest.resetModules();
- const platformGetter = jest.spyOn(global.navigator, 'platform', 'get');
- platformGetter.mockReturnValue('MacIntel');
- modulesInit();
+ initializeModules();
componentInit();
+ const target = ref.current;
- ref.current.dispatchEvent(
- createEvent('keydown', {
- key: 'Tab',
- altKey: true,
- }),
- );
- ref.current.dispatchEvent(createEvent('focus'));
+ target.dispatchEvent(keydown({key: 'Tab', altKey: true}));
+ target.dispatchEvent(focus());
expect(onFocus).toHaveBeenCalledTimes(1);
expect(onFocus).toHaveBeenCalledWith(
expect.objectContaining({
@@ -235,7 +195,7 @@ describe('Focus event responder', () => {
}),
);
- platformGetter.mockClear();
+ platform.clear();
});
});
@@ -260,18 +220,20 @@ describe('Focus event responder', () => {
});
it('is called after "blur" and "focus" events', () => {
- ref.current.dispatchEvent(createEvent('focus'));
+ const target = ref.current;
+ target.dispatchEvent(focus());
expect(onFocusChange).toHaveBeenCalledTimes(1);
expect(onFocusChange).toHaveBeenCalledWith(true);
- ref.current.dispatchEvent(createEvent('blur'));
+ target.dispatchEvent(blur());
expect(onFocusChange).toHaveBeenCalledTimes(2);
expect(onFocusChange).toHaveBeenCalledWith(false);
});
it('is not called after "blur" and "focus" events on descendants', () => {
- innerRef.current.dispatchEvent(createEvent('focus'));
+ const target = innerRef.current;
+ target.dispatchEvent(focus());
expect(onFocusChange).toHaveBeenCalledTimes(0);
- innerRef.current.dispatchEvent(createEvent('blur'));
+ target.dispatchEvent(blur());
expect(onFocusChange).toHaveBeenCalledTimes(0);
});
});
@@ -297,52 +259,48 @@ describe('Focus event responder', () => {
});
it('is called after "focus" and "blur" if keyboard navigation is active', () => {
+ const target = ref.current;
// use keyboard first
- container.dispatchEvent(createKeyboardEvent('keydown', {key: 'Tab'}));
- ref.current.dispatchEvent(createEvent('focus'));
+ container.dispatchEvent(keydown({key: 'Tab'}));
+ target.dispatchEvent(focus());
expect(onFocusVisibleChange).toHaveBeenCalledTimes(1);
expect(onFocusVisibleChange).toHaveBeenCalledWith(true);
- ref.current.dispatchEvent(
- createEvent('blur', {relatedTarget: container}),
- );
+ target.dispatchEvent(blur({relatedTarget: container}));
expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
expect(onFocusVisibleChange).toHaveBeenCalledWith(false);
});
it('is called if non-keyboard event is dispatched on target previously focused with keyboard', () => {
+ const target = ref.current;
// use keyboard first
- container.dispatchEvent(createKeyboardEvent('keydown', {key: 'Tab'}));
- ref.current.dispatchEvent(createEvent('focus'));
+ container.dispatchEvent(keydown({key: 'Tab'}));
+ target.dispatchEvent(focus());
expect(onFocusVisibleChange).toHaveBeenCalledTimes(1);
expect(onFocusVisibleChange).toHaveBeenCalledWith(true);
// then use pointer on the target, focus should no longer be visible
- ref.current.dispatchEvent(createEvent('pointerdown'));
+ dispatchPointerPressDown(target);
expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
expect(onFocusVisibleChange).toHaveBeenCalledWith(false);
// onFocusVisibleChange should not be called again
- ref.current.dispatchEvent(
- createEvent('blur', {relatedTarget: container}),
- );
+ target.dispatchEvent(blur({relatedTarget: container}));
expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
});
it('is not called after "focus" and "blur" events without keyboard', () => {
- ref.current.dispatchEvent(createEvent('pointerdown'));
- ref.current.dispatchEvent(createEvent('focus'));
- container.dispatchEvent(createEvent('pointerdown'));
- ref.current.dispatchEvent(
- createEvent('blur', {relatedTarget: container}),
- );
+ const target = ref.current;
+ dispatchPointerPressDown(target);
+ dispatchPointerPressRelease(target);
+ dispatchPointerPressDown(container);
+ target.dispatchEvent(blur({relatedTarget: container}));
expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
});
it('is not called after "blur" and "focus" events on descendants', () => {
- container.dispatchEvent(createKeyboardEvent('keydown', {key: 'Tab'}));
- innerRef.current.dispatchEvent(createEvent('focus'));
+ const target = innerRef.current;
+ container.dispatchEvent(keydown({key: 'Tab'}));
+ target.dispatchEvent(focus());
expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
- innerRef.current.dispatchEvent(
- createEvent('blur', {relatedTarget: container}),
- );
+ target.dispatchEvent(blur({relatedTarget: container}));
expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
});
});
@@ -380,10 +338,10 @@ describe('Focus event responder', () => {
ReactDOM.render(, container);
- outerRef.current.dispatchEvent(createEvent('focus'));
- outerRef.current.dispatchEvent(createEvent('blur'));
- innerRef.current.dispatchEvent(createEvent('focus'));
- innerRef.current.dispatchEvent(createEvent('blur'));
+ outerRef.current.dispatchEvent(focus());
+ outerRef.current.dispatchEvent(blur());
+ innerRef.current.dispatchEvent(focus());
+ innerRef.current.dispatchEvent(blur());
expect(events).toEqual([
'outer: onFocus',
'outer: onFocusChange',
diff --git a/packages/react-events/src/dom/__tests__/FocusWithin-test.internal.js b/packages/react-events/src/dom/__tests__/FocusWithin-test.internal.js
index 5de66686fd65f..e2340a21f669f 100644
--- a/packages/react-events/src/dom/__tests__/FocusWithin-test.internal.js
+++ b/packages/react-events/src/dom/__tests__/FocusWithin-test.internal.js
@@ -9,32 +9,24 @@
'use strict';
+import {
+ blur,
+ focus,
+ keydown,
+ setPointerEvent,
+ dispatchPointerPressDown,
+ dispatchPointerPressRelease,
+} from '../test-utils';
+
let React;
let ReactFeatureFlags;
let ReactDOM;
let FocusWithinResponder;
let useFocusWithinResponder;
-const createEvent = (type, data) => {
- const event = document.createEvent('CustomEvent');
- event.initCustomEvent(type, true, true);
- if (data != null) {
- Object.entries(data).forEach(([key, value]) => {
- event[key] = value;
- });
- }
- return event;
-};
-
-const createKeyboardEvent = (type, data) => {
- return new KeyboardEvent(type, {
- bubbles: true,
- cancelable: true,
- ...data,
- });
-};
-
-const modulesInit = () => {
+const initializeModules = hasPointerEvents => {
+ setPointerEvent(hasPointerEvents);
+ jest.resetModules();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.enableFlareAPI = true;
React = require('react');
@@ -44,13 +36,14 @@ const modulesInit = () => {
.useFocusWithinResponder;
};
-describe('FocusWithin event responder', () => {
+const forcePointerEvents = true;
+const table = [[forcePointerEvents], [!forcePointerEvents]];
+
+describe.each(table)('FocusWithin responder', hasPointerEvents => {
let container;
beforeEach(() => {
- jest.resetModules();
- modulesInit();
-
+ initializeModules();
container = document.createElement('div');
document.body.appendChild(container);
});
@@ -80,8 +73,9 @@ describe('FocusWithin event responder', () => {
});
it('prevents custom events being dispatched', () => {
- ref.current.dispatchEvent(createEvent('focus'));
- ref.current.dispatchEvent(createEvent('blur'));
+ const target = ref.current;
+ target.dispatchEvent(focus());
+ target.dispatchEvent(blur());
expect(onFocusWithinChange).not.toBeCalled();
expect(onFocusWithinVisibleChange).not.toBeCalled();
});
@@ -110,49 +104,42 @@ describe('FocusWithin event responder', () => {
});
it('is called after "blur" and "focus" events on focus target', () => {
- ref.current.dispatchEvent(createEvent('focus'));
+ const target = ref.current;
+ target.dispatchEvent(focus());
expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinChange).toHaveBeenCalledWith(true);
- ref.current.dispatchEvent(
- createEvent('blur', {relatedTarget: container}),
- );
+ target.dispatchEvent(blur({relatedTarget: container}));
expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinChange).toHaveBeenCalledWith(false);
});
it('is called after "blur" and "focus" events on descendants', () => {
- innerRef.current.dispatchEvent(createEvent('focus'));
+ const target = innerRef.current;
+ target.dispatchEvent(focus());
expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinChange).toHaveBeenCalledWith(true);
- innerRef.current.dispatchEvent(
- createEvent('blur', {relatedTarget: container}),
- );
+ target.dispatchEvent(blur({relatedTarget: container}));
expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinChange).toHaveBeenCalledWith(false);
});
it('is only called once when focus moves within and outside the subtree', () => {
+ const target = ref.current;
+ const innerTarget1 = innerRef.current;
+ const innerTarget2 = innerRef2.current;
// focus shifts into subtree
- innerRef.current.dispatchEvent(createEvent('focus'));
+ innerTarget1.dispatchEvent(focus());
expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinChange).toHaveBeenCalledWith(true);
// focus moves around subtree
- innerRef.current.dispatchEvent(
- createEvent('blur', {relatedTarget: innerRef2.current}),
- );
- innerRef2.current.dispatchEvent(createEvent('focus'));
- innerRef2.current.dispatchEvent(
- createEvent('blur', {relatedTarget: ref.current}),
- );
- ref.current.dispatchEvent(createEvent('focus'));
- ref.current.dispatchEvent(
- createEvent('blur', {relatedTarget: innerRef.current}),
- );
+ innerTarget1.dispatchEvent(blur({relatedTarget: innerTarget2}));
+ innerTarget2.dispatchEvent(focus());
+ innerTarget2.dispatchEvent(blur({relatedTarget: target}));
+ target.dispatchEvent(focus());
+ target.dispatchEvent(blur({relatedTarget: innerTarget1}));
expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
// focus shifts outside subtree
- innerRef.current.dispatchEvent(
- createEvent('blur', {relatedTarget: container}),
- );
+ innerTarget1.dispatchEvent(blur({relatedTarget: container}));
expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinChange).toHaveBeenCalledWith(false);
});
@@ -181,102 +168,87 @@ describe('FocusWithin event responder', () => {
});
it('is called after "focus" and "blur" on focus target if keyboard was used', () => {
+ const target = ref.current;
// use keyboard first
- container.dispatchEvent(createKeyboardEvent('keydown', {key: 'Tab'}));
- ref.current.dispatchEvent(createEvent('focus'));
+ container.dispatchEvent(keydown({key: 'Tab'}));
+ target.dispatchEvent(focus());
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
- ref.current.dispatchEvent(
- createEvent('blur', {relatedTarget: container}),
- );
+ target.dispatchEvent(blur({relatedTarget: container}));
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
});
it('is called after "focus" and "blur" on descendants if keyboard was used', () => {
+ const innerTarget = innerRef.current;
// use keyboard first
- container.dispatchEvent(createKeyboardEvent('keydown', {key: 'Tab'}));
- innerRef.current.dispatchEvent(createEvent('focus'));
+ container.dispatchEvent(keydown({key: 'Tab'}));
+ innerTarget.dispatchEvent(focus());
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
- innerRef.current.dispatchEvent(
- createEvent('blur', {relatedTarget: container}),
- );
+ innerTarget.dispatchEvent(blur({relatedTarget: container}));
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
});
it('is called if non-keyboard event is dispatched on target previously focused with keyboard', () => {
+ const target = ref.current;
+ const innerTarget1 = innerRef.current;
+ const innerTarget2 = innerRef2.current;
// use keyboard first
- ref.current.dispatchEvent(createEvent('focus'));
- ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'Tab'}));
- ref.current.dispatchEvent(
- createEvent('blur', {relatedTarget: innerRef.current}),
- );
- innerRef.current.dispatchEvent(createEvent('focus'));
+ target.dispatchEvent(focus());
+ target.dispatchEvent(keydown({key: 'Tab'}));
+ target.dispatchEvent(blur({relatedTarget: innerTarget1}));
+ innerTarget1.dispatchEvent(focus());
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
// then use pointer on the next target, focus should no longer be visible
- innerRef2.current.dispatchEvent(createEvent('pointerdown'));
- innerRef.current.dispatchEvent(
- createEvent('blur', {relatedTarget: innerRef2.current}),
- );
- innerRef2.current.dispatchEvent(createEvent('focus'));
+ dispatchPointerPressDown(innerTarget2);
+ innerTarget1.dispatchEvent(blur({relatedTarget: innerTarget2}));
+ innerTarget2.dispatchEvent(focus());
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
// then use keyboard again
- innerRef2.current.dispatchEvent(
- createKeyboardEvent('keydown', {key: 'Tab', shiftKey: true}),
- );
- innerRef2.current.dispatchEvent(
- createEvent('blur', {relatedTarget: innerRef.current}),
- );
- innerRef.current.dispatchEvent(createEvent('focus'));
+ innerTarget2.dispatchEvent(keydown({key: 'Tab', shiftKey: true}));
+ innerTarget2.dispatchEvent(blur({relatedTarget: innerTarget1}));
+ innerTarget1.dispatchEvent(focus());
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(3);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
// then use pointer on the target, focus should no longer be visible
- innerRef.current.dispatchEvent(createEvent('pointerdown'));
+ dispatchPointerPressDown(innerTarget1);
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
// onFocusVisibleChange should not be called again
- innerRef.current.dispatchEvent(
- createEvent('blur', {relatedTarget: container}),
- );
+ innerTarget1.dispatchEvent(blur({relatedTarget: container}));
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
});
it('is not called after "focus" and "blur" events without keyboard', () => {
- innerRef.current.dispatchEvent(createEvent('pointerdown'));
- innerRef.current.dispatchEvent(createEvent('focus'));
- container.dispatchEvent(createEvent('pointerdown'));
- innerRef.current.dispatchEvent(
- createEvent('blur', {relatedTarget: container}),
- );
+ const innerTarget = innerRef.current;
+ dispatchPointerPressDown(innerTarget);
+ dispatchPointerPressRelease(innerTarget);
+ innerTarget.dispatchEvent(blur({relatedTarget: container}));
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(0);
});
it('is only called once when focus moves within and outside the subtree', () => {
+ const target = ref.current;
+ const innerTarget1 = innerRef.current;
+ const innerTarget2 = innerRef2.current;
+
// focus shifts into subtree
- innerRef.current.dispatchEvent(createEvent('focus'));
+ innerTarget1.dispatchEvent(focus());
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
// focus moves around subtree
- innerRef.current.dispatchEvent(
- createEvent('blur', {relatedTarget: innerRef2.current}),
- );
- innerRef2.current.dispatchEvent(createEvent('focus'));
- innerRef2.current.dispatchEvent(
- createEvent('blur', {relatedTarget: ref.current}),
- );
- ref.current.dispatchEvent(createEvent('focus'));
- ref.current.dispatchEvent(
- createEvent('blur', {relatedTarget: innerRef.current}),
- );
+ innerTarget1.dispatchEvent(blur({relatedTarget: innerTarget2}));
+ innerTarget2.dispatchEvent(focus());
+ innerTarget2.dispatchEvent(blur({relatedTarget: target}));
+ target.dispatchEvent(focus());
+ target.dispatchEvent(blur({relatedTarget: innerTarget1}));
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
// focus shifts outside subtree
- innerRef.current.dispatchEvent(
- createEvent('blur', {relatedTarget: container}),
- );
+ innerTarget1.dispatchEvent(blur({relatedTarget: container}));
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
});
diff --git a/packages/react-events/src/dom/__tests__/Hover-test.internal.js b/packages/react-events/src/dom/__tests__/Hover-test.internal.js
index 73d5106e6fbab..eb9eab90c3029 100644
--- a/packages/react-events/src/dom/__tests__/Hover-test.internal.js
+++ b/packages/react-events/src/dom/__tests__/Hover-test.internal.js
@@ -76,9 +76,10 @@ describe.each(table)('Hover responder', hasPointerEvents => {
ReactDOM.render(, container);
});
- it('prevents custom events being dispatched', () => {
- dispatchPointerHoverEnter(ref);
- dispatchPointerHoverExit(ref);
+ it('does not call callbacks', () => {
+ const target = ref.current;
+ dispatchPointerHoverEnter(target);
+ dispatchPointerHoverExit(target);
expect(onHoverChange).not.toBeCalled();
expect(onHoverStart).not.toBeCalled();
expect(onHoverMove).not.toBeCalled();
@@ -102,18 +103,21 @@ describe.each(table)('Hover responder', hasPointerEvents => {
});
it('is called for mouse pointers', () => {
- dispatchPointerHoverEnter(ref);
+ const target = ref.current;
+ dispatchPointerHoverEnter(target);
expect(onHoverStart).toHaveBeenCalledTimes(1);
});
it('is not called for touch pointers', () => {
- dispatchTouchTap(ref);
+ const target = ref.current;
+ dispatchTouchTap(target);
expect(onHoverStart).not.toBeCalled();
});
it('is called if a mouse pointer is used after a touch pointer', () => {
- dispatchTouchTap(ref);
- dispatchPointerHoverEnter(ref);
+ const target = ref.current;
+ dispatchTouchTap(target);
+ dispatchPointerHoverEnter(target);
expect(onHoverStart).toHaveBeenCalledTimes(1);
});
});
@@ -134,16 +138,18 @@ describe.each(table)('Hover responder', hasPointerEvents => {
});
it('is called for mouse pointers', () => {
- dispatchPointerHoverEnter(ref);
+ const target = ref.current;
+ dispatchPointerHoverEnter(target);
expect(onHoverChange).toHaveBeenCalledTimes(1);
expect(onHoverChange).toHaveBeenCalledWith(true);
- dispatchPointerHoverExit(ref);
+ dispatchPointerHoverExit(target);
expect(onHoverChange).toHaveBeenCalledTimes(2);
expect(onHoverChange).toHaveBeenCalledWith(false);
});
it('is not called for touch pointers', () => {
- dispatchTouchTap(ref);
+ const target = ref.current;
+ dispatchTouchTap(target);
expect(onHoverChange).not.toBeCalled();
});
});
@@ -164,28 +170,31 @@ describe.each(table)('Hover responder', hasPointerEvents => {
});
it('is called for mouse pointers', () => {
- dispatchPointerHoverEnter(ref);
- dispatchPointerHoverExit(ref);
+ const target = ref.current;
+ dispatchPointerHoverEnter(target);
+ dispatchPointerHoverExit(target);
expect(onHoverEnd).toHaveBeenCalledTimes(1);
});
if (hasPointerEvents) {
it('is called once for cancelled mouse pointers', () => {
- dispatchPointerHoverEnter(ref);
- dispatchPointerCancel(ref);
+ const target = ref.current;
+ dispatchPointerHoverEnter(target);
+ dispatchPointerCancel(target);
expect(onHoverEnd).toHaveBeenCalledTimes(1);
// only called once if cancel follows exit
onHoverEnd.mockReset();
- dispatchPointerHoverEnter(ref);
- dispatchPointerHoverExit(ref);
- dispatchPointerCancel(ref);
+ dispatchPointerHoverEnter(target);
+ dispatchPointerHoverExit(target);
+ dispatchPointerCancel(target);
expect(onHoverEnd).toHaveBeenCalledTimes(1);
});
}
it('is not called for touch pointers', () => {
- dispatchTouchTap(ref);
+ const target = ref.current;
+ dispatchTouchTap(target);
expect(onHoverEnd).not.toBeCalled();
});
});
@@ -201,8 +210,10 @@ describe.each(table)('Hover responder', hasPointerEvents => {
return
;
};
ReactDOM.render(, container);
- dispatchPointerHoverEnter(ref);
- dispatchPointerHoverMove(ref, {from: {x: 0, y: 0}, to: {x: 1, y: 1}});
+
+ const target = ref.current;
+ dispatchPointerHoverEnter(target);
+ dispatchPointerHoverMove(target, {from: {x: 0, y: 0}, to: {x: 1, y: 1}});
expect(onHoverMove).toHaveBeenCalledTimes(2);
expect(onHoverMove).toHaveBeenCalledWith(
expect.objectContaining({type: 'hovermove'}),
@@ -242,12 +253,15 @@ describe.each(table)('Hover responder', hasPointerEvents => {
};
ReactDOM.render(, container);
- dispatchPointerHoverEnter(outerRef, {relatedTarget: container});
- dispatchPointerHoverExit(outerRef, {relatedTarget: innerRef.current});
- dispatchPointerHoverEnter(innerRef, {relatedTarget: outerRef.current});
- dispatchPointerHoverExit(innerRef, {relatedTarget: outerRef.current});
- dispatchPointerHoverEnter(outerRef, {relatedTarget: innerRef.current});
- dispatchPointerHoverExit(outerRef, {relatedTarget: container});
+ const innerTarget = innerRef.current;
+ const outerTarget = outerRef.current;
+
+ dispatchPointerHoverEnter(outerTarget, {relatedTarget: container});
+ dispatchPointerHoverExit(outerTarget, {relatedTarget: innerTarget});
+ dispatchPointerHoverEnter(innerTarget, {relatedTarget: outerTarget});
+ dispatchPointerHoverExit(innerTarget, {relatedTarget: outerTarget});
+ dispatchPointerHoverEnter(outerTarget, {relatedTarget: innerTarget});
+ dispatchPointerHoverExit(outerTarget, {relatedTarget: container});
expect(events).toEqual([
'outer: onHoverStart',
@@ -300,9 +314,14 @@ describe.each(table)('Hover responder', hasPointerEvents => {
};
ReactDOM.render(, container);
- dispatchPointerHoverEnter(ref, {x: 10, y: 10});
- dispatchPointerHoverMove(ref, {from: {x: 10, y: 10}, to: {x: 20, y: 20}});
- dispatchPointerHoverExit(ref, {x: 20, y: 20});
+ const target = ref.current;
+
+ dispatchPointerHoverEnter(target, {x: 10, y: 10});
+ dispatchPointerHoverMove(target, {
+ from: {x: 10, y: 10},
+ to: {x: 20, y: 20},
+ });
+ dispatchPointerHoverExit(target, {x: 20, y: 20});
expect(eventLog).toEqual([
{
@@ -312,7 +331,7 @@ describe.each(table)('Hover responder', hasPointerEvents => {
pageY: 10,
clientX: 10,
clientY: 10,
- target: ref.current,
+ target,
timeStamp: timeStamps[0],
type: 'hoverstart',
pointerType: 'mouse',
@@ -324,7 +343,7 @@ describe.each(table)('Hover responder', hasPointerEvents => {
pageY: 10,
clientX: 10,
clientY: 10,
- target: ref.current,
+ target,
timeStamp: timeStamps[1],
type: 'hovermove',
pointerType: 'mouse',
@@ -336,7 +355,7 @@ describe.each(table)('Hover responder', hasPointerEvents => {
pageY: 20,
clientX: 20,
clientY: 20,
- target: ref.current,
+ target,
timeStamp: timeStamps[2],
type: 'hovermove',
pointerType: 'mouse',
@@ -348,7 +367,7 @@ describe.each(table)('Hover responder', hasPointerEvents => {
pageY: 20,
clientX: 20,
clientY: 20,
- target: ref.current,
+ target,
timeStamp: timeStamps[3],
type: 'hoverend',
pointerType: 'mouse',
diff --git a/packages/react-events/src/dom/test-utils.js b/packages/react-events/src/dom/test-utils.js
index ddf6bf6773a1f..826ac219ce58f 100644
--- a/packages/react-events/src/dom/test-utils.js
+++ b/packages/react-events/src/dom/test-utils.js
@@ -95,6 +95,10 @@ function click(data) {
return createEvent('click', data);
}
+function contextmenu(data) {
+ return createEvent('contextmenu', data);
+}
+
function dragstart(data) {
return createEvent('dragstart', data);
}
@@ -107,6 +111,14 @@ function gotpointercapture(data) {
return createEvent('gotpointercapture', data);
}
+function keydown(data) {
+ return createKeyboardEvent('keydown', data);
+}
+
+function keyup(data) {
+ return createKeyboardEvent('keyup', data);
+}
+
function lostpointercapture(data) {
return createEvent('lostpointercapture', data);
}
@@ -191,8 +203,52 @@ function touchstart(data, id) {
* Dispatch high-level event sequences
*/
-function dispatchPointerHoverEnter(ref, {relatedTarget, x, y} = {}) {
- const dispatch = arg => ref.current.dispatchEvent(arg);
+function emptyFunction() {}
+
+function dispatchLongPressContextMenu(
+ target,
+ {preventDefault = emptyFunction} = {},
+) {
+ const dispatch = arg => target.dispatchEvent(arg);
+ const button = 0;
+ if (hasPointerEvent()) {
+ dispatch(pointerdown({button, pointerType: 'touch'}));
+ }
+ dispatch(touchstart());
+ dispatch(contextmenu({button, preventDefault}));
+}
+
+function dispatchRightClickContextMenu(
+ target,
+ {preventDefault = emptyFunction} = {},
+) {
+ const dispatch = arg => target.dispatchEvent(arg);
+ const button = 2;
+ if (hasPointerEvent()) {
+ dispatch(pointerdown({button, pointerType: 'mouse'}));
+ }
+ dispatch(mousedown({button}));
+ dispatch(contextmenu({button, preventDefault}));
+}
+
+function dispatchModifiedClickContextMenu(
+ target,
+ {preventDefault = emptyFunction} = {},
+) {
+ const dispatch = arg => target.dispatchEvent(arg);
+ const button = 0;
+ const ctrlKey = true;
+ if (hasPointerEvent()) {
+ dispatch(pointerdown({button, ctrlKey, pointerType: 'mouse'}));
+ }
+ dispatch(mousedown({button, ctrlKey}));
+ if (platform.get() === 'mac') {
+ dispatch(contextmenu({button, ctrlKey, preventDefault}));
+ }
+}
+
+function dispatchPointerHoverEnter(target, {relatedTarget, x, y} = {}) {
+ const dispatch = arg => target.dispatchEvent(arg);
const button = -1;
const pointerType = 'mouse';
const event = {
@@ -211,8 +267,8 @@ function dispatchPointerHoverEnter(ref, {relatedTarget, x, y} = {}) {
dispatch(mouseover(event));
}
-function dispatchPointerHoverMove(ref, {from, to} = {}) {
- const dispatch = arg => ref.current.dispatchEvent(arg);
+function dispatchPointerHoverMove(target, {from, to} = {}) {
+ const dispatch = arg => target.dispatchEvent(arg);
const button = -1;
const pointerId = 1;
const pointerType = 'mouse';
@@ -233,8 +289,8 @@ function dispatchPointerHoverMove(ref, {from, to} = {}) {
dispatchMove({x: to.x, y: to.y});
}
-function dispatchPointerHoverExit(ref, {relatedTarget, x, y} = {}) {
- const dispatch = arg => ref.current.dispatchEvent(arg);
+function dispatchPointerHoverExit(target, {relatedTarget, x, y} = {}) {
+ const dispatch = arg => target.dispatchEvent(arg);
const button = -1;
const pointerType = 'mouse';
const event = {
@@ -253,51 +309,82 @@ function dispatchPointerHoverExit(ref, {relatedTarget, x, y} = {}) {
dispatch(mouseleave(event));
}
-function dispatchPointerCancel(ref, options) {
- const dispatchEvent = arg => ref.current.dispatchEvent(arg);
+function dispatchPointerCancel(target, options) {
+ const dispatchEvent = arg => target.dispatchEvent(arg);
dispatchEvent(pointercancel({pointerType: 'mouse'}));
dispatchEvent(dragstart({pointerType: 'mouse'}));
}
-function dispatchPointerPressDown(ref, {button = 0, pointerType = 'mouse'}) {
- const dispatch = arg => ref.current.dispatchEvent(arg);
+function dispatchPointerPressDown(
+ target,
+ {button = 0, pointerType = 'mouse'} = {},
+) {
+ const dispatch = arg => target.dispatchEvent(arg);
const pointerId = 1;
- if (hasPointerEvent()) {
- dispatch(pointerover({pointerId, pointerType, button}));
- dispatch(pointerenter({pointerId, pointerType, button}));
- dispatch(pointerdown({pointerId, pointerType, button}));
- }
- dispatch(touchstart(null, pointerId));
- if (hasPointerEvent()) {
- dispatch(gotpointercapture({pointerId, pointerType, button}));
+ if (pointerType !== 'mouse') {
+ if (hasPointerEvent()) {
+ dispatch(pointerover({button, pointerId, pointerType}));
+ dispatch(pointerenter({button, pointerId, pointerType}));
+ dispatch(pointerdown({button, pointerId, pointerType}));
+ }
+ dispatch(touchstart(null, pointerId));
+ if (hasPointerEvent()) {
+ dispatch(gotpointercapture({button, pointerId, pointerType}));
+ }
+ } else {
+ if (hasPointerEvent()) {
+ dispatch(pointerdown({button, pointerId, pointerType}));
+ }
+ dispatch(mousedown({button}));
+ if (document.activeElement !== target) {
+ dispatch(focus({button}));
+ }
}
}
-function dispatchPointerPressRelease(ref, {button = 0, pointerType = 'mouse'}) {
- const dispatch = arg => ref.current.dispatchEvent(arg);
+function dispatchPointerPressRelease(
+ target,
+ {button = 0, pointerType = 'mouse'} = {},
+) {
+ const dispatch = arg => target.dispatchEvent(arg);
const pointerId = 1;
- if (hasPointerEvent()) {
- dispatch(pointerup({pointerId, pointerType, button}));
- dispatch(lostpointercapture({pointerId, pointerType, button}));
- dispatch(pointerout({pointerId, pointerType, button}));
- dispatch(pointerleave({pointerId, pointerType, button}));
+ if (pointerType !== 'mouse') {
+ if (hasPointerEvent()) {
+ dispatch(pointerup({button, pointerId, pointerType}));
+ dispatch(lostpointercapture({button, pointerId, pointerType}));
+ dispatch(pointerout({button, pointerId, pointerType}));
+ dispatch(pointerleave({button, pointerId, pointerType}));
+ }
+ dispatch(touchend(null, pointerId));
+ dispatch(mouseover({button}));
+ dispatch(mousemove({button}));
+ dispatch(mousedown({button}));
+ if (document.activeElement !== target) {
+ dispatch(focus({button}));
+ }
+ dispatch(mouseup({button}));
+ dispatch(click({button}));
+ } else {
+ if (hasPointerEvent()) {
+ dispatch(pointerup({button, pointerId, pointerType}));
+ }
+ dispatch(mouseup({button}));
+ dispatch(click({button}));
}
- dispatch(touchend(null, pointerId));
- dispatch(mouseover({button}));
- dispatch(mousemove({button}));
- dispatch(mousedown({button}));
- dispatch(focus({button}));
- dispatch(mouseup({button}));
- dispatch(click({button}));
}
-function dispatchTouchTap(ref) {
- dispatchPointerPressDown(ref, {pointerType: 'touch'});
- dispatchPointerPressRelease(ref, {pointerType: 'touch'});
+function dispatchTouchTap(target) {
+ dispatchPointerPressDown(target, {pointerType: 'touch'});
+ dispatchPointerPressRelease(target, {pointerType: 'touch'});
}
module.exports = {
+ blur,
+ focus,
createEvent,
+ dispatchLongPressContextMenu,
+ dispatchRightClickContextMenu,
+ dispatchModifiedClickContextMenu,
dispatchPointerCancel,
dispatchPointerHoverEnter,
dispatchPointerHoverExit,
@@ -305,6 +392,8 @@ module.exports = {
dispatchPointerPressDown,
dispatchPointerPressRelease,
dispatchTouchTap,
+ keydown,
+ keyup,
platform,
hasPointerEvent,
setPointerEvent,