From 577d313b906218299b04041a0ee36bc4c5640a0e Mon Sep 17 00:00:00 2001 From: Dylan Cooke Date: Thu, 27 Apr 2023 19:34:07 -0400 Subject: [PATCH 1/2] Add stop propagation events to portal --- .../core/src/components/overlay/overlay.tsx | 13 +++++++- .../core/src/components/portal/portal.tsx | 30 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/packages/core/src/components/overlay/overlay.tsx b/packages/core/src/components/overlay/overlay.tsx index 97568ac81b..94af5acdae 100644 --- a/packages/core/src/components/overlay/overlay.tsx +++ b/packages/core/src/components/overlay/overlay.tsx @@ -109,6 +109,13 @@ export interface IOverlayableProps extends IOverlayLifecycleProps { */ portalContainer?: HTMLElement; + /** + * A list of DOM events to pass to the portal to stop propagation on. + * This prop is ignored if `usePortal` is `false`. + * Stopgap resolution for https://github.com/facebook/react/issues/11387 + */ + stopPropagationEvents?: Array; + /** * A callback that is invoked when user interaction causes the overlay to close, such as * clicking on the overlay or pressing the `esc` key (if enabled). @@ -318,7 +325,11 @@ export class Overlay extends AbstractPureComponent2 ); if (usePortal) { return ( - + {transitionGroup} ); diff --git a/packages/core/src/components/portal/portal.tsx b/packages/core/src/components/portal/portal.tsx index 1d6880ef1c..4fb15deb61 100644 --- a/packages/core/src/components/portal/portal.tsx +++ b/packages/core/src/components/portal/portal.tsx @@ -40,6 +40,12 @@ export interface IPortalProps extends Props { * @default document.body */ container?: HTMLElement; + + /** + * A list of DOM events to stop propagation on. + * Stopgap resolution for https://github.com/facebook/react/issues/11387 + */ + stopPropagationEvents?: Array; } export interface IPortalState { @@ -103,6 +109,7 @@ export class Portal extends React.Component { } this.portalElement = this.createContainerElement(); this.props.container.appendChild(this.portalElement); + this.addEventListeners(this.props.stopPropagationEvents); /* eslint-disable-next-line react/no-did-mount-set-state */ this.setState({ hasMounted: true }, this.props.onChildrenMount); } @@ -113,9 +120,14 @@ export class Portal extends React.Component { maybeRemoveClass(this.portalElement.classList, prevProps.className); maybeAddClass(this.portalElement.classList, this.props.className); } + if(this.portalElement != null && prevProps.stopPropagationEvents !== this.props.stopPropagationEvents) { + this.removeEventListeners(prevProps.stopPropagationEvents); + this.addEventListeners(this.props.stopPropagationEvents); + } } public componentWillUnmount() { + this.removeEventListeners(this.props.stopPropagationEvents); this.portalElement?.remove(); } @@ -128,6 +140,20 @@ export class Portal extends React.Component { } return container; } + + private addEventListeners(events?: Array) { + if (events == null || events.length === 0) { + return; + } + events.map(event => this.portalElement?.addEventListener(event, handleStopProgation)); + } + + private removeEventListeners(events?: Array) { + if (events == null || events.length === 0) { + return; + } + events.map(event => this.portalElement?.removeEventListener(event, handleStopProgation)); + } } function maybeRemoveClass(classList: DOMTokenList, className?: string) { @@ -141,3 +167,7 @@ function maybeAddClass(classList: DOMTokenList, className?: string) { classList.add(...className.split(" ")); } } + +function handleStopProgation(e: Event) { + e.stopPropagation(); +} From 8624de502bbdcaf7fda2517e29e8682bfef257d2 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Thu, 4 May 2023 13:35:35 -0400 Subject: [PATCH 2/2] fix format, adjust prop name --- .../core/src/components/overlay/overlay.tsx | 10 +++--- .../core/src/components/portal/portal.tsx | 31 +++++++++---------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/packages/core/src/components/overlay/overlay.tsx b/packages/core/src/components/overlay/overlay.tsx index 94af5acdae..838283fb08 100644 --- a/packages/core/src/components/overlay/overlay.tsx +++ b/packages/core/src/components/overlay/overlay.tsx @@ -110,11 +110,13 @@ export interface IOverlayableProps extends IOverlayLifecycleProps { portalContainer?: HTMLElement; /** - * A list of DOM events to pass to the portal to stop propagation on. + * A list of DOM events which should be stopped from propagating through the Portal. * This prop is ignored if `usePortal` is `false`. - * Stopgap resolution for https://github.com/facebook/react/issues/11387 + * + * @see https://legacy.reactjs.org/docs/portals.html#event-bubbling-through-portals + * @see https://github.com/palantir/blueprint/issues/6124 */ - stopPropagationEvents?: Array; + portalStopPropagationEvents?: Array; /** * A callback that is invoked when user interaction causes the overlay to close, such as @@ -328,7 +330,7 @@ export class Overlay extends AbstractPureComponent2 {transitionGroup} diff --git a/packages/core/src/components/portal/portal.tsx b/packages/core/src/components/portal/portal.tsx index 4fb15deb61..ed11cc8c97 100644 --- a/packages/core/src/components/portal/portal.tsx +++ b/packages/core/src/components/portal/portal.tsx @@ -42,8 +42,10 @@ export interface IPortalProps extends Props { container?: HTMLElement; /** - * A list of DOM events to stop propagation on. - * Stopgap resolution for https://github.com/facebook/react/issues/11387 + * A list of DOM events which should be stopped from propagating through this portal element. + * + * @see https://legacy.reactjs.org/docs/portals.html#event-bubbling-through-portals + * @see https://github.com/palantir/blueprint/issues/6124 */ stopPropagationEvents?: Array; } @@ -109,7 +111,7 @@ export class Portal extends React.Component { } this.portalElement = this.createContainerElement(); this.props.container.appendChild(this.portalElement); - this.addEventListeners(this.props.stopPropagationEvents); + this.addStopPropagationListeners(this.props.stopPropagationEvents); /* eslint-disable-next-line react/no-did-mount-set-state */ this.setState({ hasMounted: true }, this.props.onChildrenMount); } @@ -120,14 +122,15 @@ export class Portal extends React.Component { maybeRemoveClass(this.portalElement.classList, prevProps.className); maybeAddClass(this.portalElement.classList, this.props.className); } - if(this.portalElement != null && prevProps.stopPropagationEvents !== this.props.stopPropagationEvents) { - this.removeEventListeners(prevProps.stopPropagationEvents); - this.addEventListeners(this.props.stopPropagationEvents); + + if (this.portalElement != null && prevProps.stopPropagationEvents !== this.props.stopPropagationEvents) { + this.removeStopPropagationListeners(prevProps.stopPropagationEvents); + this.addStopPropagationListeners(this.props.stopPropagationEvents); } } public componentWillUnmount() { - this.removeEventListeners(this.props.stopPropagationEvents); + this.removeStopPropagationListeners(this.props.stopPropagationEvents); this.portalElement?.remove(); } @@ -141,18 +144,12 @@ export class Portal extends React.Component { return container; } - private addEventListeners(events?: Array) { - if (events == null || events.length === 0) { - return; - } - events.map(event => this.portalElement?.addEventListener(event, handleStopProgation)); + private addStopPropagationListeners(eventNames?: Array) { + eventNames?.forEach(event => this.portalElement?.addEventListener(event, handleStopProgation)); } - private removeEventListeners(events?: Array) { - if (events == null || events.length === 0) { - return; - } - events.map(event => this.portalElement?.removeEventListener(event, handleStopProgation)); + private removeStopPropagationListeners(events?: Array) { + events?.forEach(event => this.portalElement?.removeEventListener(event, handleStopProgation)); } }