From e6db9d373045e2b7cf56646ca09fbed2e5317cbf Mon Sep 17 00:00:00 2001 From: crisbeto Date: Sat, 1 Sep 2018 12:05:41 +0200 Subject: [PATCH] fix(drag-drop): stop dragging sequence on touchcancel Adds handling for the `touchcancel` event to the the `DragDropRegistry`. --- src/cdk/drag-drop/drag-drop-registry.spec.ts | 29 ++++++++++++++------ src/cdk/drag-drop/drag-drop-registry.ts | 11 ++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/cdk/drag-drop/drag-drop-registry.spec.ts b/src/cdk/drag-drop/drag-drop-registry.spec.ts index 8af6d8407ae3..170064c6ae10 100644 --- a/src/cdk/drag-drop/drag-drop-registry.spec.ts +++ b/src/cdk/drag-drop/drag-drop-registry.spec.ts @@ -69,9 +69,9 @@ describe('DragDropRegistry', () => { const subscription = registry.pointerMove.subscribe(spy); registry.startDragging(testComponent.dragItems.first, createMouseEvent('mousedown')); - dispatchMouseEvent(document, 'mousemove'); + const event = dispatchMouseEvent(document, 'mousemove'); - expect(spy).toHaveBeenCalled(); + expect(spy).toHaveBeenCalledWith(event); subscription.unsubscribe(); }); @@ -82,9 +82,9 @@ describe('DragDropRegistry', () => { registry.startDragging(testComponent.dragItems.first, createTouchEvent('touchstart') as TouchEvent); - dispatchTouchEvent(document, 'touchmove'); + const event = dispatchTouchEvent(document, 'touchmove'); - expect(spy).toHaveBeenCalled(); + expect(spy).toHaveBeenCalledWith(event); subscription.unsubscribe(); }); @@ -107,9 +107,9 @@ describe('DragDropRegistry', () => { const subscription = registry.pointerUp.subscribe(spy); registry.startDragging(testComponent.dragItems.first, createMouseEvent('mousedown')); - dispatchMouseEvent(document, 'mouseup'); + const event = dispatchMouseEvent(document, 'mouseup'); - expect(spy).toHaveBeenCalled(); + expect(spy).toHaveBeenCalledWith(event); subscription.unsubscribe(); }); @@ -120,9 +120,22 @@ describe('DragDropRegistry', () => { registry.startDragging(testComponent.dragItems.first, createTouchEvent('touchstart') as TouchEvent); - dispatchTouchEvent(document, 'touchend'); + const event = dispatchTouchEvent(document, 'touchend'); - expect(spy).toHaveBeenCalled(); + expect(spy).toHaveBeenCalledWith(event); + + subscription.unsubscribe(); + }); + + it('should dispatch `touchcancel` events if the drag was interrupted', () => { + const spy = jasmine.createSpy('pointerUp spy'); + const subscription = registry.pointerUp.subscribe(spy); + + registry.startDragging(testComponent.dragItems.first, + createTouchEvent('touchstart') as TouchEvent); + const event = dispatchTouchEvent(document, 'touchcancel'); + + expect(spy).toHaveBeenCalledWith(event); subscription.unsubscribe(); }); diff --git a/src/cdk/drag-drop/drag-drop-registry.ts b/src/cdk/drag-drop/drag-drop-registry.ts index 7a46f667b696..f62c466f112e 100644 --- a/src/cdk/drag-drop/drag-drop-registry.ts +++ b/src/cdk/drag-drop/drag-drop-registry.ts @@ -126,6 +126,10 @@ export class DragDropRegistry implements OnDestroy { const isTouchEvent = event.type.startsWith('touch'); const moveEvent = isTouchEvent ? 'touchmove' : 'mousemove'; const upEvent = isTouchEvent ? 'touchend' : 'mouseup'; + const upConfig = { + handler: (e: Event) => this.pointerUp.next(e as TouchEvent | MouseEvent), + options: true + }; // We explicitly bind __active__ listeners here, because newer browsers will default to // passive ones for `mousemove` and `touchmove`. The events need to be active, because we @@ -151,6 +155,13 @@ export class DragDropRegistry implements OnDestroy { options: activeCapturingEventOptions }); + if (isTouchEvent) { + // Treat `touchcancel` events the same as `touchend`. `touchcancel` will fire for cases + // like an OS-level event interrupting the touch sequence or the user putting too many + // finger on the screen at the same time. + this._globalListeners.set('touchcancel', upConfig); + } + this._ngZone.runOutsideAngular(() => { this._globalListeners.forEach((config, name) => { this._document.addEventListener(name, config.handler, config.options);