mirror of
https://github.com/mathuo/dockview
synced 2025-09-30 21:18:33 +00:00
Merge branch 'master' of https://github.com/mathuo/dockview into 326-possible-to-stop-floating-windows-being-dragged-outside-visible-dockview-area
This commit is contained in:
commit
f6cfb4418c
@ -12,3 +12,16 @@ export function setMockRefElement(node: Partial<HTMLElement>): void {
|
||||
|
||||
jest.spyOn(React, 'useRef').mockReturnValueOnce(mockRef);
|
||||
}
|
||||
|
||||
export function createOffsetDragOverEvent(params: {
|
||||
clientX: number;
|
||||
clientY: number;
|
||||
}): Event {
|
||||
const event = new Event('dragover', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
Object.defineProperty(event, 'clientX', { get: () => params.clientX });
|
||||
Object.defineProperty(event, 'clientY', { get: () => params.clientY });
|
||||
return event;
|
||||
}
|
||||
|
@ -7,19 +7,7 @@ import {
|
||||
positionToDirection,
|
||||
} from '../../dnd/droptarget';
|
||||
import { fireEvent } from '@testing-library/dom';
|
||||
|
||||
function createOffsetDragOverEvent(params: {
|
||||
clientX: number;
|
||||
clientY: number;
|
||||
}): Event {
|
||||
const event = new Event('dragover', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
Object.defineProperty(event, 'clientX', { get: () => params.clientX });
|
||||
Object.defineProperty(event, 'clientY', { get: () => params.clientY });
|
||||
return event;
|
||||
}
|
||||
import { createOffsetDragOverEvent } from '../__test_utils__/utils';
|
||||
|
||||
describe('droptarget', () => {
|
||||
let element: HTMLElement;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {DockviewComponent} from '../../dockview/dockviewComponent';
|
||||
import { DockviewComponent } from '../../dockview/dockviewComponent';
|
||||
import {
|
||||
GroupviewPanelState,
|
||||
IGroupPanelInitParameters,
|
||||
@ -20,6 +20,7 @@ import { IDockviewPanel } from '../../dockview/dockviewPanel';
|
||||
import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel';
|
||||
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
||||
import { WatermarkRendererInitParameters } from '../../dockview/types';
|
||||
import { createOffsetDragOverEvent } from '../__test_utils__/utils';
|
||||
|
||||
enum GroupChangeKind2 {
|
||||
ADD_PANEL,
|
||||
@ -658,6 +659,97 @@ describe('groupview', () => {
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
test('that the .locked behaviour is as', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
options: {
|
||||
showDndOverlay: () => true,
|
||||
},
|
||||
getPanel: jest.fn(),
|
||||
onDidAddPanel: jest.fn(),
|
||||
onDidRemovePanel: jest.fn(),
|
||||
};
|
||||
});
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
||||
() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const groupView = new groupviewMock() as DockviewGroupPanelModel;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
||||
() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
const cut = new DockviewGroupPanelModel(
|
||||
container,
|
||||
accessor,
|
||||
'groupviewid',
|
||||
{},
|
||||
new groupPanelMock() as DockviewGroupPanel
|
||||
);
|
||||
|
||||
const element = container
|
||||
.getElementsByClassName('content-container')
|
||||
.item(0)!;
|
||||
|
||||
jest.spyOn(element, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 100);
|
||||
|
||||
function run(value: number) {
|
||||
fireEvent.dragEnter(element);
|
||||
fireEvent(
|
||||
element,
|
||||
createOffsetDragOverEvent({ clientX: value, clientY: value })
|
||||
);
|
||||
}
|
||||
|
||||
// base case - not locked
|
||||
cut.locked = false;
|
||||
run(10);
|
||||
expect(
|
||||
element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(1);
|
||||
fireEvent.dragEnd(element);
|
||||
|
||||
// special case - locked with no possible target
|
||||
cut.locked = 'no-drop-target';
|
||||
run(10);
|
||||
expect(
|
||||
element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(0);
|
||||
fireEvent.dragEnd(element);
|
||||
|
||||
// standard locked - only show if not center target
|
||||
cut.locked = true;
|
||||
run(10);
|
||||
expect(
|
||||
element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(1);
|
||||
fireEvent.dragEnd(element);
|
||||
|
||||
// standard locked but for center target - expect not shown
|
||||
cut.locked = true;
|
||||
run(25);
|
||||
expect(
|
||||
element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(0);
|
||||
fireEvent.dragEnd(element);
|
||||
});
|
||||
|
||||
test('that should not show drop target if dropping on self', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { DockviewDefaultTab } from '../../dockview/defaultTab';
|
||||
import * as React from 'react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
@ -40,6 +40,58 @@ describe('defaultTab', () => {
|
||||
expect(element.querySelector('.dv-react-tab-close-btn')).toBeNull();
|
||||
});
|
||||
|
||||
test('that settings closeActionOverride skips api.close()', async () => {
|
||||
const api = fromPartial<DockviewPanelApi>({
|
||||
close: jest.fn(),
|
||||
});
|
||||
const containerApi = fromPartial<DockviewApi>({});
|
||||
const params = {};
|
||||
|
||||
const closeActionOverride = jest.fn();
|
||||
|
||||
render(
|
||||
<DockviewDefaultTab
|
||||
api={api}
|
||||
containerApi={containerApi}
|
||||
params={params}
|
||||
closeActionOverride={closeActionOverride}
|
||||
/>
|
||||
);
|
||||
|
||||
const element = await screen.getByTestId('dockview-default-tab');
|
||||
const btn = element.querySelector(
|
||||
'.dv-react-tab-close-btn'
|
||||
) as HTMLElement;
|
||||
fireEvent.click(btn);
|
||||
|
||||
expect(closeActionOverride).toBeCalledTimes(1);
|
||||
expect(api.close).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
test('that clicking close calls api.close()', async () => {
|
||||
const api = fromPartial<DockviewPanelApi>({
|
||||
close: jest.fn(),
|
||||
});
|
||||
const containerApi = fromPartial<DockviewApi>({});
|
||||
const params = {};
|
||||
|
||||
render(
|
||||
<DockviewDefaultTab
|
||||
api={api}
|
||||
containerApi={containerApi}
|
||||
params={params}
|
||||
/>
|
||||
);
|
||||
|
||||
const element = await screen.getByTestId('dockview-default-tab');
|
||||
const btn = element.querySelector(
|
||||
'.dv-react-tab-close-btn'
|
||||
) as HTMLElement;
|
||||
fireEvent.click(btn);
|
||||
|
||||
expect(api.close).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('has close button when hideClose=false', async () => {
|
||||
const api = fromPartial<DockviewPanelApi>({});
|
||||
const containerApi = fromPartial<DockviewApi>({});
|
||||
@ -57,4 +109,31 @@ describe('defaultTab', () => {
|
||||
const element = await screen.getByTestId('dockview-default-tab');
|
||||
expect(element.querySelector('.dv-react-tab-close-btn')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('that mouseDown on close button prevents panel becoming active', async () => {
|
||||
const api = fromPartial<DockviewPanelApi>({
|
||||
setActive: jest.fn(),
|
||||
});
|
||||
const containerApi = fromPartial<DockviewApi>({});
|
||||
const params = {};
|
||||
|
||||
render(
|
||||
<DockviewDefaultTab
|
||||
api={api}
|
||||
containerApi={containerApi}
|
||||
params={params}
|
||||
/>
|
||||
);
|
||||
|
||||
const element = await screen.getByTestId('dockview-default-tab');
|
||||
const btn = element.querySelector(
|
||||
'.dv-react-tab-close-btn'
|
||||
) as HTMLElement;
|
||||
|
||||
fireEvent.mouseDown(btn);
|
||||
expect(api.setActive).toBeCalledTimes(0);
|
||||
|
||||
fireEvent.click(element);
|
||||
expect(api.setActive).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
@ -3,7 +3,10 @@ import * as React from 'react';
|
||||
import { CloseButton } from '../svg';
|
||||
|
||||
export type IDockviewDefaultTabProps = IDockviewPanelHeaderProps &
|
||||
React.DOMAttributes<HTMLDivElement> & { hideClose?: boolean };
|
||||
React.DOMAttributes<HTMLDivElement> & {
|
||||
hideClose?: boolean;
|
||||
closeActionOverride?: () => void;
|
||||
};
|
||||
|
||||
export const DockviewDefaultTab: React.FunctionComponent<
|
||||
IDockviewDefaultTabProps
|
||||
@ -12,18 +15,32 @@ export const DockviewDefaultTab: React.FunctionComponent<
|
||||
containerApi: _containerApi,
|
||||
params: _params,
|
||||
hideClose,
|
||||
closeActionOverride,
|
||||
...rest
|
||||
}) => {
|
||||
const onClose = React.useCallback(
|
||||
(event: React.MouseEvent<HTMLSpanElement>) => {
|
||||
event.stopPropagation();
|
||||
api.close();
|
||||
event.preventDefault();
|
||||
|
||||
if (closeActionOverride) {
|
||||
closeActionOverride();
|
||||
} else {
|
||||
api.close();
|
||||
}
|
||||
},
|
||||
[api]
|
||||
[api, closeActionOverride]
|
||||
);
|
||||
|
||||
const onMouseDown = React.useCallback((e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
}, []);
|
||||
|
||||
const onClick = React.useCallback(
|
||||
(event: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
api.setActive();
|
||||
|
||||
if (rest.onClick) {
|
||||
@ -42,7 +59,11 @@ export const DockviewDefaultTab: React.FunctionComponent<
|
||||
>
|
||||
<span className="dockview-react-tab-title">{api.title}</span>
|
||||
{!hideClose && (
|
||||
<div className="dv-react-tab-close-btn" onClick={onClose}>
|
||||
<div
|
||||
className="dv-react-tab-close-btn"
|
||||
onMouseDown={onMouseDown}
|
||||
onClick={onClose}
|
||||
>
|
||||
<CloseButton />
|
||||
</div>
|
||||
)}
|
||||
|
@ -632,6 +632,8 @@ A default implementation of `DockviewDefaultTab` is provided should you only wis
|
||||
changes and events that do not alter the default behaviour, for example to add a custom context menu event
|
||||
handler.
|
||||
|
||||
The `DockviewDefaulTab` component accepts a `hideClose` prop if you wish only to hide the close button.
|
||||
|
||||
```tsx title="Attaching a custom context menu event handlers to a custom header"
|
||||
import { IDockviewPanelHeaderProps, DockviewDefaultTab } from 'dockview';
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user