Skip to content

Commit 0e50290

Browse files
committed
fix(drag-drop): stop dragging sequence on touchcancel
Adds handling for the `touchcancel` event to the the `DragDropRegistry`.
1 parent e5efd0a commit 0e50290

File tree

2 files changed

+34
-15
lines changed

2 files changed

+34
-15
lines changed

src/cdk/drag-drop/drag-drop-registry.spec.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ describe('DragDropRegistry', () => {
6969
const subscription = registry.pointerMove.subscribe(spy);
7070

7171
registry.startDragging(testComponent.dragItems.first, createMouseEvent('mousedown'));
72-
dispatchMouseEvent(document, 'mousemove');
72+
const event = dispatchMouseEvent(document, 'mousemove');
7373

74-
expect(spy).toHaveBeenCalled();
74+
expect(spy).toHaveBeenCalledWith(event);
7575

7676
subscription.unsubscribe();
7777
});
@@ -82,9 +82,9 @@ describe('DragDropRegistry', () => {
8282

8383
registry.startDragging(testComponent.dragItems.first,
8484
createTouchEvent('touchstart') as TouchEvent);
85-
dispatchTouchEvent(document, 'touchmove');
85+
const event = dispatchTouchEvent(document, 'touchmove');
8686

87-
expect(spy).toHaveBeenCalled();
87+
expect(spy).toHaveBeenCalledWith(event);
8888

8989
subscription.unsubscribe();
9090
});
@@ -107,9 +107,9 @@ describe('DragDropRegistry', () => {
107107
const subscription = registry.pointerUp.subscribe(spy);
108108

109109
registry.startDragging(testComponent.dragItems.first, createMouseEvent('mousedown'));
110-
dispatchMouseEvent(document, 'mouseup');
110+
const event = dispatchMouseEvent(document, 'mouseup');
111111

112-
expect(spy).toHaveBeenCalled();
112+
expect(spy).toHaveBeenCalledWith(event);
113113

114114
subscription.unsubscribe();
115115
});
@@ -120,9 +120,22 @@ describe('DragDropRegistry', () => {
120120

121121
registry.startDragging(testComponent.dragItems.first,
122122
createTouchEvent('touchstart') as TouchEvent);
123-
dispatchTouchEvent(document, 'touchend');
123+
const event = dispatchTouchEvent(document, 'touchend');
124124

125-
expect(spy).toHaveBeenCalled();
125+
expect(spy).toHaveBeenCalledWith(event);
126+
127+
subscription.unsubscribe();
128+
});
129+
130+
it('should dispatch `touchcancel` events if the drag was interrupted', () => {
131+
const spy = jasmine.createSpy('pointerUp spy');
132+
const subscription = registry.pointerUp.subscribe(spy);
133+
134+
registry.startDragging(testComponent.dragItems.first,
135+
createTouchEvent('touchstart') as TouchEvent);
136+
const event = dispatchTouchEvent(document, 'touchcancel');
137+
138+
expect(spy).toHaveBeenCalledWith(event);
126139

127140
subscription.unsubscribe();
128141
});

src/cdk/drag-drop/drag-drop-registry.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
118118
const isTouchEvent = event.type.startsWith('touch');
119119
const moveEvent = isTouchEvent ? 'touchmove' : 'mousemove';
120120
const upEvent = isTouchEvent ? 'touchend' : 'mouseup';
121+
const upConfig = {
122+
handler: (e: Event) => this.pointerUp.next(e as TouchEvent | MouseEvent),
123+
options: true
124+
};
121125

122126
// We explicitly bind __active__ listeners here, because newer browsers will default to
123127
// passive ones for `mousemove` and `touchmove`. The events need to be active, because we
@@ -127,10 +131,7 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
127131
handler: (e: Event) => this.pointerMove.next(e as TouchEvent | MouseEvent),
128132
options: activeCapturingEventOptions
129133
})
130-
.set(upEvent, {
131-
handler: (e: Event) => this.pointerUp.next(e as TouchEvent | MouseEvent),
132-
options: true
133-
})
134+
.set(upEvent, upConfig)
134135
// Preventing the default action on `mousemove` isn't enough to disable text selection
135136
// on Safari so we need to prevent the selection event as well. Alternatively this can
136137
// be done by setting `user-select: none` on the `body`, however it has causes a style
@@ -140,9 +141,14 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
140141
options: activeCapturingEventOptions
141142
});
142143

143-
// TODO(crisbeto): prevent mouse wheel scrolling while
144-
// dragging until we've set up proper scroll handling.
145-
if (!isTouchEvent) {
144+
if (isTouchEvent) {
145+
// Treat `touchcancel` events the same as `touchend`. `touchcancel` will fire for cases
146+
// like an OS-level event interrupting the touch sequence or the user putting too many
147+
// finger on the screen at the same time.
148+
this._globalListeners.set('touchcancel', upConfig);
149+
} else {
150+
// TODO(crisbeto): prevent mouse wheel scrolling while
151+
// dragging until we've set up proper scroll handling.
146152
this._globalListeners.set('wheel', {
147153
handler: this._preventDefaultWhileDragging,
148154
options: activeCapturingEventOptions

0 commit comments

Comments
 (0)