From a884ab32d5f77c5dde758e72ba4846856daac519 Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Wed, 6 Oct 2021 10:33:14 -0700 Subject: [PATCH] Fix an iOS15 issue where Safari tab bar interrupts panning (#11084) (#11089) * fix an iOS15 issue where map stops when panning * fix tests and lint * Test drag pan handler does not end interaction on resize * Move blur event reset into non-touch handlers (#11087) * Move blur event reset into non-touch handlers * Fix linter * Fix/amend unit tests * Flush task queue in rotate test Co-authored-by: Ricky Reusser Co-authored-by: Ricky Reusser Co-authored-by: Vladimir Agafonkin --- src/ui/handler/box_zoom.js | 4 +++ src/ui/handler/click_zoom.js | 4 +++ src/ui/handler/keyboard.js | 4 +++ src/ui/handler/mouse.js | 4 +++ src/ui/handler/scroll_zoom.js | 4 +++ src/ui/handler_manager.js | 5 ---- src/ui/map.js | 8 ++--- test/unit/ui/control/attribution.test.js | 4 +++ test/unit/ui/control/logo.test.js | 4 +++ test/unit/ui/handler/drag_pan.test.js | 37 +++++++++++++++++++++++- test/unit/ui/handler/drag_rotate.test.js | 2 ++ test/unit/ui/map.test.js | 32 ++++++++++++++++++++ 12 files changed, 102 insertions(+), 10 deletions(-) diff --git a/src/ui/handler/box_zoom.js b/src/ui/handler/box_zoom.js index 96c6dacac87..e76e3e117e1 100644 --- a/src/ui/handler/box_zoom.js +++ b/src/ui/handler/box_zoom.js @@ -153,6 +153,10 @@ class BoxZoomHandler { } } + blur() { + this.reset(); + } + reset() { this._active = false; diff --git a/src/ui/handler/click_zoom.js b/src/ui/handler/click_zoom.js index 19362ede8c0..44d29f26976 100644 --- a/src/ui/handler/click_zoom.js +++ b/src/ui/handler/click_zoom.js @@ -16,6 +16,10 @@ export default class ClickZoomHandler { this._active = false; } + blur() { + this.reset(); + } + dblclick(e: MouseEvent, point: Point) { e.preventDefault(); return { diff --git a/src/ui/handler/keyboard.js b/src/ui/handler/keyboard.js index eb04f773aae..b5bc103ff74 100644 --- a/src/ui/handler/keyboard.js +++ b/src/ui/handler/keyboard.js @@ -45,6 +45,10 @@ class KeyboardHandler { this._rotationDisabled = false; } + blur() { + this.reset(); + } + reset() { this._active = false; } diff --git a/src/ui/handler/mouse.js b/src/ui/handler/mouse.js index d1a94551476..059e024cea6 100644 --- a/src/ui/handler/mouse.js +++ b/src/ui/handler/mouse.js @@ -31,6 +31,10 @@ class MouseHandler { this._clickTolerance = options.clickTolerance || 1; } + blur() { + this.reset(); + } + reset() { this._active = false; this._moved = false; diff --git a/src/ui/handler/scroll_zoom.js b/src/ui/handler/scroll_zoom.js index e7e6f232b51..cb589e7ff6f 100644 --- a/src/ui/handler/scroll_zoom.js +++ b/src/ui/handler/scroll_zoom.js @@ -353,6 +353,10 @@ class ScrollZoomHandler { return easing; } + blur() { + this.reset(); + } + reset() { this._active = false; } diff --git a/src/ui/handler_manager.js b/src/ui/handler_manager.js index b9b4e3a12ca..a3fa767706c 100644 --- a/src/ui/handler_manager.js +++ b/src/ui/handler_manager.js @@ -344,11 +344,6 @@ class HandlerManager { handleEvent(e: InputEvent | RenderFrameEvent, eventName?: string) { - if (e.type === 'blur') { - this.stop(true); - return; - } - this._updatingCamera = true; assert(e.timeStamp !== undefined); diff --git a/src/ui/map.js b/src/ui/map.js index dfd195f5ab5..22512f014f8 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -691,9 +691,10 @@ class Map extends Camera { * if (mapDiv.style.visibility === true) map.resize(); */ resize(eventData?: Object) { - const dimensions = this._containerDimensions(); - const width = dimensions[0]; - const height = dimensions[1]; + const [width, height] = this._containerDimensions(); + + // do nothing if container remained the same size + if (width === this.transform.width && height === this.transform.height) return this; this._resizeCanvas(width, height); @@ -702,7 +703,6 @@ class Map extends Camera { const fireMoving = !this._moving; if (fireMoving) { - this.stop(); this.fire(new Event('movestart', eventData)) .fire(new Event('move', eventData)); } diff --git a/test/unit/ui/control/attribution.test.js b/test/unit/ui/control/attribution.test.js index 76891d6129c..4aa9d0364cf 100644 --- a/test/unit/ui/control/attribution.test.js +++ b/test/unit/ui/control/attribution.test.js @@ -37,6 +37,7 @@ test('AttributionControl appears in the position specified by the position optio test('AttributionControl appears in compact mode if compact option is used', (t) => { const map = createMap(t); + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', {value: () => ({height: 200, width: 700})}); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 700, configurable: true}); let attributionControl = new AttributionControl({ @@ -49,6 +50,7 @@ test('AttributionControl appears in compact mode if compact option is used', (t) t.equal(container.querySelectorAll('.mapboxgl-ctrl-attrib.mapboxgl-compact').length, 1); map.removeControl(attributionControl); + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', {value: () => ({height: 200, width: 600})}); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 600, configurable: true}); attributionControl = new AttributionControl({ compact: false @@ -61,6 +63,7 @@ test('AttributionControl appears in compact mode if compact option is used', (t) test('AttributionControl appears in compact mode if container is less then 640 pixel wide', (t) => { const map = createMap(t); + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', {value: () => ({height: 200, width: 700})}); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 700, configurable: true}); map.addControl(new AttributionControl()); @@ -68,6 +71,7 @@ test('AttributionControl appears in compact mode if container is less then 640 p t.equal(container.querySelectorAll('.mapboxgl-ctrl-attrib:not(.mapboxgl-compact)').length, 1); + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', {value: () => ({height: 200, width: 600})}); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 600, configurable: true}); map.resize(); diff --git a/test/unit/ui/control/logo.test.js b/test/unit/ui/control/logo.test.js index ea0c77f7825..a3491f55170 100644 --- a/test/unit/ui/control/logo.test.js +++ b/test/unit/ui/control/logo.test.js @@ -104,12 +104,16 @@ test('LogoControl appears in compact mode if container is less then 250 pixel wi const map = createMap(t); const container = map.getContainer(); + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', {value: () => ({height: 200, width: 255})}); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 255, configurable: true}); map.resize(); + t.equal(container.querySelectorAll('.mapboxgl-ctrl-logo:not(.mapboxgl-compact)').length, 1); + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', {value: () => ({height: 200, width: 245})}); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 245, configurable: true}); map.resize(); + t.equal(container.querySelectorAll('.mapboxgl-ctrl-logo.mapboxgl-compact').length, 1); t.end(); diff --git a/test/unit/ui/handler/drag_pan.test.js b/test/unit/ui/handler/drag_pan.test.js index 1a476a32a47..98ea491a2ca 100644 --- a/test/unit/ui/handler/drag_pan.test.js +++ b/test/unit/ui/handler/drag_pan.test.js @@ -151,13 +151,15 @@ test('DragPanHandler ends a mouse-triggered drag if the window blurs', (t) => { map._renderTaskQueue.run(); simulate.blur(window); + map._renderTaskQueue.run(); + t.equal(dragend.callCount, 1); map.remove(); t.end(); }); -test('DragPanHandler ends a touch-triggered drag if the window blurs', (t) => { +test('DragPanHandler does not end a touch-triggered drag if the window blurs', (t) => { const map = createMap(t); const target = map.getCanvas(); @@ -171,7 +173,40 @@ test('DragPanHandler ends a touch-triggered drag if the window blurs', (t) => { map._renderTaskQueue.run(); simulate.blur(window); + map._renderTaskQueue.run(); + + t.equal(dragend.callCount, 0); + + map.remove(); + t.end(); +}); + +test('DragPanHandler does not end a touch-triggered drag if the window resizes', (t) => { + const map = createMap(t); + const target = map.getCanvas(); + + const dragend = t.spy(); + map.on('dragend', dragend); + + const drag = t.spy(); + map.on('drag', drag); + + simulate.touchstart(map.getCanvas(), {touches: [{target, clientX: 0, clientY: 0}]}); + map._renderTaskQueue.run(); + + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 10, clientY: 10}]}); + map._renderTaskQueue.run(); + + map.resize(); + + simulate.touchmove(map.getCanvas(), {touches: [{target, clientX: 20, clientY: 10}]}); + map._renderTaskQueue.run(); + + simulate.touchend(map.getCanvas()); + map._renderTaskQueue.run(); + t.equal(dragend.callCount, 1); + t.equal(drag.callCount, 2); map.remove(); t.end(); diff --git a/test/unit/ui/handler/drag_rotate.test.js b/test/unit/ui/handler/drag_rotate.test.js index 4fe68a46fd2..f12c0eba8bb 100644 --- a/test/unit/ui/handler/drag_rotate.test.js +++ b/test/unit/ui/handler/drag_rotate.test.js @@ -492,6 +492,8 @@ test('DragRotateHandler ends rotation if the window blurs (#3389)', (t) => { t.equal(rotate.callCount, 1); simulate.blur(window); + map._renderTaskQueue.run(); + t.equal(rotateend.callCount, 1); map.remove(); diff --git a/test/unit/ui/map.test.js b/test/unit/ui/map.test.js index 96aa21fea53..4fd227d8e98 100755 --- a/test/unit/ui/map.test.js +++ b/test/unit/ui/map.test.js @@ -764,10 +764,42 @@ test('Map', (t) => { t.end(); }); + t.test('does nothing if container size is the same', (t) => { + const map = createMap(t); + + t.spy(map.transform, 'resize'); + t.spy(map.painter, 'resize'); + + map.resize(); + + t.notOk(map.transform.resize.called); + t.notOk(map.painter.resize.called); + + t.end(); + }); + + t.test('does not call stop on resize', (t) => { + const map = createMap(t); + + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', + {value: () => ({height: 250, width: 250})}); + + t.spy(map, 'stop'); + + map.resize(); + + t.notOk(map.stop.called); + + t.end(); + }); + t.test('fires movestart, move, resize, and moveend events', (t) => { const map = createMap(t), events = []; + Object.defineProperty(map.getContainer(), 'getBoundingClientRect', + {value: () => ({height: 250, width: 250})}); + ['movestart', 'move', 'resize', 'moveend'].forEach((event) => { map.on(event, (e) => { events.push(e.type);