fix(app): keep popouts in the window they were opened in (#2561)

This commit is contained in:
Hampus
2026-09-06 22:42:32 +02:00
committed by GitHub
parent f38619d974
commit 69ca462930
3 changed files with 32 additions and 3 deletions
@@ -0,0 +1,23 @@
// @vitest-environment happy-dom
// SPDX-License-Identifier: AGPL-3.0-or-later
import {scopePortalHostToDocument} from '@app/features/ui/overlay/PortalHostContext';
import {describe, expect, it} from 'vitest';
describe('scopePortalHostToDocument', () => {
it('keeps a host that belongs to the scoped document', () => {
const host = document.createElement('div');
expect(scopePortalHostToDocument(host, document)).toBe(host);
});
it('drops a host owned by another document', () => {
const popoutDocument = document.implementation.createHTMLDocument('popout');
const popoutHost = popoutDocument.createElement('div');
expect(scopePortalHostToDocument(popoutHost, document)).toBeNull();
expect(scopePortalHostToDocument(popoutHost, popoutDocument)).toBe(popoutHost);
});
it('passes a missing host through', () => {
expect(scopePortalHostToDocument(null, document)).toBeNull();
});
});
@@ -39,3 +39,8 @@ export function usePortalHost(): PortalHostElement {
export function resolvePortalHost(host: PortalHostElement): HTMLElement {
return host ?? document.body;
}
export function scopePortalHostToDocument(host: PortalHostElement, scopeDocument: Document): PortalHostElement {
if (host == null) return null;
return host.ownerDocument === scopeDocument ? host : null;
}
@@ -4,7 +4,7 @@ import Accessibility from '@app/features/accessibility/state/Accessibility';
import {useAntiShiftFloating} from '@app/features/app/hooks/useAntiShiftFloating';
import {shouldDisableAutofocusOnMobile} from '@app/features/platform/utils/AutofocusUtils';
import * as PopoutCommands from '@app/features/ui/commands/PopoutCommands';
import {usePortalHost} from '@app/features/ui/overlay/PortalHostContext';
import {scopePortalHostToDocument, usePortalHost} from '@app/features/ui/overlay/PortalHostContext';
import {type Popout, PopoutKeyContext, type PopoutReferenceRect} from '@app/features/ui/popover';
import {PopoutResizePositionContext} from '@app/features/ui/popover/PopoutResizePositionContext';
import {getPopoutFocusManagerInsideElements} from '@app/features/ui/popover/PopoverFocusManagerUtils';
@@ -426,10 +426,11 @@ interface PopoutsProps {
export const Popouts: React.FC<PopoutsProps> = observer(({ownerDocument}) => {
const prevPopoutKeysRef = useRef<Set<string>>(new Set());
const portalHost = usePortalHost();
const activePortalHost = usePortalHost();
let scopeDocument = document;
if (portalHost != null) scopeDocument = portalHost.ownerDocument;
if (activePortalHost != null) scopeDocument = activePortalHost.ownerDocument;
if (ownerDocument != null) scopeDocument = ownerDocument;
const portalHost = scopePortalHostToDocument(activePortalHost, scopeDocument);
const popouts = PopoutState.getPopouts(scopeDocument);
const topPopout = popouts.length ? popouts[popouts.length - 1] : null;
const needsBackdrop = Boolean(topPopout && !topPopout.disableBackdrop);