Skip to content

Commit

Permalink
ReactDOM.useEvent: add support for beforeblur/afterblur (#18370)
Browse files Browse the repository at this point in the history
* ReactDOM.useEvent: add support for beforeblur/afterblur
  • Loading branch information
trueadm authored Mar 24, 2020
1 parent a56309f commit 0140118
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 25 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
"test-persistent": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source-persistent.js",
"debug-test-persistent": "cross-env NODE_ENV=development node --inspect-brk node_modules/jest/bin/jest.js --config ./scripts/jest/config.source-persistent.js --runInBand",
"test-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.source.js",
"debug-test-prod": "cross-env NODE_ENV=production node --inspect-brk node_modules/jest/bin/jest.js --config ./scripts/jest/config.source.js --runInBand",
"test-prod-build": "yarn test-build-prod",
"test-build": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.build.js",
"test-build-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.build.js",
Expand Down
82 changes: 58 additions & 24 deletions packages/react-dom/src/client/ReactDOMHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* @flow
*/

import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
import type {RootType} from './ReactDOMRoot';

import {
Expand Down Expand Up @@ -84,6 +85,7 @@ import {
attachTargetEventListener,
} from '../events/DOMModernPluginEventSystem';
import {getListenerMapForElement} from '../events/DOMEventListenerMap';
import {TOP_BEFORE_BLUR, TOP_AFTER_BLUR} from '../events/DOMTopLevelEventTypes';

export type ReactListenerEvent = ReactDOMListenerEvent;
export type ReactListenerMap = ReactDOMListenerMap;
Expand Down Expand Up @@ -238,11 +240,11 @@ export function resetAfterCommit(containerInfo: Container): void {
restoreSelection(selectionInformation);
ReactBrowserEventEmitterSetEnabled(eventsEnabled);
eventsEnabled = null;
if (enableDeprecatedFlareAPI) {
if (enableDeprecatedFlareAPI || enableUseEventAPI) {
const activeElementDetached = (selectionInformation: any)
.activeElementDetached;
if (activeElementDetached !== null) {
dispatchDetachedBlur(activeElementDetached);
dispatchAfterDetachedBlur(activeElementDetached);
}
}
selectionInformation = null;
Expand Down Expand Up @@ -490,34 +492,66 @@ export function insertInContainerBefore(
}
}

function createEvent(type: TopLevelType): Event {
const event = document.createEvent('Event');
event.initEvent(((type: any): string), false, false);
return event;
}

function dispatchBeforeDetachedBlur(target: HTMLElement): void {
const targetInstance = getClosestInstanceFromNode(target);
((selectionInformation: any): SelectionInformation).activeElementDetached = target;

DEPRECATED_dispatchEventForResponderEventSystem(
'beforeblur',
targetInstance,
({
if (enableDeprecatedFlareAPI) {
DEPRECATED_dispatchEventForResponderEventSystem(
'beforeblur',
targetInstance,
({
target,
timeStamp: Date.now(),
}: any),
target,
timeStamp: Date.now(),
}: any),
target,
RESPONDER_EVENT_SYSTEM | IS_PASSIVE,
);
RESPONDER_EVENT_SYSTEM | IS_PASSIVE,
);
}
if (enableUseEventAPI) {
try {
// We need to temporarily enable the event system
// to dispatch the "beforeblur" event.
ReactBrowserEventEmitterSetEnabled(true);
const event = createEvent(TOP_BEFORE_BLUR);
// Dispatch "beforeblur" directly on the target,
// so it gets picked up by the event system and
// can propagate through the React internal tree.
target.dispatchEvent(event);
} finally {
ReactBrowserEventEmitterSetEnabled(false);
}
}
}

function dispatchDetachedBlur(target: HTMLElement): void {
DEPRECATED_dispatchEventForResponderEventSystem(
'blur',
null,
({
isTargetAttached: false,
function dispatchAfterDetachedBlur(target: HTMLElement): void {
if (enableDeprecatedFlareAPI) {
DEPRECATED_dispatchEventForResponderEventSystem(
'blur',
null,
({
isTargetAttached: false,
target,
timeStamp: Date.now(),
}: any),
target,
timeStamp: Date.now(),
}: any),
target,
RESPONDER_EVENT_SYSTEM | IS_PASSIVE,
);
RESPONDER_EVENT_SYSTEM | IS_PASSIVE,
);
}
if (enableUseEventAPI) {
const event = createEvent(TOP_AFTER_BLUR);
// So we know what was detached, make the relatedTarget the
// detached target on the "afterblur" event.
(event: any).relatedTarget = target;
// Dispatch the event on the document.
document.dispatchEvent(event);
}
}

// This is a specific event for the React Flare
Expand All @@ -528,7 +562,7 @@ export function beforeRemoveInstance(
instance: Instance | TextInstance | SuspenseInstance,
): void {
if (
enableDeprecatedFlareAPI &&
(enableDeprecatedFlareAPI || enableUseEventAPI) &&
selectionInformation &&
instance === selectionInformation.focusedElem
) {
Expand Down Expand Up @@ -639,7 +673,7 @@ export function hideInstance(instance: Instance): void {
// is ether the instance of a child or the instance. We need
// to traverse the Fiber tree here rather than use node.contains()
// as the child node might be inside a Portal.
if (enableDeprecatedFlareAPI && selectionInformation) {
if ((enableDeprecatedFlareAPI || enableUseEventAPI) && selectionInformation) {
const focusedElem = selectionInformation.focusedElem;
if (focusedElem !== null && instanceContainsElem(instance, focusedElem)) {
dispatchBeforeDetachedBlur(((focusedElem: any): HTMLElement));
Expand Down
8 changes: 8 additions & 0 deletions packages/react-dom/src/events/DOMEventProperties.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
UserBlockingEvent,
ContinuousEvent,
} from 'shared/ReactTypes';
import {enableUseEventAPI} from 'shared/ReactFeatureFlags';

// Needed for SimpleEventPlugin, rather than
// do it in two places, which duplicates logic
Expand Down Expand Up @@ -95,6 +96,13 @@ const otherDiscreteEvents = [
DOMTopLevelEventTypes.TOP_COMPOSITION_UPDATE,
];

if (enableUseEventAPI) {
otherDiscreteEvents.push(
DOMTopLevelEventTypes.TOP_BEFORE_BLUR,
DOMTopLevelEventTypes.TOP_AFTER_BLUR,
);
}

// prettier-ignore
const userBlockingPairsForSimpleEventPlugin = [
DOMTopLevelEventTypes.TOP_DRAG, 'drag',
Expand Down
12 changes: 11 additions & 1 deletion packages/react-dom/src/events/DOMModernPluginEventSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ import {
TOP_PROGRESS,
TOP_PLAYING,
TOP_CLICK,
TOP_BEFORE_BLUR,
TOP_AFTER_BLUR,
} from './DOMTopLevelEventTypes';
import {
getClosestInstanceFromNode,
Expand All @@ -84,7 +86,10 @@ import {
import {COMMENT_NODE} from '../shared/HTMLNodeType';
import {topLevelEventsToDispatchConfig} from './DOMEventProperties';

import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags';
import {
enableLegacyFBSupport,
enableUseEventAPI,
} from 'shared/ReactFeatureFlags';

const capturePhaseEvents = new Set([
TOP_FOCUS,
Expand Down Expand Up @@ -122,6 +127,11 @@ const capturePhaseEvents = new Set([
TOP_WAITING,
]);

if (enableUseEventAPI) {
capturePhaseEvents.add(TOP_BEFORE_BLUR);
capturePhaseEvents.add(TOP_AFTER_BLUR);
}

const emptyDispatchConfigForCustomEvents: CustomDispatchConfig = {
customEvent: true,
phasedRegistrationNames: {
Expand Down
3 changes: 3 additions & 0 deletions packages/react-dom/src/events/DOMTopLevelEventTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ export const TOP_VOLUME_CHANGE = unsafeCastStringToDOMTopLevelType(
export const TOP_WAITING = unsafeCastStringToDOMTopLevelType('waiting');
export const TOP_WHEEL = unsafeCastStringToDOMTopLevelType('wheel');

export const TOP_AFTER_BLUR = unsafeCastStringToDOMTopLevelType('afterblur');
export const TOP_BEFORE_BLUR = unsafeCastStringToDOMTopLevelType('beforeblur');

// 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 whitelisted in `ReactBrowserEventEmitter.listenTo`.
Expand Down
2 changes: 2 additions & 0 deletions packages/react-dom/src/events/SimpleEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
break;
case DOMTopLevelEventTypes.TOP_BLUR:
case DOMTopLevelEventTypes.TOP_FOCUS:
case DOMTopLevelEventTypes.TOP_BEFORE_BLUR:
case DOMTopLevelEventTypes.TOP_AFTER_BLUR:
EventConstructor = SyntheticFocusEvent;
break;
case DOMTopLevelEventTypes.TOP_CLICK:
Expand Down
Loading

0 comments on commit 0140118

Please sign in to comment.