Skip to content

Commit 8fbcd06

Browse files
fix: additional check for touch event before calling preventDefault (#138)
1 parent a1e8edc commit 8fbcd06

5 files changed

Lines changed: 44 additions & 31 deletions

File tree

projects/angular-resizable-element/src/lib/resizable.directive.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { BoundingRectangle } from './interfaces/bounding-rectangle.interface';
2929
import { ResizeEvent } from './interfaces/resize-event.interface';
3030
import { IS_TOUCH_DEVICE } from './util/is-touch-device';
3131
import { deepCloneNode } from './util/clone-node';
32+
import { isTouchEvent } from './util/is-touch-event';
3233

3334
interface PointerEventCoordinate {
3435
clientX: number;
@@ -333,7 +334,7 @@ export class ResizableDirective implements OnInit, OnDestroy {
333334
this.mousemove,
334335
).pipe(
335336
tap(({ event }) => {
336-
if (currentResize && event.cancelable) {
337+
if (currentResize && event.cancelable && !isTouchEvent(event)) {
337338
event.preventDefault();
338339
}
339340
}),

projects/angular-resizable-element/src/lib/resize-handle.directive.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import { fromEvent, merge, Subject } from 'rxjs';
1212
import { takeUntil } from 'rxjs/operators';
1313
import { ResizableDirective } from './resizable.directive';
1414
import { Edges } from './interfaces/edges.interface';
15-
import { getListenOptions } from './util/get-listen-options';
1615
import { IS_TOUCH_DEVICE } from './util/is-touch-device';
16+
import { isTouchEvent } from './util/is-touch-event';
1717

1818
/**
1919
* An element placed inside a `mwlResizable` directive to be used as a drag and resize handle
@@ -100,7 +100,7 @@ export class ResizeHandleDirective implements OnInit, OnDestroy {
100100
clientX: number,
101101
clientY: number,
102102
): void {
103-
if (event.cancelable) {
103+
if (event.cancelable && !isTouchEvent(event)) {
104104
event.preventDefault();
105105
}
106106
if (!this.eventListeners.touchmove) {
@@ -174,10 +174,8 @@ export class ResizeHandleDirective implements OnInit, OnDestroy {
174174
}
175175

176176
private listenOnTheHost<T extends Event>(eventName: string) {
177-
return fromEvent<T>(
178-
this.element.nativeElement,
179-
eventName,
180-
getListenOptions(eventName),
181-
).pipe(takeUntil(this.destroy$));
177+
return fromEvent<T>(this.element.nativeElement, eventName).pipe(
178+
takeUntil(this.destroy$),
179+
);
182180
}
183181
}

projects/angular-resizable-element/src/lib/util/get-listen-options.ts

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Check if the event is a touch event
3+
* @param event - The event to check
4+
* @returns True if the event is a touch event, false otherwise
5+
* @hidden
6+
*/
7+
8+
export function isTouchEvent(event: Event): boolean {
9+
return event.type.startsWith('touch');
10+
}

projects/angular-resizable-element/src/test/resizable.spec.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
ResizeEvent,
66
ResizeHandleDirective,
77
} from 'angular-resizable-element';
8-
import { getListenOptions } from '../lib/util/get-listen-options';
98
import { ComponentFixture, TestBed } from '@angular/core/testing';
109
import { expect } from 'chai';
1110
import * as sinon from 'sinon';
@@ -104,6 +103,7 @@ import { NgStyle } from '@angular/common';
104103
})
105104
class TestComponent {
106105
@ViewChild(ResizableDirective) resizable: ResizableDirective;
106+
@ViewChild(ResizeHandleDirective) handleDirective: ResizeHandleDirective;
107107
@ViewChild('handle') handle: ElementRef;
108108
style: object = {};
109109
resizeStart: sinon.SinonSpy = sinon.spy();
@@ -508,16 +508,34 @@ describe('resizable directive', () => {
508508
});
509509

510510
describe('touch event listeners', () => {
511-
['touchstart', 'touchend', 'touchcancel'].forEach((eventName) => {
512-
it(`when eventName is ${eventName}, getListenOptions returns passive: false so fromEvent is called with passive: false`, () => {
513-
expect(getListenOptions(eventName)).to.deep.equal({ passive: false });
514-
});
511+
it('should not call preventDefault for touch events when cancelable', () => {
512+
const fixture: ComponentFixture<TestComponent> = createComponent();
513+
const touchEvent = {
514+
type: 'touchstart',
515+
cancelable: true,
516+
preventDefault: sinon.spy(),
517+
} as unknown as TouchEvent;
518+
fixture.componentInstance.handleDirective.onMousedown(
519+
touchEvent,
520+
100,
521+
200,
522+
);
523+
expect(touchEvent.preventDefault).not.to.have.been.called;
515524
});
516525

517-
['mousedown', 'mouseup'].forEach((eventName) => {
518-
it(`when eventName is ${eventName}, getListenOptions returns empty options`, () => {
519-
expect(getListenOptions(eventName)).to.deep.equal({});
520-
});
526+
it('should call preventDefault for mouse events when cancelable', () => {
527+
const fixture: ComponentFixture<TestComponent> = createComponent();
528+
const mouseEvent = {
529+
type: 'mousedown',
530+
cancelable: true,
531+
preventDefault: sinon.spy(),
532+
} as unknown as MouseEvent;
533+
fixture.componentInstance.handleDirective.onMousedown(
534+
mouseEvent,
535+
100,
536+
200,
537+
);
538+
expect(mouseEvent.preventDefault).to.have.been.calledOnce;
521539
});
522540
});
523541

0 commit comments

Comments
 (0)