Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modern Event System: use focusin/focusout for onFocus/onBlur #19186

Merged
merged 1 commit into from
Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/react-dom/src/__tests__/ReactDOMInput-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ describe('ReactDOMInput', () => {
// bypass the lazy event attachment system so we won't actually test this.
dispatchEventOnNode(instance.a, 'input');
dispatchEventOnNode(instance.a, 'blur');
dispatchEventOnNode(instance.a, 'focusout');

expect(instance.a.value).toBe('giraffe');
expect(instance.switchedFocus).toBe(true);
Expand Down Expand Up @@ -684,6 +685,7 @@ describe('ReactDOMInput', () => {
expect(node.hasAttribute('value')).toBe(false);
} else {
dispatchEventOnNode(node, 'blur');
dispatchEventOnNode(node, 'focusout');

expect(node.value).toBe('0.0');
expect(node.getAttribute('value')).toBe('0.0');
Expand Down Expand Up @@ -1771,6 +1773,7 @@ describe('ReactDOMInput', () => {
// be the only way to remove focus in JSDOM
node.blur();
dispatchEventOnNode(node, 'blur');
dispatchEventOnNode(node, 'focusout');

if (disableInputAttributeSyncing) {
expect(node.value).toBe('2');
Expand All @@ -1796,6 +1799,7 @@ describe('ReactDOMInput', () => {
// be the only way to remove focus in JSDOM
node.blur();
dispatchEventOnNode(node, 'blur');
dispatchEventOnNode(node, 'focusout');

expect(node.getAttribute('value')).toBe('1');
});
Expand All @@ -1815,6 +1819,7 @@ describe('ReactDOMInput', () => {
// be the only way to remove focus in JSDOM
node.blur();
dispatchEventOnNode(node, 'blur');
dispatchEventOnNode(node, 'focusout');

expect(node.getAttribute('value')).toBe('1');
});
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
4 changes: 0 additions & 4 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 @@ -207,8 +205,6 @@ function extractEvents(
}

export const capturePhaseEvents: Set<DOMTopLevelEventType> = new Set([
TOP_FOCUS,
TOP_BLUR,
TOP_SCROLL,
TOP_LOAD,
TOP_ABORT,
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
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ function handleGlobalFocusVisibleEvent(
}

const passiveObject = {passive: true};
const passiveObjectWithPriority = {passive: true, priority: 0};

function handleFocusVisibleTargetEvent(
type: string,
Expand Down Expand Up @@ -243,8 +242,8 @@ export function useFocus(
): void {
// Setup controlled state for this useFocus hook
const stateRef = useRef({isFocused: false, isFocusVisible: false});
const focusHandle = useEvent('focusin', passiveObjectWithPriority);
const blurHandle = useEvent('focusout', passiveObjectWithPriority);
const focusHandle = useEvent('focusin', passiveObject);
const blurHandle = useEvent('focusout', passiveObject);
const focusVisibleHandles = useFocusVisibleInputHandles();

useEffect(() => {
Expand Down Expand Up @@ -334,8 +333,8 @@ export function useFocusWithin(
const stateRef = useRef<null | {isFocused: boolean, isFocusVisible: boolean}>(
{isFocused: false, isFocusVisible: false},
);
const focusHandle = useEvent('focusin', passiveObjectWithPriority);
const blurHandle = useEvent('focusout', passiveObjectWithPriority);
const focusHandle = useEvent('focusin', passiveObject);
const blurHandle = useEvent('focusout', passiveObject);
const afterBlurHandle = useEvent('afterblur', passiveObject);
const beforeBlurHandle = useEvent('beforeblur', passiveObject);
const focusVisibleHandles = useFocusVisibleInputHandles();
Expand Down Expand Up @@ -397,7 +396,7 @@ export function useFocusWithin(
if (disabled) {
return;
}
const {relatedTarget} = (event.nativeEvent: any);
const {relatedTarget} = (event: any);

if (
state.isFocused &&
Expand Down