diff --git a/nx.json b/nx.json index f53473cfc7..11f689eae3 100644 --- a/nx.json +++ b/nx.json @@ -40,7 +40,7 @@ "outputs": ["{projectRoot}/lib/css"] }, "dev": { - "dependsOn": ["^dev"] + "dependsOn": ["^compile"] }, "compile:cjs": { "dependsOn": ["^compile:cjs"], @@ -54,11 +54,14 @@ "dependsOn": ["^dist:bundle"], "outputs": ["{projectRoot}/dist"] }, + "test": { + "dependsOn": ["^compile"] + }, "test:karma": { - "dependsOn": ["^test:karma"] + "dependsOn": [] }, "test:karma:debug": { - "dependsOn": ["^test:karma:debug"] + "dependsOn": [] }, "bundle": { "dependsOn": ["^bundle"], diff --git a/package.json b/package.json index 5ed662e0c6..2a8ea530fd 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "verify": "npm-run-all -s compile dist:libs dist:apps -p test lint format-check" }, "dependencies": { - "@types/chai": "~4.3.4", + "@types/chai": "~4.3.5", "@types/enzyme": "~3.10.12", "@types/enzyme-adapter-react-16": "~1.0.6", "@types/mocha": "~10.0.1", diff --git a/packages/core/src/components/overlay/overlay.tsx b/packages/core/src/components/overlay/overlay.tsx index d1c9c5670f..d6dc3bc20f 100644 --- a/packages/core/src/components/overlay/overlay.tsx +++ b/packages/core/src/components/overlay/overlay.tsx @@ -105,6 +105,15 @@ export interface OverlayableProps extends OverlayLifecycleProps { */ portalContainer?: HTMLElement; + /** + * A list of DOM events which should be stopped from propagating through the Portal. + * This prop is ignored if `usePortal` is `false`. + * + * @see https://legacy.reactjs.org/docs/portals.html#event-bubbling-through-portals + * @see https://github.com/palantir/blueprint/issues/6124 + */ + portalStopPropagationEvents?: 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). @@ -301,7 +310,11 @@ export class Overlay extends AbstractPureComponent { ); if (usePortal) { return ( - + {transitionGroup} ); diff --git a/packages/core/src/components/portal/portal.tsx b/packages/core/src/components/portal/portal.tsx index 74776f8cf8..fe29b265af 100644 --- a/packages/core/src/components/portal/portal.tsx +++ b/packages/core/src/components/portal/portal.tsx @@ -38,6 +38,14 @@ export interface PortalProps extends Props { * @default document.body */ container?: HTMLElement; + + /** + * 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; } export interface PortalLegacyContext { @@ -78,6 +86,7 @@ export function Portal(props: PortalProps, legacyContext: PortalLegacyContext = container.classList.add(Classes.PORTAL); maybeAddClass(container.classList, props.className); // directly added to this portal element maybeAddClass(container.classList, context.portalClassName); // added via PortalProvider context + addStopPropagationListeners(container, props.stopPropagationEvents); // TODO: remove legacy context support in Blueprint v6.0 const { blueprintPortalClassName } = legacyContext; @@ -100,6 +109,7 @@ export function Portal(props: PortalProps, legacyContext: PortalLegacyContext = setHasMounted(true); return () => { + removeStopPropagationListeners(newPortalElement, props.stopPropagationEvents); newPortalElement.remove(); setHasMounted(false); setPortalElement(undefined); @@ -122,6 +132,15 @@ export function Portal(props: PortalProps, legacyContext: PortalLegacyContext = } }, [props.className]); + // update stopPropagation listeners when props change + const prevStopPropagationEvents = usePrevious(props.stopPropagationEvents); + React.useEffect(() => { + if (portalElement != null) { + removeStopPropagationListeners(portalElement, prevStopPropagationEvents); + addStopPropagationListeners(portalElement, props.stopPropagationEvents); + } + }, [props.stopPropagationEvents]); + // Only render `children` once this component has mounted in a browser environment, so they are // immediately attached to the DOM tree and can do DOM things like measuring or `autoFocus`. // See long comment on componentDidMount in https://reactjs.org/docs/portals.html#event-bubbling-through-portals @@ -149,3 +168,15 @@ function maybeAddClass(classList: DOMTokenList, className?: string) { classList.add(...className.split(" ")); } } + +function addStopPropagationListeners(portalElement: HTMLElement, eventNames?: Array) { + eventNames?.forEach(event => portalElement.addEventListener(event, handleStopProgation)); +} + +function removeStopPropagationListeners(portalElement: HTMLElement, events?: Array) { + events?.forEach(event => portalElement.removeEventListener(event, handleStopProgation)); +} + +function handleStopProgation(e: Event) { + e.stopPropagation(); +} diff --git a/yarn.lock b/yarn.lock index c81ec90923..e34d1eeced 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1454,10 +1454,10 @@ resolved "https://registry.yarnpkg.com/@types/btoa-lite/-/btoa-lite-1.0.0.tgz#e190a5a548e0b348adb0df9ac7fa5f1151c7cca4" integrity sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg== -"@types/chai@~4.3.4": - version "4.3.4" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4" - integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw== +"@types/chai@~4.3.5": + version "4.3.5" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.5.tgz#ae69bcbb1bebb68c4ac0b11e9d8ed04526b3562b" + integrity sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng== "@types/cheerio@*": version "0.22.23"