Clear search with no results when closing drawer #106
@@ -176,6 +176,18 @@ export default class SearchController extends Controller {
|
||||
return saved || p;
|
||||
});
|
||||
|
||||
// A search with results is a restorable context (e.g. for "up" navigation
|
||||
// from place details). Empty searches are intentionally not restorable, so
|
||||
// they don't reopen an invisible "no results" drawer later on.
|
||||
if (pois.length > 0) {
|
||||
this.mapUi.currentSearch = {
|
||||
q: params.q,
|
||||
category: params.category,
|
||||
lat: params.lat,
|
||||
lon: params.lon,
|
||||
};
|
||||
}
|
||||
|
||||
const targetName = params.selected || params.q;
|
||||
|
||||
if (targetName && pois.length > 0) {
|
||||
|
||||
@@ -24,10 +24,6 @@ export default class SearchRoute extends Route {
|
||||
|
||||
// Trigger the background task to fetch results
|
||||
controller.fetchResultsTask.perform(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 };
|
||||
}
|
||||
|
||||
resetController(controller, isExiting) {
|
||||
|
||||
@@ -72,6 +72,9 @@ export default class ApplicationComponent extends Component {
|
||||
} else {
|
||||
this.router.transitionTo('index');
|
||||
}
|
||||
} else if (name === 'search' && this.mapUi.searchResults.length === 0) {
|
||||
// An empty search has no markers worth keeping, so clear the route
|
||||
this.router.replaceWith('index');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,20 @@ export default class SearchTemplate extends Component {
|
||||
this.mapUi.showSidebar();
|
||||
this.mapUi.preventNextZoom = true;
|
||||
// We don't need to manually set currentSearch here because
|
||||
// it was already set in the route's setupController
|
||||
// it was already set in the search controller
|
||||
this.router.transitionTo('place', place);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
close() {
|
||||
this.mapUi.hideSidebar();
|
||||
// With no results there is nothing to keep around (no map markers), so
|
||||
// dismissing the drawer clears the search route entirely.
|
||||
if (this.mapUi.searchResults.length === 0) {
|
||||
this.router.replaceWith('index');
|
||||
} else {
|
||||
this.mapUi.hideSidebar();
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
|
||||
@@ -129,4 +129,47 @@ module('Acceptance | navigation', function (hooks) {
|
||||
backStub.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test('an empty search is not restored when navigating back from a place', async function (assert) {
|
||||
const mapUi = this.owner.lookup('service:map-ui');
|
||||
|
||||
// Nearby search that yields no results, but with a fetchable place
|
||||
this.owner.register(
|
||||
'service:osm',
|
||||
class extends Service {
|
||||
async getNearbyPois() {
|
||||
return [];
|
||||
}
|
||||
async fetchOsmObject(id, type) {
|
||||
return {
|
||||
osmId: id,
|
||||
osmType: type,
|
||||
lat: 1,
|
||||
lon: 1,
|
||||
osmTags: { name: 'Test Place', amenity: 'cafe' },
|
||||
title: 'Test Place',
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
await visit('/search?lat=1&lon=1');
|
||||
assert.strictEqual(
|
||||
mapUi.currentSearch,
|
||||
null,
|
||||
'Empty search is not restorable'
|
||||
);
|
||||
|
||||
// Simulate opening a place while the (empty) search route is active
|
||||
mapUi.returnToSearch = true;
|
||||
|
||||
await visit('/place/osm:node:123');
|
||||
await click('.back-btn');
|
||||
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
'/',
|
||||
'Back navigation does not reopen the empty search'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { visit, currentURL } from '@ember/test-helpers';
|
||||
import { visit, currentURL, click } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'marco/tests/helpers';
|
||||
import Service from '@ember/service';
|
||||
|
||||
@@ -342,4 +342,46 @@ module('Acceptance | search', function (hooks) {
|
||||
.dom('.search-input')
|
||||
.hasValue('', 'Search input is cleared on transitioning to index');
|
||||
});
|
||||
|
||||
test('closing the drawer clears the search route when there are no results', async function (assert) {
|
||||
class MockPhotonService extends Service {
|
||||
async search() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
this.owner.register('service:photon', MockPhotonService);
|
||||
|
||||
class MockStorageService extends Service {
|
||||
savedPlaces = [];
|
||||
findPlaceById() {
|
||||
return null;
|
||||
}
|
||||
isPlaceSaved() {
|
||||
return false;
|
||||
}
|
||||
rs = { on: () => {} };
|
||||
placesInView = [];
|
||||
loadPlacesInBounds() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
this.owner.register('service:storage', MockStorageService);
|
||||
|
||||
const mapUi = this.owner.lookup('service:map-ui');
|
||||
|
||||
await visit('/search?q=nowhere');
|
||||
|
||||
assert.dom('.empty-state').hasText('No results found.');
|
||||
assert.strictEqual(
|
||||
mapUi.currentSearch,
|
||||
null,
|
||||
'An empty search is not kept as a restorable search context'
|
||||
);
|
||||
|
||||
await click('.close-btn');
|
||||
|
||||
assert.dom('.sidebar').doesNotExist('Sidebar should be closed');
|
||||
assert.strictEqual(currentURL(), '/', 'Search route is cleared');
|
||||
assert.strictEqual(mapUi.searchResults.length, 0, 'Results are cleared');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user