Merge pull request #422 from mathuo/360-investigate-opening-tabs-in-new-browser-window

test: add tests
This commit is contained in:
mathuo 2024-01-08 22:03:44 +00:00 committed by GitHub
commit b78a7a6207
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1339 additions and 1084 deletions

View File

@ -1,4 +1,8 @@
import { CompositeDisposable, MutableDisposable } from '../lifecycle'; import {
CompositeDisposable,
Disposable,
MutableDisposable,
} from '../lifecycle';
describe('lifecycle', () => { describe('lifecycle', () => {
test('mutable disposable', () => { test('mutable disposable', () => {
@ -64,4 +68,16 @@ describe('lifecycle', () => {
expect(cut.checkIsDisposed()).toBeTruthy(); expect(cut.checkIsDisposed()).toBeTruthy();
}); });
test('Disposable.from(...)', () => {
const func = jest.fn();
const disposable = Disposable.from(func);
expect(func).not.toHaveBeenCalled();
disposable.dispose();
expect(func).toHaveBeenCalledTimes(1);
});
}); });

View File

@ -1,4 +1,4 @@
import { Position } from '../dnd/droptarget'; import { Position, positionToDirection } from '../dnd/droptarget';
import { DockviewComponent } from '../dockview/dockviewComponent'; import { DockviewComponent } from '../dockview/dockviewComponent';
import { DockviewGroupPanel } from '../dockview/dockviewGroupPanel'; import { DockviewGroupPanel } from '../dockview/dockviewGroupPanel';
import { DockviewGroupLocation } from '../dockview/dockviewGroupPanelModel'; import { DockviewGroupLocation } from '../dockview/dockviewGroupPanelModel';
@ -6,9 +6,9 @@ import { Emitter, Event } from '../events';
import { GridviewPanelApi, GridviewPanelApiImpl } from './gridviewPanelApi'; import { GridviewPanelApi, GridviewPanelApiImpl } from './gridviewPanelApi';
export interface DockviewGroupPanelApi extends GridviewPanelApi { export interface DockviewGroupPanelApi extends GridviewPanelApi {
readonly onDidRenderPositionChange: Event<DockviewGroupPanelFloatingChangeEvent>; readonly onDidLocationChange: Event<DockviewGroupPanelFloatingChangeEvent>;
readonly location: DockviewGroupLocation; readonly location: DockviewGroupLocation;
moveTo(options: { group: DockviewGroupPanel; position?: Position }): void; moveTo(options: { group?: DockviewGroupPanel; position?: Position }): void;
maximize(): void; maximize(): void;
isMaximized(): boolean; isMaximized(): boolean;
exitMaximized(): void; exitMaximized(): void;
@ -24,10 +24,10 @@ 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;
readonly _onDidRenderPositionChange = readonly _onDidLocationChange =
new Emitter<DockviewGroupPanelFloatingChangeEvent>(); new Emitter<DockviewGroupPanelFloatingChangeEvent>();
readonly onDidRenderPositionChange: Event<DockviewGroupPanelFloatingChangeEvent> = readonly onDidLocationChange: Event<DockviewGroupPanelFloatingChangeEvent> =
this._onDidRenderPositionChange.event; this._onDidLocationChange.event;
get location(): DockviewGroupLocation { get location(): DockviewGroupLocation {
if (!this._group) { if (!this._group) {
@ -39,19 +39,25 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
constructor(id: string, private readonly accessor: DockviewComponent) { constructor(id: string, private readonly accessor: DockviewComponent) {
super(id); super(id);
this.addDisposables(this._onDidRenderPositionChange); this.addDisposables(this._onDidLocationChange);
} }
moveTo(options: { group: DockviewGroupPanel; position?: Position }): void { moveTo(options: { group?: DockviewGroupPanel; position?: Position }): void {
if (!this._group) { if (!this._group) {
throw new Error(NOT_INITIALIZED_MESSAGE); throw new Error(NOT_INITIALIZED_MESSAGE);
} }
const group =
options.group ??
this.accessor.addGroup({
direction: positionToDirection(options.position ?? 'right'),
});
this.accessor.moveGroupOrPanel( this.accessor.moveGroupOrPanel(
options.group, group,
this._group.id, this._group.id,
undefined, undefined,
options.position ?? 'center' options.group ? options.position ?? 'center' : 'center'
); );
} }
@ -60,6 +66,11 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
throw new Error(NOT_INITIALIZED_MESSAGE); throw new Error(NOT_INITIALIZED_MESSAGE);
} }
if (this.location !== 'grid') {
// only grid groups can be maximized
return;
}
this.accessor.maximizeGroup(this._group); this.accessor.maximizeGroup(this._group);
} }

View File

@ -7,7 +7,7 @@ import {
import { directionToPosition, Droptarget, Position } from '../dnd/droptarget'; import { directionToPosition, Droptarget, Position } from '../dnd/droptarget';
import { tail, sequenceEquals, remove } from '../array'; import { tail, sequenceEquals, remove } from '../array';
import { DockviewPanel, IDockviewPanel } from './dockviewPanel'; import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
import { CompositeDisposable } from '../lifecycle'; import { CompositeDisposable, Disposable } from '../lifecycle';
import { Event, Emitter } from '../events'; import { Event, Emitter } from '../events';
import { Watermark } from './components/watermark/watermark'; import { Watermark } from './components/watermark/watermark';
import { import {
@ -58,7 +58,10 @@ import {
DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE, DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE,
DEFAULT_FLOATING_GROUP_POSITION, DEFAULT_FLOATING_GROUP_POSITION,
} from '../constants'; } from '../constants';
import { DockviewPanelRenderer, OverlayRenderContainer } from '../overlayRenderContainer'; import {
DockviewPanelRenderer,
OverlayRenderContainer,
} from '../overlayRenderContainer';
function getTheme(element: HTMLElement): string | undefined { function getTheme(element: HTMLElement): string | undefined {
function toClassList(element: HTMLElement) { function toClassList(element: HTMLElement) {
@ -386,6 +389,17 @@ export class DockviewComponent
this.onDidActivePanelChange this.onDidActivePanelChange
)(() => { )(() => {
this._bufferOnDidLayoutChange.fire(); this._bufferOnDidLayoutChange.fire();
}),
Disposable.from(() => {
// iterate over a copy of the array since .dispose() mutates the original array
for (const group of [...this._floatingGroups]) {
group.dispose();
}
// iterate over a copy of the array since .dispose() mutates the original array
for (const group of [...this._popoutGroups]) {
group.dispose();
}
}) })
); );

View File

@ -283,7 +283,7 @@ export class DockviewGroupPanelModel
break; break;
} }
this.groupPanel.api._onDidRenderPositionChange.fire({ this.groupPanel.api._onDidLocationChange.fire({
location: this.location, location: this.location,
}); });
} }

View File

@ -13,6 +13,14 @@ export namespace Disposable {
// noop // noop
}, },
}; };
export function from(func: () => void): IDisposable {
return {
dispose: () => {
func();
},
};
}
} }
export class CompositeDisposable { export class CompositeDisposable {

View File

@ -75,10 +75,7 @@ export class PopoutWindow extends CompositeDisposable {
this._window = { value: externalWindow, disposable }; this._window = { value: externalWindow, disposable };
const grievingParent = content.parentElement;
const cleanUp = () => { const cleanUp = () => {
grievingParent?.appendChild(content);
this._onDidClose.fire(); this._onDidClose.fire();
this._window = null; this._window = null;
}; };

View File

@ -399,6 +399,17 @@ From within a panel you may say
props.containerApi.addPopoutGroup(props.api.group); props.containerApi.addPopoutGroup(props.api.group);
``` ```
To programatically move the popout group back into the main grid you can use the `moveTo` method in many ways, one of the following would suffice
```tsx
// option 1: add absolutely to the right-side of the grid
props.group.api.moveTo({position: 'right'});
// option 2: create a new group and move the contents of the popout group to it
const group = props.containerApi.addGroup();
props.group.api.moveTo({ group });
```
<MultiFrameworkContainer <MultiFrameworkContainer
height={600} height={600}
sandboxId="popoutgroup-dockview" sandboxId="popoutgroup-dockview"

View File

@ -109,7 +109,7 @@ const Icon = (props: {
); );
}; };
const groupControlsComponents = { const groupControlsComponents: Record<string, React.FC> = {
panel_1: () => { panel_1: () => {
return <Icon icon="file_download" />; return <Icon icon="file_download" />;
}, },
@ -130,6 +130,10 @@ const RightControls = (props: IDockviewHeaderActionsProps) => {
: 'expand_content' : 'expand_content'
); );
const [popoutIcon, setPopoutIcon] = React.useState<string>(
props.api.location === 'popout' ? 'close_fullscreen' : 'open_in_new'
);
React.useEffect(() => { React.useEffect(() => {
const disposable = props.containerApi.onDidMaxmizedGroupChange(() => { const disposable = props.containerApi.onDidMaxmizedGroupChange(() => {
setIcon( setIcon(
@ -139,8 +143,17 @@ const RightControls = (props: IDockviewHeaderActionsProps) => {
); );
}); });
const disposable2 = props.api.onDidLocationChange(() => {
setPopoutIcon(
props.api.location === 'popout'
? 'close_fullscreen'
: 'open_in_new'
);
});
return () => { return () => {
disposable.dispose(); disposable.dispose();
disposable2.dispose();
}; };
}, [props.containerApi]); }, [props.containerApi]);
@ -152,6 +165,14 @@ const RightControls = (props: IDockviewHeaderActionsProps) => {
} }
}; };
const onClick2 = () => {
if (props.api.location !== 'popout') {
props.containerApi.addPopoutGroup(props.group);
} else {
props.api.moveTo({ position: 'right' });
}
};
return ( return (
<div <div
className="group-control" className="group-control"
@ -165,6 +186,7 @@ const RightControls = (props: IDockviewHeaderActionsProps) => {
> >
{props.isGroupActive && <Icon icon="star" />} {props.isGroupActive && <Icon icon="star" />}
{Component && <Component />} {Component && <Component />}
<Icon icon={popoutIcon} onClick={onClick2} />
<Icon icon={icon} onClick={onClick} /> <Icon icon={icon} onClick={onClick} />
</div> </div>
); );

View File

@ -259,7 +259,7 @@ const RightComponent = (props: IDockviewHeaderActionsProps) => {
); );
React.useEffect(() => { React.useEffect(() => {
const disposable = props.group.api.onDidRenderPositionChange( const disposable = props.group.api.onDidLocationChange(
(event) => { (event) => {
setFloating(event.location === 'floating'); setFloating(event.location === 'floating');
} }

View File

@ -218,7 +218,7 @@ const RightComponent = (props: IDockviewHeaderActionsProps) => {
); );
React.useEffect(() => { React.useEffect(() => {
const disposable = props.group.api.onDidRenderPositionChange( const disposable = props.group.api.onDidLocationChange(
(event) => [setPopout(event.location === 'popout')] (event) => [setPopout(event.location === 'popout')]
); );