diff --git a/packages/dockview/src/__tests__/groupview/groupview.spec.ts b/packages/dockview/src/__tests__/groupview/groupview.spec.ts index 266eaf36c..ebe94b389 100644 --- a/packages/dockview/src/__tests__/groupview/groupview.spec.ts +++ b/packages/dockview/src/__tests__/groupview/groupview.spec.ts @@ -12,11 +12,8 @@ import { IWatermarkRenderer, } from '../../groupview/types'; import { PanelUpdateEvent } from '../../panel/types'; -import { fireEvent } from '@testing-library/dom'; -import { LocalSelectionTransfer } from '../../dnd/dataTransfer'; -import { Position } from '../../dnd/droptarget'; import { GroupviewPanel } from '../../groupview/groupviewPanel'; -import { GroupOptions } from '../../groupview/groupview'; +import { GroupChangeKind, GroupOptions } from '../../groupview/groupview'; import { DockviewPanelApi } from '../../api/groupPanelApi'; import { DefaultGroupPanelView, @@ -222,6 +219,7 @@ describe('groupview', () => { tabHeight: 30, }; groupview = new GroupviewPanel(dockview, 'groupview-1', options); + groupview.initialize(); }); test('serialized layout shows active panel', () => { @@ -234,6 +232,7 @@ describe('groupview', () => { panels: [panel1, panel2, panel3], activePanel: panel2, }); + groupview2.initialize(); expect(groupview2.model.activePanel).toBe(panel2); @@ -248,6 +247,138 @@ describe('groupview', () => { ).toBeFalsy(); }); + test('panel events are captured during de-serialization', () => { + const panel1 = new TestPanel('panel1', jest.fn() as any); + const panel2 = new TestPanel('panel2', jest.fn() as any); + const panel3 = new TestPanel('panel3', jest.fn() as any); + + const groupview2 = new GroupviewPanel(dockview, 'groupview-2', { + tabHeight: 25, + panels: [panel1, panel2, panel3], + activePanel: panel2, + }); + + const events: Array<{ + kind: GroupChangeKind; + }> = []; + const disposable = groupview2.model.onDidGroupChange((e) => { + events.push(e); + }); + + groupview2.initialize(); + + expect(events).toEqual([ + { + kind: GroupChangeKind.ADD_PANEL, + panel: panel1, + }, + { + kind: GroupChangeKind.ADD_PANEL, + panel: panel2, + }, + { + kind: GroupChangeKind.ADD_PANEL, + panel: panel3, + }, + { + kind: GroupChangeKind.PANEL_ACTIVE, + panel: panel2, + }, + ]); + + disposable.dispose(); + }); + + test('panel events flow', () => { + let events: Array<{ + kind: GroupChangeKind; + }> = []; + const disposable = groupview.model.onDidGroupChange((e) => { + events.push(e); + }); + + const panel1 = new TestPanel('panel1', jest.fn() as any); + const panel2 = new TestPanel('panel2', jest.fn() as any); + const panel3 = new TestPanel('panel3', jest.fn() as any); + + expect(events.length).toBe(0); + + groupview.model.openPanel(panel1); + expect(events).toEqual([ + { + kind: GroupChangeKind.ADD_PANEL, + panel: panel1, + }, + { + kind: GroupChangeKind.PANEL_ACTIVE, + panel: panel1, + }, + ]); + events = []; + + groupview.model.openPanel(panel2); + expect(events).toEqual([ + { + kind: GroupChangeKind.ADD_PANEL, + panel: panel2, + }, + { + kind: GroupChangeKind.PANEL_ACTIVE, + panel: panel2, + }, + ]); + events = []; + + groupview.model.openPanel(panel3); + expect(events).toEqual([ + { + kind: GroupChangeKind.ADD_PANEL, + panel: panel3, + }, + { + kind: GroupChangeKind.PANEL_ACTIVE, + panel: panel3, + }, + ]); + events = []; + + groupview.model.removePanel(panel3); + expect(events).toEqual([ + { + kind: GroupChangeKind.REMOVE_PANEL, + panel: panel3, + }, + { + kind: GroupChangeKind.PANEL_ACTIVE, + panel: panel2, + }, + ]); + events = []; + + groupview.model.removePanel(panel1); + expect(events).toEqual([ + { + kind: GroupChangeKind.REMOVE_PANEL, + panel: panel1, + }, + ]); + events = []; + + groupview.model.removePanel(panel2); + expect(events).toEqual([ + { + kind: GroupChangeKind.REMOVE_PANEL, + panel: panel2, + }, + { + kind: GroupChangeKind.PANEL_ACTIVE, + }, + ]); + events = []; + + disposable.dispose(); + }); + test('moveToPrevious and moveToNext', () => { const panel1 = new TestPanel('panel1', jest.fn() as any); const panel2 = new TestPanel('panel2', jest.fn() as any); @@ -293,45 +424,4 @@ describe('groupview', () => { ); expect(viewQuery).toBeTruthy(); }); - - // test('dnd', () => { - // const panel1 = new TestPanel('panel1', jest.fn() as any); - // const panel2 = new TestPanel('panel2', jest.fn() as any); - - // groupview.model.openPanel(panel1); - // groupview.model.openPanel(panel2); - - // const events: GroupDropEvent[] = []; - - // groupview.model.onDrop((event) => { - // events.push(event); - // }); - - // const viewQuery = groupview.element.querySelectorAll( - // '.groupview > .tabs-and-actions-container > .tabs-container > .tab' - // ); - // expect(viewQuery.length).toBe(2); - - // LocalSelectionTransfer.getInstance().setData([], 'dockview-1'); - - // fireEvent.dragEnter(viewQuery[0]); - - // let dropTarget = viewQuery[0].querySelector('.drop-target-dropzone'); - // fireEvent.dragOver(dropTarget); - // fireEvent.drop(dropTarget); - - // expect(events.length).toBe(1); - // expect(events[0].target).toBe(Position.Center); - // expect(events[0].index).toBe(0); - - // fireEvent.dragEnter(viewQuery[1]); - - // dropTarget = viewQuery[1].querySelector('.drop-target-dropzone'); - // fireEvent.dragOver(dropTarget); - // fireEvent.drop(dropTarget); - - // expect(events.length).toBe(2); - // expect(events[1].target).toBe(Position.Center); - // expect(events[1].index).toBe(1); - // }); }); diff --git a/packages/dockview/src/__tests__/react/react.spec.tsx b/packages/dockview/src/__tests__/react/react.spec.tsx index 4bd7dbbf2..9be93848d 100644 --- a/packages/dockview/src/__tests__/react/react.spec.tsx +++ b/packages/dockview/src/__tests__/react/react.spec.tsx @@ -1,6 +1,6 @@ import { ReactPart } from '../../react/react'; import * as React from 'react'; -import { render, screen } from '@testing-library/react'; +import { render, screen, act } from '@testing-library/react'; interface TestInterface { valueA: string; @@ -23,12 +23,16 @@ describe('react', () => { expect(screen.getByTestId('valueA').textContent).toBe('stringA'); expect(screen.getByTestId('valueB').textContent).toBe('42'); - api.update({ valueB: '32' }); + act(() => { + api.update({ valueB: '32' }); + }); expect(screen.getByTestId('valueA').textContent).toBe('stringA'); expect(screen.getByTestId('valueB').textContent).toBe('32'); - api.update({ valueA: 'anotherStringA', valueB: '22' }); + act(() => { + api.update({ valueA: 'anotherStringA', valueB: '22' }); + }); expect(screen.getByTestId('valueA').textContent).toBe( 'anotherStringA' diff --git a/packages/dockview/src/api/gridviewPanelApi.ts b/packages/dockview/src/api/gridviewPanelApi.ts index 6b04dcebe..8523a0c4f 100644 --- a/packages/dockview/src/api/gridviewPanelApi.ts +++ b/packages/dockview/src/api/gridviewPanelApi.ts @@ -3,43 +3,45 @@ import { FunctionOrValue } from '../types'; import { PanelApiImpl, PanelApi } from './panelApi'; export interface GridConstraintChangeEvent { - minimumWidth?: number; - minimumHeight?: number; - maximumWidth?: number; - maximumHeight?: number; + readonly minimumWidth?: number; + readonly minimumHeight?: number; + readonly maximumWidth?: number; + readonly maximumHeight?: number; } interface GridConstraintChangeEvent2 { - minimumWidth?: FunctionOrValue; - minimumHeight?: FunctionOrValue; - maximumWidth?: FunctionOrValue; - maximumHeight?: FunctionOrValue; + readonly minimumWidth?: FunctionOrValue; + readonly minimumHeight?: FunctionOrValue; + readonly maximumWidth?: FunctionOrValue; + readonly maximumHeight?: FunctionOrValue; } export interface SizeEvent { - width?: number; - height?: number; + readonly width?: number; + readonly height?: number; } export interface GridviewPanelApi extends PanelApi { - onDidConstraintsChange: Event; + readonly onDidConstraintsChange: Event; setConstraints(value: GridConstraintChangeEvent2): void; setSize(event: SizeEvent): void; } export class GridviewPanelApiImpl extends PanelApiImpl - implements GridviewPanelApi { - readonly _onDidConstraintsChangeInternal = new Emitter(); - readonly onDidConstraintsChangeInternal: Event = this - ._onDidConstraintsChangeInternal.event; + implements GridviewPanelApi +{ + readonly _onDidConstraintsChangeInternal = + new Emitter(); + readonly onDidConstraintsChangeInternal: Event = + this._onDidConstraintsChangeInternal.event; // readonly _onDidConstraintsChange = new Emitter({ replay: true, }); - readonly onDidConstraintsChange: Event = this - ._onDidConstraintsChange.event; + readonly onDidConstraintsChange: Event = + this._onDidConstraintsChange.event; // readonly _onDidSizeChange = new Emitter(); diff --git a/packages/dockview/src/api/groupPanelApi.ts b/packages/dockview/src/api/groupPanelApi.ts index 9446aa345..8a0f34ee7 100644 --- a/packages/dockview/src/api/groupPanelApi.ts +++ b/packages/dockview/src/api/groupPanelApi.ts @@ -4,11 +4,11 @@ import { IGroupPanel } from '../groupview/groupPanel'; import { GroupviewPanel } from '../groupview/groupviewPanel'; export interface TitleEvent { - title: string; + readonly title: string; } export interface SuppressClosableEvent { - suppressClosable: boolean; + readonly suppressClosable: boolean; } /* @@ -21,7 +21,7 @@ export interface DockviewPanelApi readonly isGroupActive: boolean; readonly title: string; readonly suppressClosable: boolean; - onDidDirtyChange: Event; + readonly onDidDirtyChange: Event; close: () => Promise; interceptOnCloseAction(interceptor: () => Promise): void; setTitle(title: string): void; @@ -29,17 +29,14 @@ export interface DockviewPanelApi export class DockviewPanelApiImpl extends GridviewPanelApiImpl - implements DockviewPanelApi { + implements DockviewPanelApi +{ private _group: GroupviewPanel | undefined; private _interceptor: undefined | (() => Promise); readonly _onDidDirtyChange = new Emitter(); readonly onDidDirtyChange = this._onDidDirtyChange.event; - // readonly _onDidGroupPanelVisibleChange = new Emitter({ - // replay: true, - // }); - // readonly onDidGroupPanelVisibleChange: Event = this - // ._onDidGroupPanelVisibleChange.event; + readonly _onDidTitleChange = new Emitter(); readonly onDidTitleChange = this._onDidTitleChange.event; @@ -49,10 +46,6 @@ export class DockviewPanelApiImpl readonly _suppressClosableChanged = new Emitter(); readonly suppressClosableChanged = this._suppressClosableChanged.event; - // get isGroupVisible() { - // return this._isGroupVisible; - // } - get tryClose(): undefined | (() => Promise) { return this._interceptor; } @@ -81,13 +74,7 @@ export class DockviewPanelApiImpl super(panel.id); this._group = group; - this.addDisposables( - // this._onDidGroupPanelVisibleChange, - this._onDidDirtyChange - // this.onDidGroupPanelVisibleChange((event) => { - // this._isGroupVisible = event.isVisible; - // }) - ); + this.addDisposables(this._onDidDirtyChange); } public setTitle(title: string) { diff --git a/packages/dockview/src/api/panelApi.ts b/packages/dockview/src/api/panelApi.ts index 7769810d1..68ec9d8b7 100644 --- a/packages/dockview/src/api/panelApi.ts +++ b/packages/dockview/src/api/panelApi.ts @@ -21,29 +21,29 @@ export interface State { } export interface FocusEvent { - isFocused: boolean; + readonly isFocused: boolean; } export interface PanelDimensionChangeEvent { - width: number; - height: number; + readonly width: number; + readonly height: number; } export interface VisibilityEvent { - isVisible: boolean; + readonly isVisible: boolean; } export interface ActiveEvent { - isActive: boolean; + readonly isActive: boolean; } export interface PanelApi { // events - onDidDimensionsChange: Event; - onDidStateChange: Event; - onDidFocusChange: Event; - onDidVisibilityChange: Event; - onDidActiveChange: Event; - onFocusEvent: Event; + readonly onDidDimensionsChange: Event; + readonly onDidStateChange: Event; + readonly onDidFocusChange: Event; + readonly onDidVisibilityChange: Event; + readonly onDidActiveChange: Event; + readonly onFocusEvent: Event; // setVisible(isVisible: boolean): void; setActive(): void; @@ -92,11 +92,10 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi { readonly _onDidStateChange = new Emitter(); readonly onDidStateChange: Event = this._onDidStateChange.event; // - readonly _onDidPanelDimensionChange = new Emitter( - { + readonly _onDidPanelDimensionChange = + new Emitter({ replay: true, - } - ); + }); readonly onDidDimensionsChange = this._onDidPanelDimensionChange.event; // readonly _onDidChangeFocus = new Emitter({ @@ -110,19 +109,19 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi { readonly _onDidVisibilityChange = new Emitter({ replay: true, }); - readonly onDidVisibilityChange: Event = this - ._onDidVisibilityChange.event; + readonly onDidVisibilityChange: Event = + this._onDidVisibilityChange.event; // readonly _onVisibilityChange = new Emitter(); - readonly onVisibilityChange: Event = this - ._onVisibilityChange.event; + readonly onVisibilityChange: Event = + this._onVisibilityChange.event; // readonly _onDidActiveChange = new Emitter({ replay: true, }); - readonly onDidActiveChange: Event = this._onDidActiveChange - .event; + readonly onDidActiveChange: Event = + this._onDidActiveChange.event; // readonly _onActiveChange = new Emitter(); readonly onActiveChange: Event = this._onActiveChange.event; diff --git a/packages/dockview/src/api/paneviewPanelApi.ts b/packages/dockview/src/api/paneviewPanelApi.ts index 9b33d0348..4391edf66 100644 --- a/packages/dockview/src/api/paneviewPanelApi.ts +++ b/packages/dockview/src/api/paneviewPanelApi.ts @@ -3,25 +3,26 @@ import { PaneviewPanel } from '../paneview/paneviewPanel'; import { SplitviewPanelApi, SplitviewPanelApiImpl } from './splitviewPanelApi'; export interface ExpansionEvent { - isExpanded: boolean; + readonly isExpanded: boolean; } export interface PaneviewPanelApi extends SplitviewPanelApi { - onDidExpansionChange: Event; + readonly isExpanded: boolean; + readonly onDidExpansionChange: Event; readonly onMouseEnter: Event; readonly onMouseLeave: Event; setExpanded(isExpanded: boolean): void; - readonly isExpanded: boolean; } export class PaneviewPanelApiImpl extends SplitviewPanelApiImpl - implements PaneviewPanelApi { + implements PaneviewPanelApi +{ readonly _onDidExpansionChange = new Emitter({ replay: true, }); - readonly onDidExpansionChange: Event = this - ._onDidExpansionChange.event; + readonly onDidExpansionChange: Event = + this._onDidExpansionChange.event; readonly _onMouseEnter = new Emitter({}); readonly onMouseEnter: Event = this._onMouseEnter.event; diff --git a/packages/dockview/src/api/splitviewPanelApi.ts b/packages/dockview/src/api/splitviewPanelApi.ts index 2ca41d471..a90f71639 100644 --- a/packages/dockview/src/api/splitviewPanelApi.ts +++ b/packages/dockview/src/api/splitviewPanelApi.ts @@ -4,43 +4,45 @@ import { FunctionOrValue } from '../types'; import { PanelApiImpl, PanelApi } from './panelApi'; interface PanelConstraintChangeEvent2 { - minimumSize?: FunctionOrValue; - maximumSize?: FunctionOrValue; + readonly minimumSize?: FunctionOrValue; + readonly maximumSize?: FunctionOrValue; } export interface PanelConstraintChangeEvent { - minimumSize?: number; - maximumSize?: number; + readonly minimumSize?: number; + readonly maximumSize?: number; } export interface PanelSizeEvent { - size: number; + readonly size: number; } export interface SplitviewPanelApi extends PanelApi { - onDidConstraintsChange: Event; + readonly onDidConstraintsChange: Event; setConstraints(value: PanelConstraintChangeEvent2): void; setSize(event: PanelSizeEvent): void; } export class SplitviewPanelApiImpl extends PanelApiImpl - implements SplitviewPanelApi, IDisposable { - readonly _onDidConstraintsChangeInternal = new Emitter(); - readonly onDidConstraintsChangeInternal: Event = this - ._onDidConstraintsChangeInternal.event; + implements SplitviewPanelApi, IDisposable +{ + readonly _onDidConstraintsChangeInternal = + new Emitter(); + readonly onDidConstraintsChangeInternal: Event = + this._onDidConstraintsChangeInternal.event; // readonly _onDidConstraintsChange = new Emitter({ replay: true, }); - readonly onDidConstraintsChange: Event = this - ._onDidConstraintsChange.event; + readonly onDidConstraintsChange: Event = + this._onDidConstraintsChange.event; // readonly _onDidSizeChange = new Emitter(); - readonly onDidSizeChange: Event = this._onDidSizeChange - .event; + readonly onDidSizeChange: Event = + this._onDidSizeChange.event; // constructor(id: string) { diff --git a/packages/dockview/src/dockview/dockviewComponent.ts b/packages/dockview/src/dockview/dockviewComponent.ts index f66752a49..8f9de1df3 100644 --- a/packages/dockview/src/dockview/dockviewComponent.ts +++ b/packages/dockview/src/dockview/dockviewComponent.ts @@ -625,10 +625,6 @@ export class DockviewComponent const view = new GroupviewPanel(this, id, options); - if (typeof this.options.tabHeight === 'number') { - view.model.tabHeight = this.options.tabHeight; - } - if (!this._groups.has(view.id)) { const disposable = new CompositeDisposable( view.model.onMove((event) => { @@ -643,6 +639,14 @@ export class DockviewComponent this._groups.set(view.id, { value: view, disposable }); } + // TODO: must be called after the above listeners have been setup, + // not an ideal pattern + view.initialize(); + + if (typeof this.options.tabHeight === 'number') { + view.model.tabHeight = this.options.tabHeight; + } + return view; } diff --git a/packages/dockview/src/groupview/groupview.ts b/packages/dockview/src/groupview/groupview.ts index e115a17a5..04c0d7e54 100644 --- a/packages/dockview/src/groupview/groupview.ts +++ b/packages/dockview/src/groupview/groupview.ts @@ -552,7 +552,7 @@ export class Groupview extends CompositeDisposable implements IGroupview { } if (this._activePanel && this.panels.length === 0) { - this._activePanel = undefined; + this.doSetActivePanel(undefined); } this.updateContainer(); @@ -601,20 +601,24 @@ export class Groupview extends CompositeDisposable implements IGroupview { this.updateMru(panel); this.panels.splice(index, 0, panel); - this._onDidGroupChange.fire({ kind: GroupChangeKind.ADD_PANEL }); + this._onDidGroupChange.fire({ kind: GroupChangeKind.ADD_PANEL, panel }); } - private doSetActivePanel(panel: IGroupPanel) { + private doSetActivePanel(panel: IGroupPanel | undefined) { this._activePanel = panel; - this.tabsContainer.setActivePanel(panel); - // this.contentContainer.openPanel(panel.content); + if (panel) { + this.tabsContainer.setActivePanel(panel); - panel.layout(this._width, this._height); + panel.layout(this._width, this._height); - this.updateMru(panel); + this.updateMru(panel); + } - this._onDidGroupChange.fire({ kind: GroupChangeKind.PANEL_ACTIVE }); + this._onDidGroupChange.fire({ + kind: GroupChangeKind.PANEL_ACTIVE, + panel, + }); } private updateMru(panel: IGroupPanel) { diff --git a/packages/dockview/src/groupview/groupviewPanel.ts b/packages/dockview/src/groupview/groupviewPanel.ts index 9133f7c6a..377410f41 100644 --- a/packages/dockview/src/groupview/groupviewPanel.ts +++ b/packages/dockview/src/groupview/groupviewPanel.ts @@ -35,6 +35,9 @@ export class GroupviewPanel extends GridviewPanel { super(id, 'groupview_default', new GridviewPanelApiImpl(id)); this._model = new Groupview(this.element, accessor, id, options, this); + } + + initialize() { this.model.initialize(); } diff --git a/packages/dockview/src/index.ts b/packages/dockview/src/index.ts index 9f2c6a2dc..7ac35a065 100644 --- a/packages/dockview/src/index.ts +++ b/packages/dockview/src/index.ts @@ -28,6 +28,7 @@ export { PanelDimensionChangeEvent, VisibilityEvent, ActiveEvent, + PanelApi, } from './api/panelApi'; export { SizeEvent,