diff --git a/packages/dockview-core/jest.config.ts b/packages/dockview-core/jest.config.ts index e047e27b2..d5aa1ca7f 100644 --- a/packages/dockview-core/jest.config.ts +++ b/packages/dockview-core/jest.config.ts @@ -20,6 +20,14 @@ const config: JestConfigWithTsJest = { coverageDirectory: '/packages/dockview-core/coverage/', testResultsProcessor: 'jest-sonar-reporter', testEnvironment: 'jsdom', + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.test.json', + }, + ], + }, }; export default config; diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewPanelModel.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewPanelModel.spec.ts index b1cf604b0..aacf0ca93 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewPanelModel.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewPanelModel.spec.ts @@ -4,27 +4,39 @@ import { } from '../../dockview/dockviewComponent'; import { DockviewPanelModel } from '../../dockview/dockviewPanelModel'; import { IContentRenderer, ITabRenderer } from '../../dockview/types'; +import { GroupPanelFrameworkComponentFactory } from '../../dockview/options'; +import { DefaultTab } from '../../dockview/components/tab/defaultTab'; describe('dockviewGroupPanel', () => { - test('that dispose is called on content and tab renderers when present', () => { - const contentMock = jest.fn(() => { + let contentMock: jest.Mock; + let tabMock: jest.Mock; + let accessorMock: jest.Mock; + + beforeEach(() => { + contentMock = jest.fn(() => { const partial: Partial = { element: document.createElement('div'), dispose: jest.fn(), + update: jest.fn(), + onGroupChange: jest.fn(), + onPanelVisibleChange: jest.fn(), }; return partial as IContentRenderer; }); - const tabMock = jest.fn(() => { + tabMock = jest.fn(() => { const partial: Partial = { element: document.createElement('div'), dispose: jest.fn(), + update: jest.fn(), + onGroupChange: jest.fn(), + onPanelVisibleChange: jest.fn(), }; return partial as IContentRenderer; }); - const accessorMock = jest.fn, []>(() => { - return { + accessorMock = jest.fn(() => { + const partial: Partial = { options: { components: { contentComponent: contentMock, @@ -34,8 +46,12 @@ describe('dockviewGroupPanel', () => { }, }, }; - }); + return partial as DockviewComponent; + }); + }); + + test('that dispose is called on content and tab renderers when present', () => { const cut = new DockviewPanelModel( new accessorMock(), 'id', @@ -50,34 +66,6 @@ describe('dockviewGroupPanel', () => { }); test('that update is called on content and tab renderers when present', () => { - const contentMock = jest.fn(() => { - const partial: Partial = { - element: document.createElement('div'), - update: jest.fn(), - }; - return partial as IContentRenderer; - }); - - const tabMock = jest.fn(() => { - const partial: Partial = { - element: document.createElement('div'), - update: jest.fn(), - }; - return partial as IContentRenderer; - }); - - const accessorMock = jest.fn, []>(() => { - return { - options: { - components: { - contentComponent: contentMock, - }, - tabComponents: { - tabComponent: tabMock, - }, - }, - }; - }); const cut = new DockviewPanelModel( new accessorMock(), 'id', @@ -94,36 +82,6 @@ describe('dockviewGroupPanel', () => { }); test('that events are fired', () => { - const contentMock = jest.fn(() => { - const partial: Partial = { - element: document.createElement('div'), - onGroupChange: jest.fn(), - onPanelVisibleChange: jest.fn(), - }; - return partial as IContentRenderer; - }); - - const tabMock = jest.fn(() => { - const partial: Partial = { - element: document.createElement('div'), - onGroupChange: jest.fn(), - onPanelVisibleChange: jest.fn(), - }; - return partial as IContentRenderer; - }); - - const accessorMock = jest.fn, []>(() => { - return { - options: { - components: { - contentComponent: contentMock, - }, - tabComponents: { - tabComponent: tabMock, - }, - }, - }; - }); const cut = new DockviewPanelModel( new accessorMock(), 'id', @@ -168,4 +126,176 @@ describe('dockviewGroupPanel', () => { expect(cut.content.onPanelVisibleChange).toHaveBeenCalledTimes(2); expect(cut.tab.onPanelVisibleChange).toHaveBeenCalledTimes(2); }); + + test('that the default tab is created', () => { + accessorMock = jest.fn(() => { + const partial: Partial = { + options: { + components: { + contentComponent: contentMock, + }, + tabComponents: { + tabComponent: jest + .fn() + .mockImplementation(() => tabMock), + }, + }, + }; + + return partial as DockviewComponent; + }); + + const cut = new DockviewPanelModel( + new accessorMock(), + 'id', + 'contentComponent', + 'tabComponent' + ); + + expect(cut.tab).toEqual(tabMock); + }); + + test('that the provided default tab is chosen when no implementation is provided', () => { + accessorMock = jest.fn(() => { + const partial: Partial = { + options: { + components: { + contentComponent: contentMock, + }, + tabComponents: { + tabComponent: jest + .fn() + .mockImplementation(() => tabMock), + }, + defaultTabComponent: 'tabComponent', + }, + }; + + return partial as DockviewComponent; + }); + + const cut = new DockviewPanelModel( + new accessorMock(), + 'id', + 'contentComponent' + ); + + expect(cut.tab).toEqual(tabMock); + }); + + test('that the framework tab is created when provided tab is a framework tab', () => { + const tab = jest.fn(); + const tabFactory = jest.fn().mockImplementation(() => tab); + + accessorMock = jest.fn(() => { + const partial: Partial = { + options: { + components: { + contentComponent: contentMock, + }, + frameworkTabComponents: { + tabComponent: tabMock, + }, + frameworkComponentFactory: (< + Partial + >{ + tab: { createComponent: tabFactory }, + }) as GroupPanelFrameworkComponentFactory, + }, + }; + + return partial as DockviewComponent; + }); + + const cut = new DockviewPanelModel( + new accessorMock(), + 'id', + 'contentComponent', + 'tabComponent' + ); + + expect(tabFactory).toHaveBeenCalledWith('id', 'tabComponent', tabMock); + expect(cut.tab).toEqual(tab); + }); + + test('that is library default tab instance is created when no alternative exists', () => { + accessorMock = jest.fn(() => { + const partial: Partial = { + options: { + components: { + contentComponent: contentMock, + }, + }, + }; + + return partial as DockviewComponent; + }); + + const cut = new DockviewPanelModel( + new accessorMock(), + 'id', + 'contentComponent' + ); + + expect(cut.tab instanceof DefaultTab).toBeTruthy(); + }); + + test('that the default content is created', () => { + accessorMock = jest.fn(() => { + const partial: Partial = { + options: { + components: { + contentComponent: jest.fn().mockImplementation(() => { + return contentMock; + }), + }, + }, + }; + + return partial as DockviewComponent; + }); + + const cut = new DockviewPanelModel( + new accessorMock(), + 'id', + 'contentComponent' + ); + + expect(cut.content).toEqual(contentMock); + }); + + test('that the framework content is created', () => { + const content = jest.fn(); + const contentFactory = jest.fn().mockImplementation(() => content); + + accessorMock = jest.fn(() => { + const partial: Partial = { + options: { + frameworkComponents: { + contentComponent: contentMock, + }, + frameworkComponentFactory: (< + Partial + >{ + content: { createComponent: contentFactory }, + }) as GroupPanelFrameworkComponentFactory, + }, + }; + + return partial as DockviewComponent; + }); + + const cut = new DockviewPanelModel( + new accessorMock(), + 'id', + 'contentComponent' + ); + + expect(contentFactory).toHaveBeenCalledWith( + 'id', + 'contentComponent', + contentMock + ); + expect(cut.content).toEqual(content); + }); }); diff --git a/packages/dockview-core/src/__tests__/gridview/baseComponentGridview.spec.ts b/packages/dockview-core/src/__tests__/gridview/baseComponentGridview.spec.ts index 592598807..27e8925b1 100644 --- a/packages/dockview-core/src/__tests__/gridview/baseComponentGridview.spec.ts +++ b/packages/dockview-core/src/__tests__/gridview/baseComponentGridview.spec.ts @@ -114,7 +114,7 @@ describe('baseComponentGridview', () => { proportionalLayout: true, }); - const events: TestPanel[] = []; + const events: (TestPanel | undefined)[] = []; const disposable = new CompositeDisposable( cut.onDidAddGroup((event) => { diff --git a/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts b/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts index ef2ed7cad..b00522d05 100644 --- a/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts +++ b/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts @@ -18,11 +18,9 @@ import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer'; import { CompositeDisposable } from '../../lifecycle'; import { DockviewPanelApi } from '../../api/dockviewPanelApi'; import { IDockviewPanel } from '../../dockview/dockviewPanel'; -import { - IDockviewPanelModel, - DockviewPanelModel, -} from '../../dockview/dockviewPanelModel'; +import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel'; import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel'; +import { WatermarkRendererInitParameters } from '../../dockview/types'; enum GroupChangeKind2 { ADD_PANEL, @@ -76,7 +74,7 @@ class Watermark implements IWatermarkRenderer { return 'watermark-id'; } - init(params: GroupPanelPartInitParameters) { + init(params: WatermarkRendererInitParameters) { // } @@ -170,7 +168,7 @@ class TestHeaderPart implements ITabRenderer { export class TestPanel implements IDockviewPanel { private _group: DockviewGroupPanel | undefined; - private _params: IGroupPanelInitParameters; + private _params: IGroupPanelInitParameters | undefined; readonly view: IDockviewPanelModel; get title() { @@ -516,7 +514,7 @@ describe('groupview', () => { dockviewComponent, 'id', {}, - null + null as any ); expect(cut.toJSON()).toEqual({ @@ -539,7 +537,7 @@ describe('groupview', () => { dockviewComponent, 'id', {}, - null + null as any ); cut.locked = true; @@ -568,25 +566,25 @@ describe('groupview', () => { dockviewComponent, 'id', {}, - null + null as any ); const contentContainer = groupviewContainer .getElementsByClassName('content-container') .item(0)!.childNodes; - const panel1 = new TestPanel('id_1', null); + const panel1 = new TestPanel('id_1', null as any); cut.openPanel(panel1); expect(contentContainer.length).toBe(1); expect(contentContainer.item(0)).toBe(panel1.view.content.element); - const panel2 = new TestPanel('id_2', null); + const panel2 = new TestPanel('id_2', null as any); cut.openPanel(panel2); expect(contentContainer.length).toBe(1); expect(contentContainer.item(0)).toBe(panel2.view.content.element); - const panel3 = new TestPanel('id_2', null); + const panel3 = new TestPanel('id_2', null as any); cut.openPanel(panel3, { skipSetPanelActive: true }); expect(contentContainer.length).toBe(1); diff --git a/packages/dockview-core/src/dockview/deserializer.ts b/packages/dockview-core/src/dockview/deserializer.ts index 5c6936c4d..82daa7381 100644 --- a/packages/dockview-core/src/dockview/deserializer.ts +++ b/packages/dockview-core/src/dockview/deserializer.ts @@ -1,9 +1,7 @@ -import { GroupviewPanelState, ITabRenderer } from './types'; +import { GroupviewPanelState } from './types'; import { DockviewGroupPanel } from './dockviewGroupPanel'; import { DockviewPanel, IDockviewPanel } from './dockviewPanel'; import { IDockviewComponent } from './dockviewComponent'; -import { createComponent } from '../panel/componentFactory'; -import { DefaultTab } from './components/tab/defaultTab'; import { DockviewPanelModel } from './dockviewPanelModel'; import { DockviewApi } from '../api/component.api'; @@ -14,7 +12,7 @@ export interface IPanelDeserializer { ): IDockviewPanel; } -// depreciated +// @depreciated interface LegacyState extends GroupviewPanelState { view?: { tab?: { id: string }; @@ -42,30 +40,6 @@ export class DefaultDockviewDeserialzier implements IPanelDeserializer { ? viewData.tab?.id : panelData.tabComponent; - let tab: ITabRenderer; - - if (tabComponent) { - tab = createComponent( - panelId, - tabComponent, - this.layout.options.tabComponents, - this.layout.options.frameworkTabComponents, - this.layout.options.frameworkComponentFactory?.tab, - () => new DefaultTab() - ); - } else if (this.layout.options.defaultTabComponent) { - tab = createComponent( - panelId, - this.layout.options.defaultTabComponent, - this.layout.options.tabComponents, - this.layout.options.frameworkTabComponents, - this.layout.options.frameworkComponentFactory?.tab, - () => new DefaultTab() - ); - } else { - tab = new DefaultTab(); - } - const view = new DockviewPanelModel( this.layout, panelId, diff --git a/packages/dockview-core/src/dockview/dockviewPanelModel.ts b/packages/dockview-core/src/dockview/dockviewPanelModel.ts index c8e2aee8f..b29bcd12c 100644 --- a/packages/dockview-core/src/dockview/dockviewPanelModel.ts +++ b/packages/dockview-core/src/dockview/dockviewPanelModel.ts @@ -43,8 +43,7 @@ export class DockviewPanelModel implements IDockviewPanelModel { readonly tabComponent?: string ) { this._content = this.createContentComponent(this.id, contentComponent); - this._tab = - this.createTabComponent(this.id, tabComponent) ?? new DefaultTab(); + this._tab = this.createTabComponent(this.id, tabComponent); } init(params: GroupPanelPartInitParameters): void { @@ -108,13 +107,26 @@ export class DockviewPanelModel implements IDockviewPanelModel { id: string, componentName?: string ): ITabRenderer { - return createComponent( - id, - componentName, - this.accessor.options.tabComponents || {}, - this.accessor.options.frameworkTabComponents, - this.accessor.options.frameworkComponentFactory?.tab, - () => new DefaultTab() - ); + if (componentName) { + return createComponent( + id, + componentName, + this.accessor.options.tabComponents, + this.accessor.options.frameworkTabComponents, + this.accessor.options.frameworkComponentFactory?.tab, + () => new DefaultTab() + ); + } else if (this.accessor.options.defaultTabComponent) { + return createComponent( + id, + this.accessor.options.defaultTabComponent, + this.accessor.options.tabComponents, + this.accessor.options.frameworkTabComponents, + this.accessor.options.frameworkComponentFactory?.tab, + () => new DefaultTab() + ); + } else { + return new DefaultTab(); + } } } diff --git a/packages/dockview/jest.config.ts b/packages/dockview/jest.config.ts index f6b1969ca..b909c68ef 100644 --- a/packages/dockview/jest.config.ts +++ b/packages/dockview/jest.config.ts @@ -27,7 +27,7 @@ const config: JestConfigWithTsJest = { '^.+\\.tsx?$': [ 'ts-jest', { - tsconfig, + tsconfig: '/tsconfig.test.json', }, ], }, diff --git a/packages/dockview/src/__tests__/__test_utils__/utils.ts b/packages/dockview/src/__tests__/__test_utils__/utils.ts index ab8dc0a9e..675a30c17 100644 --- a/packages/dockview/src/__tests__/__test_utils__/utils.ts +++ b/packages/dockview/src/__tests__/__test_utils__/utils.ts @@ -1,5 +1,3 @@ -import * as React from 'react'; - export function setMockRefElement(node: Partial): void { const mockRef = { get current() { @@ -10,5 +8,5 @@ export function setMockRefElement(node: Partial): void { }, }; - jest.spyOn(React, 'useRef').mockReturnValueOnce(mockRef); + jest.spyOn(require('react'), 'useRef').mockReturnValueOnce(mockRef); } diff --git a/packages/dockview/src/__tests__/dockview/dockview.spec.tsx b/packages/dockview/src/__tests__/dockview/dockview.spec.tsx index 2c55b29b7..6fe0872a7 100644 --- a/packages/dockview/src/__tests__/dockview/dockview.spec.tsx +++ b/packages/dockview/src/__tests__/dockview/dockview.spec.tsx @@ -48,7 +48,7 @@ describe('gridview react', () => { render(); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/gridview/gridview.spec.tsx b/packages/dockview/src/__tests__/gridview/gridview.spec.tsx index 92f81f4c3..0881e00b4 100644 --- a/packages/dockview/src/__tests__/gridview/gridview.spec.tsx +++ b/packages/dockview/src/__tests__/gridview/gridview.spec.tsx @@ -59,7 +59,7 @@ describe('gridview react', () => { /> ); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/paneview/paneview.spec.tsx b/packages/dockview/src/__tests__/paneview/paneview.spec.tsx index 0a69965f0..830861e89 100644 --- a/packages/dockview/src/__tests__/paneview/paneview.spec.tsx +++ b/packages/dockview/src/__tests__/paneview/paneview.spec.tsx @@ -46,7 +46,7 @@ describe('gridview react', () => { render(); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/react.spec.tsx b/packages/dockview/src/__tests__/react.spec.tsx index 2abf76845..298d96fed 100644 --- a/packages/dockview/src/__tests__/react.spec.tsx +++ b/packages/dockview/src/__tests__/react.spec.tsx @@ -18,7 +18,7 @@ describe('react', () => { render(); - expect(api).toBeTruthy(); + expect(api!).toBeTruthy(); expect(screen.getByTestId('valueA').textContent).toBe('stringA'); expect(screen.getByTestId('valueB').textContent).toBe('42'); @@ -60,7 +60,7 @@ const TestWrapper = (props: { React.useEffect(() => { const cut = new ReactPart( - ref.current, + ref.current!, { addPortal: (portal: React.ReactPortal) => { setPortal((_) => [..._, portal]); diff --git a/packages/dockview/src/__tests__/react/dockview/dockview.spec.tsx b/packages/dockview/src/__tests__/react/dockview/dockview.spec.tsx index b8e9f1bcb..12048cac5 100644 --- a/packages/dockview/src/__tests__/react/dockview/dockview.spec.tsx +++ b/packages/dockview/src/__tests__/react/dockview/dockview.spec.tsx @@ -47,7 +47,7 @@ describe('dockview', () => { render(); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/react/gridview/gridview.spec.tsx b/packages/dockview/src/__tests__/react/gridview/gridview.spec.tsx index 8e76030d9..5e5ff70e9 100644 --- a/packages/dockview/src/__tests__/react/gridview/gridview.spec.tsx +++ b/packages/dockview/src/__tests__/react/gridview/gridview.spec.tsx @@ -58,7 +58,7 @@ describe('gridview react', () => { /> ); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/react/paneview/paneview.spec.tsx b/packages/dockview/src/__tests__/react/paneview/paneview.spec.tsx index 3223a8503..4dc8b8198 100644 --- a/packages/dockview/src/__tests__/react/paneview/paneview.spec.tsx +++ b/packages/dockview/src/__tests__/react/paneview/paneview.spec.tsx @@ -46,7 +46,7 @@ describe('gridview react', () => { render(); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/react/react.spec.tsx b/packages/dockview/src/__tests__/react/react.spec.tsx index f9799c0a5..b89d09d74 100644 --- a/packages/dockview/src/__tests__/react/react.spec.tsx +++ b/packages/dockview/src/__tests__/react/react.spec.tsx @@ -18,7 +18,7 @@ describe('react', () => { render(); - expect(api).toBeTruthy(); + expect(api!).toBeTruthy(); expect(screen.getByTestId('valueA').textContent).toBe('stringA'); expect(screen.getByTestId('valueB').textContent).toBe('42'); @@ -60,7 +60,7 @@ const TestWrapper = (props: { React.useEffect(() => { const cut = new ReactPart( - ref.current, + ref.current!, { addPortal: (portal: React.ReactPortal) => { setPortal((_) => [..._, portal]); diff --git a/packages/dockview/src/__tests__/react/splitview/splitview.spec.tsx b/packages/dockview/src/__tests__/react/splitview/splitview.spec.tsx index 852171aa9..9ffc01974 100644 --- a/packages/dockview/src/__tests__/react/splitview/splitview.spec.tsx +++ b/packages/dockview/src/__tests__/react/splitview/splitview.spec.tsx @@ -58,7 +58,7 @@ describe('splitview react', () => { /> ); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/splitview/splitview.spec.tsx b/packages/dockview/src/__tests__/splitview/splitview.spec.tsx index e0da9dd72..34e0027db 100644 --- a/packages/dockview/src/__tests__/splitview/splitview.spec.tsx +++ b/packages/dockview/src/__tests__/splitview/splitview.spec.tsx @@ -58,7 +58,7 @@ describe('splitview react', () => { /> ); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/tsconfig.base.json b/tsconfig.base.json index 77f0e8b99..2ca86a1c1 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -21,5 +21,6 @@ "ES2019", "DOM" ] - } + }, + "exclude": ["**/*.spec.ts"] } diff --git a/tsconfig.test.json b/tsconfig.test.json index d58b7d79f..5001874b2 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -1,22 +1,7 @@ - { + "extends": "./tsconfig.base.json", "compilerOptions": { - "module": "commonjs", - "target": "es2019", - "sourceMap": true, - "declaration": true, - "jsx": "react", - "noImplicitReturns": true, - "noImplicitAny": true, - "allowUnreachableCode": false, - "forceConsistentCasingInFileNames": true, - // "strict": true, - "strictBindCallApply": true, // pass - "alwaysStrict": true, // pass - "noImplicitThis": true, // pass - "strictFunctionTypes": true, // pass - "strictNullChecks": false, - "strictPropertyInitialization": false, + "jsx": "react-jsx" }, - "exclude": ["node_modules", "dist"] -} \ No newline at end of file + "include": ["**/*.spec.ts"] +}