Modern Event System: register onMouseEnter for portals (#18720)
Dominic Gannaway committed
Apr 23, 2020 at 20:57 UTC
30cee2f4c7eb9e4097f4dc55555f4682f79f2f1d
10 files changed
+63
packages/react-art/src/ReactARTHostConfig.js
+4
@@ -500,3 +500,7 @@ export function beforeActiveInstanceBlur() {
500
export function afterActiveInstanceBlur() {
501
// noop
502
}
503
+
504
+export function preparePortalMount(portalInstance: any): void {
505
+ // noop
506
+}
packages/react-dom/src/client/ReactDOMHostConfig.js
+8
@@ -64,8 +64,10 @@ import {
64
enableSuspenseServerRenderer,
65
enableDeprecatedFlareAPI,
66
enableFundamentalAPI,
67
+ enableModernEventSystem,
68
} from 'shared/ReactFeatureFlags';
69
import {TOP_BEFORE_BLUR, TOP_AFTER_BLUR} from '../events/DOMTopLevelEventTypes';
70
+import {listenToEvent} from '../events/DOMModernPluginEventSystem';
71
72
export type Type = string;
73
export type Props = {
@@ -1098,3 +1100,9 @@ export function makeOpaqueHydratingObject(
1100
valueOf: attemptToReadValue,
1101
};
1102
}
1103
+
1104
+export function preparePortalMount(portalInstance: Instance): void {
1105
+ if (enableModernEventSystem) {
1106
+ listenToEvent('onMouseEnter', portalInstance);
1107
+ }
1108
+}
packages/react-dom/src/events/plugins/__tests__/ModernEnterLeaveEventPlugin-test.internal.js
+26
@@ -239,4 +239,30 @@ describe('EnterLeaveEventPlugin', () => {
239
240
ReactDOM.render(<Parent />, container);
241
});
242
+
243
+ it('should work with portals outside of the root', () => {
244
+ const divRef = React.createRef();
245
+ const onMouseLeave = jest.fn();
246
+
247
+ function Component() {
248
+ return (
249
+ <div onMouseLeave={onMouseLeave}>
250
+ {ReactDOM.createPortal(<div ref={divRef} />, document.body)}
251
+ </div>
252
+ );
253
+ }
254
+
255
+ ReactDOM.render(<Component />, container);
256
+
257
+ // Leave from the portal div
258
+ divRef.current.dispatchEvent(
259
+ new MouseEvent('mouseout', {
260
+ bubbles: true,
261
+ cancelable: true,
262
+ relatedTarget: document.body,
263
+ }),
264
+ );
265
+
266
+ expect(onMouseLeave).toHaveBeenCalledTimes(1);
267
+ });
268
});
packages/react-native-renderer/src/ReactFabricHostConfig.js
+4
@@ -514,3 +514,7 @@ export function beforeActiveInstanceBlur() {
514
export function afterActiveInstanceBlur() {
515
// noop
516
}
517
+
518
+export function preparePortalMount(portalInstance: Instance): void {
519
+ // noop
520
+}
packages/react-native-renderer/src/ReactNativeHostConfig.js
+4
@@ -562,3 +562,7 @@ export function beforeActiveInstanceBlur() {
562
export function afterActiveInstanceBlur() {
563
// noop
564
}
565
+
566
+export function preparePortalMount(portalInstance: Instance): void {
567
+ // noop
568
+}
packages/react-noop-renderer/src/createReactNoop.js
+4
@@ -449,6 +449,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
449
afterActiveInstanceBlur() {
450
// NO-OP
451
},
452
+
453
+ preparePortalMount() {
454
+ // NO-OP
455
+ },
456
};
457
458
const hostConfig = useMutation
packages/react-reconciler/src/ReactFiberCompleteWork.new.js
+4
@@ -82,6 +82,7 @@ import {
82
mountFundamentalComponent,
83
cloneFundamentalInstance,
84
shouldUpdateFundamentalComponent,
85
+ preparePortalMount,
86
} from './ReactFiberHostConfig';
87
import {
88
getRootHostContainer,
@@ -973,6 +974,9 @@ function completeWork(
974
case HostPortal:
975
popHostContainer(workInProgress);
976
updateHostContainer(workInProgress);
977
+ if (current === null) {
978
+ preparePortalMount(workInProgress.stateNode.containerInfo);
979
+ }
980
return null;
981
case ContextProvider:
982
// Pop provider fiber
packages/react-reconciler/src/ReactFiberCompleteWork.old.js
+4
@@ -82,6 +82,7 @@ import {
82
mountFundamentalComponent,
83
cloneFundamentalInstance,
84
shouldUpdateFundamentalComponent,
85
+ preparePortalMount,
86
} from './ReactFiberHostConfig';
87
import {
88
getRootHostContainer,
@@ -973,6 +974,9 @@ function completeWork(
974
case HostPortal:
975
popHostContainer(workInProgress);
976
updateHostContainer(workInProgress);
977
+ if (current === null) {
978
+ preparePortalMount(workInProgress.stateNode.containerInfo);
979
+ }
980
return null;
981
case ContextProvider:
982
// Pop provider fiber
packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js
+1
@@ -83,6 +83,7 @@ export const makeClientIdInDEV = $$$hostConfig.makeClientIdInDEV;
83
export const makeServerId = $$$hostConfig.makeServerId;
84
export const beforeActiveInstanceBlur = $$$hostConfig.beforeActiveInstanceBlur;
85
export const afterActiveInstanceBlur = $$$hostConfig.afterActiveInstanceBlur;
86
+export const preparePortalMount = $$$hostConfig.preparePortalMount;
87
88
// -------------------
89
// Mutation
packages/react-test-renderer/src/ReactTestHostConfig.js
+4
@@ -435,3 +435,7 @@ export function beforeActiveInstanceBlur() {
435
export function afterActiveInstanceBlur() {
436
// noop
437
}
438
+
439
+export function preparePortalMount(portalInstance: Instance): void {
440
+ // noop
441
+}