From 809b7665f515152f72ccfdee0226f9d81bc0cb57 Mon Sep 17 00:00:00 2001 From: Mathias Borglin Date: Fri, 9 May 2025 10:15:24 +0200 Subject: [PATCH 1/2] Return popouts whn blocked by browser --- .../src/dockview/components/titlebar/tabs.ts | 3 ++ .../src/dockview/dockviewComponent.ts | 46 ++++++++++++------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/packages/dockview-core/src/dockview/components/titlebar/tabs.ts b/packages/dockview-core/src/dockview/components/titlebar/tabs.ts index 53486ed7c..062c1e683 100644 --- a/packages/dockview-core/src/dockview/components/titlebar/tabs.ts +++ b/packages/dockview-core/src/dockview/components/titlebar/tabs.ts @@ -251,6 +251,9 @@ export class Tabs extends CompositeDisposable { delete(id: string): void { const index = this.indexOf(id); const tabToRemove = this._tabs.splice(index, 1)[0]; + if (!tabToRemove) { + return; + } const { value, disposable } = tabToRemove; diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index ff1284d07..ee9035ab3 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -718,27 +718,11 @@ export class DockviewComponent return false; } - if (popoutContainer === null) { - popoutWindowDisposable.dispose(); - this._onDidBlockPopout.fire(); - return false; - } - - const gready = document.createElement('div'); - gready.className = 'dv-overlay-render-container'; - - const overlayRenderContainer = new OverlayRenderContainer( - gready, - this - ); - const referenceGroup = itemToPopout instanceof DockviewPanel ? itemToPopout.group : itemToPopout; - const referenceLocation = itemToPopout.api.location.type; - /** * The group that is being added doesn't already exist within the DOM, the most likely occurrence * of this case is when being called from the `fromJSON(...)` method @@ -757,6 +741,36 @@ export class DockviewComponent this._onDidAddGroup.fire(group); } + if (popoutContainer === null) { + popoutWindowDisposable.dispose(); + this._onDidBlockPopout.fire(); + + // if the popout window was blocked, we need to move the group back to the reference group + // and set it to visible + this.movingLock(() => + moveGroupWithoutDestroying({ + from: group, + to: referenceGroup, + }) + ); + + if (!referenceGroup.api.isVisible) { + referenceGroup.api.setVisible(true); + } + + return false; + } + + const gready = document.createElement('div'); + gready.className = 'dv-overlay-render-container'; + + const overlayRenderContainer = new OverlayRenderContainer( + gready, + this + ); + + const referenceLocation = itemToPopout.api.location.type; + group.model.renderContainer = overlayRenderContainer; group.layout( _window.window!.innerWidth, From 86e8e6371875665a6b8be13e2ee64c0651d0858a Mon Sep 17 00:00:00 2001 From: Mathias Borglin Date: Sun, 11 May 2025 21:42:39 +0200 Subject: [PATCH 2/2] Add tests --- .../dockview/dockviewComponent.spec.ts | 48 +++++++++++++++++++ .../src/dockview/dockviewComponent.ts | 4 +- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts index 2c08f0708..02689e075 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts @@ -5791,6 +5791,54 @@ describe('dockviewComponent', () => { ]); }); + describe('when browsers block popups', () => { + let container: HTMLDivElement; + let dockview: DockviewComponent; + let panel: DockviewPanel; + + beforeEach(() => { + jest.spyOn(window, 'open').mockReturnValue(null); + + container = document.createElement('div'); + + dockview = new DockviewComponent(container, { + createComponent(options) { + switch (options.name) { + case 'default': + return new PanelContentPartTest( + options.id, + options.name + ); + default: + throw new Error(`unsupported`); + } + }, + }); + + dockview.layout(1000, 500); + + panel = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); + }); + + test('onDidBlockPopout event is emitted', async () => { + const onDidBlockPopoutHandler = jest.fn(); + dockview.onDidBlockPopout(onDidBlockPopoutHandler); + + await dockview.addPopoutGroup(panel.group); + + expect(onDidBlockPopoutHandler).toHaveBeenCalledTimes(1); + }); + + test('popout group is restored to its original position', async () => { + await dockview.addPopoutGroup(panel.group); + + expect(panel.group.api.location.type).toBe('grid'); + }); + }); + test('dispose of dockview instance when popup is open', async () => { const container = document.createElement('div'); diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index ee9035ab3..c06f1c776 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -738,7 +738,9 @@ export class DockviewComponent group = options.overridePopoutGroup; } else { group = this.createGroup({ id: groupId }); - this._onDidAddGroup.fire(group); + if (popoutContainer) { + this._onDidAddGroup.fire(group); + } } if (popoutContainer === null) {