From afc4556ada9288a8e87a4695d91ebb0e28b2589a Mon Sep 17 00:00:00 2001 From: Anna Peery <42715836+avpeery@users.noreply.github.com> Date: Thu, 7 Oct 2021 11:36:01 -0700 Subject: [PATCH] Fix an iOS15 issue where Safari tab bar interrupts panning (#11084) (#11101) * 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 <ricky.reusser@mapbox.com> Co-authored-by: Ricky Reusser <rreusser@users.noreply.github.com> Co-authored-by: Vladimir Agafonkin <agafonkin@gmail.com> * Removed getBoundingClientRect conflict for unit tests * Added offsetHeight to replace getBoundingClientRect to fix unit tests * add clientWidth and clientHeight to unit tests * container -> map.getContainer() * Replaced offsetWidth with clientWidth * removing change difference in attribution and logo unit tests from v1.13.2 * Change size of container instead of canvas container to trigger resize in unit tests * Added change in height to trigger resize * fix to attribution.test.js * Fixes logo.test.js to pass * removed unneeded changes Co-authored-by: Ricky Reusser <rreusser@users.noreply.github.com> Co-authored-by: Vladimir Agafonkin <agafonkin@gmail.com> --- 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 | 2 ++ test/unit/ui/control/logo.test.js | 2 ++ 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, 98 insertions(+), 10 deletions(-) diff --git a/src/ui/handler/box_zoom.js b/src/ui/handler/box_zoom.js index be8415ab7e3..c004e3637d0 100644 --- a/src/ui/handler/box_zoom.js +++ b/src/ui/handler/box_zoom.js @@ -142,6 +142,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 822de909395..2d8f5662b7f 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 faf22a172d2..15de2d54153 100644 --- a/src/ui/handler/keyboard.js +++ b/src/ui/handler/keyboard.js @@ -41,6 +41,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 3cf5c532816..55b235fe39f 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 1ae4553487f..40eb32a89d4 100644 --- a/src/ui/handler/scroll_zoom.js +++ b/src/ui/handler/scroll_zoom.js @@ -338,6 +338,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 56d538443c4..4b4d2d9f046 100644 --- a/src/ui/handler_manager.js +++ b/src/ui/handler_manager.js @@ -291,11 +291,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 851236d15bb..e8321037778 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -591,9 +591,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); this.transform.resize(width, height); @@ -601,7 +602,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 99cd411ea52..ed6ae6e882d 100644 --- a/test/unit/ui/control/attribution.test.js +++ b/test/unit/ui/control/attribution.test.js @@ -63,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.getCanvasContainer(), 'offsetWidth', {value: 700, configurable: true}); + Object.defineProperty(map.getContainer(), 'clientWidth', {value: 700, configurable: true}); map.addControl(new AttributionControl()); const container = map.getContainer(); @@ -70,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.getCanvasContainer(), 'offsetWidth', {value: 600, configurable: true}); + Object.defineProperty(map.getContainer(), 'clientWidth', {value: 600, configurable: true}); map.resize(); t.equal(container.querySelectorAll('.mapboxgl-ctrl-attrib.mapboxgl-compact').length, 1); diff --git a/test/unit/ui/control/logo.test.js b/test/unit/ui/control/logo.test.js index 0b271b4a6cf..749985137ba 100644 --- a/test/unit/ui/control/logo.test.js +++ b/test/unit/ui/control/logo.test.js @@ -93,10 +93,12 @@ test('LogoControl appears in compact mode if container is less then 250 pixel wi const container = map.getContainer(); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 255, configurable: true}); + Object.defineProperty(map.getContainer(), 'clientWidth', {value: 255, configurable: true}); map.resize(); t.equal(container.querySelectorAll('.mapboxgl-ctrl-logo:not(.mapboxgl-compact)').length, 1); Object.defineProperty(map.getCanvasContainer(), 'offsetWidth', {value: 245, configurable: true}); + Object.defineProperty(map.getContainer(), 'clientWidth', {value: 245, configurable: true}); map.resize(); t.equal(container.querySelectorAll('.mapboxgl-ctrl-logo.mapboxgl-compact').length, 1); diff --git a/test/unit/ui/handler/drag_pan.test.js b/test/unit/ui/handler/drag_pan.test.js index 726900e9e82..32f95555d51 100644 --- a/test/unit/ui/handler/drag_pan.test.js +++ b/test/unit/ui/handler/drag_pan.test.js @@ -150,13 +150,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(); @@ -170,7 +172,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 f5dd762e084..3ce816d1f11 100644 --- a/test/unit/ui/handler/drag_rotate.test.js +++ b/test/unit/ui/handler/drag_rotate.test.js @@ -491,6 +491,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 159597fcb52..8695831c520 100755 --- a/test/unit/ui/map.test.js +++ b/test/unit/ui/map.test.js @@ -534,10 +534,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(), 'clientWidth', {value: 250}); + Object.defineProperty(map.getContainer(), 'clientHeight', {value: 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(), 'clientWidth', {value: 250}); + Object.defineProperty(map.getContainer(), 'clientHeight', {value: 250}); + ['movestart', 'move', 'resize', 'moveend'].forEach((event) => { map.on(event, (e) => { events.push(e.type);