mirror of
https://github.com/mathuo/dockview
synced 2025-09-18 15:18:07 +00:00
Merge pull request #999 from mathuo/fix/github-issue-995-chrome-drag-prevention
Fix Chrome drag event prevention when disableDnd=true
This commit is contained in:
commit
4615f4d984
@ -176,4 +176,120 @@ describe('abstractDragHandler', () => {
|
||||
|
||||
handler.dispose();
|
||||
});
|
||||
|
||||
test('that disabled handler calls preventDefault on dragstart', () => {
|
||||
const element = document.createElement('div');
|
||||
|
||||
const handler = new (class TestClass extends DragHandler {
|
||||
constructor(el: HTMLElement, disabled?: boolean) {
|
||||
super(el, disabled);
|
||||
}
|
||||
|
||||
getData(): IDisposable {
|
||||
return {
|
||||
dispose: () => {
|
||||
// /
|
||||
},
|
||||
};
|
||||
}
|
||||
})(element, true);
|
||||
|
||||
const event = new Event('dragstart');
|
||||
const spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(element, event);
|
||||
expect(spy).toBeCalledTimes(1);
|
||||
|
||||
handler.dispose();
|
||||
});
|
||||
|
||||
test('that non-disabled handler does not call preventDefault on dragstart', () => {
|
||||
const element = document.createElement('div');
|
||||
|
||||
const handler = new (class TestClass extends DragHandler {
|
||||
constructor(el: HTMLElement, disabled?: boolean) {
|
||||
super(el, disabled);
|
||||
}
|
||||
|
||||
getData(): IDisposable {
|
||||
return {
|
||||
dispose: () => {
|
||||
// /
|
||||
},
|
||||
};
|
||||
}
|
||||
})(element, false);
|
||||
|
||||
const event = new Event('dragstart');
|
||||
const spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(0);
|
||||
|
||||
handler.dispose();
|
||||
});
|
||||
|
||||
test('that setDisabled method updates disabled state', () => {
|
||||
const element = document.createElement('div');
|
||||
|
||||
const handler = new (class TestClass extends DragHandler {
|
||||
constructor(el: HTMLElement, disabled?: boolean) {
|
||||
super(el, disabled);
|
||||
}
|
||||
|
||||
getData(): IDisposable {
|
||||
return {
|
||||
dispose: () => {
|
||||
// /
|
||||
},
|
||||
};
|
||||
}
|
||||
})(element, false);
|
||||
|
||||
// Initially not disabled
|
||||
let event = new Event('dragstart');
|
||||
let spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(0);
|
||||
|
||||
// Disable and test
|
||||
handler.setDisabled(true);
|
||||
event = new Event('dragstart');
|
||||
spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(element, event);
|
||||
expect(spy).toBeCalledTimes(1);
|
||||
|
||||
// Re-enable and test
|
||||
handler.setDisabled(false);
|
||||
event = new Event('dragstart');
|
||||
spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(0);
|
||||
|
||||
handler.dispose();
|
||||
});
|
||||
|
||||
test('that disabled handler does not fire onDragStart event', () => {
|
||||
const element = document.createElement('div');
|
||||
|
||||
const handler = new (class TestClass extends DragHandler {
|
||||
constructor(el: HTMLElement, disabled?: boolean) {
|
||||
super(el, disabled);
|
||||
}
|
||||
|
||||
getData(): IDisposable {
|
||||
return {
|
||||
dispose: () => {
|
||||
// /
|
||||
},
|
||||
};
|
||||
}
|
||||
})(element, true);
|
||||
|
||||
const spy = jest.fn();
|
||||
handler.onDragStart(spy);
|
||||
|
||||
fireEvent.dragStart(element);
|
||||
expect(spy).toHaveBeenCalledTimes(0);
|
||||
|
||||
handler.dispose();
|
||||
});
|
||||
});
|
||||
|
@ -338,5 +338,104 @@ describe('tab', () => {
|
||||
cut.updateDragAndDropState();
|
||||
expect(cut.element.draggable).toBe(true);
|
||||
});
|
||||
|
||||
test('that dragstart is prevented when disableDnd is true', () => {
|
||||
const accessor = fromPartial<DockviewComponent>({
|
||||
options: { disableDnd: true }
|
||||
});
|
||||
const groupMock = jest.fn();
|
||||
|
||||
const cut = new Tab(
|
||||
{ id: 'panelId' } as IDockviewPanel,
|
||||
accessor,
|
||||
new groupMock()
|
||||
);
|
||||
|
||||
const event = new Event('dragstart');
|
||||
const spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(cut.element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
|
||||
cut.dispose();
|
||||
});
|
||||
|
||||
test('that dragstart is not prevented when disableDnd is false', () => {
|
||||
const accessor = fromPartial<DockviewComponent>({
|
||||
options: { disableDnd: false }
|
||||
});
|
||||
const groupMock = jest.fn();
|
||||
|
||||
const cut = new Tab(
|
||||
{ id: 'panelId' } as IDockviewPanel,
|
||||
accessor,
|
||||
new groupMock()
|
||||
);
|
||||
|
||||
const event = new Event('dragstart');
|
||||
const spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(cut.element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(0);
|
||||
|
||||
cut.dispose();
|
||||
});
|
||||
|
||||
test('that updateDragAndDropState updates drag handler disabled state', () => {
|
||||
const options = { disableDnd: false };
|
||||
const accessor = fromPartial<DockviewComponent>({
|
||||
options
|
||||
});
|
||||
const groupMock = jest.fn();
|
||||
|
||||
const cut = new Tab(
|
||||
{ id: 'panelId' } as IDockviewPanel,
|
||||
accessor,
|
||||
new groupMock()
|
||||
);
|
||||
|
||||
// Initially not disabled
|
||||
let event = new Event('dragstart');
|
||||
let spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(cut.element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(0);
|
||||
|
||||
// Simulate option change to disabled
|
||||
options.disableDnd = true;
|
||||
cut.updateDragAndDropState();
|
||||
event = new Event('dragstart');
|
||||
spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(cut.element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Change back to enabled
|
||||
options.disableDnd = false;
|
||||
cut.updateDragAndDropState();
|
||||
event = new Event('dragstart');
|
||||
spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(cut.element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(0);
|
||||
|
||||
cut.dispose();
|
||||
});
|
||||
|
||||
test('that onDragStart is not fired when disableDnd is true', () => {
|
||||
const accessor = fromPartial<DockviewComponent>({
|
||||
options: { disableDnd: true }
|
||||
});
|
||||
const groupMock = jest.fn();
|
||||
|
||||
const cut = new Tab(
|
||||
{ id: 'panelId' } as IDockviewPanel,
|
||||
accessor,
|
||||
new groupMock()
|
||||
);
|
||||
|
||||
const spy = jest.fn();
|
||||
cut.onDragStart(spy);
|
||||
|
||||
fireEvent.dragStart(cut.element);
|
||||
expect(spy).toHaveBeenCalledTimes(0);
|
||||
|
||||
cut.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -111,5 +111,100 @@ describe('voidContainer', () => {
|
||||
cut.updateDragAndDropState();
|
||||
expect(cut.element.classList.contains('dv-draggable')).toBe(true);
|
||||
});
|
||||
|
||||
test('that dragstart is prevented when disableDnd is true', () => {
|
||||
const accessor = fromPartial<DockviewComponent>({
|
||||
options: { disableDnd: true }
|
||||
});
|
||||
const group = fromPartial<DockviewGroupPanel>({
|
||||
api: {
|
||||
location: { type: 'grid' }
|
||||
}
|
||||
});
|
||||
const cut = new VoidContainer(accessor, group);
|
||||
|
||||
const event = new Event('dragstart');
|
||||
const spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(cut.element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
|
||||
cut.dispose();
|
||||
});
|
||||
|
||||
test('that dragstart is not prevented when disableDnd is false', () => {
|
||||
const accessor = fromPartial<DockviewComponent>({
|
||||
options: { disableDnd: false }
|
||||
});
|
||||
const group = fromPartial<DockviewGroupPanel>({
|
||||
api: {
|
||||
location: { type: 'grid' }
|
||||
}
|
||||
});
|
||||
const cut = new VoidContainer(accessor, group);
|
||||
|
||||
const event = new Event('dragstart');
|
||||
const spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(cut.element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(0);
|
||||
|
||||
cut.dispose();
|
||||
});
|
||||
|
||||
test('that updateDragAndDropState updates drag handler disabled state', () => {
|
||||
const options = { disableDnd: false };
|
||||
const accessor = fromPartial<DockviewComponent>({
|
||||
options
|
||||
});
|
||||
const group = fromPartial<DockviewGroupPanel>({
|
||||
api: {
|
||||
location: { type: 'grid' }
|
||||
}
|
||||
});
|
||||
const cut = new VoidContainer(accessor, group);
|
||||
|
||||
// Initially not disabled
|
||||
let event = new Event('dragstart');
|
||||
let spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(cut.element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(0);
|
||||
|
||||
// Simulate option change to disabled
|
||||
options.disableDnd = true;
|
||||
cut.updateDragAndDropState();
|
||||
event = new Event('dragstart');
|
||||
spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(cut.element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Change back to enabled
|
||||
options.disableDnd = false;
|
||||
cut.updateDragAndDropState();
|
||||
event = new Event('dragstart');
|
||||
spy = jest.spyOn(event, 'preventDefault');
|
||||
fireEvent(cut.element, event);
|
||||
expect(spy).toHaveBeenCalledTimes(0);
|
||||
|
||||
cut.dispose();
|
||||
});
|
||||
|
||||
test('that onDragStart is not fired when disableDnd is true', () => {
|
||||
const accessor = fromPartial<DockviewComponent>({
|
||||
options: { disableDnd: true }
|
||||
});
|
||||
const group = fromPartial<DockviewGroupPanel>({
|
||||
api: {
|
||||
location: { type: 'grid' }
|
||||
}
|
||||
});
|
||||
const cut = new VoidContainer(accessor, group);
|
||||
|
||||
const spy = jest.fn();
|
||||
cut.onDragStart(spy);
|
||||
|
||||
fireEvent.dragStart(cut.element);
|
||||
expect(spy).toHaveBeenCalledTimes(0);
|
||||
|
||||
cut.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -13,7 +13,7 @@ export abstract class DragHandler extends CompositeDisposable {
|
||||
private readonly _onDragStart = new Emitter<DragEvent>();
|
||||
readonly onDragStart = this._onDragStart.event;
|
||||
|
||||
constructor(protected readonly el: HTMLElement) {
|
||||
constructor(protected readonly el: HTMLElement, private disabled?: boolean) {
|
||||
super();
|
||||
|
||||
this.addDisposables(
|
||||
@ -25,6 +25,10 @@ export abstract class DragHandler extends CompositeDisposable {
|
||||
this.configure();
|
||||
}
|
||||
|
||||
public setDisabled(disabled: boolean): void {
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
abstract getData(event: DragEvent): IDisposable;
|
||||
|
||||
protected isCancelled(_event: DragEvent): boolean {
|
||||
@ -35,7 +39,7 @@ export abstract class DragHandler extends CompositeDisposable {
|
||||
this.addDisposables(
|
||||
this._onDragStart,
|
||||
addDisposableListener(this.el, 'dragstart', (event) => {
|
||||
if (event.defaultPrevented || this.isCancelled(event)) {
|
||||
if (event.defaultPrevented || this.isCancelled(event) || this.disabled) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
@ -14,9 +14,10 @@ export class GroupDragHandler extends DragHandler {
|
||||
constructor(
|
||||
element: HTMLElement,
|
||||
private readonly accessor: DockviewComponent,
|
||||
private readonly group: DockviewGroupPanel
|
||||
private readonly group: DockviewGroupPanel,
|
||||
disabled?: boolean
|
||||
) {
|
||||
super(element);
|
||||
super(element, disabled);
|
||||
|
||||
this.addDisposables(
|
||||
addDisposableListener(
|
||||
|
@ -26,9 +26,10 @@ class TabDragHandler extends DragHandler {
|
||||
element: HTMLElement,
|
||||
private readonly accessor: DockviewComponent,
|
||||
private readonly group: DockviewGroupPanel,
|
||||
private readonly panel: IDockviewPanel
|
||||
private readonly panel: IDockviewPanel,
|
||||
disabled?: boolean
|
||||
) {
|
||||
super(element);
|
||||
super(element, disabled);
|
||||
}
|
||||
|
||||
getData(event: DragEvent): IDisposable {
|
||||
@ -49,6 +50,7 @@ export class Tab extends CompositeDisposable {
|
||||
private readonly _element: HTMLElement;
|
||||
private readonly dropTarget: Droptarget;
|
||||
private content: ITabRenderer | undefined = undefined;
|
||||
private readonly dragHandler: TabDragHandler;
|
||||
|
||||
private readonly _onPointDown = new Emitter<MouseEvent>();
|
||||
readonly onPointerDown: Event<MouseEvent> = this._onPointDown.event;
|
||||
@ -79,11 +81,12 @@ export class Tab extends CompositeDisposable {
|
||||
|
||||
toggleClass(this.element, 'dv-inactive-tab', true);
|
||||
|
||||
const dragHandler = new TabDragHandler(
|
||||
this.dragHandler = new TabDragHandler(
|
||||
this._element,
|
||||
this.accessor,
|
||||
this.group,
|
||||
this.panel
|
||||
this.panel,
|
||||
!!this.accessor.options.disableDnd
|
||||
);
|
||||
|
||||
this.dropTarget = new Droptarget(this._element, {
|
||||
@ -115,7 +118,7 @@ export class Tab extends CompositeDisposable {
|
||||
this._onPointDown,
|
||||
this._onDropped,
|
||||
this._onDragStart,
|
||||
dragHandler.onDragStart((event) => {
|
||||
this.dragHandler.onDragStart((event) => {
|
||||
if (event.dataTransfer) {
|
||||
const style = getComputedStyle(this.element);
|
||||
const newNode = this.element.cloneNode(true) as HTMLElement;
|
||||
@ -135,7 +138,7 @@ export class Tab extends CompositeDisposable {
|
||||
}
|
||||
this._onDragStart.fire(event);
|
||||
}),
|
||||
dragHandler,
|
||||
this.dragHandler,
|
||||
addDisposableListener(this._element, 'pointerdown', (event) => {
|
||||
this._onPointDown.fire(event);
|
||||
}),
|
||||
@ -161,6 +164,7 @@ export class Tab extends CompositeDisposable {
|
||||
|
||||
public updateDragAndDropState(): void {
|
||||
this._element.draggable = !this.accessor.options.disableDnd;
|
||||
this.dragHandler.setDisabled(!!this.accessor.options.disableDnd);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
@ -15,6 +15,7 @@ import { toggleClass } from '../../../dom';
|
||||
export class VoidContainer extends CompositeDisposable {
|
||||
private readonly _element: HTMLElement;
|
||||
private readonly dropTarget: Droptarget;
|
||||
private readonly handler: GroupDragHandler;
|
||||
|
||||
private readonly _onDrop = new Emitter<DroptargetEvent>();
|
||||
readonly onDrop: Event<DroptargetEvent> = this._onDrop.event;
|
||||
@ -49,7 +50,7 @@ export class VoidContainer extends CompositeDisposable {
|
||||
})
|
||||
);
|
||||
|
||||
const handler = new GroupDragHandler(this._element, accessor, group);
|
||||
this.handler = new GroupDragHandler(this._element, accessor, group, !!this.accessor.options.disableDnd);
|
||||
|
||||
this.dropTarget = new Droptarget(this._element, {
|
||||
acceptedTargetZones: ['center'],
|
||||
@ -72,8 +73,8 @@ export class VoidContainer extends CompositeDisposable {
|
||||
this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
|
||||
|
||||
this.addDisposables(
|
||||
handler,
|
||||
handler.onDragStart((event) => {
|
||||
this.handler,
|
||||
this.handler.onDragStart((event) => {
|
||||
this._onDragStart.fire(event);
|
||||
}),
|
||||
this.dropTarget.onDrop((event) => {
|
||||
@ -86,5 +87,6 @@ export class VoidContainer extends CompositeDisposable {
|
||||
updateDragAndDropState(): void {
|
||||
this._element.draggable = !this.accessor.options.disableDnd;
|
||||
toggleClass(this._element, 'dv-draggable', !this.accessor.options.disableDnd);
|
||||
this.handler.setDisabled(!!this.accessor.options.disableDnd);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user