Ensure map marker clicks preserve search context

Fixes the back button just closing the sidebar and clearing the whole
search after having seleted a result via map marker
This commit is contained in:
2026-03-23 16:32:49 +04:00
parent 46605dbd32
commit 818ec35071
7 changed files with 45 additions and 25 deletions

View File

@@ -1099,6 +1099,20 @@ export default class MapComponent extends Component {
}
}
// Helper to transition with proper state
const transitionToPlace = (place) => {
// If we are currently in search mode OR have active search results,
// we want the "Back" button on the place details to return to the search results.
if (
this.router.currentRouteName === 'search' ||
(this.mapUi.currentSearch && this.mapUi.searchResults.length > 0)
) {
this.mapUi.returnToSearch = true;
}
this.mapUi.preventNextZoom = true;
this.router.transitionTo('place', place);
};
// Special handling when sidebar is OPEN
if (this.args.isSidebarOpen) {
// If it's a bookmark or search result, we allow "switching" to it even if sidebar is open
@@ -1108,8 +1122,7 @@ export default class MapComponent extends Component {
'Clicked feature while sidebar open (switching):',
targetPlace
);
this.mapUi.preventNextZoom = true;
this.router.transitionTo('place', targetPlace);
transitionToPlace(targetPlace);
return;
}
@@ -1123,15 +1136,13 @@ export default class MapComponent extends Component {
// Normal behavior (sidebar is closed)
if (clickedBookmark) {
console.debug('Clicked bookmark:', clickedBookmark);
this.mapUi.preventNextZoom = true;
this.router.transitionTo('place', clickedBookmark);
transitionToPlace(clickedBookmark);
return;
}
if (clickedSearchResult) {
console.debug('Clicked search result:', clickedSearchResult);
this.mapUi.preventNextZoom = true;
this.router.transitionTo('place', clickedSearchResult);
transitionToPlace(clickedSearchResult);
return;
}

View File

@@ -170,6 +170,10 @@ export default class SearchRoute extends Route {
// Ensure pulse is stopped if we reach here
this.mapUi.stopSearch();
this.mapUi.setSearchResults(model);
// Store current search params to allow "Up" navigation from place details
const { q, category, lat, lon } = this.paramsFor('search');
this.mapUi.currentSearch = { q, category, lat, lon };
}
@action

View File

@@ -13,6 +13,7 @@ export default class MapUiService extends Service {
@tracked selectionOptions = {};
@tracked preventNextZoom = false;
@tracked searchResults = [];
@tracked currentSearch = null;
selectPlace(place, options = {}) {
this.selectedPlace = place;
@@ -31,6 +32,7 @@ export default class MapUiService extends Service {
clearSearchResults() {
this.searchResults = [];
this.currentSearch = null;
}
startSearch() {

View File

@@ -77,9 +77,11 @@ export default class PlaceTemplate extends Component {
navigateBack(place) {
// The sidebar calls this with null when "Back" is clicked.
if (place === null) {
// If we came from search results, go back in history
if (this.mapUi.returnToSearch) {
window.history.back();
// If we have an active search context, return to it (UP navigation)
if (this.mapUi.returnToSearch && this.mapUi.currentSearch) {
this.router.transitionTo('search', {
queryParams: this.mapUi.currentSearch
});
} else {
// Otherwise just close the sidebar (return to map index)
this.router.transitionTo('index');

View File

@@ -11,6 +11,8 @@ export default class SearchTemplate extends Component {
selectPlace(place) {
if (place) {
this.mapUi.returnToSearch = true;
// We don't need to manually set currentSearch here because
// it was already set in the route's setupController
this.router.transitionTo('place', place);
}
}

View File

@@ -64,25 +64,24 @@ module('Acceptance | navigation', function (hooks) {
this.owner.register('service:storage', MockStorageService);
});
test('navigating from search results to place and back uses history', async function (assert) {
test('navigating from search results to place and back returns to search', async function (assert) {
const mapUi = this.owner.lookup('service:map-ui');
const backStub = sinon.stub(window.history, 'back');
try {
await visit('/search?lat=1&lon=1');
assert.strictEqual(currentURL(), '/search?lat=1&lon=1');
await visit('/search?lat=1&lon=1');
assert.strictEqual(currentURL(), '/search?lat=1&lon=1');
await click('.place-item');
assert.ok(currentURL().includes('/place/'), 'Navigated to place');
assert.true(mapUi.returnToSearch, 'Flag returnToSearch is set');
await click('.place-item');
assert.ok(currentURL().includes('/place/'), 'Navigated to place');
assert.true(mapUi.returnToSearch, 'Flag returnToSearch is set');
// Click the back button in the sidebar
await click('.back-btn');
// Click the back button in the sidebar
await click('.back-btn');
assert.true(backStub.calledOnce, 'window.history.back() was called');
} finally {
backStub.restore();
}
assert.strictEqual(
currentURL(),
'/search?lat=1&lon=1',
'Returned to search results'
);
});
test('closing the sidebar resets the returnToSearch flag', async function (assert) {

View File

@@ -27,9 +27,9 @@ module('Unit | Utility | osm-icons', function () {
assert.strictEqual(result, 'shopping-basket');
});
test('it returns shopping-basket for unknown shop types (catch-all)', function (assert) {
test('it returns shopping-bag for unknown shop types (catch-all)', function (assert) {
let result = getIconNameForTags({ shop: 'unknown_shop_type' });
assert.strictEqual(result, 'shopping-basket');
assert.strictEqual(result, 'shopping-bag');
});
test('it returns null for unknown tags', function (assert) {