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

Fix ReactDOMInput-test

Fix
  • Loading branch information
trueadm committed Jul 10, 2020
1 parent d5d6590 commit e8205ae
Show file tree
Hide file tree
Showing 11 changed files with 45 additions and 44 deletions.
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

0 comments on commit e8205ae

Please sign in to comment.