Skip to content

Commit

Permalink
Modern Event System: use focusin/focusout for onFocus/onBlur
Browse files Browse the repository at this point in the history
Fix test now we use capture phase

Cleanup

Address feedback

Revise
  • Loading branch information
trueadm committed Jul 7, 2020
1 parent cf78f09 commit 671aacc
Show file tree
Hide file tree
Showing 11 changed files with 48 additions and 41 deletions.
8 changes: 8 additions & 0 deletions packages/dom-event-testing-library/domEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ export function blur({relatedTarget} = {}) {
return new FocusEvent('blur', {relatedTarget});
}

export function focusout({relatedTarget} = {}) {
return new FocusEvent('focusout', {relatedTarget, bubbles: true});
}

export function click(payload) {
return createMouseEvent('click', {
button: buttonType.primary,
Expand All @@ -259,6 +263,10 @@ export function focus({relatedTarget} = {}) {
return new FocusEvent('focus', {relatedTarget});
}

export function focusin({relatedTarget} = {}) {
return new FocusEvent('focusin', {relatedTarget, bubbles: true});
}

export function scroll() {
return createEvent('scroll');
}
Expand Down
2 changes: 2 additions & 0 deletions packages/dom-event-testing-library/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ const createEventTarget = node => ({
*/
blur(payload) {
node.dispatchEvent(domEvents.blur(payload));
node.dispatchEvent(domEvents.focusout(payload));
},
click(payload) {
node.dispatchEvent(domEvents.click(payload));
},
focus(payload) {
node.dispatchEvent(domEvents.focus(payload));
node.dispatchEvent(domEvents.focusin(payload));
node.focus();
},
keydown(payload) {
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dom/src/events/DOMEventProperties.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ const eventPriorities = new Map();

// prettier-ignore
const discreteEventPairsForSimpleEventPlugin = [
DOMTopLevelEventTypes.TOP_BLUR, 'blur',
DOMTopLevelEventTypes.TOP_CANCEL, 'cancel',
DOMTopLevelEventTypes.TOP_CLICK, 'click',
DOMTopLevelEventTypes.TOP_CLOSE, 'close',
Expand All @@ -53,7 +52,8 @@ const discreteEventPairsForSimpleEventPlugin = [
DOMTopLevelEventTypes.TOP_DRAG_END, 'dragEnd',
DOMTopLevelEventTypes.TOP_DRAG_START, 'dragStart',
DOMTopLevelEventTypes.TOP_DROP, 'drop',
DOMTopLevelEventTypes.TOP_FOCUS, 'focus',
DOMTopLevelEventTypes.TOP_FOCUS_IN, 'focus',
DOMTopLevelEventTypes.TOP_FOCUS_OUT, 'blur',
DOMTopLevelEventTypes.TOP_INPUT, 'input',
DOMTopLevelEventTypes.TOP_INVALID, 'invalid',
DOMTopLevelEventTypes.TOP_KEY_DOWN, 'keyDown',
Expand Down
10 changes: 3 additions & 7 deletions packages/react-dom/src/events/DOMModernPluginEventSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,10 @@ import {

import getEventTarget from './getEventTarget';
import {
TOP_FOCUS,
TOP_LOAD,
TOP_ABORT,
TOP_CANCEL,
TOP_INVALID,
TOP_BLUR,
TOP_SCROLL,
TOP_CLOSE,
TOP_RESET,
Expand Down Expand Up @@ -179,8 +177,6 @@ function extractEvents(
}

export const capturePhaseEvents: Set<DOMTopLevelEventType> = new Set([
TOP_FOCUS,
TOP_BLUR,
TOP_SCROLL,
TOP_LOAD,
TOP_ABORT,
Expand Down Expand Up @@ -381,7 +377,7 @@ function addTrappedEventListener(
targetContainer: EventTarget,
topLevelType: DOMTopLevelEventType,
eventSystemFlags: EventSystemFlags,
capturePhase: boolean,
capture: boolean,
isDeferredListenerForLegacyFBSupport?: boolean,
passive?: boolean,
priority?: EventPriority,
Expand Down Expand Up @@ -427,12 +423,12 @@ function addTrappedEventListener(
targetContainer,
rawEventName,
unsubscribeListener,
capturePhase,
capture,
);
}
};
}
if (capturePhase) {
if (capture) {
if (enableCreateEventHandleAPI && passive !== undefined) {
unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
targetContainer,
Expand Down
5 changes: 3 additions & 2 deletions packages/react-dom/src/events/DOMTopLevelEventTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export const TOP_ANIMATION_ITERATION = unsafeCastStringToDOMTopLevelType(
export const TOP_ANIMATION_START = unsafeCastStringToDOMTopLevelType(
getVendorPrefixedEventName('animationstart'),
);
export const TOP_BLUR = unsafeCastStringToDOMTopLevelType('blur');
export const TOP_CAN_PLAY = unsafeCastStringToDOMTopLevelType('canplay');
export const TOP_CAN_PLAY_THROUGH = unsafeCastStringToDOMTopLevelType(
'canplaythrough',
Expand Down Expand Up @@ -72,7 +71,6 @@ export const TOP_EMPTIED = unsafeCastStringToDOMTopLevelType('emptied');
export const TOP_ENCRYPTED = unsafeCastStringToDOMTopLevelType('encrypted');
export const TOP_ENDED = unsafeCastStringToDOMTopLevelType('ended');
export const TOP_ERROR = unsafeCastStringToDOMTopLevelType('error');
export const TOP_FOCUS = unsafeCastStringToDOMTopLevelType('focus');
export const TOP_GOT_POINTER_CAPTURE = unsafeCastStringToDOMTopLevelType(
'gotpointercapture',
);
Expand Down Expand Up @@ -152,6 +150,9 @@ export const TOP_WHEEL = unsafeCastStringToDOMTopLevelType('wheel');
export const TOP_AFTER_BLUR = unsafeCastStringToDOMTopLevelType('afterblur');
export const TOP_BEFORE_BLUR = unsafeCastStringToDOMTopLevelType('beforeblur');

export const TOP_FOCUS_IN = unsafeCastStringToDOMTopLevelType('focusin');
export const TOP_FOCUS_OUT = unsafeCastStringToDOMTopLevelType('focusout');

// List of events that need to be individually attached to media elements.
// Note that events in this list will *not* be listened to at the top level
// unless they're explicitly listed in `ReactBrowserEventEmitter.listenTo`.
Expand Down
14 changes: 7 additions & 7 deletions packages/react-dom/src/events/ReactDOMEventReplaying.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ import {
TOP_POINTER_OUT,
TOP_GOT_POINTER_CAPTURE,
TOP_LOST_POINTER_CAPTURE,
TOP_FOCUS,
TOP_BLUR,
TOP_FOCUS_IN,
TOP_FOCUS_OUT,
} from './DOMTopLevelEventTypes';
import {IS_REPLAYED, PLUGIN_EVENT_SYSTEM} from './EventSystemFlags';
import {
Expand Down Expand Up @@ -216,10 +216,10 @@ const discreteReplayableEvents = [
];

const continuousReplayableEvents = [
TOP_FOCUS,
TOP_BLUR,
TOP_DRAG_ENTER,
TOP_DRAG_LEAVE,
TOP_FOCUS_IN,
TOP_FOCUS_OUT,
TOP_MOUSE_OVER,
TOP_MOUSE_OUT,
TOP_POINTER_OVER,
Expand Down Expand Up @@ -362,8 +362,8 @@ export function clearIfContinuousEvent(
nativeEvent: AnyNativeEvent,
): void {
switch (topLevelType) {
case TOP_FOCUS:
case TOP_BLUR:
case TOP_FOCUS_IN:
case TOP_FOCUS_OUT:
queuedFocus = null;
break;
case TOP_DRAG_ENTER:
Expand Down Expand Up @@ -443,7 +443,7 @@ export function queueIfContinuousEvent(
// moved from outside the window (no target) onto the target once it hydrates.
// Instead of mutating we could clone the event.
switch (topLevelType) {
case TOP_FOCUS: {
case TOP_FOCUS_IN: {
const focusEvent = ((nativeEvent: any): FocusEvent);
queuedFocus = accumulateOrCreateContinuousQueuedReplayableEvent(
queuedFocus,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -855,9 +855,9 @@ describe('DOMModernPluginEventSystem', () => {
expect(onFocus).toHaveBeenCalledTimes(3);
expect(onFocusCapture).toHaveBeenCalledTimes(3);
expect(log[2]).toEqual(['capture', buttonElement]);
expect(log[3]).toEqual(['bubble', buttonElement]);
expect(log[4]).toEqual(['capture', divElement]);
expect(log[5]).toEqual(['bubble', divElement]);
expect(log[3]).toEqual(['capture', divElement]);
expect(log[4]).toEqual(['bubble', divElement]);
expect(log[5]).toEqual(['bubble', buttonElement]);
});

it('handle propagation of focus events between portals', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {canUseDOM} from 'shared/ExecutionEnvironment';

import {registerTwoPhaseEvent} from '../EventRegistry';
import {
TOP_BLUR,
TOP_FOCUS_OUT,
TOP_COMPOSITION_START,
TOP_COMPOSITION_END,
TOP_COMPOSITION_UPDATE,
Expand Down Expand Up @@ -66,24 +66,24 @@ function registerEvents() {
TOP_PASTE,
]);
registerTwoPhaseEvent('onCompositionEnd', [
TOP_BLUR,
TOP_COMPOSITION_END,
TOP_FOCUS_OUT,
TOP_KEY_DOWN,
TOP_KEY_PRESS,
TOP_KEY_UP,
TOP_MOUSE_DOWN,
]);
registerTwoPhaseEvent('onCompositionStart', [
TOP_BLUR,
TOP_COMPOSITION_START,
TOP_FOCUS_OUT,
TOP_KEY_DOWN,
TOP_KEY_PRESS,
TOP_KEY_UP,
TOP_MOUSE_DOWN,
]);
registerTwoPhaseEvent('onCompositionUpdate', [
TOP_BLUR,
TOP_COMPOSITION_UPDATE,
TOP_FOCUS_OUT,
TOP_KEY_DOWN,
TOP_KEY_PRESS,
TOP_KEY_UP,
Expand Down Expand Up @@ -154,7 +154,7 @@ function isFallbackCompositionEnd(topLevelType, nativeEvent) {
return nativeEvent.keyCode !== START_KEYCODE;
case TOP_KEY_PRESS:
case TOP_MOUSE_DOWN:
case TOP_BLUR:
case TOP_FOCUS_OUT:
// Events are not possible without cancelling IME.
return true;
default:
Expand Down
14 changes: 7 additions & 7 deletions packages/react-dom/src/events/plugins/ModernChangeEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import isTextInputElement from '../isTextInputElement';
import {canUseDOM} from 'shared/ExecutionEnvironment';

import {
TOP_BLUR,
TOP_FOCUS_OUT,
TOP_CHANGE,
TOP_CLICK,
TOP_FOCUS,
TOP_FOCUS_IN,
TOP_INPUT,
TOP_KEY_DOWN,
TOP_KEY_UP,
Expand All @@ -42,10 +42,10 @@ import {

function registerEvents() {
registerTwoPhaseEvent('onChange', [
TOP_BLUR,
TOP_CHANGE,
TOP_CLICK,
TOP_FOCUS,
TOP_FOCUS_IN,
TOP_FOCUS_OUT,
TOP_INPUT,
TOP_KEY_DOWN,
TOP_KEY_UP,
Expand Down Expand Up @@ -172,7 +172,7 @@ function handlePropertyChange(nativeEvent) {
}

function handleEventsForInputEventPolyfill(topLevelType, target, targetInst) {
if (topLevelType === TOP_FOCUS) {
if (topLevelType === TOP_FOCUS_IN) {
// In IE9, propertychange fires for most input events but is buggy and
// doesn't fire when text is deleted, but conveniently, selectionchange
// appears to fire in all of the remaining cases so we catch those and
Expand All @@ -185,7 +185,7 @@ function handleEventsForInputEventPolyfill(topLevelType, target, targetInst) {
// missed a blur event somehow.
stopWatchingForValueChange();
startWatchingForValueChange(target, targetInst);
} else if (topLevelType === TOP_BLUR) {
} else if (topLevelType === TOP_FOCUS_OUT) {
stopWatchingForValueChange();
}
}
Expand Down Expand Up @@ -304,7 +304,7 @@ function extractEvents(
}

// When blurring, set the value attribute for number inputs
if (topLevelType === TOP_BLUR) {
if (topLevelType === TOP_FOCUS_OUT) {
handleControlledInputBlur(((targetNode: any): HTMLInputElement));
}
}
Expand Down
12 changes: 6 additions & 6 deletions packages/react-dom/src/events/plugins/ModernSelectEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import shallowEqual from 'shared/shallowEqual';

import {registerTwoPhaseEvent} from '../EventRegistry';
import {
TOP_BLUR,
TOP_FOCUS_OUT,
TOP_CONTEXT_MENU,
TOP_DRAG_END,
TOP_FOCUS,
TOP_FOCUS_IN,
TOP_KEY_DOWN,
TOP_KEY_UP,
TOP_MOUSE_DOWN,
Expand All @@ -35,10 +35,10 @@ const skipSelectionChangeEvent =
canUseDOM && 'documentMode' in document && document.documentMode <= 11;

const rootTargetDependencies = [
TOP_BLUR,
TOP_FOCUS_OUT,
TOP_CONTEXT_MENU,
TOP_DRAG_END,
TOP_FOCUS,
TOP_FOCUS_IN,
TOP_KEY_DOWN,
TOP_KEY_UP,
TOP_MOUSE_DOWN,
Expand Down Expand Up @@ -186,7 +186,7 @@ function extractEvents(

switch (topLevelType) {
// Track the input node that has focus.
case TOP_FOCUS:
case TOP_FOCUS_IN:
if (
isTextInputElement(targetNode) ||
targetNode.contentEditable === 'true'
Expand All @@ -196,7 +196,7 @@ function extractEvents(
lastSelection = null;
}
break;
case TOP_BLUR:
case TOP_FOCUS_OUT:
activeElement = null;
activeElementInst = null;
lastSelection = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ function extractEvents(
case DOMTopLevelEventTypes.TOP_KEY_UP:
EventConstructor = SyntheticKeyboardEvent;
break;
case DOMTopLevelEventTypes.TOP_BLUR:
case DOMTopLevelEventTypes.TOP_FOCUS:
case DOMTopLevelEventTypes.TOP_FOCUS_IN:
case DOMTopLevelEventTypes.TOP_FOCUS_OUT:
case DOMTopLevelEventTypes.TOP_BEFORE_BLUR:
case DOMTopLevelEventTypes.TOP_AFTER_BLUR:
EventConstructor = SyntheticFocusEvent;
Expand Down

0 comments on commit 671aacc

Please sign in to comment.