Merge pull request #419 from mathuo/361-full-screen-mode-for-a-single-tab

test: gridview tests
This commit is contained in:
mathuo 2024-01-03 13:56:47 +00:00 committed by GitHub
commit 5a2d7394be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 335 additions and 15 deletions

View File

@ -19,13 +19,17 @@ class MockGridview implements IGridView {
>().event; >().event;
element: HTMLElement = document.createElement('div'); element: HTMLElement = document.createElement('div');
width: number = 0;
height: number = 0;
constructor(private id?: string) { constructor(private id?: string) {
this.element.className = 'mock-grid-view'; this.element.className = 'mock-grid-view';
this.element.id = `${id ?? ''}`; this.element.id = `${id ?? ''}`;
} }
layout(width: number, height: number): void { layout(width: number, height: number): void {
// this.width = width;
this.height = height;
} }
toJSON(): object { toJSON(): object {
@ -760,4 +764,255 @@ describe('gridview', () => {
el = gridview.element.querySelectorAll('.mock-grid-view'); el = gridview.element.querySelectorAll('.mock-grid-view');
expect(el.length).toBe(5); expect(el.length).toBe(5);
}); });
test('gridview nested proportional layouts', () => {
const gridview = new Gridview(
true,
{ separatorBorder: '' },
Orientation.HORIZONTAL
);
gridview.layout(1000, 1000);
const view1 = new MockGridview('1');
const view2 = new MockGridview('2');
const view3 = new MockGridview('3');
const view4 = new MockGridview('4');
const view5 = new MockGridview('5');
const view6 = new MockGridview('6');
gridview.addView(view1, Sizing.Distribute, [0]);
gridview.addView(view2, Sizing.Distribute, [1]);
gridview.addView(view3, Sizing.Distribute, [1, 1]);
gridview.addView(view4, Sizing.Distribute, [1, 1, 0]);
gridview.addView(view5, Sizing.Distribute, [1, 1, 0, 0]);
gridview.addView(view6, Sizing.Distribute, [1, 1, 0, 0, 0]);
const views = [view1, view2, view3, view4, view5, view6];
const dimensions = [
{ width: 500, height: 1000 },
{ width: 500, height: 500 },
{ width: 250, height: 500 },
{ width: 250, height: 250 },
{ width: 125, height: 250 },
{ width: 125, height: 250 },
];
expect(
views.map((view) => ({
width: view.width,
height: view.height,
}))
).toEqual(dimensions);
gridview.layout(2000, 1500);
expect(
views.map((view) => ({
width: view.width,
height: view.height,
}))
).toEqual(
dimensions.map(({ width, height }) => ({
width: width * 2,
height: height * 1.5,
}))
);
gridview.layout(200, 2000);
expect(
views.map((view) => ({
width: view.width,
height: view.height,
}))
).toEqual(
dimensions.map(({ width, height }) => ({
width: width * 0.2,
height: height * 2,
}))
);
});
test('that maximizeView retains original dimensions when restored', () => {
const gridview = new Gridview(
true,
{ separatorBorder: '' },
Orientation.HORIZONTAL
);
gridview.layout(1000, 1000);
let counter = 0;
const subscription = gridview.onDidMaxmizedNodeChange(() => {
counter++;
});
const view1 = new MockGridview('1');
const view2 = new MockGridview('2');
const view3 = new MockGridview('3');
const view4 = new MockGridview('4');
const view5 = new MockGridview('5');
const view6 = new MockGridview('6');
gridview.addView(view1, Sizing.Distribute, [0]);
gridview.addView(view2, Sizing.Distribute, [1]);
gridview.addView(view3, Sizing.Distribute, [1, 1]);
gridview.addView(view4, Sizing.Distribute, [1, 1, 0]);
gridview.addView(view5, Sizing.Distribute, [1, 1, 0, 0]);
gridview.addView(view6, Sizing.Distribute, [1, 1, 0, 0, 0]);
/**
* _____________________________________________
* | | |
* | | 2 |
* | | |
* | 1 |_______________________|
* | | | 4 |
* | | 3 |_____________|
* | | | 5 | 6 |
* |_____________________|_________|______|______|
*/
const views = [view1, view2, view3, view4, view5, view6];
const dimensions = [
{ width: 500, height: 1000 },
{ width: 500, height: 500 },
{ width: 250, height: 500 },
{ width: 250, height: 250 },
{ width: 125, height: 250 },
{ width: 125, height: 250 },
];
function assertLayout() {
expect(
views.map((view) => ({
width: view.width,
height: view.height,
}))
).toEqual(dimensions);
}
// base case assertions
assertLayout();
expect(gridview.hasMaximizedView()).toBeFalsy();
expect(counter).toBe(0);
/**
* maximize each view individually and then return to the standard view
* checking on each iteration that the original layout dimensions
* are restored
*/
for (let i = 0; i < views.length; i++) {
const view = views[i];
gridview.maximizeView(view);
expect(counter).toBe(i * 2 + 1);
expect(gridview.hasMaximizedView()).toBeTruthy();
gridview.exitMaximizedView();
expect(counter).toBe(i * 2 + 2);
assertLayout();
}
subscription.dispose();
});
test('that maximizedView is exited when a views visibility is changed', () => {
const gridview = new Gridview(
true,
{ separatorBorder: '' },
Orientation.HORIZONTAL
);
gridview.layout(1000, 1000);
const view1 = new MockGridview('1');
const view2 = new MockGridview('2');
const view3 = new MockGridview('3');
gridview.addView(view1, Sizing.Distribute, [0]);
gridview.addView(view2, Sizing.Distribute, [1]);
gridview.addView(view3, Sizing.Distribute, [1, 1]);
expect(gridview.hasMaximizedView()).toBeFalsy();
gridview.maximizeView(view2);
expect(gridview.hasMaximizedView()).toBeTruthy();
gridview.setViewVisible([0], true);
expect(gridview.hasMaximizedView()).toBeFalsy();
});
test('that maximizedView is exited when a view is moved', () => {
const gridview = new Gridview(
true,
{ separatorBorder: '' },
Orientation.HORIZONTAL
);
gridview.layout(1000, 1000);
const view1 = new MockGridview('1');
const view2 = new MockGridview('2');
const view3 = new MockGridview('3');
const view4 = new MockGridview('4');
gridview.addView(view1, Sizing.Distribute, [0]);
gridview.addView(view2, Sizing.Distribute, [1]);
gridview.addView(view3, Sizing.Distribute, [1, 1]);
gridview.addView(view4, Sizing.Distribute, [1, 1, 0]);
expect(gridview.hasMaximizedView()).toBeFalsy();
gridview.maximizeView(view2);
expect(gridview.hasMaximizedView()).toBeTruthy();
gridview.moveView([1, 1], 0, 1);
expect(gridview.hasMaximizedView()).toBeFalsy();
});
test('that maximizedView is exited when a view is added', () => {
const gridview = new Gridview(
true,
{ separatorBorder: '' },
Orientation.HORIZONTAL
);
gridview.layout(1000, 1000);
const view1 = new MockGridview('1');
const view2 = new MockGridview('2');
const view3 = new MockGridview('3');
const view4 = new MockGridview('4');
gridview.addView(view1, Sizing.Distribute, [0]);
gridview.addView(view2, Sizing.Distribute, [1]);
gridview.addView(view3, Sizing.Distribute, [1, 1]);
expect(gridview.hasMaximizedView()).toBeFalsy();
gridview.maximizeView(view2);
expect(gridview.hasMaximizedView()).toBeTruthy();
gridview.addView(view4, Sizing.Distribute, [1, 1, 0]);
expect(gridview.hasMaximizedView()).toBeFalsy();
});
test('that maximizedView is exited when a view is removed', () => {
const gridview = new Gridview(
true,
{ separatorBorder: '' },
Orientation.HORIZONTAL
);
gridview.layout(1000, 1000);
const view1 = new MockGridview('1');
const view2 = new MockGridview('2');
const view3 = new MockGridview('3');
gridview.addView(view1, Sizing.Distribute, [0]);
gridview.addView(view2, Sizing.Distribute, [1]);
gridview.addView(view3, Sizing.Distribute, [1, 1]);
expect(gridview.hasMaximizedView()).toBeFalsy();
gridview.maximizeView(view2);
expect(gridview.hasMaximizedView()).toBeTruthy();
gridview.removeView([1, 1]);
expect(gridview.hasMaximizedView()).toBeFalsy();
});
}); });

