Skip to content

Commit 1456074

Browse files
committed
fix(cdk/drag-drop): stop dragging on touchcancel (#30184)
In some cases we might not get a `touchend`, because the sequence was interrupted. These changes add a fallback. (cherry picked from commit 454d9f9)
1 parent eef0536 commit 1456074

File tree

3 files changed

+42
-6
lines changed

3 files changed

+42
-6
lines changed

src/cdk/drag-drop/directives/standalone-drag.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,22 @@ describe('Standalone CdkDrag', () => {
364364

365365
expect(dragElement.style.transform).toBeFalsy();
366366
}));
367+
368+
it('should stop dragging on touchcancel', fakeAsync(() => {
369+
const fixture = createComponent(StandaloneDraggable);
370+
fixture.detectChanges();
371+
const dragElement = fixture.componentInstance.dragElement.nativeElement;
372+
const x = 50;
373+
const y = 100;
374+
375+
expect(dragElement.style.transform).toBeFalsy();
376+
startDraggingViaTouch(fixture, dragElement);
377+
continueDraggingViaTouch(fixture, x, y);
378+
dispatchTouchEvent(document, 'touchcancel', x, y);
379+
fixture.detectChanges();
380+
expect(dragElement.style.transform).toBe('translate3d(50px, 100px, 0px)');
381+
expect(fixture.componentInstance.endedSpy).toHaveBeenCalled();
382+
}));
367383
});
368384

369385
describe('mouse dragging when initial transform is none', () => {

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,19 @@ describe('DragDropRegistry', () => {
243243
subscription.unsubscribe();
244244
});
245245

246+
it('should dispatch `touchcancel` events if the drag was interrupted', () => {
247+
const spy = jasmine.createSpy('pointerUp spy');
248+
const subscription = registry.pointerUp.subscribe(spy);
249+
const item = new DragItem() as unknown as DragRef;
250+
251+
registry.startDragging(item, createTouchEvent('touchstart') as TouchEvent);
252+
const event = dispatchTouchEvent(document, 'touchcancel');
253+
254+
expect(spy).toHaveBeenCalledWith(event);
255+
256+
subscription.unsubscribe();
257+
});
258+
246259
class DragItem {
247260
isDragging() {
248261
return this.shouldBeDragging;

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,16 +170,23 @@ export class DragDropRegistry<_ = unknown, __ = unknown> implements OnDestroy {
170170
this._activeDragInstances.update(instances => [...instances, drag]);
171171

172172
if (this._activeDragInstances().length === 1) {
173-
const isTouchEvent = event.type.startsWith('touch');
174-
175173
// We explicitly bind __active__ listeners here, because newer browsers will default to
176174
// passive ones for `mousemove` and `touchmove`. The events need to be active, because we
177175
// use `preventDefault` to prevent the page from scrolling while the user is dragging.
176+
const isTouchEvent = event.type.startsWith('touch');
177+
const endEventHandler = {
178+
handler: (e: Event) => this.pointerUp.next(e as TouchEvent | MouseEvent),
179+
options: true,
180+
};
181+
182+
if (isTouchEvent) {
183+
this._globalListeners.set('touchend', endEventHandler);
184+
this._globalListeners.set('touchcancel', endEventHandler);
185+
} else {
186+
this._globalListeners.set('mouseup', endEventHandler);
187+
}
188+
178189
this._globalListeners
179-
.set(isTouchEvent ? 'touchend' : 'mouseup', {
180-
handler: (e: Event) => this.pointerUp.next(e as TouchEvent | MouseEvent),
181-
options: true,
182-
})
183190
.set('scroll', {
184191
handler: (e: Event) => this.scroll.next(e),
185192
// Use capturing so that we pick up scroll changes in any scrollable nodes that aren't

0 commit comments

Comments
 (0)