Merge branch 'master' of https://github.com/mathuo/dockview into 263-left-header-actions

This commit is contained in:
mathuo 2023-06-11 11:34:16 +01:00
commit 2ca4c6fd65
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
42 changed files with 726 additions and 455 deletions

View File

@ -3,7 +3,7 @@
"packages/*"
],
"useWorkspaces": true,
"version": "1.7.3",
"version": "1.7.4",
"npmClient": "yarn",
"command": {
"publish": {

View File

@ -35,6 +35,7 @@
"homepage": "https://github.com/mathuo/dockview#readme",
"devDependencies": {
"@testing-library/dom": "^8.20.0",
"@testing-library/jest-dom": "^5.16.5",
"@types/jest": "^29.4.0",
"@typescript-eslint/eslint-plugin": "^5.52.0",
"@typescript-eslint/parser": "^5.52.0",
@ -58,8 +59,8 @@
"style-loader": "^3.3.1",
"ts-jest": "^29.0.5",
"ts-loader": "^9.4.2",
"tslib": "^2.5.0",
"ts-node": "^10.9.1",
"tslib": "^2.5.0",
"typedoc": "^0.24.7",
"typescript": "^4.9.5",
"webpack": "^5.75.0",
@ -67,4 +68,4 @@
"webpack-dev-server": "^4.11.1"
},
"dependencies": {}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "dockview-core",
"version": "1.7.3",
"version": "1.7.4",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support",
"main": "./dist/cjs/index.js",
"types": "./dist/cjs/index.d.ts",

View File

@ -2,10 +2,10 @@ import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel';
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
import {
GroupPanelPartInitParameters,
GroupPanelUpdateEvent,
IContentRenderer,
ITabRenderer,
} from '../../dockview/types';
import { PanelUpdateEvent } from '../../panel/types';
export class DockviewPanelModelMock implements IDockviewPanelModel {
constructor(
@ -21,7 +21,14 @@ export class DockviewPanelModelMock implements IDockviewPanelModel {
//
}
update(event: GroupPanelUpdateEvent): void {
updateParentGroup(
group: DockviewGroupPanel,
isPanelVisible: boolean
): void {
//
}
update(event: PanelUpdateEvent): void {
//
}

View File

@ -1,4 +1,5 @@
import { PanelApiImpl } from '../../api/panelApi';
import { IPanel } from '../../panel/types';
describe('api', () => {
let api: PanelApiImpl;
@ -7,7 +8,23 @@ describe('api', () => {
api = new PanelApiImpl('dummy_id');
});
it('should update isFcoused getter', () => {
test('updateParameters', () => {
const panel = {
update: jest.fn(),
} as Partial<IPanel>;
api.initialize(panel as IPanel);
expect(panel.update).toHaveBeenCalledTimes(0);
api.updateParameters({ keyA: 'valueA' });
expect(panel.update).toHaveBeenCalledTimes(1);
expect(panel.update).toHaveBeenCalledWith({
params: { keyA: 'valueA' },
});
});
test('should update isFcoused getter', () => {
expect(api.isFocused).toBeFalsy();
api._onDidChangeFocus.fire({ isFocused: true });
@ -17,7 +34,7 @@ describe('api', () => {
expect(api.isFocused).toBeFalsy();
});
it('should update isActive getter', () => {
test('should update isActive getter', () => {
expect(api.isFocused).toBeFalsy();
api._onDidActiveChange.fire({ isActive: true });
@ -27,7 +44,7 @@ describe('api', () => {
expect(api.isActive).toBeFalsy();
});
it('should update isActive getter', () => {
test('should update isActive getter', () => {
expect(api.isVisible).toBeTruthy();
api._onDidVisibilityChange.fire({ isVisible: false });
@ -37,7 +54,7 @@ describe('api', () => {
expect(api.isVisible).toBeTruthy();
});
it('should update width and height getter', () => {
test('should update width and height getter', () => {
expect(api.height).toBe(0);
expect(api.width).toBe(0);

View File

@ -1,4 +1,4 @@
import { DockviewPanelApiImpl, TitleEvent } from '../../api/dockviewPanelApi';
import { DockviewPanelApiImpl } from '../../api/dockviewPanelApi';
import { DockviewComponent } from '../../dockview/dockviewComponent';
import { DockviewPanel, IDockviewPanel } from '../../dockview/dockviewPanel';
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
@ -8,6 +8,7 @@ describe('groupPanelApi', () => {
const panelMock = jest.fn<DockviewPanel, []>(() => {
return {
update: jest.fn(),
setTitle: jest.fn(),
} as any;
});
const groupMock = jest.fn<DockviewGroupPanel, []>(() => {
@ -20,11 +21,38 @@ describe('groupPanelApi', () => {
const cut = new DockviewPanelApiImpl(panel, group);
cut.setTitle('test_title');
expect(panel.setTitle).toBeCalledTimes(1);
expect(panel.setTitle).toBeCalledWith('test_title');
});
expect(panel.update).toBeCalledTimes(1);
expect(panel.update).toBeCalledWith({
params: { title: 'test_title' },
test('updateParameters', () => {
const groupPanel: Partial<IDockviewPanel> = {
id: 'test_id',
update: jest.fn(),
};
const accessor: Partial<DockviewComponent> = {
onDidAddPanel: jest.fn(),
onDidRemovePanel: jest.fn(),
options: {},
};
const groupViewPanel = new DockviewGroupPanel(
<DockviewComponent>accessor,
'',
{}
);
const cut = new DockviewPanelApiImpl(
<IDockviewPanel>groupPanel,
<DockviewGroupPanel>groupViewPanel
);
cut.updateParameters({ keyA: 'valueA' });
expect(groupPanel.update).toHaveBeenCalledWith({
params: { keyA: 'valueA' },
});
expect(groupPanel.update).toHaveBeenCalledTimes(1);
});
test('onDidGroupChange', () => {

View File

@ -1,6 +1,5 @@
import { DockviewComponent } from '../../dockview/dockviewComponent';
import {
GroupPanelUpdateEvent,
GroupviewPanelState,
IGroupPanelInitParameters,
GroupPanelPartInitParameters,
@ -39,7 +38,7 @@ class TestModel implements IDockviewPanelModel {
this.tab = new TestContentPart(id);
}
update(event: GroupPanelUpdateEvent): void {
update(event: PanelUpdateEvent): void {
//
}
@ -203,6 +202,10 @@ export class TestPanel implements IDockviewPanel {
//noop
}
setTitle(title: string): void {
//
}
update(event: PanelUpdateEvent) {
//noop
}

View File

@ -43,7 +43,7 @@ describe('dockviewPanel', () => {
expect(latestTitle).toBe('new title');
expect(cut.title).toBe('new title');
cut.update({ params: { title: 'another title' } });
cut.setTitle('another title');
expect(latestTitle).toBe('another title');
expect(cut.title).toBe('another title');
@ -81,6 +81,9 @@ describe('dockviewPanel', () => {
cut.setTitle('newTitle');
expect(cut.title).toBe('newTitle');
cut.api.setTitle('new title 2');
expect(cut.title).toBe('new title 2');
});
test('dispose cleanup', () => {
@ -142,7 +145,7 @@ describe('dockviewPanel', () => {
expect(cut.params).toEqual(undefined);
cut.update({ params: { params: { variableA: 'A', variableB: 'B' } } });
cut.update({ params: { variableA: 'A', variableB: 'B' } });
expect(cut.params).toEqual({ variableA: 'A', variableB: 'B' });
});
@ -181,4 +184,67 @@ describe('dockviewPanel', () => {
expect(group.api.setSize).toBeCalledWith({ height: 123, width: 456 });
expect(group.api.setSize).toBeCalledTimes(1);
});
test('updateParameter', () => {
const dockviewApiMock = jest.fn<DockviewApi, []>(() => {
return {} as any;
});
const accessorMock = jest.fn<DockviewComponent, []>(() => {
return {} as any;
});
const groupMock = jest.fn<DockviewGroupPanel, []>(() => {
return {} as any;
});
const panelModelMock = jest.fn<Partial<IDockviewPanelModel>, []>(() => {
return {
update: jest.fn(),
init: jest.fn(),
dispose: jest.fn(),
};
});
const api = new dockviewApiMock();
const accessor = new accessorMock();
const group = new groupMock();
const model = <IDockviewPanelModel>new panelModelMock();
const cut = new DockviewPanel('fake-id', accessor, api, group, model);
cut.init({ params: { a: '1', b: '2' }, title: 'A title' });
expect(cut.params).toEqual({ a: '1', b: '2' });
// update 'a' and add 'c'
cut.update({ params: { a: '-1', c: '3' } });
expect(cut.params).toEqual({ a: '-1', b: '2', c: '3' });
cut.update({ params: { d: '4', e: '5', f: '6' } });
expect(cut.params).toEqual({
a: '-1',
b: '2',
c: '3',
d: '4',
e: '5',
f: '6',
});
cut.update({
params: {
d: '',
e: null,
f: undefined,
g: '',
h: null,
i: undefined,
},
});
expect(cut.params).toEqual({
a: '-1',
b: '2',
c: '3',
d: '',
e: null,
g: '',
h: null,
});
});
});

View File

@ -2,6 +2,10 @@ import { Emitter, Event } from '../events';
describe('events', () => {
describe('emitter', () => {
it('debug mode is off', () => {
expect(Emitter.ENABLE_TRACKING).toBeFalsy();
});
it('should emit values', () => {
const emitter = new Emitter<number>();
let value: number | undefined = undefined;

View File

@ -89,7 +89,7 @@ export class DockviewPanelApiImpl
}
public setTitle(title: string): void {
this.panel.update({ params: { title } });
this.panel.setTitle(title);
}
public close(): void {

View File

@ -155,9 +155,7 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi {
this.panelUpdatesDisposable.value = this._onUpdateParameters.event(
(parameters) => {
panel.update({
params: {
params: parameters,
},
params: parameters,
});
}
);

View File

@ -3,14 +3,10 @@ import {
DockviewPanelApi,
DockviewPanelApiImpl,
} from '../api/dockviewPanelApi';
import {
GroupPanelUpdateEvent,
GroupviewPanelState,
IGroupPanelInitParameters,
} from './types';
import { GroupviewPanelState, IGroupPanelInitParameters } from './types';
import { DockviewGroupPanel } from './dockviewGroupPanel';
import { CompositeDisposable, IDisposable } from '../lifecycle';
import { IPanel, Parameters } from '../panel/types';
import { IPanel, PanelUpdateEvent, Parameters } from '../panel/types';
import { IDockviewPanelModel } from './dockviewPanelModel';
import { IDockviewComponent } from './dockviewComponent';
@ -23,7 +19,8 @@ export interface IDockviewPanel extends IDisposable, IPanel {
updateParentGroup(group: DockviewGroupPanel, isGroupActive: boolean): void;
init(params: IGroupPanelInitParameters): void;
toJSON(): GroupviewPanelState;
update(event: GroupPanelUpdateEvent): void;
setTitle(title: string): void;
update(event: PanelUpdateEvent): void;
}
export class DockviewPanel
@ -117,19 +114,24 @@ export class DockviewPanel
}
}
public update(event: GroupPanelUpdateEvent): void {
const params = event.params as IGroupPanelInitParameters;
public update(event: PanelUpdateEvent): void {
// merge the new parameters with the existing parameters
this._params = {
...(this._params || {}),
...event.params.params,
...event.params,
};
if (params.title !== this.title) {
this._title = params.title;
this.api._onDidTitleChange.fire({ title: params.title });
/**
* delete new keys that have a value of undefined,
* allow values of null
*/
for (const key of Object.keys(event.params)) {
if (event.params[key] === undefined) {
delete this._params[key];
}
}
// update the view with the updated props
this.view.update({
params: {
params: this._params,

View File

@ -3,19 +3,19 @@ import {
GroupPanelPartInitParameters,
IContentRenderer,
ITabRenderer,
GroupPanelUpdateEvent,
} from './types';
import { DockviewGroupPanel } from './dockviewGroupPanel';
import { IDisposable } from '../lifecycle';
import { createComponent } from '../panel/componentFactory';
import { IDockviewComponent } from './dockviewComponent';
import { PanelUpdateEvent } from '../panel/types';
export interface IDockviewPanelModel extends IDisposable {
readonly contentComponent: string;
readonly tabComponent?: string;
readonly content: IContentRenderer;
readonly tab?: ITabRenderer;
update(event: GroupPanelUpdateEvent): void;
update(event: PanelUpdateEvent): void;
layout(width: number, height: number): void;
init(params: GroupPanelPartInitParameters): void;
updateParentGroup(group: DockviewGroupPanel, isPanelVisible: boolean): void;
@ -80,7 +80,7 @@ export class DockviewPanelModel implements IDockviewPanelModel {
this.content.layout?.(width, height);
}
update(event: GroupPanelUpdateEvent): void {
update(event: PanelUpdateEvent): void {
this.content.update?.(event);
this.tab.update?.(event);
}

View File

@ -1,11 +1,6 @@
import { IDockviewComponent } from './dockviewComponent';
import { DockviewPanelApi } from '../api/dockviewPanelApi';
import {
PanelInitParameters,
IPanel,
PanelUpdateEvent,
Parameters,
} from '../panel/types';
import { PanelInitParameters, IPanel } from '../panel/types';
import { DockviewApi } from '../api/component.api';
import { Event } from '../events';
import { Optional } from '../types';
@ -91,11 +86,6 @@ export interface IGroupPanelInitParameters
//
}
export type GroupPanelUpdateEvent = PanelUpdateEvent<{
params?: Parameters;
title?: string;
}>;
export interface GroupviewPanelState {
id: string;
contentComponent?: string;

View File

@ -102,10 +102,10 @@ export class Emitter<T> implements IDisposable {
if (index > -1) {
this._listeners.splice(index, 1);
} else if (Emitter.ENABLE_TRACKING) {
console.warn(
`Listener already disposed`,
Stacktrace.create().print()
);
// console.warn(
// `Listener already disposed`,
// Stacktrace.create().print()
// );
}
},
};

View File

@ -104,6 +104,7 @@ export abstract class BasePanelView<T extends PanelApiImpl>
}
update(event: PanelUpdateEvent): void {
// merge the new parameters with the existing parameters
this._params = {
...this._params,
params: {
@ -111,6 +112,18 @@ export abstract class BasePanelView<T extends PanelApiImpl>
...event.params,
},
};
/**
* delete new keys that have a value of undefined,
* allow values of null
*/
for (const key of Object.keys(event.params)) {
if (event.params[key] === undefined) {
delete this._params.params[key];
}
}
// update the view with the updated props
this.part?.update({ params: this._params.params });
}

View File

@ -15,6 +15,7 @@ const config: JestConfigWithTsJest = {
setupFiles: [
'<rootDir>/packages/dockview/src/__tests__/__mocks__/resizeObserver.js',
],
setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
coveragePathIgnorePatterns: ['/node_modules/'],
modulePathIgnorePatterns: [
'<rootDir>/packages/dockview/src/__tests__/__mocks__',

View File

@ -1,6 +1,6 @@
{
"name": "dockview",
"version": "1.7.3",
"version": "1.7.4",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support",
"main": "./dist/cjs/index.js",
"types": "./dist/cjs/index.d.ts",
@ -56,7 +56,7 @@
"author": "https://github.com/mathuo",
"license": "MIT",
"dependencies": {
"dockview-core": "^1.7.3"
"dockview-core": "^1.7.4"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.1",

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { DockviewApi } from 'dockview-core';
import { act, render, waitFor } from '@testing-library/react';
import { DockviewApi, IDockviewPanel } from 'dockview-core';
import {
IDockviewPanelProps,
DockviewReact,
@ -15,7 +15,17 @@ describe('gridview react', () => {
beforeEach(() => {
components = {
default: (props: IDockviewPanelProps) => {
return <div>hello world</div>;
return (
<div>
{Object.keys(props.params).map((key) => {
return (
<div
key={key}
>{`key=${key},value=${props.params[key]}`}</div>
);
})}
</div>
);
},
};
});
@ -51,4 +61,84 @@ describe('gridview react', () => {
expect(api!.width).toBe(650);
expect(api!.height).toBe(450);
});
test('that the component can update parameters', async () => {
let api: DockviewApi;
const onReady = (event: DockviewReadyEvent) => {
api = event.api;
};
const wrapper = render(
<DockviewReact components={components} onReady={onReady} />
);
let panel: IDockviewPanel;
act(() => {
panel = api!.addPanel({
id: 'panel_1',
component: 'default',
params: {
keyA: 'valueA',
keyB: 'valueB',
},
});
});
await waitFor(() => {
expect(
wrapper.queryByText(/key=keyA,value=valueA/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
});
act(() => {
panel.api.updateParameters({ keyA: 'valueAA', keyC: 'valueC' });
});
await waitFor(() => {
expect(
wrapper.queryByText(/key=keyA,value=valueAA/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyC,value=valueC/i)
).toBeInTheDocument();
});
act(() => {
panel.api.updateParameters({ keyC: null });
});
await waitFor(() => {
expect(
wrapper.queryByText(/key=keyA,value=valueAA/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyC,value=null/i)
).toBeInTheDocument();
});
act(() => {
panel.api.updateParameters({ keyA: undefined });
});
await waitFor(() => {
expect(wrapper.queryByText(/key=keyA/i)).not.toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyC,value=null/i)
).toBeInTheDocument();
});
});
});

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { GridviewApi, Orientation } from 'dockview-core';
import { act, render, waitFor } from '@testing-library/react';
import { GridviewApi, IGridviewPanel, Orientation } from 'dockview-core';
import {
IGridviewPanelProps,
GridviewReact,
@ -15,7 +15,17 @@ describe('gridview react', () => {
beforeEach(() => {
components = {
default: (props: IGridviewPanelProps) => {
return <div>hello world</div>;
return (
<div>
{Object.keys(props.params).map((key) => {
return (
<div
key={key}
>{`key=${key},value=${props.params[key]}`}</div>
);
})}
</div>
);
},
};
});
@ -62,4 +72,88 @@ describe('gridview react', () => {
expect(api!.width).toBe(650);
expect(api!.height).toBe(450);
});
test('that the component can update parameters', async () => {
let api: GridviewApi;
const onReady = (event: GridviewReadyEvent) => {
api = event.api;
};
const wrapper = render(
<GridviewReact
orientation={Orientation.VERTICAL}
components={components}
onReady={onReady}
/>
);
let panel: IGridviewPanel;
act(() => {
panel = api!.addPanel({
id: 'panel_1',
component: 'default',
params: {
keyA: 'valueA',
keyB: 'valueB',
},
});
});
await waitFor(() => {
expect(
wrapper.queryByText(/key=keyA,value=valueA/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
});
act(() => {
panel.api.updateParameters({ keyA: 'valueAA', keyC: 'valueC' });
});
await waitFor(() => {
expect(
wrapper.queryByText(/key=keyA,value=valueAA/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyC,value=valueC/i)
).toBeInTheDocument();
});
act(() => {
panel.api.updateParameters({ keyC: null });
});
await waitFor(() => {
expect(
wrapper.queryByText(/key=keyA,value=valueAA/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyC,value=null/i)
).toBeInTheDocument();
});
act(() => {
panel.api.updateParameters({ keyA: undefined });
});
await waitFor(() => {
expect(wrapper.queryByText(/key=keyA/i)).not.toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyC,value=null/i)
).toBeInTheDocument();
});
});
});

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { PaneviewApi } from 'dockview-core';
import { act, render, waitFor } from '@testing-library/react';
import { IPaneviewPanel, PaneviewApi } from 'dockview-core';
import {
IPaneviewPanelProps,
PaneviewReact,
@ -15,7 +15,17 @@ describe('gridview react', () => {
beforeEach(() => {
components = {
default: (props: IPaneviewPanelProps) => {
return <div>hello world</div>;
return (
<div>
{Object.keys(props.params).map((key) => {
return (
<div
key={key}
>{`key=${key},value=${props.params[key]}`}</div>
);
})}
</div>
);
},
};
});
@ -49,4 +59,85 @@ describe('gridview react', () => {
expect(api!.width).toBe(650);
expect(api!.height).toBe(450);
});
test('that the component can update parameters', async () => {
let api: PaneviewApi;
const onReady = (event: PaneviewReadyEvent) => {
api = event.api;
};
const wrapper = render(
<PaneviewReact components={components} onReady={onReady} />
);
let panel: IPaneviewPanel;
act(() => {
panel = api!.addPanel({
id: 'panel_1',
component: 'default',
title: 'Panel 1',
params: {
keyA: 'valueA',
keyB: 'valueB',
},
});
});
await waitFor(() => {
expect(
wrapper.queryByText(/key=keyA,value=valueA/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
});
act(() => {
panel.api.updateParameters({ keyA: 'valueAA', keyC: 'valueC' });
});
await waitFor(() => {
expect(
wrapper.queryByText(/key=keyA,value=valueAA/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyC,value=valueC/i)
).toBeInTheDocument();
});
act(() => {
panel.api.updateParameters({ keyC: null });
});
await waitFor(() => {
expect(
wrapper.queryByText(/key=keyA,value=valueAA/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyC,value=null/i)
).toBeInTheDocument();
});
act(() => {
panel.api.updateParameters({ keyA: undefined });
});
await waitFor(() => {
expect(wrapper.queryByText(/key=keyA/i)).not.toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyC,value=null/i)
).toBeInTheDocument();
});
});
});

View File

@ -1,53 +0,0 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { DockviewApi } from 'dockview-core';
import {
IDockviewPanelProps,
DockviewReact,
DockviewReadyEvent,
} from '../../../dockview/dockview';
import { PanelCollection } from '../../../types';
import { setMockRefElement } from '../../__test_utils__/utils';
describe('dockview', () => {
let components: PanelCollection<IDockviewPanelProps>;
beforeEach(() => {
components = {
default: (props: IDockviewPanelProps) => {
return <div>hello world</div>;
},
};
});
test('default', () => {
let api: DockviewApi | undefined;
const onReady = (event: DockviewReadyEvent) => {
api = event.api;
};
render(<DockviewReact components={components} onReady={onReady} />);
expect(api).toBeTruthy();
});
test('is sized to container', () => {
const el = document.createElement('div') as any;
jest.spyOn(el, 'clientHeight', 'get').mockReturnValue(450);
jest.spyOn(el, 'clientWidth', 'get').mockReturnValue(650);
setMockRefElement(el);
let api: DockviewApi | undefined;
const onReady = (event: DockviewReadyEvent) => {
api = event.api;
};
render(<DockviewReact components={components} onReady={onReady} />);
expect(api!.width).toBe(650);
expect(api!.height).toBe(450);
});
});

View File

@ -1,58 +0,0 @@
import {
DockviewGroupPanel,
DockviewGroupPanelApi,
DockviewGroupPanelModel,
} from 'dockview-core';
import { ReactHeaderActionsRendererPart } from '../../../dockview/headerActionsRenderer';
describe('headerActionsRenderer', () => {
test('#1', () => {
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
() => {
return {
onDidAddPanel: jest.fn(),
onDidRemovePanel: jest.fn(),
onDidActivePanelChange: jest.fn(),
};
}
);
const groupview = new groupviewMock() as DockviewGroupPanelModel;
const groupPanelMock = jest.fn<Partial<DockviewGroupPanel>, []>(() => {
return {
api: {} as DockviewGroupPanelApi as any,
model: groupview,
};
});
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
const cut = new ReactHeaderActionsRendererPart(
jest.fn(),
{
addPortal: jest.fn(),
},
groupPanel
);
expect(cut.element.childNodes.length).toBe(0);
expect(cut.element.className).toBe('dockview-react-part');
expect(cut.part).toBeUndefined();
cut.init({
containerApi: <any>jest.fn(),
api: <any>{
onDidActiveChange: jest.fn(),
},
});
const update = jest.fn();
jest.spyOn(cut.part!, 'update').mockImplementation(update);
cut.update({ params: { valueA: 'A' } });
expect(update).toBeCalledWith({ valueA: 'A' });
});
});

View File

@ -1,64 +0,0 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { GridviewApi, Orientation } from 'dockview-core';
import {
IGridviewPanelProps,
GridviewReact,
GridviewReadyEvent,
} from '../../../gridview/gridview';
import { PanelCollection } from '../../../types';
import { setMockRefElement } from '../../__test_utils__/utils';
describe('gridview react', () => {
let components: PanelCollection<IGridviewPanelProps>;
beforeEach(() => {
components = {
default: (props: IGridviewPanelProps) => {
return <div>hello world</div>;
},
};
});
test('default', () => {
let api: GridviewApi | undefined;
const onReady = (event: GridviewReadyEvent) => {
api = event.api;
};
render(
<GridviewReact
orientation={Orientation.VERTICAL}
components={components}
onReady={onReady}
/>
);
expect(api).toBeTruthy();
});
test('is sized to container', () => {
setMockRefElement({
clientHeight: 450,
clientWidth: 650,
appendChild: jest.fn(),
});
let api: GridviewApi | undefined;
const onReady = (event: GridviewReadyEvent) => {
api = event.api;
};
render(
<GridviewReact
orientation={Orientation.VERTICAL}
components={components}
onReady={onReady}
/>
);
expect(api!.width).toBe(650);
expect(api!.height).toBe(450);
});
});

View File

@ -1,52 +0,0 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { PaneviewApi } from 'dockview-core';
import {
IPaneviewPanelProps,
PaneviewReact,
PaneviewReadyEvent,
} from '../../../paneview/paneview';
import { PanelCollection } from '../../../types';
import { setMockRefElement } from '../../__test_utils__/utils';
describe('gridview react', () => {
let components: PanelCollection<IPaneviewPanelProps>;
beforeEach(() => {
components = {
default: (props: IPaneviewPanelProps) => {
return <div>hello world</div>;
},
};
});
test('default', () => {
let api: PaneviewApi | undefined;
const onReady = (event: PaneviewReadyEvent) => {
api = event.api;
};
render(<PaneviewReact components={components} onReady={onReady} />);
expect(api).toBeTruthy();
});
test('is sized to container', () => {
setMockRefElement({
clientHeight: 450,
clientWidth: 650,
appendChild: jest.fn(),
});
let api: PaneviewApi | undefined;
const onReady = (event: PaneviewReadyEvent) => {
api = event.api;
};
render(<PaneviewReact components={components} onReady={onReady} />);
expect(api!.width).toBe(650);
expect(api!.height).toBe(450);
});
});

View File

@ -1,90 +0,0 @@
import { ReactPart } from '../../react';
import * as React from 'react';
import { render, screen, act } from '@testing-library/react';
interface TestInterface {
valueA: string;
valueB: number;
}
describe('react', () => {
describe('ReactPart', () => {
test('update underlying component via ReactPart class', () => {
let api: ReactPart<TestInterface>;
const onReady = (_api: ReactPart<TestInterface>) => {
api = _api;
};
render(<TestWrapper onReady={onReady} component={Component} />);
expect(api!).toBeTruthy();
expect(screen.getByTestId('valueA').textContent).toBe('stringA');
expect(screen.getByTestId('valueB').textContent).toBe('42');
act(() => {
api.update({ valueB: '32' });
});
expect(screen.getByTestId('valueA').textContent).toBe('stringA');
expect(screen.getByTestId('valueB').textContent).toBe('32');
act(() => {
api.update({ valueA: 'anotherStringA', valueB: '22' });
});
expect(screen.getByTestId('valueA').textContent).toBe(
'anotherStringA'
);
expect(screen.getByTestId('valueB').textContent).toBe('22');
});
});
});
const Component = (props: TestInterface) => {
return (
<div>
<div data-testid="valueA">{props.valueA}</div>
<div data-testid="valueB">{props.valueB}</div>
</div>
);
};
const TestWrapper = (props: {
component: React.FunctionComponent<TestInterface>;
onReady: (api: ReactPart<TestInterface>) => void;
}) => {
const [portal, setPortal] = React.useState<React.ReactPortal[]>([]);
const ref = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
const cut = new ReactPart<TestInterface>(
ref.current!,
{
addPortal: (portal: React.ReactPortal) => {
setPortal((_) => [..._, portal]);
return {
dispose: () => {
setPortal((_) => _.filter((_) => _ !== portal));
},
};
},
},
props.component,
{
valueA: 'stringA',
valueB: 42,
}
);
props.onReady(cut);
return () => {
cut.dispose();
};
}, []);
return <div ref={ref}>{portal}</div>;
};

View File

@ -1,64 +0,0 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { SplitviewApi, Orientation } from 'dockview-core';
import {
ISplitviewPanelProps,
SplitviewReact,
SplitviewReadyEvent,
} from '../../../splitview/splitview';
import { PanelCollection } from '../../../types';
import { setMockRefElement } from '../../__test_utils__/utils';
describe('splitview react', () => {
let components: PanelCollection<ISplitviewPanelProps>;
beforeEach(() => {
components = {
default: (props: ISplitviewPanelProps) => {
return <div>hello world</div>;
},
};
});
test('default', () => {
let api: SplitviewApi | undefined;
const onReady = (event: SplitviewReadyEvent) => {
api = event.api;
};
render(
<SplitviewReact
orientation={Orientation.VERTICAL}
components={components}
onReady={onReady}
/>
);
expect(api).toBeTruthy();
});
test('is sized to container', () => {
setMockRefElement({
clientHeight: 450,
clientWidth: 650,
appendChild: jest.fn(),
});
let api: SplitviewApi | undefined;
const onReady = (event: SplitviewReadyEvent) => {
api = event.api;
};
render(
<SplitviewReact
orientation={Orientation.VERTICAL}
components={components}
onReady={onReady}
/>
);
expect(api!.width).toBe(650);
expect(api!.height).toBe(450);
});
});

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { SplitviewApi, Orientation } from 'dockview-core';
import { act, render, waitFor } from '@testing-library/react';
import { SplitviewApi, Orientation, ISplitviewPanel } from 'dockview-core';
import {
ISplitviewPanelProps,
SplitviewReact,
@ -15,7 +15,17 @@ describe('splitview react', () => {
beforeEach(() => {
components = {
default: (props: ISplitviewPanelProps) => {
return <div>hello world</div>;
return (
<div>
{Object.keys(props.params).map((key) => {
return (
<div
key={key}
>{`key=${key},value=${props.params[key]}`}</div>
);
})}
</div>
);
},
};
});
@ -61,4 +71,88 @@ describe('splitview react', () => {
expect(api!.width).toBe(650);
expect(api!.height).toBe(450);
});
test('that the component can update parameters', async () => {
let api: SplitviewApi;
const onReady = (event: SplitviewReadyEvent) => {
api = event.api;
};
const wrapper = render(
<SplitviewReact
orientation={Orientation.VERTICAL}
components={components}
onReady={onReady}
/>
);
let panel: ISplitviewPanel;
act(() => {
panel = api!.addPanel({
id: 'panel_1',
component: 'default',
params: {
keyA: 'valueA',
keyB: 'valueB',
},
});
});
await waitFor(() => {
expect(
wrapper.queryByText(/key=keyA,value=valueA/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
});
act(() => {
panel.api.updateParameters({ keyA: 'valueAA', keyC: 'valueC' });
});
await waitFor(() => {
expect(
wrapper.queryByText(/key=keyA,value=valueAA/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyC,value=valueC/i)
).toBeInTheDocument();
});
act(() => {
panel.api.updateParameters({ keyC: null });
});
await waitFor(() => {
expect(
wrapper.queryByText(/key=keyA,value=valueAA/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyC,value=null/i)
).toBeInTheDocument();
});
act(() => {
panel.api.updateParameters({ keyA: undefined });
});
await waitFor(() => {
expect(wrapper.queryByText(/key=keyA/i)).not.toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyB,value=valueB/i)
).toBeInTheDocument();
expect(
wrapper.queryByText(/key=keyC,value=null/i)
).toBeInTheDocument();
});
});
});

View File

@ -0,0 +1,20 @@
---
slug: dockview-1.7.4-release
title: Dockview 1.7.4
tags: [release]
---
# Release Notes
Please reference to docs @ [dockview.dev](https://dockview.dev).
## 🚀 Features
- Improvements and tests added to the panel `api.updateParameters(...)` method [#265](https://github.com/mathuo/dockview/pull/265)
## 🛠 Miscs
- Fix bug associated with overidding panel titles when using `api.updateParameters(...)` [#265](https://github.com/mathuo/dockview/pull/265)
- Cleanup listeners and disposables after use [#257](https://github.com/mathuo/dockview/pull/257)
## 🔥 Breaking changes

View File

@ -436,6 +436,40 @@ const panel2 = api.addPanel({
});
```
### Update Panel
You can programatically update the `params` passed through to the panel through the panal api using `api.updateParameters`.
```ts
const panel = api.addPanel({
id: 'panel_1',
component: 'default',
params: {
keyA: 'valueA',
},
});
// ...
panel.api.updateParameters({
keyB: 'valueB',
});
// ...
panel.api.updateParameters({
keyA: 'anotherValueA',
});
```
To delete a parameter you should pass a value of `undefined` for the key.
```ts
panel.api.updateParameters({
keyA: undefined, // this will delete 'keyA'.
});
```
### Panel Rendering
By default `DockviewReact` only adds to the DOM those panels that are visible,

View File

@ -1,6 +1,6 @@
{
"name": "dockview-docs",
"version": "1.7.3",
"version": "1.7.4",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@ -22,7 +22,7 @@
"@minoru/react-dnd-treeview": "^3.4.3",
"axios": "^1.3.3",
"clsx": "^1.2.1",
"dockview": "^1.7.3",
"dockview": "^1.7.4",
"prism-react-renderer": "^1.3.5",
"react": "^18.2.0",
"react-dnd": "^16.0.1",

View File

@ -435,6 +435,40 @@ const panel2 = api.addPanel({
});
```
### Update Panel
You can programatically update the `params` passed through to the panel through the panal api using `api.updateParameters`.
```ts
const panel = api.addPanel({
id: 'panel_1',
component: 'default',
params: {
keyA: 'valueA',
},
});
// ...
panel.api.updateParameters({
keyB: 'valueB',
});
// ...
panel.api.updateParameters({
keyA: 'anotherValueA',
});
```
To delete a parameter you should pass a value of `undefined` for the key.
```ts
panel.api.updateParameters({
keyA: undefined, // this will delete 'keyA'.
});
```
### Panel Rendering
By default `DockviewReact` only adds to the DOM those panels that are visible,

View File

@ -1,3 +1,3 @@
[
"1.7.3"
]
"1.7.4"
]

View File

@ -2,6 +2,11 @@
# yarn lockfile v1
"@adobe/css-tools@^4.0.1":
version "4.2.0"
resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.2.0.tgz#e1a84fca468f4b337816fcb7f0964beb620ba855"
integrity sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==
"@algolia/autocomplete-core@1.7.4":
version "1.7.4"
resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.4.tgz#85ff36b2673654a393c8c505345eaedd6eaa4f70"
@ -2888,6 +2893,21 @@
lz-string "^1.4.4"
pretty-format "^27.0.2"
"@testing-library/jest-dom@^5.16.5":
version "5.16.5"
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e"
integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==
dependencies:
"@adobe/css-tools" "^4.0.1"
"@babel/runtime" "^7.9.2"
"@types/testing-library__jest-dom" "^5.9.1"
aria-query "^5.0.0"
chalk "^3.0.0"
css.escape "^1.5.1"
dom-accessibility-api "^0.5.6"
lodash "^4.17.15"
redent "^3.0.0"
"@testing-library/react@^13.4.0":
version "13.4.0"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.4.0.tgz#6a31e3bf5951615593ad984e96b9e5e2d9380966"
@ -3107,6 +3127,14 @@
dependencies:
"@types/istanbul-lib-report" "*"
"@types/jest@*":
version "29.5.2"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.2.tgz#86b4afc86e3a8f3005b297ed8a72494f89e6395b"
integrity sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg==
dependencies:
expect "^29.0.0"
pretty-format "^29.0.0"
"@types/jest@^29.4.0":
version "29.5.0"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.0.tgz#337b90bbcfe42158f39c2fb5619ad044bbb518ac"
@ -3292,6 +3320,13 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
"@types/testing-library__jest-dom@^5.9.1":
version "5.14.6"
resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.6.tgz#4887f6e1af11215428ab02777873bcede98a53b0"
integrity sha512-FkHXCb+ikSoUP4Y4rOslzTdX5sqYwMxfefKh1GmZ8ce1GOkEHntSp6b5cGadmNfp5e4BMEWOMx+WSKd5/MqlDA==
dependencies:
"@types/jest" "*"
"@types/tough-cookie@*":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397"
@ -4659,6 +4694,14 @@ chalk@^2.0.0, chalk@^2.3.0:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
@ -5488,6 +5531,11 @@ css-what@^6.0.1, css-what@^6.1.0:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
css.escape@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
@ -5928,7 +5976,7 @@ docusaurus-plugin-sass@^0.2.3:
dependencies:
sass-loader "^10.1.1"
dom-accessibility-api@^0.5.9:
dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9:
version "0.5.16"
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==
@ -9918,7 +9966,7 @@ markdown-escapes@^1.0.0:
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==
marked@^4.3.0:
marked@^4.2.12, marked@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3"
integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==
@ -10136,6 +10184,13 @@ minimatch@^6.1.6:
dependencies:
brace-expansion "^2.0.1"
minimatch@^7.1.3:
version "7.4.6"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb"
integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==
dependencies:
brace-expansion "^2.0.1"
minimatch@^7.4.1, minimatch@^7.4.2:
version "7.4.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.3.tgz#012cbf110a65134bb354ae9773b55256cdb045a2"
@ -14256,6 +14311,16 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==
typedoc@^0.23.25:
version "0.23.28"
resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.23.28.tgz#3ce9c36ef1c273fa849d2dea18651855100d3ccd"
integrity sha512-9x1+hZWTHEQcGoP7qFmlo4unUoVJLB0H/8vfO/7wqTnZxg4kPuji9y3uRzEu0ZKez63OJAUmiGhUrtukC6Uj3w==
dependencies:
lunr "^2.3.9"
marked "^4.2.12"
minimatch "^7.1.3"
shiki "^0.14.1"
typedoc@^0.24.7:
version "0.24.7"
resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.24.7.tgz#7eeb272a1894b3789acc1a94b3f2ae8e7330ee39"