Skip to content

Commit

Permalink
[react-interactions] Add Portal propagation configuration (#16889)
Browse files Browse the repository at this point in the history
  • Loading branch information
trueadm authored Sep 25, 2019
1 parent d0ebde7 commit d6d83d7
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 3 deletions.
10 changes: 7 additions & 3 deletions packages/react-dom/src/events/DOMEventResponderSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
PASSIVE_NOT_SUPPORTED,
} from 'legacy-events/EventSystemFlags';
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
import {HostComponent, ScopeComponent} from 'shared/ReactWorkTags';
import {HostComponent, ScopeComponent, HostPortal} from 'shared/ReactWorkTags';
import type {EventPriority} from 'shared/ReactTypes';
import type {
ReactDOMEventResponder,
Expand Down Expand Up @@ -451,9 +451,12 @@ function traverseAndHandleEventResponderInstances(
isPassiveSupported,
);
let node = targetFiber;
let insidePortal = false;
while (node !== null) {
const {dependencies, tag} = node;
if (
if (tag === HostPortal) {
insidePortal = true;
} else if (
(tag === HostComponent || tag === ScopeComponent) &&
dependencies !== null
) {
Expand All @@ -465,7 +468,8 @@ function traverseAndHandleEventResponderInstances(
const {props, responder, state} = responderInstance;
if (
!visitedResponders.has(responder) &&
validateResponderTargetEventTypes(eventType, responder)
validateResponderTargetEventTypes(eventType, responder) &&
(!insidePortal || responder.targetPortalPropagation)
) {
visitedResponders.add(responder);
const onEvent = responder.onEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function createEventResponder({
onMount,
onUnmount,
getInitialState,
targetPortalPropagation,
}) {
return React.unstable_createResponder('TestEventResponder', {
targetEventTypes,
Expand All @@ -37,6 +38,7 @@ function createEventResponder({
onMount,
onUnmount,
getInitialState,
targetPortalPropagation,
});
}

Expand Down Expand Up @@ -1034,4 +1036,51 @@ describe('DOMEventResponderSystem', () => {
},
]);
});

it('should not propagate target events through portals by default', () => {
const buttonRef = React.createRef();
const onEvent = jest.fn();
const TestResponder = createEventResponder({
targetEventTypes: ['click'],
onEvent,
});
const domNode = document.createElement('div');
document.body.appendChild(domNode);
const Component = () => {
const listener = React.unstable_useResponder(TestResponder, {});
return (
<div listeners={listener}>
{ReactDOM.createPortal(<button ref={buttonRef} />, domNode)}
</div>
);
};
ReactDOM.render(<Component />, container);
dispatchClickEvent(buttonRef.current);
document.body.removeChild(domNode);
expect(onEvent).not.toBeCalled();
});

it('should propagate target events through portals when enabled', () => {
const buttonRef = React.createRef();
const onEvent = jest.fn();
const TestResponder = createEventResponder({
targetPortalPropagation: true,
targetEventTypes: ['click'],
onEvent,
});
const domNode = document.createElement('div');
document.body.appendChild(domNode);
const Component = () => {
const listener = React.unstable_useResponder(TestResponder, {});
return (
<div listeners={listener}>
{ReactDOM.createPortal(<button ref={buttonRef} />, domNode)}
</div>
);
};
ReactDOM.render(<Component />, container);
dispatchClickEvent(buttonRef.current);
document.body.removeChild(domNode);
expect(onEvent).toBeCalled();
});
});
2 changes: 2 additions & 0 deletions packages/react-interactions/events/src/dom/Focus.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ function unmountFocusResponder(

const focusResponderImpl = {
targetEventTypes,
targetPortalPropagation: true,
rootEventTypes,
getInitialState(): FocusState {
return {
Expand Down Expand Up @@ -430,6 +431,7 @@ function unmountFocusWithinResponder(

const focusWithinResponderImpl = {
targetEventTypes,
targetPortalPropagation: true,
rootEventTypes,
getInitialState(): FocusState {
return {
Expand Down
1 change: 1 addition & 0 deletions packages/react-interactions/events/src/dom/Keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ function dispatchKeyboardEvent(

const keyboardResponderImpl = {
targetEventTypes,
targetPortalPropagation: true,
getInitialState(): KeyboardState {
return {
isActive: false,
Expand Down
1 change: 1 addition & 0 deletions packages/shared/ReactTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export type ReactEventResponder<E, C> = {
$$typeof: Symbol | number,
displayName: string,
targetEventTypes: null | Array<string>,
targetPortalPropagation: boolean,
rootEventTypes: null | Array<string>,
getInitialState: null | ((props: Object) => Object),
onEvent:
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/createEventResponder.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default function createEventResponder<E, C>(
onRootEvent,
rootEventTypes,
targetEventTypes,
targetPortalPropagation,
} = responderConfig;
const eventResponder = {
$$typeof: REACT_RESPONDER_TYPE,
Expand All @@ -33,6 +34,7 @@ export default function createEventResponder<E, C>(
onUnmount: onUnmount || null,
rootEventTypes: rootEventTypes || null,
targetEventTypes: targetEventTypes || null,
targetPortalPropagation: targetPortalPropagation || false,
};
// We use responder as a Map key later on. When we have a bad
// polyfill, then we can't use it as a key as the polyfill tries
Expand Down

0 comments on commit d6d83d7

Please sign in to comment.