View File

@ -9,12 +9,18 @@ export interface DockviewGroupPanelApi extends GridviewPanelApi {
readonly onDidRenderPositionChange: Event<DockviewGroupPanelFloatingChangeEvent>; readonly onDidRenderPositionChange: Event<DockviewGroupPanelFloatingChangeEvent>;
readonly location: DockviewGroupLocation; readonly location: DockviewGroupLocation;
moveTo(options: { group: DockviewGroupPanel; position?: Position }): void; moveTo(options: { group: DockviewGroupPanel; position?: Position }): void;
maximize(): void;
isMaximized(): boolean;
exitMaximized(): void;
} }
export interface DockviewGroupPanelFloatingChangeEvent { export interface DockviewGroupPanelFloatingChangeEvent {
readonly location: DockviewGroupLocation; readonly location: DockviewGroupLocation;
} }
// TODO find a better way to initialize and avoid needing null checks
const NOT_INITIALIZED_MESSAGE = 'DockviewGroupPanelApiImpl not initialized';
export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl { export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
private _group: DockviewGroupPanel | undefined; private _group: DockviewGroupPanel | undefined;
@ -25,7 +31,7 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
get location(): DockviewGroupLocation { get location(): DockviewGroupLocation {
if (!this._group) { if (!this._group) {
throw new Error(`DockviewGroupPanelApiImpl not initialized`); throw new Error(NOT_INITIALIZED_MESSAGE);
} }
return this._group.model.location; return this._group.model.location;
} }
@ -38,7 +44,7 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
moveTo(options: { group: DockviewGroupPanel; position?: Position }): void { moveTo(options: { group: DockviewGroupPanel; position?: Position }): void {
if (!this._group) { if (!this._group) {
throw new Error(`DockviewGroupPanelApiImpl not initialized`); throw new Error(NOT_INITIALIZED_MESSAGE);
} }
this.accessor.moveGroupOrPanel( this.accessor.moveGroupOrPanel(
@ -49,6 +55,32 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
); );
} }
maximize(): void {
if (!this._group) {
throw new Error(NOT_INITIALIZED_MESSAGE);
}
this.accessor.maximizeGroup(this._group);
}
isMaximized(): boolean {
if (!this._group) {
throw new Error(NOT_INITIALIZED_MESSAGE);
}
return this.accessor.isMaximizedGroup(this._group);
}
exitMaximized(): void {
if (!this._group) {
throw new Error(NOT_INITIALIZED_MESSAGE);
}
if (this.isMaximized()) {
this.accessor.exitMaximizedGroup();
}
}
initialize(group: DockviewGroupPanel): void { initialize(group: DockviewGroupPanel): void {
this._group = group; this._group = group;
} }

View File

@ -15,13 +15,10 @@ export interface RendererChangedEvent {
renderer: DockviewPanelRenderer; renderer: DockviewPanelRenderer;
} }
/*
* omit visibility modifiers since the visibility of a single group doesn't make sense
* because it belongs to a groupview
*/
export interface DockviewPanelApi export interface DockviewPanelApi
extends Omit< extends Omit<
GridviewPanelApi, GridviewPanelApi,
// omit properties that do not make sense here
'setVisible' | 'onDidConstraintsChange' | 'setConstraints' 'setVisible' | 'onDidConstraintsChange' | 'setConstraints'
> { > {
readonly group: DockviewGroupPanel; readonly group: DockviewGroupPanel;
@ -40,6 +37,8 @@ export interface DockviewPanelApi
index?: number; index?: number;
}): void; }): void;
maximize(): void; maximize(): void;
isMaximized(): boolean;
exitMaximized(): void;
} }
export class DockviewPanelApiImpl export class DockviewPanelApiImpl
@ -67,7 +66,7 @@ export class DockviewPanelApiImpl
} }
get isGroupActive(): boolean { get isGroupActive(): boolean {
return !!this.group?.isActive; return this.group.isActive;
} }
get renderer(): DockviewPanelRenderer { get renderer(): DockviewPanelRenderer {
@ -143,6 +142,14 @@ export class DockviewPanelApiImpl
} }
maximize(): void { maximize(): void {
this.accessor.maximizeGroup(this.panel.group); this.group.api.maximize();
}
isMaximized(): boolean {
return this.group.api.isMaximized();
}
exitMaximized(): void {
this.group.api.exitMaximized();
} }
} }

