diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts index 450800fb5..9dd25066d 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts @@ -17,6 +17,7 @@ import { GroupDragEvent, TabDragEvent, } from '../../dockview/components/titlebar/tabsContainer'; +import { fromPartial } from '@total-typescript/shoehorn'; class PanelContentPartTest implements IContentRenderer { element: HTMLElement = document.createElement('div'); @@ -105,68 +106,105 @@ describe('dockviewComponent', () => { default: PanelContentPartTest, }, }); + + window.open = jest.fn(); // not implemented by jest }); - test('event leakage', () => { - Emitter.setLeakageMonitorEnabled(true); + describe('memory leakage', () => { + test('event leakage', () => { + Emitter.setLeakageMonitorEnabled(true); - dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - }); + dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + }); - dockview.layout(500, 1000); + dockview.layout(500, 1000); - dockview.addPanel({ - id: 'panel1', - component: 'default', - }); + const panel1 = dockview.addPanel({ + id: 'panel1', + component: 'default', + }); - const panel2 = dockview.addPanel({ - id: 'panel2', - component: 'default', - }); + const panel2 = dockview.addPanel({ + id: 'panel2', + component: 'default', + }); - dockview.removePanel(panel2); + dockview.removePanel(panel2); - const panel3 = dockview.addPanel({ - id: 'panel3', - component: 'default', - position: { - direction: 'right', - referencePanel: 'panel1', - }, - }); + const panel3 = dockview.addPanel({ + id: 'panel3', + component: 'default', + position: { + direction: 'right', + referencePanel: 'panel1', + }, + }); - const panel4 = dockview.addPanel({ - id: 'panel4', - component: 'default', - position: { - direction: 'above', - }, - }); + const panel4 = dockview.addPanel({ + id: 'panel4', + component: 'default', + position: { + direction: 'above', + }, + }); - dockview.moveGroupOrPanel( - panel4.group, - panel3.group.id, - panel3.id, - 'center' - ); + dockview.moveGroupOrPanel( + panel4.group, + panel3.group.id, + panel3.id, + 'center' + ); - dockview.dispose(); + dockview.addPanel({ + id: 'panel5', + component: 'default', + floating: true, + }); + + const panel6 = dockview.addPanel({ + id: 'panel6', + component: 'default', + position: { + referencePanel: 'panel5', + direction: 'within', + }, + }); - if (Emitter.MEMORY_LEAK_WATCHER.size > 0) { - for (const entry of Array.from( - Emitter.MEMORY_LEAK_WATCHER.events - )) { - console.log('disposal', entry[1]); + dockview.addFloatingGroup(panel4.api.group); + + dockview.addPopoutGroup(panel6); + + dockview.moveGroupOrPanel( + panel1.group, + panel6.group.id, + panel6.id, + 'center' + ); + + dockview.moveGroupOrPanel( + panel4.group, + panel6.group.id, + panel6.id, + 'center' + ); + + dockview.dispose(); + + if (Emitter.MEMORY_LEAK_WATCHER.size > 0) { + for (const entry of Array.from( + Emitter.MEMORY_LEAK_WATCHER.events + )) { + console.log('disposal', entry[1]); + } + throw new Error('not all listeners disposed'); } - throw new Error('not all listeners disposed'); - } - Emitter.setLeakageMonitorEnabled(false); + Emitter.setLeakageMonitorEnabled(false); + }); }); test('duplicate panel', () => { @@ -2807,7 +2845,7 @@ describe('dockviewComponent', () => { }); }); - test('floating: group is removed', async () => { + test('that moving the last panel to be floating should leave an empty gridview', () => { const container = document.createElement('div'); const dockview = new DockviewComponent({ @@ -2823,19 +2861,23 @@ describe('dockviewComponent', () => { dockview.layout(1000, 500); - expect(dockview.groups.length).toBe(0); - const panel = dockview.addPanel({ + const panel1 = dockview.addPanel({ id: 'panel_1', component: 'default', - floating: true, }); - expect(dockview.groups.length).toBe(1); - dockview.removePanel(panel); - expect(dockview.groups.length).toBe(0); + expect( + dockview.element.querySelectorAll('.view-container > .view').length + ).toBe(1); + + dockview.addFloatingGroup(panel1); + + expect( + dockview.element.querySelectorAll('.view-container > .view').length + ).toBe(0); }); - test('floating: move a floating group of one tab to a new fixed group', () => { + test('that api.setSize applies to the overlay for floating panels', () => { const container = document.createElement('div'); const dockview = new DockviewComponent({ @@ -2854,35 +2896,25 @@ describe('dockviewComponent', () => { const panel1 = dockview.addPanel({ id: 'panel_1', component: 'default', - }); - - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', floating: true, }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(2); + panel1.api.setSize({ height: 123, width: 256 }); - dockview.moveGroupOrPanel( - panel1.group, - panel2.group.id, - undefined, - 'right' - ); + const items = dockview.element.querySelectorAll('.dv-resize-container'); + expect(items.length).toBe(1); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(2); + const el = items[0] as HTMLElement; + + expect(el.style.height).toBe('123px'); + expect(el.style.width).toBe('256px'); }); - test('floating: move a floating group of one tab to an existing fixed group', () => { + test('that external dnd events do not trigger the top-level center dnd target unless empty', () => { const container = document.createElement('div'); + const showDndOverlay = jest.fn().mockReturnValue(true); + const dockview = new DockviewComponent({ parentElement: container, components: { @@ -2892,6 +2924,7 @@ describe('dockviewComponent', () => { test_tab_id: PanelTabPartTest, }, orientation: Orientation.HORIZONTAL, + showDndOverlay: showDndOverlay, }); dockview.layout(1000, 500); @@ -2900,85 +2933,140 @@ describe('dockviewComponent', () => { id: 'panel_1', component: 'default', }); - const panel2 = dockview.addPanel({ id: 'panel_2', component: 'default', - floating: true, + position: { direction: 'right' }, }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(2); + Object.defineProperty(dockview.element, 'clientWidth', { + get: () => 100, + }); + Object.defineProperty(dockview.element, 'clientHeight', { + get: () => 100, + }); - dockview.moveGroupOrPanel( - panel1.group, - panel2.group.id, - undefined, - 'center' - ); + jest.spyOn(dockview.element, 'getBoundingClientRect').mockReturnValue({ + left: 0, + top: 0, + width: 100, + height: 100, + } as any); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(dockview.groups.length).toBe(1); - expect(dockview.panels.length).toBe(2); - }); + // left - test('floating: move a floating group of one tab to an existing floating group', () => { - const container = document.createElement('div'); + const eventLeft = new KeyboardEvent('dragover'); + Object.defineProperty(eventLeft, 'clientX', { + get: () => 0, + }); + Object.defineProperty(eventLeft, 'clientY', { + get: () => 0, + }); + fireEvent(dockview.element, eventLeft); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, + expect(showDndOverlay).toHaveBeenCalledWith({ + nativeEvent: eventLeft, + position: 'left', + target: DockviewDropTargets.Edge, + getData: getPanelData, }); + expect(showDndOverlay).toBeCalledTimes(1); - dockview.layout(1000, 500); + // right - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', + const eventRight = new KeyboardEvent('dragover'); + Object.defineProperty(eventRight, 'clientX', { + get: () => 100, + }); + Object.defineProperty(eventRight, 'clientY', { + get: () => 100, }); + fireEvent(dockview.element, eventRight); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - floating: true, + expect(showDndOverlay).toHaveBeenCalledWith({ + nativeEvent: eventRight, + position: 'right', + target: DockviewDropTargets.Edge, + getData: getPanelData, }); + expect(showDndOverlay).toBeCalledTimes(2); - const panel3 = dockview.addPanel({ - id: 'panel_3', - component: 'default', - floating: true, + // top + + const eventTop = new KeyboardEvent('dragover'); + Object.defineProperty(eventTop, 'clientX', { + get: () => 50, + }); + Object.defineProperty(eventTop, 'clientY', { + get: () => 0, }); + fireEvent(dockview.element, eventTop); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(3); - expect(dockview.panels.length).toBe(3); + expect(showDndOverlay).toHaveBeenCalledWith({ + nativeEvent: eventTop, + position: 'top', + target: DockviewDropTargets.Edge, + getData: getPanelData, + }); + expect(showDndOverlay).toBeCalledTimes(3); - dockview.moveGroupOrPanel( - panel2.group, - panel3.group.id, - undefined, - 'center' - ); + // top - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); + const eventBottom = new KeyboardEvent('dragover'); + Object.defineProperty(eventBottom, 'clientX', { + get: () => 50, + }); + Object.defineProperty(eventBottom, 'clientY', { + get: () => 100, + }); + fireEvent(dockview.element, eventBottom); + + expect(showDndOverlay).toHaveBeenCalledWith({ + nativeEvent: eventBottom, + position: 'bottom', + target: DockviewDropTargets.Edge, + getData: getPanelData, + }); + expect(showDndOverlay).toBeCalledTimes(4); + + // center + + const eventCenter = new KeyboardEvent('dragover'); + Object.defineProperty(eventCenter, 'clientX', { + get: () => 50, + }); + Object.defineProperty(eventCenter, 'clientY', { + get: () => 50, + }); + fireEvent(dockview.element, eventCenter); + + // expect not to be called for center + expect(showDndOverlay).toBeCalledTimes(4); + + dockview.removePanel(panel1); + dockview.removePanel(panel2); + + // center, but empty + + const eventCenter2 = new KeyboardEvent('dragover'); + Object.defineProperty(eventCenter2, 'clientX', { + get: () => 50, + }); + Object.defineProperty(eventCenter2, 'clientY', { + get: () => 50, + }); + fireEvent(dockview.element, eventCenter2); + + expect(showDndOverlay).toHaveBeenCalledWith({ + nativeEvent: eventTop, + position: 'center', + target: DockviewDropTargets.Edge, + getData: getPanelData, + }); + expect(showDndOverlay).toBeCalledTimes(5); }); - test('floating: move a floating group of many tabs to a new fixed group', () => { + test('that dragging a tab triggers onWillDragPanel', () => { const container = document.createElement('div'); const dockview = new DockviewComponent({ @@ -2994,97 +3082,31 @@ describe('dockviewComponent', () => { dockview.layout(1000, 500); - const panel1 = dockview.addPanel({ + dockview.addPanel({ id: 'panel_1', component: 'default', }); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - floating: true, - }); - - const panel3 = dockview.addPanel({ - id: 'panel_3', - component: 'default', - position: { referencePanel: panel2 }, - }); - - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); - - dockview.moveGroupOrPanel( - panel1.group, - panel2.group.id, - undefined, - 'right' - ); - - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(panel3.group.api.location).toBe('grid'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); - }); - - test('floating: move a floating group of many tabs to an existing fixed group', () => { - const container = document.createElement('div'); - - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); - - dockview.layout(1000, 500); - - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + const tabDragEvents: TabDragEvent[] = []; + const groupDragEvents: GroupDragEvent[] = []; - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - floating: true, + dockview.onWillDragPanel((event) => { + tabDragEvents.push(event); }); - - const panel3 = dockview.addPanel({ - id: 'panel_3', - component: 'default', - position: { referencePanel: panel2 }, + dockview.onWillDragGroup((event) => { + groupDragEvents.push(event); }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); + const el = dockview.element.querySelector('.tab')!; + expect(el).toBeTruthy(); - dockview.moveGroupOrPanel( - panel1.group, - panel2.group.id, - undefined, - 'center' - ); + fireEvent.dragStart(el); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(panel3.group.api.location).toBe('grid'); - expect(dockview.groups.length).toBe(1); - expect(dockview.panels.length).toBe(3); + expect(tabDragEvents.length).toBe(1); + expect(groupDragEvents.length).toBe(0); }); - test('floating: move a floating group of many tabs to an existing floating group', () => { + test('that dragging a group triggers onWillDragGroup', () => { const container = document.createElement('div'); const dockview = new DockviewComponent({ @@ -3100,58 +3122,38 @@ describe('dockviewComponent', () => { dockview.layout(1000, 500); - const panel1 = dockview.addPanel({ + dockview.addPanel({ id: 'panel_1', component: 'default', }); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - floating: true, - }); + const tabDragEvents: TabDragEvent[] = []; + const groupDragEvents: GroupDragEvent[] = []; - const panel3 = dockview.addPanel({ - id: 'panel_3', - component: 'default', - position: { referencePanel: panel2 }, + dockview.onWillDragPanel((event) => { + tabDragEvents.push(event); }); - - const panel4 = dockview.addPanel({ - id: 'panel_4', - component: 'default', - floating: true, + dockview.onWillDragGroup((event) => { + groupDragEvents.push(event); }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(panel4.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(3); - expect(dockview.panels.length).toBe(4); + const el = dockview.element.querySelector('.void-container')!; + expect(el).toBeTruthy(); - dockview.moveGroupOrPanel( - panel4.group, - panel2.group.id, - undefined, - 'center' - ); + fireEvent.dragStart(el); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(panel4.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(4); + expect(tabDragEvents.length).toBe(0); + expect(groupDragEvents.length).toBe(1); }); - test('floating: move a floating tab of one tab to a new fixed group', () => { + test('corrupt layout: bad inline view', () => { const container = document.createElement('div'); const dockview = new DockviewComponent({ parentElement: container, components: { - default: PanelContentPartTest, + panelA: PanelContentPartTest, + panelB: PanelContentPartTest, }, tabComponents: { test_tab_id: PanelTabPartTest, @@ -3161,42 +3163,87 @@ describe('dockviewComponent', () => { dockview.layout(1000, 500); - const panel1 = dockview.addPanel({ + let el = dockview.element.querySelector('.view-container'); + expect(el).toBeTruthy(); + expect(el!.childNodes.length).toBe(0); + + dockview.addPanel({ id: 'panel_1', - component: 'default', + component: 'panelA', }); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - floating: true, - }); + expect(dockview.groups.length).toBe(1); + expect(dockview.panels.length).toBe(1); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(2); + el = dockview.element.querySelector('.view-container'); + expect(el).toBeTruthy(); + expect(el!.childNodes.length).toBeGreaterThan(0); - dockview.moveGroupOrPanel( - panel1.group, - panel2.group.id, - panel2.id, - 'right' + expect(() => { + dockview.fromJSON({ + grid: { + root: { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + views: ['panelA'], + activeView: 'panelA', + id: '1', + }, + size: 841, + }, + { + type: 'leaf', + data: { + views: ['panelB'], + activeView: 'panelB', + id: '2', + }, + size: 842, + }, + ], + size: 530, + }, + width: 1683, + height: 530, + orientation: Orientation.HORIZONTAL, + }, + panels: { + panelA: { + id: 'panelA', + contentComponent: 'panelA', + title: 'Panel A', + }, + panelB: { + id: 'panelB', + contentComponent: 'somethingBad', + title: 'Panel B', + }, + }, + activeGroup: '1', + }); + }).toThrow( + "Cannot create 'panelB', no component 'somethingBad' provided" ); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(2); + expect(dockview.groups.length).toBe(0); + expect(dockview.panels.length).toBe(0); + + el = dockview.element.querySelector('.view-container'); + expect(el).toBeTruthy(); + expect(el!.childNodes.length).toBe(0); }); - test('floating: move a floating tab of one tab to an existing fixed group', () => { + test('corrupt layout: bad floating view', () => { const container = document.createElement('div'); const dockview = new DockviewComponent({ parentElement: container, components: { - default: PanelContentPartTest, + panelA: PanelContentPartTest, + panelB: PanelContentPartTest, }, tabComponents: { test_tab_id: PanelTabPartTest, @@ -3206,95 +3253,120 @@ describe('dockviewComponent', () => { dockview.layout(1000, 500); - const panel1 = dockview.addPanel({ + let el = dockview.element.querySelector('.view-container'); + expect(el).toBeTruthy(); + expect(el!.childNodes.length).toBe(0); + + dockview.addPanel({ id: 'panel_1', - component: 'default', + component: 'panelA', }); - const panel2 = dockview.addPanel({ + dockview.addPanel({ id: 'panel_2', - component: 'default', + component: 'panelA', floating: true, }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); expect(dockview.groups.length).toBe(2); expect(dockview.panels.length).toBe(2); - dockview.moveGroupOrPanel( - panel1.group, - panel2.group.id, - panel2.id, - 'center' - ); - - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(dockview.groups.length).toBe(1); - expect(dockview.panels.length).toBe(2); - }); - - test('floating: move a floating tab of one tab to an existing floating group', () => { - const container = document.createElement('div'); - - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); - - dockview.layout(1000, 500); - - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); - - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - floating: true, - }); - - const panel3 = dockview.addPanel({ - id: 'panel_3', - component: 'default', - floating: true, - }); - - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(3); - expect(dockview.panels.length).toBe(3); + el = dockview.element.querySelector('.dv-resize-container'); + expect(el).toBeTruthy(); - dockview.moveGroupOrPanel( - panel2.group, - panel3.group.id, - panel3.id, - 'center' - ); + el = dockview.element.querySelector('.view-container'); + expect(el).toBeTruthy(); + expect(el!.childNodes.length).toBeGreaterThan(0); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); + expect(() => { + dockview.fromJSON({ + grid: { + root: { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + views: ['panelA'], + activeView: 'panelA', + id: '1', + }, + size: 841, + }, + { + type: 'leaf', + data: { + views: ['panelB'], + activeView: 'panelB', + id: '2', + }, + size: 842, + }, + ], + size: 530, + }, + width: 1683, + height: 530, + orientation: Orientation.HORIZONTAL, + }, + floatingGroups: [ + { + data: { + views: ['panelB'], + activeView: 'panelB', + id: '3', + }, + position: { left: 0, top: 0, height: 100, width: 100 }, + }, + { + data: { + views: ['panelC'], + activeView: 'panelC', + id: '4', + }, + position: { left: 0, top: 0, height: 100, width: 100 }, + }, + ], + panels: { + panelA: { + id: 'panelA', + contentComponent: 'panelA', + title: 'Panel A', + }, + panelB: { + id: 'panelB', + contentComponent: 'panelB', + title: 'Panel B', + }, + panelC: { + id: 'panelC', + contentComponent: 'panelC', + title: 'Panel C', + }, + }, + activeGroup: '1', + }); + }).toThrow("Cannot create 'panelC', no component 'panelC' provided"); + + expect(dockview.groups.length).toBe(0); + expect(dockview.panels.length).toBe(0); + + el = dockview.element.querySelector('.dv-resize-container'); + expect(el).toBeFalsy(); + + el = dockview.element.querySelector('.view-container'); + expect(el).toBeTruthy(); + expect(el!.childNodes.length).toBe(0); }); - test('floating: move a floating tab of many tabs to a new fixed group', () => { + test('that disableAutoResizing is false by default', () => { const container = document.createElement('div'); const dockview = new DockviewComponent({ parentElement: container, components: { - default: PanelContentPartTest, + panelA: PanelContentPartTest, + panelB: PanelContentPartTest, }, tabComponents: { test_tab_id: PanelTabPartTest, @@ -3302,1075 +3374,1179 @@ describe('dockviewComponent', () => { orientation: Orientation.HORIZONTAL, }); - dockview.layout(1000, 500); - - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); - - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - floating: true, - }); - - const panel3 = dockview.addPanel({ - id: 'panel_3', - component: 'default', - position: { referencePanel: panel2 }, - }); - - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); - - dockview.moveGroupOrPanel( - panel1.group, - panel2.group.id, - panel2.id, - 'right' - ); - - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(3); - expect(dockview.panels.length).toBe(3); + expect(dockview.disableResizing).toBeFalsy(); }); - test('floating: move a floating tab of many tabs to an existing fixed group', () => { + test('that disableAutoResizing can be enabled', () => { const container = document.createElement('div'); const dockview = new DockviewComponent({ parentElement: container, components: { - default: PanelContentPartTest, + panelA: PanelContentPartTest, + panelB: PanelContentPartTest, }, tabComponents: { test_tab_id: PanelTabPartTest, }, orientation: Orientation.HORIZONTAL, + disableAutoResizing: true, }); - dockview.layout(1000, 500); + expect(dockview.disableResizing).toBeTruthy(); + }); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + describe('floating groups', () => { + test('that a floating group can be removed', async () => { + const container = document.createElement('div'); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - floating: true, - }); + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - const panel3 = dockview.addPanel({ - id: 'panel_3', - component: 'default', - position: { referencePanel: panel2 }, - }); + dockview.layout(1000, 500); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); + expect(dockview.groups.length).toBe(0); + const panel = dockview.addPanel({ + id: 'panel_1', + component: 'default', + floating: true, + }); + expect(dockview.groups.length).toBe(1); - dockview.moveGroupOrPanel( - panel1.group, - panel2.group.id, - panel2.id, - 'center' - ); + dockview.removePanel(panel); + expect(dockview.groups.length).toBe(0); + }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); - }); + test('move a floating group of one tab to a new fixed group', () => { + const container = document.createElement('div'); - test('floating: move a floating tab of many tabs to an existing floating group', () => { - const container = document.createElement('div'); + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + dockview.layout(1000, 500); - dockview.layout(1000, 500); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + floating: true, + }); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - floating: true, - }); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(2); - const panel3 = dockview.addPanel({ - id: 'panel_3', - component: 'default', - position: { referencePanel: panel2 }, - }); + dockview.moveGroupOrPanel( + panel1.group, + panel2.group.id, + undefined, + 'right' + ); - const panel4 = dockview.addPanel({ - id: 'panel_4', - component: 'default', - floating: true, + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(2); }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(panel4.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(3); - expect(dockview.panels.length).toBe(4); - - dockview.moveGroupOrPanel( - panel4.group, - panel2.group.id, - panel2.id, - 'center' - ); + test('move a floating group of one tab to an existing fixed group', () => { + const container = document.createElement('div'); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(panel4.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(3); - expect(dockview.panels.length).toBe(4); - }); + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - test('floating: move a fixed tab of one tab to an existing floating group', () => { - const container = document.createElement('div'); + dockview.layout(1000, 500); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - dockview.layout(1000, 500); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + floating: true, + }); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(2); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - position: { direction: 'right' }, - }); + dockview.moveGroupOrPanel( + panel1.group, + panel2.group.id, + undefined, + 'center' + ); - const panel3 = dockview.addPanel({ - id: 'panel_3', - component: 'default', - floating: true, + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(1); + expect(dockview.panels.length).toBe(2); }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(3); - expect(dockview.panels.length).toBe(3); + test('move a floating group of one tab to an existing floating group', () => { + const container = document.createElement('div'); - dockview.moveGroupOrPanel( - panel3.group, - panel1.group.id, - panel1.id, - 'center' - ); + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - expect(panel1.group.api.location).toBe('floating'); - expect(panel2.group.api.location).toBe('grid'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); - }); + dockview.layout(1000, 500); - test('floating: move a fixed tab of many tabs to an existing floating group', () => { - const container = document.createElement('div'); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + floating: true, + }); - dockview.layout(1000, 500); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + floating: true, + }); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(3); + expect(dockview.panels.length).toBe(3); + + dockview.moveGroupOrPanel( + panel2.group, + panel3.group.id, + undefined, + 'center' + ); + + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + }); + + test('move a floating group of many tabs to a new fixed group', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - }); + dockview.layout(1000, 500); - const panel3 = dockview.addPanel({ - id: 'panel_3', - component: 'default', - floating: true, - }); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + floating: true, + }); - dockview.moveGroupOrPanel( - panel3.group, - panel1.group.id, - panel1.id, - 'center' - ); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + position: { referencePanel: panel2 }, + }); - expect(panel1.group.api.location).toBe('floating'); - expect(panel2.group.api.location).toBe('grid'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); - }); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + + dockview.moveGroupOrPanel( + panel1.group, + panel2.group.id, + undefined, + 'right' + ); + + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + }); + + test('move a floating group of many tabs to an existing fixed group', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - test('floating: move a fixed group of one tab to an existing floating group', () => { - const container = document.createElement('div'); + dockview.layout(1000, 500); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - dockview.layout(1000, 500); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + floating: true, + }); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + position: { referencePanel: panel2 }, + }); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - position: { direction: 'right' }, - }); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + + dockview.moveGroupOrPanel( + panel1.group, + panel2.group.id, + undefined, + 'center' + ); + + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(1); + expect(dockview.panels.length).toBe(3); + }); + + test('move a floating group of many tabs to an existing floating group', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - const panel3 = dockview.addPanel({ - id: 'panel_3', - component: 'default', - floating: true, - }); + dockview.layout(1000, 500); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(3); - expect(dockview.panels.length).toBe(3); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - dockview.moveGroupOrPanel( - panel3.group, - panel1.group.id, - undefined, - 'center' - ); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + floating: true, + }); - expect(panel1.group.api.location).toBe('floating'); - expect(panel2.group.api.location).toBe('grid'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); - }); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + position: { referencePanel: panel2 }, + }); - test('floating: move a fixed group of many tabs to an existing floating group', () => { - const container = document.createElement('div'); + const panel4 = dockview.addPanel({ + id: 'panel_4', + component: 'default', + floating: true, + }); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(panel4.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(3); + expect(dockview.panels.length).toBe(4); + + dockview.moveGroupOrPanel( + panel4.group, + panel2.group.id, + undefined, + 'center' + ); + + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(panel4.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(4); + }); + + test('move a floating tab of one tab to a new fixed group', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - dockview.layout(1000, 500); + dockview.layout(1000, 500); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - }); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + floating: true, + }); - const panel3 = dockview.addPanel({ - id: 'panel_3', - component: 'default', - floating: true, + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(2); + + dockview.moveGroupOrPanel( + panel1.group, + panel2.group.id, + panel2.id, + 'right' + ); + + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(2); }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(3); + test('move a floating tab of one tab to an existing fixed group', () => { + const container = document.createElement('div'); - dockview.moveGroupOrPanel( - panel3.group, - panel1.group.id, - undefined, - 'center' - ); + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - expect(panel1.group.api.location).toBe('floating'); - expect(panel2.group.api.location).toBe('floating'); - expect(panel3.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(1); - expect(dockview.panels.length).toBe(3); - }); + dockview.layout(1000, 500); - test('floating: move a fixed tab of one tab to a new floating group', () => { - const container = document.createElement('div'); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + floating: true, + }); - dockview.layout(1000, 500); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(2); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + dockview.moveGroupOrPanel( + panel1.group, + panel2.group.id, + panel2.id, + 'center' + ); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - position: { direction: 'right' }, + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(1); + expect(dockview.panels.length).toBe(2); }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(2); + test('move a floating tab of one tab to an existing floating group', () => { + const container = document.createElement('div'); - dockview.addFloatingGroup(panel2); + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(2); - }); + dockview.layout(1000, 500); - test('floating: move a fixed tab of many tabs to a new floating group', () => { - const container = document.createElement('div'); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + floating: true, + }); - dockview.layout(1000, 500); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + floating: true, + }); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(3); + expect(dockview.panels.length).toBe(3); + + dockview.moveGroupOrPanel( + panel2.group, + panel3.group.id, + panel3.id, + 'center' + ); + + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + }); + + test('move a floating tab of many tabs to a new fixed group', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - }); + dockview.layout(1000, 500); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(dockview.groups.length).toBe(1); - expect(dockview.panels.length).toBe(2); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - dockview.addFloatingGroup(panel2); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + floating: true, + }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(2); - }); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + position: { referencePanel: panel2 }, + }); - test('floating: move a fixed group of one tab to a new floating group', () => { - const container = document.createElement('div'); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + + dockview.moveGroupOrPanel( + panel1.group, + panel2.group.id, + panel2.id, + 'right' + ); + + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(3); + expect(dockview.panels.length).toBe(3); + }); + + test('move a floating tab of many tabs to an existing fixed group', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + dockview.layout(1000, 500); - dockview.layout(1000, 500); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + floating: true, + }); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - position: { direction: 'right' }, - }); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + position: { referencePanel: panel2 }, + }); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(2); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + + dockview.moveGroupOrPanel( + panel1.group, + panel2.group.id, + panel2.id, + 'center' + ); + + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + }); + + test('move a floating tab of many tabs to an existing floating group', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - dockview.addFloatingGroup(panel2.group); + dockview.layout(1000, 500); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(2); - }); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - test('floating: move a fixed group of many tabs to a new floating group', () => { - const container = document.createElement('div'); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + floating: true, + }); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + position: { referencePanel: panel2 }, + }); - dockview.layout(1000, 500); + const panel4 = dockview.addPanel({ + id: 'panel_4', + component: 'default', + floating: true, + }); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(panel4.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(3); + expect(dockview.panels.length).toBe(4); + + dockview.moveGroupOrPanel( + panel4.group, + panel2.group.id, + panel2.id, + 'center' + ); + + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(panel4.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(3); + expect(dockview.panels.length).toBe(4); + }); + + test('move a fixed tab of one tab to an existing floating group', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - }); + dockview.layout(1000, 500); - expect(panel1.group.api.location).toBe('grid'); - expect(panel2.group.api.location).toBe('grid'); - expect(dockview.groups.length).toBe(1); - expect(dockview.panels.length).toBe(2); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - dockview.addFloatingGroup(panel2.group); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + position: { direction: 'right' }, + }); - expect(panel1.group.api.location).toBe('floating'); - expect(panel2.group.api.location).toBe('floating'); - expect(dockview.groups.length).toBe(1); - expect(dockview.panels.length).toBe(2); - }); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + floating: true, + }); - test('that moving the last panel to be floating should leave an empty gridview', () => { - const container = document.createElement('div'); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(3); + expect(dockview.panels.length).toBe(3); + + dockview.moveGroupOrPanel( + panel3.group, + panel1.group.id, + panel1.id, + 'center' + ); + + expect(panel1.group.api.location).toBe('floating'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + }); + + test('move a fixed tab of many tabs to an existing floating group', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + dockview.layout(1000, 500); - dockview.layout(1000, 500); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + }); - expect( - dockview.element.querySelectorAll('.view-container > .view').length - ).toBe(1); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + floating: true, + }); - dockview.addFloatingGroup(panel1); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + + dockview.moveGroupOrPanel( + panel3.group, + panel1.group.id, + panel1.id, + 'center' + ); + + expect(panel1.group.api.location).toBe('floating'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + }); + + test('move a fixed group of one tab to an existing floating group', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - expect( - dockview.element.querySelectorAll('.view-container > .view').length - ).toBe(0); - }); + dockview.layout(1000, 500); - test('that api.setSize applies to the overlay for floating panels', () => { - const container = document.createElement('div'); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + position: { direction: 'right' }, + }); - dockview.layout(1000, 500); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + floating: true, + }); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - floating: true, - }); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(3); + expect(dockview.panels.length).toBe(3); + + dockview.moveGroupOrPanel( + panel3.group, + panel1.group.id, + undefined, + 'center' + ); + + expect(panel1.group.api.location).toBe('floating'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + }); + + test('move a fixed group of many tabs to an existing floating group', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - panel1.api.setSize({ height: 123, width: 256 }); + dockview.layout(1000, 500); - const items = dockview.element.querySelectorAll('.dv-resize-container'); - expect(items.length).toBe(1); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - const el = items[0] as HTMLElement; + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + }); - expect(el.style.height).toBe('123px'); - expect(el.style.width).toBe('256px'); - }); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + floating: true, + }); - test('that external dnd events do not trigger the top-level center dnd target unless empty', () => { - const container = document.createElement('div'); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + + dockview.moveGroupOrPanel( + panel3.group, + panel1.group.id, + undefined, + 'center' + ); + + expect(panel1.group.api.location).toBe('floating'); + expect(panel2.group.api.location).toBe('floating'); + expect(panel3.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(1); + expect(dockview.panels.length).toBe(3); + }); + + test('move a fixed tab of one tab to a new floating group', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - const showDndOverlay = jest.fn().mockReturnValue(true); + dockview.layout(1000, 500); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - showDndOverlay: showDndOverlay, - }); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - dockview.layout(1000, 500); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + position: { direction: 'right' }, + }); - const panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); - const panel2 = dockview.addPanel({ - id: 'panel_2', - component: 'default', - position: { direction: 'right' }, - }); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(2); - Object.defineProperty(dockview.element, 'clientWidth', { - get: () => 100, - }); - Object.defineProperty(dockview.element, 'clientHeight', { - get: () => 100, + dockview.addFloatingGroup(panel2); + + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(2); }); - jest.spyOn(dockview.element, 'getBoundingClientRect').mockReturnValue({ - left: 0, - top: 0, - width: 100, - height: 100, - } as any); + test('move a fixed tab of many tabs to a new floating group', () => { + const container = document.createElement('div'); - // left + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - const eventLeft = new KeyboardEvent('dragover'); - Object.defineProperty(eventLeft, 'clientX', { - get: () => 0, - }); - Object.defineProperty(eventLeft, 'clientY', { - get: () => 0, - }); - fireEvent(dockview.element, eventLeft); + dockview.layout(1000, 500); - expect(showDndOverlay).toHaveBeenCalledWith({ - nativeEvent: eventLeft, - position: 'left', - target: DockviewDropTargets.Edge, - getData: getPanelData, - }); - expect(showDndOverlay).toBeCalledTimes(1); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - // right + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + }); - const eventRight = new KeyboardEvent('dragover'); - Object.defineProperty(eventRight, 'clientX', { - get: () => 100, - }); - Object.defineProperty(eventRight, 'clientY', { - get: () => 100, - }); - fireEvent(dockview.element, eventRight); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(1); + expect(dockview.panels.length).toBe(2); - expect(showDndOverlay).toHaveBeenCalledWith({ - nativeEvent: eventRight, - position: 'right', - target: DockviewDropTargets.Edge, - getData: getPanelData, + dockview.addFloatingGroup(panel2); + + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(2); }); - expect(showDndOverlay).toBeCalledTimes(2); - // top + test('move a fixed group of one tab to a new floating group', () => { + const container = document.createElement('div'); - const eventTop = new KeyboardEvent('dragover'); - Object.defineProperty(eventTop, 'clientX', { - get: () => 50, - }); - Object.defineProperty(eventTop, 'clientY', { - get: () => 0, - }); - fireEvent(dockview.element, eventTop); + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - expect(showDndOverlay).toHaveBeenCalledWith({ - nativeEvent: eventTop, - position: 'top', - target: DockviewDropTargets.Edge, - getData: getPanelData, - }); - expect(showDndOverlay).toBeCalledTimes(3); + dockview.layout(1000, 500); - // top + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - const eventBottom = new KeyboardEvent('dragover'); - Object.defineProperty(eventBottom, 'clientX', { - get: () => 50, - }); - Object.defineProperty(eventBottom, 'clientY', { - get: () => 100, - }); - fireEvent(dockview.element, eventBottom); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + position: { direction: 'right' }, + }); - expect(showDndOverlay).toHaveBeenCalledWith({ - nativeEvent: eventBottom, - position: 'bottom', - target: DockviewDropTargets.Edge, - getData: getPanelData, - }); - expect(showDndOverlay).toBeCalledTimes(4); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(2); - // center + dockview.addFloatingGroup(panel2.group); - const eventCenter = new KeyboardEvent('dragover'); - Object.defineProperty(eventCenter, 'clientX', { - get: () => 50, + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(2); }); - Object.defineProperty(eventCenter, 'clientY', { - get: () => 50, - }); - fireEvent(dockview.element, eventCenter); - // expect not to be called for center - expect(showDndOverlay).toBeCalledTimes(4); + test('move a fixed group of many tabs to a new floating group', () => { + const container = document.createElement('div'); - dockview.removePanel(panel1); - dockview.removePanel(panel2); + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - // center, but empty + dockview.layout(1000, 500); - const eventCenter2 = new KeyboardEvent('dragover'); - Object.defineProperty(eventCenter2, 'clientX', { - get: () => 50, - }); - Object.defineProperty(eventCenter2, 'clientY', { - get: () => 50, - }); - fireEvent(dockview.element, eventCenter2); - - expect(showDndOverlay).toHaveBeenCalledWith({ - nativeEvent: eventTop, - position: 'center', - target: DockviewDropTargets.Edge, - getData: getPanelData, - }); - expect(showDndOverlay).toBeCalledTimes(5); - }); - - test('that dragging a tab triggers onWillDragPanel', () => { - const container = document.createElement('div'); - - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - dockview.layout(1000, 500); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + }); - dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(1); + expect(dockview.panels.length).toBe(2); - const tabDragEvents: TabDragEvent[] = []; - const groupDragEvents: GroupDragEvent[] = []; + dockview.addFloatingGroup(panel2.group); - dockview.onWillDragPanel((event) => { - tabDragEvents.push(event); + expect(panel1.group.api.location).toBe('floating'); + expect(panel2.group.api.location).toBe('floating'); + expect(dockview.groups.length).toBe(1); + expect(dockview.panels.length).toBe(2); }); - dockview.onWillDragGroup((event) => { - groupDragEvents.push(event); - }); - - const el = dockview.element.querySelector('.tab')!; - expect(el).toBeTruthy(); - - fireEvent.dragStart(el); - - expect(tabDragEvents.length).toBe(1); - expect(groupDragEvents.length).toBe(0); }); - test('that dragging a group triggers onWillDragGroup', () => { - const container = document.createElement('div'); - - const dockview = new DockviewComponent({ - parentElement: container, - components: { - default: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); - - dockview.layout(1000, 500); - - dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); - - const tabDragEvents: TabDragEvent[] = []; - const groupDragEvents: GroupDragEvent[] = []; - - dockview.onWillDragPanel((event) => { - tabDragEvents.push(event); - }); - dockview.onWillDragGroup((event) => { - groupDragEvents.push(event); - }); - - const el = dockview.element.querySelector('.void-container')!; - expect(el).toBeTruthy(); + describe('popout group', () => { + test('that can remove a popout group', () => { + const container = document.createElement('div'); - fireEvent.dragStart(el); + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); - expect(tabDragEvents.length).toBe(0); - expect(groupDragEvents.length).toBe(1); - }); + dockview.layout(1000, 500); - test('corrupt layout: bad inline view', () => { - const container = document.createElement('div'); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - panelA: PanelContentPartTest, - panelB: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + dockview.addPopoutGroup(panel1); - dockview.layout(1000, 500); + expect(dockview.panels.length).toBe(1); + expect(dockview.groups.length).toBe(1); + expect(panel1.api.group.api.location).toBe('popout'); - let el = dockview.element.querySelector('.view-container'); - expect(el).toBeTruthy(); - expect(el!.childNodes.length).toBe(0); + dockview.removePanel(panel1); - dockview.addPanel({ - id: 'panel_1', - component: 'panelA', + expect(dockview.panels.length).toBe(0); + expect(dockview.groups.length).toBe(0); }); - expect(dockview.groups.length).toBe(1); - expect(dockview.panels.length).toBe(1); - - el = dockview.element.querySelector('.view-container'); - expect(el).toBeTruthy(); - expect(el!.childNodes.length).toBeGreaterThan(0); + test('add a popout group', () => { + const container = document.createElement('div'); - expect(() => { - dockview.fromJSON({ - grid: { - root: { - type: 'branch', - data: [ - { - type: 'leaf', - data: { - views: ['panelA'], - activeView: 'panelA', - id: '1', - }, - size: 841, - }, - { - type: 'leaf', - data: { - views: ['panelB'], - activeView: 'panelB', - id: '2', - }, - size: 842, - }, - ], - size: 530, - }, - width: 1683, - height: 530, - orientation: Orientation.HORIZONTAL, + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, }, - panels: { - panelA: { - id: 'panelA', - contentComponent: 'panelA', - title: 'Panel A', - }, - panelB: { - id: 'panelB', - contentComponent: 'somethingBad', - title: 'Panel B', - }, + tabComponents: { + test_tab_id: PanelTabPartTest, }, - activeGroup: '1', + orientation: Orientation.HORIZONTAL, }); - }).toThrow( - "Cannot create 'panelB', no component 'somethingBad' provided" - ); - expect(dockview.groups.length).toBe(0); - expect(dockview.panels.length).toBe(0); + dockview.layout(1000, 500); - el = dockview.element.querySelector('.view-container'); - expect(el).toBeTruthy(); - expect(el!.childNodes.length).toBe(0); - }); - - test('corrupt layout: bad floating view', () => { - const container = document.createElement('div'); - - const dockview = new DockviewComponent({ - parentElement: container, - components: { - panelA: PanelContentPartTest, - panelB: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - dockview.layout(1000, 500); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + }); - let el = dockview.element.querySelector('.view-container'); - expect(el).toBeTruthy(); - expect(el!.childNodes.length).toBe(0); + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(1); + expect(dockview.panels.length).toBe(2); - dockview.addPanel({ - id: 'panel_1', - component: 'panelA', - }); + dockview.addPopoutGroup(panel2.group); - dockview.addPanel({ - id: 'panel_2', - component: 'panelA', - floating: true, + expect(panel1.group.api.location).toBe('popout'); + expect(panel2.group.api.location).toBe('popout'); + expect(dockview.groups.length).toBe(1); + expect(dockview.panels.length).toBe(2); }); - expect(dockview.groups.length).toBe(2); - expect(dockview.panels.length).toBe(2); - - el = dockview.element.querySelector('.dv-resize-container'); - expect(el).toBeTruthy(); + test('move from fixed to popout group and back', () => { + const container = document.createElement('div'); - el = dockview.element.querySelector('.view-container'); - expect(el).toBeTruthy(); - expect(el!.childNodes.length).toBeGreaterThan(0); - - expect(() => { - dockview.fromJSON({ - grid: { - root: { - type: 'branch', - data: [ - { - type: 'leaf', - data: { - views: ['panelA'], - activeView: 'panelA', - id: '1', - }, - size: 841, - }, - { - type: 'leaf', - data: { - views: ['panelB'], - activeView: 'panelB', - id: '2', - }, - size: 842, - }, - ], - size: 530, - }, - width: 1683, - height: 530, - orientation: Orientation.HORIZONTAL, + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, }, - floatingGroups: [ - { - data: { - views: ['panelB'], - activeView: 'panelB', - id: '3', - }, - position: { left: 0, top: 0, height: 100, width: 100 }, - }, - { - data: { - views: ['panelC'], - activeView: 'panelC', - id: '4', - }, - position: { left: 0, top: 0, height: 100, width: 100 }, - }, - ], - panels: { - panelA: { - id: 'panelA', - contentComponent: 'panelA', - title: 'Panel A', - }, - panelB: { - id: 'panelB', - contentComponent: 'panelB', - title: 'Panel B', - }, - panelC: { - id: 'panelC', - contentComponent: 'panelC', - title: 'Panel C', - }, + tabComponents: { + test_tab_id: PanelTabPartTest, }, - activeGroup: '1', + orientation: Orientation.HORIZONTAL, }); - }).toThrow("Cannot create 'panelC', no component 'panelC' provided"); - - expect(dockview.groups.length).toBe(0); - expect(dockview.panels.length).toBe(0); - - el = dockview.element.querySelector('.dv-resize-container'); - expect(el).toBeFalsy(); - - el = dockview.element.querySelector('.view-container'); - expect(el).toBeTruthy(); - expect(el!.childNodes.length).toBe(0); - }); - test('that disableAutoResizing is false by default', () => { - const container = document.createElement('div'); + dockview.layout(1000, 500); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - panelA: PanelContentPartTest, - panelB: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - }); + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); - expect(dockview.disableResizing).toBeFalsy(); - }); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + }); - test('that disableAutoResizing can be enabled', () => { - const container = document.createElement('div'); + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + position: { + direction: 'right', + }, + }); - const dockview = new DockviewComponent({ - parentElement: container, - components: { - panelA: PanelContentPartTest, - panelB: PanelContentPartTest, - }, - tabComponents: { - test_tab_id: PanelTabPartTest, - }, - orientation: Orientation.HORIZONTAL, - disableAutoResizing: true, + expect(panel1.group.api.location).toBe('grid'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + + dockview.addPopoutGroup(panel2.group); + + expect(panel1.group.api.location).toBe('popout'); + expect(panel2.group.api.location).toBe('popout'); + expect(panel3.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(2); + expect(dockview.panels.length).toBe(3); + + dockview.moveGroupOrPanel( + panel3.api.group, + panel2.api.group.id, + panel2.api.id, + 'right' + ); + + expect(panel1.group.api.location).toBe('popout'); + expect(panel2.group.api.location).toBe('grid'); + expect(panel3.group.api.location).toBe('grid'); + expect(dockview.groups.length).toBe(3); + expect(dockview.panels.length).toBe(3); }); - - expect(dockview.disableResizing).toBeTruthy(); }); }); diff --git a/packages/dockview-core/src/__tests__/lifecycle.spec.ts b/packages/dockview-core/src/__tests__/lifecycle.spec.ts index db67c750c..8a7ae1332 100644 --- a/packages/dockview-core/src/__tests__/lifecycle.spec.ts +++ b/packages/dockview-core/src/__tests__/lifecycle.spec.ts @@ -1,4 +1,8 @@ -import { CompositeDisposable, MutableDisposable } from '../lifecycle'; +import { + CompositeDisposable, + Disposable, + MutableDisposable, +} from '../lifecycle'; describe('lifecycle', () => { test('mutable disposable', () => { @@ -64,4 +68,16 @@ describe('lifecycle', () => { expect(cut.checkIsDisposed()).toBeTruthy(); }); + + test('Disposable.from(...)', () => { + const func = jest.fn(); + + const disposable = Disposable.from(func); + + expect(func).not.toHaveBeenCalled(); + + disposable.dispose(); + + expect(func).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/dockview-core/src/api/dockviewGroupPanelApi.ts b/packages/dockview-core/src/api/dockviewGroupPanelApi.ts index 8b075d620..c4b349bdf 100644 --- a/packages/dockview-core/src/api/dockviewGroupPanelApi.ts +++ b/packages/dockview-core/src/api/dockviewGroupPanelApi.ts @@ -1,4 +1,4 @@ -import { Position } from '../dnd/droptarget'; +import { Position, positionToDirection } from '../dnd/droptarget'; import { DockviewComponent } from '../dockview/dockviewComponent'; import { DockviewGroupPanel } from '../dockview/dockviewGroupPanel'; import { DockviewGroupLocation } from '../dockview/dockviewGroupPanelModel'; @@ -6,9 +6,9 @@ import { Emitter, Event } from '../events'; import { GridviewPanelApi, GridviewPanelApiImpl } from './gridviewPanelApi'; export interface DockviewGroupPanelApi extends GridviewPanelApi { - readonly onDidRenderPositionChange: Event; + readonly onDidLocationChange: Event; readonly location: DockviewGroupLocation; - moveTo(options: { group: DockviewGroupPanel; position?: Position }): void; + moveTo(options: { group?: DockviewGroupPanel; position?: Position }): void; maximize(): void; isMaximized(): boolean; exitMaximized(): void; @@ -24,10 +24,10 @@ const NOT_INITIALIZED_MESSAGE = 'DockviewGroupPanelApiImpl not initialized'; export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl { private _group: DockviewGroupPanel | undefined; - readonly _onDidRenderPositionChange = + readonly _onDidLocationChange = new Emitter(); - readonly onDidRenderPositionChange: Event = - this._onDidRenderPositionChange.event; + readonly onDidLocationChange: Event = + this._onDidLocationChange.event; get location(): DockviewGroupLocation { if (!this._group) { @@ -39,19 +39,25 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl { constructor(id: string, private readonly accessor: DockviewComponent) { super(id); - this.addDisposables(this._onDidRenderPositionChange); + this.addDisposables(this._onDidLocationChange); } - moveTo(options: { group: DockviewGroupPanel; position?: Position }): void { + moveTo(options: { group?: DockviewGroupPanel; position?: Position }): void { if (!this._group) { throw new Error(NOT_INITIALIZED_MESSAGE); } + const group = + options.group ?? + this.accessor.addGroup({ + direction: positionToDirection(options.position ?? 'right'), + }); + this.accessor.moveGroupOrPanel( - options.group, + group, this._group.id, undefined, - options.position ?? 'center' + options.group ? options.position ?? 'center' : 'center' ); } @@ -60,6 +66,11 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl { throw new Error(NOT_INITIALIZED_MESSAGE); } + if (this.location !== 'grid') { + // only grid groups can be maximized + return; + } + this.accessor.maximizeGroup(this._group); } diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index e17087902..019b2789a 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -7,7 +7,7 @@ import { import { directionToPosition, Droptarget, Position } from '../dnd/droptarget'; import { tail, sequenceEquals, remove } from '../array'; import { DockviewPanel, IDockviewPanel } from './dockviewPanel'; -import { CompositeDisposable } from '../lifecycle'; +import { CompositeDisposable, Disposable } from '../lifecycle'; import { Event, Emitter } from '../events'; import { Watermark } from './components/watermark/watermark'; import { @@ -58,7 +58,10 @@ import { DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE, DEFAULT_FLOATING_GROUP_POSITION, } from '../constants'; -import { DockviewPanelRenderer, OverlayRenderContainer } from '../overlayRenderContainer'; +import { + DockviewPanelRenderer, + OverlayRenderContainer, +} from '../overlayRenderContainer'; function getTheme(element: HTMLElement): string | undefined { function toClassList(element: HTMLElement) { @@ -386,6 +389,17 @@ export class DockviewComponent this.onDidActivePanelChange )(() => { this._bufferOnDidLayoutChange.fire(); + }), + Disposable.from(() => { + // iterate over a copy of the array since .dispose() mutates the original array + for (const group of [...this._floatingGroups]) { + group.dispose(); + } + + // iterate over a copy of the array since .dispose() mutates the original array + for (const group of [...this._popoutGroups]) { + group.dispose(); + } }) ); diff --git a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts index 69c1c0abc..4f2c0f9ad 100644 --- a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts +++ b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts @@ -283,7 +283,7 @@ export class DockviewGroupPanelModel break; } - this.groupPanel.api._onDidRenderPositionChange.fire({ + this.groupPanel.api._onDidLocationChange.fire({ location: this.location, }); } diff --git a/packages/dockview-core/src/lifecycle.ts b/packages/dockview-core/src/lifecycle.ts index c2a307adc..69936fff2 100644 --- a/packages/dockview-core/src/lifecycle.ts +++ b/packages/dockview-core/src/lifecycle.ts @@ -13,6 +13,14 @@ export namespace Disposable { // noop }, }; + + export function from(func: () => void): IDisposable { + return { + dispose: () => { + func(); + }, + }; + } } export class CompositeDisposable { diff --git a/packages/dockview-core/src/popoutWindow.ts b/packages/dockview-core/src/popoutWindow.ts index 2994e44ec..c73334549 100644 --- a/packages/dockview-core/src/popoutWindow.ts +++ b/packages/dockview-core/src/popoutWindow.ts @@ -75,10 +75,7 @@ export class PopoutWindow extends CompositeDisposable { this._window = { value: externalWindow, disposable }; - const grievingParent = content.parentElement; - const cleanUp = () => { - grievingParent?.appendChild(content); this._onDidClose.fire(); this._window = null; }; diff --git a/packages/docs/docs/components/dockview.mdx b/packages/docs/docs/components/dockview.mdx index 6dd3a8935..35231ef41 100644 --- a/packages/docs/docs/components/dockview.mdx +++ b/packages/docs/docs/components/dockview.mdx @@ -399,6 +399,17 @@ From within a panel you may say props.containerApi.addPopoutGroup(props.api.group); ``` +To programatically move the popout group back into the main grid you can use the `moveTo` method in many ways, one of the following would suffice + +```tsx +// option 1: add absolutely to the right-side of the grid +props.group.api.moveTo({position: 'right'}); + +// option 2: create a new group and move the contents of the popout group to it +const group = props.containerApi.addGroup(); +props.group.api.moveTo({ group }); +``` + = { panel_1: () => { return ; }, @@ -130,6 +130,10 @@ const RightControls = (props: IDockviewHeaderActionsProps) => { : 'expand_content' ); + const [popoutIcon, setPopoutIcon] = React.useState( + props.api.location === 'popout' ? 'close_fullscreen' : 'open_in_new' + ); + React.useEffect(() => { const disposable = props.containerApi.onDidMaxmizedGroupChange(() => { setIcon( @@ -139,8 +143,17 @@ const RightControls = (props: IDockviewHeaderActionsProps) => { ); }); + const disposable2 = props.api.onDidLocationChange(() => { + setPopoutIcon( + props.api.location === 'popout' + ? 'close_fullscreen' + : 'open_in_new' + ); + }); + return () => { disposable.dispose(); + disposable2.dispose(); }; }, [props.containerApi]); @@ -152,6 +165,14 @@ const RightControls = (props: IDockviewHeaderActionsProps) => { } }; + const onClick2 = () => { + if (props.api.location !== 'popout') { + props.containerApi.addPopoutGroup(props.group); + } else { + props.api.moveTo({ position: 'right' }); + } + }; + return (
{ > {props.isGroupActive && } {Component && } +
); diff --git a/packages/docs/sandboxes/floatinggroup-dockview/src/app.tsx b/packages/docs/sandboxes/floatinggroup-dockview/src/app.tsx index d90e8c5ff..990913d2e 100644 --- a/packages/docs/sandboxes/floatinggroup-dockview/src/app.tsx +++ b/packages/docs/sandboxes/floatinggroup-dockview/src/app.tsx @@ -259,7 +259,7 @@ const RightComponent = (props: IDockviewHeaderActionsProps) => { ); React.useEffect(() => { - const disposable = props.group.api.onDidRenderPositionChange( + const disposable = props.group.api.onDidLocationChange( (event) => { setFloating(event.location === 'floating'); } diff --git a/packages/docs/sandboxes/popoutgroup-dockview/src/app.tsx b/packages/docs/sandboxes/popoutgroup-dockview/src/app.tsx index e1d130070..d000bc3c4 100644 --- a/packages/docs/sandboxes/popoutgroup-dockview/src/app.tsx +++ b/packages/docs/sandboxes/popoutgroup-dockview/src/app.tsx @@ -218,7 +218,7 @@ const RightComponent = (props: IDockviewHeaderActionsProps) => { ); React.useEffect(() => { - const disposable = props.group.api.onDidRenderPositionChange( + const disposable = props.group.api.onDidLocationChange( (event) => [setPopout(event.location === 'popout')] );