feat: scrollbars

This commit is contained in:
mathuo 2025-03-01 21:04:06 +00:00
parent 7a6b2cb26d
commit cfe37766a9
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
13 changed files with 432 additions and 216 deletions

View File

@ -18,7 +18,7 @@ export class DockviewPanelModelMock implements IDockviewPanelModel {
//
}
copyTabComponent(tabLocation: TabLocation): ITabRenderer {
createTabRenderer(tabLocation: TabLocation): ITabRenderer {
return this.tab;
}

View File

@ -133,11 +133,15 @@ describe('dockviewComponent', () => {
},
className: 'test-a test-b',
});
expect(dockview.element.className).toBe('test-a test-b dockview-theme-abyss');
expect(dockview.element.className).toBe(
'test-a test-b dockview-theme-abyss'
);
dockview.updateOptions({ className: 'test-b test-c' });
expect(dockview.element.className).toBe('dockview-theme-abyss test-b test-c');
expect(dockview.element.className).toBe(
'dockview-theme-abyss test-b test-c'
);
});
describe('memory leakage', () => {
@ -2453,17 +2457,17 @@ describe('dockviewComponent', () => {
const group = dockview.getGroupPanel('panel2')!.api.group;
const viewQuery = group.element.querySelectorAll(
'.dv-groupview > .dv-tabs-and-actions-container > .dv-tabs-panel > .dv-tabs-container > .dv-tab'
'.dv-groupview > .dv-tabs-and-actions-container > .dv-scrollable > .dv-tabs-container > .dv-tab'
);
expect(viewQuery.length).toBe(2);
const viewQuery2 = group.element.querySelectorAll(
'.dv-groupview > .dv-tabs-and-actions-container > .dv-tabs-panel > .dv-tabs-container > .dv-tab > .dv-default-tab'
'.dv-groupview > .dv-tabs-and-actions-container > .dv-scrollable > .dv-tabs-container > .dv-tab > .dv-default-tab'
);
expect(viewQuery2.length).toBe(1);
const viewQuery3 = group.element.querySelectorAll(
'.dv-groupview > .dv-tabs-and-actions-container > .dv-tabs-panel > .dv-tabs-container > .dv-tab > .panel-tab-part-panel2'
'.dv-groupview > .dv-tabs-and-actions-container > .dv-scrollable > .dv-tabs-container > .dv-tab > .panel-tab-part-panel2'
);
expect(viewQuery3.length).toBe(1);
});

View File

@ -43,7 +43,7 @@ class TestModel implements IDockviewPanelModel {
this.tab = new TestContentPart(id);
}
copyTabComponent(tabLocation: TabLocation): ITabRenderer {
createTabRenderer(tabLocation: TabLocation): ITabRenderer {
return new TestHeaderPart(this.id);
}

View File

@ -0,0 +1,19 @@
.dv-tabs-overflow-dropdown-default {
height: 100%;
color: var(--dv-activegroup-hiddenpanel-tab-color);
margin: var(--dv-tab-margin);
display: flex;
align-items: center;
flex-shrink: 0;
padding: 0.25rem 0.5rem;
cursor: pointer;
> span {
padding-left: 0.25rem;
}
> svg {
transform: rotate(90deg);
}
}

View File

@ -0,0 +1,25 @@
import { createChevronRightButton } from '../../../svg';
export type DropdownElement = {
element: HTMLElement;
update: (params: { tabs: number }) => void;
dispose?: () => void;
};
export function createDropdownElementHandle(): DropdownElement {
const el = document.createElement('div');
el.className = 'dv-tabs-overflow-dropdown-default';
const text = document.createElement('span');
text.textContent = ``;
const icon = createChevronRightButton();
el.appendChild(icon);
el.appendChild(text);
return {
element: el,
update: (params: { tabs: number }) => {
text.textContent = `${params.tabs}`;
},
};
}

View File

@ -1,6 +1,5 @@
.dv-tabs-panel {
.dv-tabs-container {
overflow: hidden;
&.dv-horizontal {
.dv-tabs-container {
.dv-tab {
@ -27,62 +26,38 @@
}
}
.dv-tabs-container {
display: flex;
overflow: hidden;
scrollbar-width: thin; // firefox
display: flex;
height: 100%;
overflow: hidden;
scrollbar-width: thin; // firefox
&::-webkit-scrollbar {
height: 3px;
}
/* Track */
&::-webkit-scrollbar-track {
background: transparent;
}
/* Handle */
&::-webkit-scrollbar-thumb {
background: var(--dv-tabs-container-scrollbar-color);
}
.dv-tab {
-webkit-user-drag: element;
outline: none;
padding: 0.25rem 0.5rem;
cursor: pointer;
position: relative;
box-sizing: border-box;
font-size: var(--dv-tab-font-size);
margin: var(--dv-tab-margin);
}
&::-webkit-scrollbar {
height: 3px;
}
.dv-tabs-overflow-dropdown-default {
background-color: var(
--dv-activegroup-hiddenpanel-tab-background-color
);
height: 100%;
color: var(--dv-activegroup-hiddenpanel-tab-color);
border-left: 1px solid var(--dv-tab-divider-color);
/* Track */
&::-webkit-scrollbar-track {
background: transparent;
}
margin: var(--dv-tab-margin);
display: flex;
align-items: center;
flex-shrink: 0;
/* Handle */
&::-webkit-scrollbar-thumb {
background: var(--dv-tabs-container-scrollbar-color);
}
.dv-tab {
-webkit-user-drag: element;
outline: none;
padding: 0.25rem 0.5rem;
cursor: pointer;
> span {
padding-left: 0.25rem;
}
> svg {
transform: rotate(90deg);
}
position: relative;
box-sizing: border-box;
font-size: var(--dv-tab-font-size);
margin: var(--dv-tab-margin);
}
}
.dv-tabs-overflow-container {
flex-direction: column;
height: unset;

View File

@ -2,7 +2,6 @@ import { getPanelData } from '../../../dnd/dataTransfer';
import {
isChildEntirelyVisibleWithinParent,
OverflowObserver,
toggleClass,
} from '../../../dom';
import { addDisposableListener, Emitter, Event } from '../../../events';
import {
@ -11,7 +10,7 @@ import {
IValueDisposable,
MutableDisposable,
} from '../../../lifecycle';
import { createChevronRightButton } from '../../../svg';
import { Scrollbar } from '../../../scrollbar';
import { DockviewComponent } from '../../dockviewComponent';
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
import { WillShowOverlayLocationEvent } from '../../dockviewGroupPanelModel';
@ -19,38 +18,14 @@ import { DockviewPanel, IDockviewPanel } from '../../dockviewPanel';
import { Tab } from '../tab/tab';
import { TabDragEvent, TabDropIndexEvent } from './tabsContainer';
type DropdownElement = {
element: HTMLElement;
update: (params: { tabs: number }) => void;
dispose?: () => void;
};
function createDropdownElementHandle(): DropdownElement {
const el = document.createElement('div');
el.className = 'dv-tabs-overflow-dropdown-default';
const text = document.createElement('span');
text.textContent = ``;
const icon = createChevronRightButton();
el.appendChild(icon);
el.appendChild(text);
return {
element: el,
update: (params: { tabs: number }) => {
text.textContent = `${params.tabs}`;
},
};
}
export class Tabs extends CompositeDisposable {
private readonly _element: HTMLElement;
private readonly _tabsList: HTMLElement;
private readonly _observerDisposable = new MutableDisposable();
private tabs: IValueDisposable<Tab>[] = [];
private _tabs: IValueDisposable<Tab>[] = [];
private selectedIndex = -1;
private readonly _dropdownDisposable = new MutableDisposable();
private _showTabsOverflowControl = false;
private readonly _onTabDragStart = new Emitter<TabDragEvent>();
readonly onTabDragStart: Event<TabDragEvent> = this._onTabDragStart.event;
@ -63,50 +38,79 @@ export class Tabs extends CompositeDisposable {
readonly onWillShowOverlay: Event<WillShowOverlayLocationEvent> =
this._onWillShowOverlay.event;
private dropdownPart: DropdownElement | null = null;
private _overflowTabs: string[] = [];
private readonly _onOverflowTabsChange = new Emitter<{
tabs: string[];
reset: boolean;
}>();
readonly onOverflowTabsChange = this._onOverflowTabsChange.event;
get showTabsOverflowControl(): boolean {
return this._showTabsOverflowControl;
}
set showTabsOverflowControl(value: boolean) {
if (this._showTabsOverflowControl == value) {
return;
}
this._showTabsOverflowControl = value;
if (value) {
const observer = new OverflowObserver(this._tabsList);
this._observerDisposable.value = new CompositeDisposable(
observer,
observer.onDidChange((event) => {
const hasOverflow = event.hasScrollX || event.hasScrollY;
this.toggleDropdown({ reset: !hasOverflow });
}),
addDisposableListener(this._tabsList, 'scroll', () => {
this.toggleDropdown({ reset: false });
})
);
}
}
get element(): HTMLElement {
return this._element;
}
get panels(): string[] {
return this.tabs.map((_) => _.value.panel.id);
return this._tabs.map((_) => _.value.panel.id);
}
get size(): number {
return this.tabs.length;
return this._tabs.length;
}
get tabs(): Tab[] {
return this._tabs.map((_) => _.value);
}
constructor(
private readonly group: DockviewGroupPanel,
private readonly accessor: DockviewComponent
private readonly accessor: DockviewComponent,
options: {
showTabsOverflowControl: boolean;
}
) {
super();
this._element = document.createElement('div');
this._element.className = 'dv-tabs-panel dv-horizontal';
this._element.style.display = 'flex';
this._element.style.overflow = 'auto';
this._tabsList = document.createElement('div');
this._tabsList.className = 'dv-tabs-container';
this._element.appendChild(this._tabsList);
this._tabsList.className = 'dv-tabs-container dv-horizontal';
const observer = new OverflowObserver(this._tabsList);
this.showTabsOverflowControl = options.showTabsOverflowControl;
const scrollbar = new Scrollbar(this._tabsList);
this._element = scrollbar.element;
this.addDisposables(
this._dropdownDisposable,
this._onOverflowTabsChange,
this._observerDisposable,
scrollbar,
this._onWillShowOverlay,
this._onDrop,
this._onTabDragStart,
observer,
observer.onDidChange((event) => {
const hasOverflow = event.hasScrollX || event.hasScrollY;
this.toggleDropdown({ reset: !hasOverflow });
}),
addDisposableListener(this._tabsList, 'scroll', () => {
this.toggleDropdown({ reset: false });
}),
addDisposableListener(this.element, 'pointerdown', (event) => {
if (event.defaultPrevented) {
return;
@ -119,31 +123,31 @@ export class Tabs extends CompositeDisposable {
}
}),
Disposable.from(() => {
for (const { value, disposable } of this.tabs) {
for (const { value, disposable } of this._tabs) {
disposable.dispose();
value.dispose();
}
this.tabs = [];
this._tabs = [];
})
);
}
indexOf(id: string): number {
return this.tabs.findIndex((tab) => tab.value.panel.id === id);
return this._tabs.findIndex((tab) => tab.value.panel.id === id);
}
isActive(tab: Tab): boolean {
return (
this.selectedIndex > -1 &&
this.tabs[this.selectedIndex].value === tab
this._tabs[this.selectedIndex].value === tab
);
}
setActivePanel(panel: IDockviewPanel): void {
let runningWidth = 0;
for (const tab of this.tabs) {
for (const tab of this._tabs) {
const isActivePanel = panel.id === tab.value.panel.id;
tab.value.setActive(isActivePanel);
@ -164,8 +168,8 @@ export class Tabs extends CompositeDisposable {
}
}
openPanel(panel: IDockviewPanel, index: number = this.tabs.length): void {
if (this.tabs.find((tab) => tab.value.panel.id === panel.id)) {
openPanel(panel: IDockviewPanel, index: number = this._tabs.length): void {
if (this._tabs.find((tab) => tab.value.panel.id === panel.id)) {
return;
}
const tab = new Tab(panel, this.accessor, this.group);
@ -219,7 +223,7 @@ export class Tabs extends CompositeDisposable {
tab.onDrop((event) => {
this._onDrop.fire({
event: event.nativeEvent,
index: this.tabs.findIndex((x) => x.value === tab),
index: this._tabs.findIndex((x) => x.value === tab),
});
}),
tab.onWillShowOverlay((event) => {
@ -242,7 +246,7 @@ export class Tabs extends CompositeDisposable {
delete(id: string): void {
const index = this.indexOf(id);
const tabToRemove = this.tabs.splice(index, 1)[0];
const tabToRemove = this._tabs.splice(index, 1)[0];
const { value, disposable } = tabToRemove;
@ -253,9 +257,9 @@ export class Tabs extends CompositeDisposable {
private addTab(
tab: IValueDisposable<Tab>,
index: number = this.tabs.length
index: number = this._tabs.length
): void {
if (index < 0 || index > this.tabs.length) {
if (index < 0 || index > this._tabs.length) {
throw new Error('invalid location');
}
@ -264,10 +268,10 @@ export class Tabs extends CompositeDisposable {
this._tabsList.children[index]
);
this.tabs = [
...this.tabs.slice(0, index),
this._tabs = [
...this._tabs.slice(0, index),
tab,
...this.tabs.slice(index),
...this._tabs.slice(index),
];
if (this.selectedIndex < 0) {
@ -278,7 +282,7 @@ export class Tabs extends CompositeDisposable {
private toggleDropdown(options: { reset: boolean }): void {
const tabs = options.reset
? []
: this.tabs
: this._tabs
.filter(
(tab) =>
!isChildEntirelyVisibleWithinParent(
@ -288,92 +292,6 @@ export class Tabs extends CompositeDisposable {
)
.map((x) => x.value.panel.id);
this._overflowTabs = tabs;
if (this._overflowTabs.length > 0 && this.dropdownPart) {
this.dropdownPart.update({ tabs: tabs.length });
return;
}
if (this._overflowTabs.length === 0) {
this._dropdownDisposable.dispose();
return;
}
const root = document.createElement('div');
root.className = 'dv-tabs-overflow-dropdown-root';
const part = createDropdownElementHandle();
part.update({ tabs: tabs.length });
this.dropdownPart = part;
root.appendChild(part.element);
this.element.appendChild(root);
this._dropdownDisposable.value = new CompositeDisposable(
Disposable.from(() => {
root.remove();
this.dropdownPart?.dispose?.();
this.dropdownPart = null;
}),
addDisposableListener(
root,
'pointerdown',
(event) => {
event.preventDefault();
},
{ capture: true }
),
addDisposableListener(root, 'click', (event) => {
const el = document.createElement('div');
el.style.overflow = 'auto';
el.className = 'dv-tabs-overflow-container';
this.tabs
.filter((tab) =>
this._overflowTabs.includes(tab.value.panel.id)
)
.map((tab) => {
const panelObject = this.group.panels.find(
(panel) => panel === tab.value.panel
)!;
const tabComponent =
panelObject.view.createTabRenderer(
'headerOverflow'
);
const child = tabComponent.element;
const wrapper = document.createElement('div');
toggleClass(wrapper, 'dv-tab', true);
toggleClass(
wrapper,
'dv-active-tab',
panelObject.api.isActive
);
toggleClass(
wrapper,
'dv-inactive-tab',
!panelObject.api.isActive
);
wrapper.addEventListener('mousedown', () => {
this.accessor.popupService.close();
tab.value.element.scrollIntoView();
tab.value.panel.api.setActive();
});
wrapper.appendChild(child);
el.appendChild(wrapper);
});
this.accessor.popupService.openPopover(el, {
x: event.clientX,
y: event.clientY,
});
})
);
this._onOverflowTabsChange.fire({ tabs, reset: options.reset });
}
}

View File

@ -25,4 +25,8 @@
flex-grow: 1;
cursor: grab;
}
.dv-right-actions-container {
display: flex;
}
}

View File

@ -1,4 +1,9 @@
import { IDisposable, CompositeDisposable } from '../../../lifecycle';
import {
IDisposable,
CompositeDisposable,
Disposable,
MutableDisposable,
} from '../../../lifecycle';
import { addDisposableListener, Emitter, Event } from '../../../events';
import { Tab } from '../tab/tab';
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
@ -6,12 +11,13 @@ import { VoidContainer } from './voidContainer';
import { toggleClass } from '../../../dom';
import { IDockviewPanel } from '../../dockviewPanel';
import { DockviewComponent } from '../../dockviewComponent';
import {
DockviewGroupPanelModel,
WillShowOverlayLocationEvent,
} from '../../dockviewGroupPanelModel';
import { WillShowOverlayLocationEvent } from '../../dockviewGroupPanelModel';
import { getPanelData } from '../../../dnd/dataTransfer';
import { Tabs } from './tabs';
import {
createDropdownElementHandle,
DropdownElement,
} from './tabOverflowControl';
export interface TabDropIndexEvent {
readonly event: DragEvent;
@ -68,6 +74,10 @@ export class TabsContainer
private _hidden = false;
private dropdownPart: DropdownElement | null = null;
private _overflowTabs: string[] = [];
private readonly _dropdownDisposable = new MutableDisposable();
private readonly _onDrop = new Emitter<TabDropIndexEvent>();
readonly onDrop: Event<TabDropIndexEvent> = this._onDrop.event;
@ -129,7 +139,13 @@ export class TabsContainer
this.preActionsContainer = document.createElement('div');
this.preActionsContainer.className = 'dv-pre-actions-container';
this.tabs = new Tabs(group, accessor);
this.tabs = new Tabs(group, accessor, {
showTabsOverflowControl: false,
});
this.tabs.onOverflowTabsChange((event) => {
this.toggleDropdown(event);
});
this.voidContainer = new VoidContainer(this.accessor, this.group);
@ -287,4 +303,93 @@ export class TabsContainer
private updateClassnames(): void {
toggleClass(this._element, 'dv-single-tab', this.size === 1);
}
private toggleDropdown(options: { tabs: string[]; reset: boolean }): void {
const tabs = options.reset ? [] : options.tabs;
this._overflowTabs = tabs;
if (this._overflowTabs.length > 0 && this.dropdownPart) {
this.dropdownPart.update({ tabs: tabs.length });
return;
}
if (this._overflowTabs.length === 0) {
this._dropdownDisposable.dispose();
return;
}
const root = document.createElement('div');
root.className = 'dv-tabs-overflow-dropdown-root';
const part = createDropdownElementHandle();
part.update({ tabs: tabs.length });
this.dropdownPart = part;
root.appendChild(part.element);
this.rightActionsContainer.prepend(root);
this._dropdownDisposable.value = new CompositeDisposable(
Disposable.from(() => {
root.remove();
this.dropdownPart?.dispose?.();
this.dropdownPart = null;
}),
addDisposableListener(
root,
'pointerdown',
(event) => {
event.preventDefault();
},
{ capture: true }
),
addDisposableListener(root, 'click', (event) => {
const el = document.createElement('div');
el.style.overflow = 'auto';
el.className = 'dv-tabs-overflow-container';
this.tabs.tabs
.filter((tab) => this._overflowTabs.includes(tab.panel.id))
.map((tab) => {
const panelObject = this.group.panels.find(
(panel) => panel === tab.panel
)!;
const tabComponent =
panelObject.view.createTabRenderer(
'headerOverflow'
);
const child = tabComponent.element;
const wrapper = document.createElement('div');
toggleClass(wrapper, 'dv-tab', true);
toggleClass(
wrapper,
'dv-active-tab',
panelObject.api.isActive
);
toggleClass(
wrapper,
'dv-inactive-tab',
!panelObject.api.isActive
);
wrapper.addEventListener('mousedown', () => {
this.accessor.popupService.close();
tab.element.scrollIntoView();
tab.panel.api.setActive();
});
wrapper.appendChild(child);
el.appendChild(wrapper);
});
this.accessor.popupService.openPopover(el, {
x: event.clientX,
y: event.clientY,
});
})
);
}
}

View File

@ -19,7 +19,7 @@
.dv-groupview {
&.dv-active-group {
> .dv-tabs-and-actions-container
> .dv-tabs-panel
> .dv-scrollable
> .dv-tabs-container
> .dv-tab {
&.dv-active-tab {
@ -38,7 +38,7 @@
}
&.dv-inactive-group {
> .dv-tabs-and-actions-container
> .dv-tabs-panel
> .dv-scrollable
> .dv-tabs-container
> .dv-tab {
&.dv-active-tab {

View File

@ -0,0 +1,28 @@
.dv-scrollable {
position: relative;
overflow: hidden;
.dv-scrollbar-horizontal {
position: absolute;
bottom: 0px;
left: 0px;
height: 4px;
border-radius: 2px;
background-color: transparent;
transition-property: background-color;
transition-timing-function: ease-in-out;
transition-duration: 1s;
transition-delay: 0s;
}
&:hover,
&.dv-scrollable-resizing,
&.dv-scrollable-scrolling {
.dv-scrollbar-horizontal {
background-color: var(
--dv-scrollbar-background-color,
rgba(255, 255, 255, 0.25)
);
}
}
}

View File

@ -0,0 +1,131 @@
import { toggleClass, watchElementResize } from './dom';
import { addDisposableListener } from './events';
import { CompositeDisposable } from './lifecycle';
import { clamp } from './math';
export class Scrollbar extends CompositeDisposable {
private _element: HTMLElement;
private _horizontalScrollbar: HTMLElement;
private _scrollLeft: number = 0;
private _animationTimer: any;
static MouseWheelSpeed = 1;
get element(): HTMLElement {
return this._element;
}
constructor(private readonly scrollableElement: HTMLElement) {
super();
this._element = document.createElement('div');
this._element.className = 'dv-scrollable';
this._horizontalScrollbar = document.createElement('div');
this._horizontalScrollbar.className = 'dv-scrollbar-horizontal';
this.element.appendChild(scrollableElement);
this.element.appendChild(this._horizontalScrollbar);
this.addDisposables(
addDisposableListener(this.element, 'wheel', (event) => {
this._scrollLeft += event.deltaY * Scrollbar.MouseWheelSpeed;
this.calculateScrollbarStyles();
}),
addDisposableListener(
this._horizontalScrollbar,
'pointerdown',
(event) => {
event.preventDefault();
toggleClass(this.element, 'dv-scrollable-scrolling', true);
const originalClientX = event.clientX;
const originalScrollLeft = this._scrollLeft;
const onPointerMove = (event: PointerEvent) => {
const deltaX = event.clientX - originalClientX;
const { clientWidth } = this.element;
const { scrollWidth } = this.scrollableElement;
const p = clientWidth / scrollWidth;
this._scrollLeft = originalScrollLeft + deltaX / p;
this.calculateScrollbarStyles();
};
const onEnd = () => {
toggleClass(
this.element,
'dv-scrollable-scrolling',
false
);
document.removeEventListener(
'pointermove',
onPointerMove
);
document.removeEventListener('pointerup', onEnd);
document.removeEventListener('pointercancel', onEnd);
};
document.addEventListener('pointermove', onPointerMove);
document.addEventListener('pointerup', onEnd);
document.addEventListener('pointercancel', onEnd);
}
),
addDisposableListener(this.element, 'scroll', () => {
this.calculateScrollbarStyles();
}),
addDisposableListener(this.scrollableElement, 'scroll', () => {
this._scrollLeft = this.scrollableElement.scrollLeft;
this.calculateScrollbarStyles();
}),
watchElementResize(this.element, () => {
toggleClass(this.element, 'dv-scrollable-resizing', true);
if (this._animationTimer) {
clearTimeout(this._animationTimer);
}
this._animationTimer = setTimeout(() => {
clearTimeout(this._animationTimer);
toggleClass(this.element, 'dv-scrollable-resizing', false);
}, 500);
this.calculateScrollbarStyles();
})
);
}
private calculateScrollbarStyles(): void {
const { clientWidth } = this.element;
const { scrollWidth } = this.scrollableElement;
const hasScrollbar = scrollWidth > clientWidth;
if (hasScrollbar) {
const px = clientWidth * (clientWidth / scrollWidth);
this._horizontalScrollbar.style.width = `${px}px`;
this._scrollLeft = clamp(
this._scrollLeft,
0,
this.scrollableElement.scrollWidth - clientWidth
);
this.scrollableElement.scrollLeft = this._scrollLeft;
const percentageComplete =
this._scrollLeft / (scrollWidth - clientWidth);
this._horizontalScrollbar.style.left = `${
(clientWidth - px) * percentageComplete
}px`;
} else {
this._horizontalScrollbar.style.width = `0px`;
this._horizontalScrollbar.style.left = `0px`;
this._scrollLeft = 0;
}
}
}

View File

@ -19,6 +19,7 @@ describe('defaultTab', () => {
render(
<DockviewDefaultTab
tabLocation="header"
api={api}
containerApi={containerApi}
params={params}
@ -41,6 +42,7 @@ describe('defaultTab', () => {
render(
<DockviewDefaultTab
tabLocation="header"
api={api}
containerApi={containerApi}
params={params}
@ -65,6 +67,7 @@ describe('defaultTab', () => {
render(
<DockviewDefaultTab
tabLocation="header"
api={api}
containerApi={containerApi}
params={params}
@ -97,6 +100,7 @@ describe('defaultTab', () => {
render(
<DockviewDefaultTab
tabLocation="header"
api={api}
containerApi={containerApi}
params={params}
@ -122,6 +126,7 @@ describe('defaultTab', () => {
render(
<DockviewDefaultTab
tabLocation="header"
api={api}
containerApi={containerApi}
params={params}
@ -151,6 +156,7 @@ describe('defaultTab', () => {
render(
<DockviewDefaultTab
tabLocation="header"
api={api}
containerApi={containerApi}
params={params}
@ -177,6 +183,7 @@ describe('defaultTab', () => {
render(
<DockviewDefaultTab
tabLocation="header"
api={api}
containerApi={containerApi}
params={params}