mirror of
https://github.com/mathuo/dockview
synced 2025-09-20 16:18:05 +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();
|
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();
|
cut.updateDragAndDropState();
|
||||||
expect(cut.element.draggable).toBe(true);
|
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();
|
cut.updateDragAndDropState();
|
||||||
expect(cut.element.classList.contains('dv-draggable')).toBe(true);
|
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>();
|
private readonly _onDragStart = new Emitter<DragEvent>();
|
||||||
readonly onDragStart = this._onDragStart.event;
|
readonly onDragStart = this._onDragStart.event;
|
||||||
|
|
||||||
constructor(protected readonly el: HTMLElement) {
|
constructor(protected readonly el: HTMLElement, private disabled?: boolean) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
@ -25,6 +25,10 @@ export abstract class DragHandler extends CompositeDisposable {
|
|||||||
this.configure();
|
this.configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setDisabled(disabled: boolean): void {
|
||||||
|
this.disabled = disabled;
|
||||||
|
}
|
||||||
|
|
||||||
abstract getData(event: DragEvent): IDisposable;
|
abstract getData(event: DragEvent): IDisposable;
|
||||||
|
|
||||||
protected isCancelled(_event: DragEvent): boolean {
|
protected isCancelled(_event: DragEvent): boolean {
|
||||||
@ -35,7 +39,7 @@ export abstract class DragHandler extends CompositeDisposable {
|
|||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this._onDragStart,
|
this._onDragStart,
|
||||||
addDisposableListener(this.el, 'dragstart', (event) => {
|
addDisposableListener(this.el, 'dragstart', (event) => {
|
||||||
if (event.defaultPrevented || this.isCancelled(event)) {
|
if (event.defaultPrevented || this.isCancelled(event) || this.disabled) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,10 @@ export class GroupDragHandler extends DragHandler {
|
|||||||
constructor(
|
constructor(
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
private readonly accessor: DockviewComponent,
|
private readonly accessor: DockviewComponent,
|
||||||
private readonly group: DockviewGroupPanel
|
private readonly group: DockviewGroupPanel,
|
||||||
|
disabled?: boolean
|
||||||
) {
|
) {
|
||||||
super(element);
|
super(element, disabled);
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
addDisposableListener(
|
addDisposableListener(
|
||||||
|
@ -26,9 +26,10 @@ class TabDragHandler extends DragHandler {
|
|||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
private readonly accessor: DockviewComponent,
|
private readonly accessor: DockviewComponent,
|
||||||
private readonly group: DockviewGroupPanel,
|
private readonly group: DockviewGroupPanel,
|
||||||
private readonly panel: IDockviewPanel
|
private readonly panel: IDockviewPanel,
|
||||||
|
disabled?: boolean
|
||||||
) {
|
) {
|
||||||
super(element);
|
super(element, disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
getData(event: DragEvent): IDisposable {
|
getData(event: DragEvent): IDisposable {
|
||||||
@ -49,6 +50,7 @@ export class Tab extends CompositeDisposable {
|
|||||||
private readonly _element: HTMLElement;
|
private readonly _element: HTMLElement;
|
||||||
private readonly dropTarget: Droptarget;
|
private readonly dropTarget: Droptarget;
|
||||||
private content: ITabRenderer | undefined = undefined;
|
private content: ITabRenderer | undefined = undefined;
|
||||||
|
private readonly dragHandler: TabDragHandler;
|
||||||
|
|
||||||
private readonly _onPointDown = new Emitter<MouseEvent>();
|
private readonly _onPointDown = new Emitter<MouseEvent>();
|
||||||
readonly onPointerDown: Event<MouseEvent> = this._onPointDown.event;
|
readonly onPointerDown: Event<MouseEvent> = this._onPointDown.event;
|
||||||
@ -79,11 +81,12 @@ export class Tab extends CompositeDisposable {
|
|||||||
|
|
||||||
toggleClass(this.element, 'dv-inactive-tab', true);
|
toggleClass(this.element, 'dv-inactive-tab', true);
|
||||||
|
|
||||||
const dragHandler = new TabDragHandler(
|
this.dragHandler = new TabDragHandler(
|
||||||
this._element,
|
this._element,
|
||||||
this.accessor,
|
this.accessor,
|
||||||
this.group,
|
this.group,
|
||||||
this.panel
|
this.panel,
|
||||||
|
!!this.accessor.options.disableDnd
|
||||||
);
|
);
|
||||||
|
|
||||||
this.dropTarget = new Droptarget(this._element, {
|
this.dropTarget = new Droptarget(this._element, {
|
||||||
@ -115,7 +118,7 @@ export class Tab extends CompositeDisposable {
|
|||||||
this._onPointDown,
|
this._onPointDown,
|
||||||
this._onDropped,
|
this._onDropped,
|
||||||
this._onDragStart,
|
this._onDragStart,
|
||||||
dragHandler.onDragStart((event) => {
|
this.dragHandler.onDragStart((event) => {
|
||||||
if (event.dataTransfer) {
|
if (event.dataTransfer) {
|
||||||
const style = getComputedStyle(this.element);
|
const style = getComputedStyle(this.element);
|
||||||
const newNode = this.element.cloneNode(true) as HTMLElement;
|
const newNode = this.element.cloneNode(true) as HTMLElement;
|
||||||
@ -135,7 +138,7 @@ export class Tab extends CompositeDisposable {
|
|||||||
}
|
}
|
||||||
this._onDragStart.fire(event);
|
this._onDragStart.fire(event);
|
||||||
}),
|
}),
|
||||||
dragHandler,
|
this.dragHandler,
|
||||||
addDisposableListener(this._element, 'pointerdown', (event) => {
|
addDisposableListener(this._element, 'pointerdown', (event) => {
|
||||||
this._onPointDown.fire(event);
|
this._onPointDown.fire(event);
|
||||||
}),
|
}),
|
||||||
@ -161,6 +164,7 @@ export class Tab extends CompositeDisposable {
|
|||||||
|
|
||||||
public updateDragAndDropState(): void {
|
public updateDragAndDropState(): void {
|
||||||
this._element.draggable = !this.accessor.options.disableDnd;
|
this._element.draggable = !this.accessor.options.disableDnd;
|
||||||
|
this.dragHandler.setDisabled(!!this.accessor.options.disableDnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
|
@ -15,6 +15,7 @@ import { toggleClass } from '../../../dom';
|
|||||||
export class VoidContainer extends CompositeDisposable {
|
export class VoidContainer extends CompositeDisposable {
|
||||||
private readonly _element: HTMLElement;
|
private readonly _element: HTMLElement;
|
||||||
private readonly dropTarget: Droptarget;
|
private readonly dropTarget: Droptarget;
|
||||||
|
private readonly handler: GroupDragHandler;
|
||||||
|
|
||||||
private readonly _onDrop = new Emitter<DroptargetEvent>();
|
private readonly _onDrop = new Emitter<DroptargetEvent>();
|
||||||
readonly onDrop: Event<DroptargetEvent> = this._onDrop.event;
|
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, {
|
this.dropTarget = new Droptarget(this._element, {
|
||||||
acceptedTargetZones: ['center'],
|
acceptedTargetZones: ['center'],
|
||||||
@ -72,8 +73,8 @@ export class VoidContainer extends CompositeDisposable {
|
|||||||
this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
|
this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
handler,
|
this.handler,
|
||||||
handler.onDragStart((event) => {
|
this.handler.onDragStart((event) => {
|
||||||
this._onDragStart.fire(event);
|
this._onDragStart.fire(event);
|
||||||
}),
|
}),
|
||||||
this.dropTarget.onDrop((event) => {
|
this.dropTarget.onDrop((event) => {
|
||||||
@ -86,5 +87,6 @@ export class VoidContainer extends CompositeDisposable {
|
|||||||
updateDragAndDropState(): void {
|
updateDragAndDropState(): void {
|
||||||
this._element.draggable = !this.accessor.options.disableDnd;
|
this._element.draggable = !this.accessor.options.disableDnd;
|
||||||
toggleClass(this._element, 'dv-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