View File

@ -0,0 +1,3 @@
export const DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE = 100;
export const DEFAULT_FLOATING_GROUP_POSITION = { left: 100, top: 100 };

View File

@ -58,6 +58,10 @@ import {
DockviewPanelRenderer, DockviewPanelRenderer,
} from './components/greadyRenderContainer'; } from './components/greadyRenderContainer';
import { DockviewPopoutGroupPanel } from './dockviewPopoutGroupPanel'; import { DockviewPopoutGroupPanel } from './dockviewPopoutGroupPanel';
import {
DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE,
DEFAULT_FLOATING_GROUP_POSITION,
} from '../constants';
function getTheme(element: HTMLElement): string | undefined { function getTheme(element: HTMLElement): string | undefined {
function toClassList(element: HTMLElement) { function toClassList(element: HTMLElement) {
@ -86,8 +90,6 @@ function getTheme(element: HTMLElement): string | undefined {
return theme; return theme;
} }
const DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE = 100;
export interface PanelReference { export interface PanelReference {
update: (event: { params: { [key: string]: any } }) => void; update: (event: { params: { [key: string]: any } }) => void;
remove: () => void; remove: () => void;
@ -582,9 +584,13 @@ export class DockviewComponent
group.model.location = 'floating'; group.model.location = 'floating';
const overlayLeft = const overlayLeft =
typeof coord?.x === 'number' ? Math.max(coord.x, 0) : 100; typeof coord?.x === 'number'
? Math.max(coord.x, 0)
: DEFAULT_FLOATING_GROUP_POSITION.left;
const overlayTop = const overlayTop =
typeof coord?.y === 'number' ? Math.max(coord.y, 0) : 100; typeof coord?.y === 'number'
? Math.max(coord.y, 0)
: DEFAULT_FLOATING_GROUP_POSITION.top;
const overlay = new Overlay({ const overlay = new Overlay({
container: this.gridview.element, container: this.gridview.element,

View File

@ -65,6 +65,7 @@ export interface IBaseGrid<T extends IGridPanelView> {
setVisible(panel: T, visible: boolean): void; setVisible(panel: T, visible: boolean): void;
isVisible(panel: T): boolean; isVisible(panel: T): boolean;
maximizeGroup(panel: T): void; maximizeGroup(panel: T): void;
isMaximizedGroup(panel: T): boolean;
exitMaximizedGroup(): void; exitMaximizedGroup(): void;
hasMaximizedGroup(): boolean; hasMaximizedGroup(): boolean;
readonly onDidMaxmizedGroupChange: Event<void>; readonly onDidMaxmizedGroupChange: Event<void>;
@ -182,6 +183,10 @@ export abstract class BaseGrid<T extends IGridPanelView>
this.gridview.maximizeView(panel); this.gridview.maximizeView(panel);
} }
isMaximizedGroup(panel: T): boolean {
return this.gridview.maximizedView() === panel;
}
exitMaximizedGroup(): void { exitMaximizedGroup(): void {
this.gridview.exitMaximizedView(); this.gridview.exitMaximizedView();
} }

View File

@ -273,7 +273,7 @@ export class Gridview implements IDisposable {
readonly element: HTMLElement; readonly element: HTMLElement;
private _root: BranchNode | undefined; private _root: BranchNode | undefined;
private _maximizedNode: Node | undefined = undefined; private _maximizedNode: LeafNode | undefined = undefined;
private readonly disposable: MutableDisposable = new MutableDisposable(); private readonly disposable: MutableDisposable = new MutableDisposable();
private readonly _onDidChange = new Emitter<{ private readonly _onDidChange = new Emitter<{
@ -307,6 +307,7 @@ export class Gridview implements IDisposable {
get width(): number { get width(): number {
return this.root.width; return this.root.width;
} }
get height(): number { get height(): number {
return this.root.height; return this.root.height;
} }
@ -314,16 +315,23 @@ export class Gridview implements IDisposable {
get minimumWidth(): number { get minimumWidth(): number {
return this.root.minimumWidth; return this.root.minimumWidth;
} }
get minimumHeight(): number { get minimumHeight(): number {
return this.root.minimumHeight; return this.root.minimumHeight;
} }
get maximumWidth(): number { get maximumWidth(): number {
return this.root.maximumHeight; return this.root.maximumHeight;
} }
get maximumHeight(): number { get maximumHeight(): number {
return this.root.maximumHeight; return this.root.maximumHeight;
} }
maximizedView(): IGridView | undefined {
return this._maximizedNode?.view;
}
hasMaximizedView(): boolean { hasMaximizedView(): boolean {
return this._maximizedNode !== undefined; return this._maximizedNode !== undefined;
} }
@ -332,6 +340,10 @@ export class Gridview implements IDisposable {
const location = getGridLocation(view.element); const location = getGridLocation(view.element);
const [_, node] = this.getNode(location); const [_, node] = this.getNode(location);
if (!(node instanceof LeafNode)) {
return;
}
if (this._maximizedNode === node) { if (this._maximizedNode === node) {
return; return;
} }
@ -353,7 +365,7 @@ export class Gridview implements IDisposable {
} }
} }
hideAllViewsBut(this.root, node as LeafNode); hideAllViewsBut(this.root, node);
this._maximizedNode = node; this._maximizedNode = node;
this._onDidMaxmizedNodeChange.fire(); this._onDidMaxmizedNodeChange.fire();
} }