diff --git a/CHANGELOG.md b/CHANGELOG.md index 50bbbbd9..21962e2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## HEAD +### Bug fixes 🐛 + +- The `marker` and `flyTo` options are now respected when geolocation is enabled [#462](https://github.com/mapbox/mapbox-gl-geocoder/pull/462) + ## 5.0.1 ### Bug fixes 🐛 diff --git a/lib/index.js b/lib/index.js index c182ca8d..7583afec 100644 --- a/lib/index.js +++ b/lib/index.js @@ -292,7 +292,7 @@ MapboxGeocoder.prototype = { footerNode.addEventListener('mousedown', function() { this.selectingListItem = true; }.bind(this)); - + footerNode.addEventListener('mouseup', function() { this.selectingListItem = false; }.bind(this)); @@ -331,8 +331,13 @@ MapboxGeocoder.prototype = { } }; - this._handleMarker(geojson); - this._fly(geojson); + if (this._shouldAddMarker()) { + this._handleMarker(geojson); + } + + if (this._shouldFly()) { + this._fly(geojson); + } this._typeahead.clear(); this._typeahead.selected = true; @@ -355,11 +360,11 @@ MapboxGeocoder.prototype = { } else { this.geocoderService.reverseGeocode(config).send().then(function (resp) { const feature = resp.body.features[0]; - + if (feature) { const locationText = utils.transformFeatureToGeolocationText(feature, this.options.addressAccuracy); this._setInputValue(locationText); - + feature.user_coordinates = geojson.geometry.coordinates; this._eventEmitter.emit('result', { result: feature }); } else { @@ -407,7 +412,7 @@ MapboxGeocoder.prototype = { _setInputValue: function (value) { this._inputEl.value = value; - + setTimeout(function () { this._inputEl.focus(); this._inputEl.scrollLeft = 0; @@ -487,7 +492,7 @@ MapboxGeocoder.prototype = { _showLoadingIcon: function() { this._loadingEl.style.display = 'block'; }, - + _hideLoadingIcon: function() { this._loadingEl.style.display = 'none'; }, @@ -495,7 +500,7 @@ MapboxGeocoder.prototype = { _showAttribution: function() { this._footerNode.style.display = 'block' }, - + _hideAttribution: function() { this._footerNode.style.display = 'none' }, @@ -508,14 +513,17 @@ MapboxGeocoder.prototype = { this._collapse(); } }, + _onChange: function() { var selected = this._typeahead.selected; if (selected && JSON.stringify(selected) !== this.lastSelected) { this._hideClearButton(); - if (this.options.flyTo) { + + if (this._shouldFly()) { this._fly(selected); } - if (this.options.marker && this._mapboxgl){ + + if (this._shouldAddMarker()) { this._handleMarker(selected); } @@ -530,6 +538,15 @@ MapboxGeocoder.prototype = { } }, + /** + * Determine whether the map should fly to a new location after geocoding. + * @returns {Boolean} `true` if the map should fly to new locations, `false` if not + * @private + */ + _shouldFly: function() { + return this.options.flyTo; + }, + _fly: function(selected) { var flyOptions; if (selected.properties && exceptions[selected.properties.short_code]) { @@ -1282,6 +1299,15 @@ MapboxGeocoder.prototype = { return this.options.worldview }, + /** + * Determine whether a marker should be added to the map. + * @returns {Boolean} `true` if a marker should be added, `false` if not + * @private + */ + _shouldAddMarker: function() { + return this.options.marker && this._mapboxgl; + }, + /** * Handle the placement of a result marking the selected result * @private diff --git a/test/test.geocoder.js b/test/test.geocoder.js index 86cd6409..2642421b 100644 --- a/test/test.geocoder.js +++ b/test/test.geocoder.js @@ -26,6 +26,26 @@ test('geocoder', function(tt) { map.addControl(geocoder); } + function stubGeolocationPosition() { + const geolocationPositionStub = { + coords: { + accuracy: 10, + altitude: null, + altitudeAccuracy: null, + heading: null, + latitude: 38.8999242, + longitude: -77.0361813, + speed: null + }, + timestamp: new Date('2022-01-01T00:00:00Z') + }; + + sinon.replace(geocoder.geolocation, 'getCurrentPosition', + sinon.fake.resolves(geolocationPositionStub)); + + return geolocationPositionStub; + } + tt.test('initialized', function(t) { setup(); t.ok(geocoder, 'geocoder is initialized'); @@ -717,39 +737,114 @@ test('geocoder', function(tt) { t.end() }); - tt.test('options.flyTo [false]', function(t){ - t.plan(1) + tt.test('options.flyTo [false] when querying', function(t){ + t.plan(1); + setup({ flyTo: false }); - var mapFlyMethod = sinon.spy(map, "flyTo"); + const mapFlySpy = sinon.spy(map, 'flyTo'); + geocoder.query('Golden Gate Bridge'); + geocoder.on( 'result', once(function() { - t.ok(mapFlyMethod.notCalled, "The map flyTo was not called when the option was set to false") + t.ok(mapFlySpy.notCalled, 'flyTo() was not called after querying'); }) ); }); + tt.test('options.flyTo [false] when geolocating', function(t) { + t.plan(1); + + t.teardown(function() { + sinon.restore(); + }); + + setup({ + flyTo: false + }); + + stubGeolocationPosition(); + const flyToSpy = sinon.spy(map, 'flyTo'); + + geocoder._geolocateUser(); + + geocoder.on( + 'result', + once(function() { + t.ok(flyToSpy.notCalled, 'flyTo() was not called after geolocating'); + }) + ); + }); + + tt.test('options.flyTo [true] when querying', function(t){ + t.plan(4); - tt.test('options.flyTo [true]', function(t){ - t.plan(4) setup({ flyTo: true }); - var mapFlyMethod = sinon.spy(map, "flyTo"); + const flyToSpy = sinon.spy(map, 'flyTo'); + geocoder.query('Golden Gate Bridge'); + geocoder.on( 'result', once(function() { - t.ok(mapFlyMethod.calledOnce, "The map flyTo was called when the option was set to true"); - var calledWithArgs = mapFlyMethod.args[0][0]; - t.equals(+calledWithArgs.center[0].toFixed(4), +-122.4809.toFixed(4), 'the map is directed to fly to the right longitude'); - t.equals(+calledWithArgs.center[1].toFixed(4), +37.8181.toFixed(4), 'the map is directed to fly to the right latitude'); - t.deepEqual(calledWithArgs.zoom, 16, 'the map is directed to fly to the right zoom'); + t.ok(flyToSpy.calledOnce, 'flyTo() is called after querying'); + + const calledWithArgs = flyToSpy.args[0][0]; + + t.equals(+calledWithArgs.center[0].toFixed(4), + +-122.4809.toFixed(4), + 'the map is directed to fly to the right longitude'); + + t.equals(+calledWithArgs.center[1].toFixed(4), + +37.8181.toFixed(4), + 'the map is directed to fly to the right latitude'); + + t.deepEqual(calledWithArgs.zoom, 16, + 'the map is directed to fly to the right zoom level'); + }) + ); + }); + + tt.test('options.flyTo [true] when geolocating', function(t) { + t.plan(4); + + t.teardown(function() { + sinon.restore(); + }); + + setup({ + flyTo: true + }); + + const geolocationPositionStub = stubGeolocationPosition(); + const flyToSpy = sinon.spy(map, 'flyTo'); + + geocoder._geolocateUser(); + + geocoder.on( + 'result', + once(function() { + t.ok(flyToSpy.calledOnce, 'flyTo() is called after geolocating'); + + const calledWithArgs = flyToSpy.args[0][0]; + + t.equals(+calledWithArgs.center[0].toFixed(4), + +geolocationPositionStub.coords.longitude.toFixed(4), + 'the map is directed to fly to the right longitude'); + + t.equals(+calledWithArgs.center[1].toFixed(4), + +geolocationPositionStub.coords.latitude.toFixed(4), + 'the map is directed to fly to the right latitude'); + + t.deepEqual(calledWithArgs.zoom, 16, + 'the map is directed to fly to the right zoom level'); }) ); }); @@ -828,7 +923,7 @@ test('geocoder', function(tt) { t.end(); }); - tt.test('options.marker [true]', function(t) { + tt.test('options.marker [true] when querying', function(t) { t.plan(2); setup({ @@ -849,6 +944,43 @@ test('geocoder', function(tt) { ); }); + tt.test('options.marker [true] when geolocating', function(t) { + t.plan(4); + + t.teardown(function() { + sinon.restore(); + }); + + setup({ + marker: true, + mapboxgl: mapboxgl + }); + + const geolocationPositionStub = stubGeolocationPosition(); + const markerConstructorSpy = sinon.spy(mapboxgl, 'Marker'); + + geocoder._geolocateUser(); + + geocoder.on( + 'result', + once(function(event) { + t.ok(markerConstructorSpy.calledOnce, 'a new marker is added to the map'); + + const calledWithOptions = markerConstructorSpy.args[0][0]; + + t.equals(calledWithOptions.color, '#4668F2', 'a default color is set'); + + t.equals(+event.result.user_coordinates[1].toFixed(4), + +geolocationPositionStub.coords.latitude.toFixed(4), + 'the marker is placed at the correct latitude'); + + t.equals(+event.result.user_coordinates[0].toFixed(4), + +geolocationPositionStub.coords.longitude.toFixed(4), + 'the marker is placed at the correct longitude'); + }) + ); + }); + tt.test('options.marker [constructor properties]', function(t) { t.plan(4); @@ -876,7 +1008,7 @@ test('geocoder', function(tt) { ); }); - tt.test('options.marker [false]', function(t) { + tt.test('options.marker [false] when querying', function(t) { t.plan(1); setup({ @@ -894,6 +1026,30 @@ test('geocoder', function(tt) { ); }); + tt.test('options.marker [false] when geolocating', function(t) { + t.plan(1); + + t.teardown(function() { + sinon.restore(); + }); + + setup({ + marker: false + }); + + stubGeolocationPosition(); + const markerConstructorSpy = sinon.spy(mapboxgl, 'Marker'); + + geocoder._geolocateUser(); + + geocoder.on( + 'result', + once(function() { + t.ok(markerConstructorSpy.notCalled, 'a new marker is not added to the map'); + }) + ); + }); + tt.test('geocode#onRemove', function(t){ setup({marker: true});