Rename Collections, add special list for all saved #99
@@ -22,7 +22,7 @@ import iconRounded from '../../icons/icon-rounded.svg?raw';
|
||||
<li>
|
||||
<button type="button" {{on "click" @onSavedPlaces}}>
|
||||
<Icon @name="bookmark" @size={{20}} />
|
||||
<span>Collections</span>
|
||||
<span>Saved Places</span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
@@ -14,6 +14,8 @@ function getPlaceTime(place) {
|
||||
return isNaN(parsed) ? 0 : parsed;
|
||||
}
|
||||
|
||||
const SAVED_LIST_ID = 'saved';
|
||||
|
||||
export default class ListsListController extends Controller {
|
||||
@service router;
|
||||
@service mapUi;
|
||||
@@ -41,6 +43,12 @@ export default class ListsListController extends Controller {
|
||||
}
|
||||
|
||||
get listColor() {
|
||||
if (this.listId === SAVED_LIST_ID) {
|
||||
return getComputedStyle(document.documentElement)
|
||||
.getPropertyValue('--default-list-color')
|
||||
.trim();
|
||||
}
|
||||
|
||||
const list = this.storage.lists.find((l) => l.id === this.listId);
|
||||
if (list && list.color) {
|
||||
return list.color;
|
||||
@@ -51,11 +59,21 @@ export default class ListsListController extends Controller {
|
||||
}
|
||||
|
||||
get listTitle() {
|
||||
if (this.listId === SAVED_LIST_ID) {
|
||||
return 'Saved Places';
|
||||
}
|
||||
|
||||
const list = this.storage.lists.find((l) => l.id === this.listId);
|
||||
return list ? list.title : 'Collections';
|
||||
}
|
||||
|
||||
get places() {
|
||||
if (this.listId === SAVED_LIST_ID) {
|
||||
return [...this.storage.savedPlaces].sort(
|
||||
(a, b) => getPlaceTime(b) - getPlaceTime(a)
|
||||
);
|
||||
}
|
||||
|
||||
const currentList = this.storage.lists.find((l) => l.id === this.listId);
|
||||
const placeRefsIds = new Set(
|
||||
currentList?.placeRefs?.map((ref) => ref.id) || []
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { service } from '@ember/service';
|
||||
|
||||
export default class ListsListRoute extends Route {
|
||||
@service storage;
|
||||
|
||||
model(params) {
|
||||
// Resolve instantly so transition happens in 0ms!
|
||||
return { list_id: params.list_id };
|
||||
}
|
||||
|
||||
setupController(controller, model) {
|
||||
console.debug('DEBUG: setupController controller is:', controller);
|
||||
console.debug(
|
||||
'DEBUG: controller.loadPlacesTask is:',
|
||||
controller?.loadPlacesTask
|
||||
);
|
||||
controller.model = model;
|
||||
super.setupController(controller, model);
|
||||
if (model.list_id === 'saved') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (controller && controller.loadPlacesTask) {
|
||||
controller.loadPlacesTask.perform(model.list_id);
|
||||
} else {
|
||||
console.error('DEBUG: ERROR! controller.loadPlacesTask is undefined!');
|
||||
console.error('controller.loadPlacesTask is undefined');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@ export default class ListsIndexTemplate extends Component {
|
||||
@service router;
|
||||
@service mapUi;
|
||||
|
||||
savedList = {
|
||||
id: 'saved',
|
||||
title: 'Saved',
|
||||
color: 'var(--default-list-color)',
|
||||
};
|
||||
|
||||
styleFor(color) {
|
||||
const finalColor =
|
||||
color ||
|
||||
@@ -46,7 +52,7 @@ export default class ListsIndexTemplate extends Component {
|
||||
<span class="sidebar-header-icon-wrapper">
|
||||
<Icon @name="bookmark" @size={{20}} @color="#898989" />
|
||||
</span>
|
||||
Collections
|
||||
Saved Places
|
||||
</h2>
|
||||
<button type="button" class="close-btn" {{on "click" this.close}}>
|
||||
<Icon @name="x" @size={{20}} @color="#333" />
|
||||
@@ -55,6 +61,30 @@ export default class ListsIndexTemplate extends Component {
|
||||
|
||||
<div class="sidebar-content">
|
||||
<ul class="places-list">
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="lists-index-item"
|
||||
{{on "click" (fn this.selectList this.savedList.id)}}
|
||||
>
|
||||
<div class="lists-index-item-left">
|
||||
{{! template-lint-disable no-inline-styles }}
|
||||
<span
|
||||
class="list-color-dot"
|
||||
style={{this.styleFor this.savedList.color}}
|
||||
></span>
|
||||
<div class="lists-index-name">{{this.savedList.title}}</div>
|
||||
</div>
|
||||
<div class="lists-index-count">
|
||||
{{#if this.storage.savedPlaces.length}}
|
||||
{{this.storage.savedPlaces.length}}
|
||||
places
|
||||
{{else}}
|
||||
empty
|
||||
{{/if}}
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
{{#each this.storage.lists as |list|}}
|
||||
<li>
|
||||
<button
|
||||
|
||||
@@ -16,8 +16,16 @@ class MockStorageService extends Service {
|
||||
id: 'place-123',
|
||||
title: 'Mountain Trail',
|
||||
geohash: 'u33dc0',
|
||||
createdAt: '2023-01-02T12:00:00.000Z',
|
||||
osmTags: { name: 'Mountain Trail' },
|
||||
},
|
||||
{
|
||||
id: 'place-456',
|
||||
title: 'Beach View',
|
||||
geohash: 'u33dc1',
|
||||
createdAt: '2023-01-03T12:00:00.000Z',
|
||||
osmTags: { name: 'Beach View' },
|
||||
},
|
||||
];
|
||||
lists = [
|
||||
{
|
||||
@@ -74,27 +82,56 @@ module('Acceptance | collections navigation', function (hooks) {
|
||||
assert.dom('.sidebar.app-menu-pane').exists('App menu sidebar is open');
|
||||
assert
|
||||
.dom('.app-menu')
|
||||
.includesText('Collections', 'Menu contains Collections link');
|
||||
.includesText('Saved Places', 'Menu contains Saved Places link');
|
||||
|
||||
// 3. Transition to Collections Index (List of lists)
|
||||
await click(document.querySelectorAll('.app-menu button')[0]); // Click "Collections"
|
||||
// 3. Transition to Saved Places index (list of lists)
|
||||
await click(document.querySelectorAll('.app-menu button')[0]); // Click "Saved Places"
|
||||
assert.strictEqual(currentURL(), '/lists', 'Transitions to /lists index');
|
||||
assert
|
||||
.dom('.sidebar-header-text-centered')
|
||||
.includesText('Collections', 'Header is centered and titled Collections');
|
||||
.includesText(
|
||||
'Saved Places',
|
||||
'Header is centered and titled Saved Places'
|
||||
);
|
||||
assert
|
||||
.dom('.lists-index-item')
|
||||
.exists({ count: 2 }, 'Renders our 2 mocked list items');
|
||||
.exists({ count: 3 }, 'Renders Saved plus our 2 mocked list items');
|
||||
assert
|
||||
.dom(document.querySelectorAll('.lists-index-item')[0])
|
||||
.includesText('Saved', 'Saved appears as the first list item');
|
||||
assert
|
||||
.dom(document.querySelectorAll('.lists-index-item')[0])
|
||||
.includesText('2 places', 'Saved shows the total saved places count');
|
||||
|
||||
// 4. Transition to a specific list (Want to go)
|
||||
await click(document.querySelectorAll('.lists-index-item')[0]); // Click "Want to go"
|
||||
// 4. Transition to the synthetic Saved list
|
||||
await click(document.querySelectorAll('.lists-index-item')[0]);
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
'/lists/saved',
|
||||
'Transitions to /lists/saved'
|
||||
);
|
||||
|
||||
await waitFor('.places-list');
|
||||
assert
|
||||
.dom(document.querySelectorAll('.places-list .place-name')[0])
|
||||
.hasText('Beach View', 'Saved Places shows newest saved place first');
|
||||
assert
|
||||
.dom(document.querySelectorAll('.places-list .place-name')[1])
|
||||
.hasText('Mountain Trail', 'Saved Places includes all saved places');
|
||||
|
||||
// 5. Go back to the Saved Places index
|
||||
await click('.sidebar-header .back-btn');
|
||||
assert.strictEqual(currentURL(), '/lists', 'Goes back to /lists index');
|
||||
|
||||
// 6. Transition to a specific real list (Want to go)
|
||||
await click(document.querySelectorAll('.lists-index-item')[1]); // Click "Want to go"
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
'/lists/to-go',
|
||||
'Transitions instantly to /lists/to-go'
|
||||
);
|
||||
|
||||
// 5. Verify background loading spinner shows up, then results populate
|
||||
// 7. Verify background loading spinner shows up, then results populate
|
||||
await waitFor('.places-list');
|
||||
assert
|
||||
.dom('.places-list .place-name')
|
||||
@@ -103,19 +140,37 @@ module('Acceptance | collections navigation', function (hooks) {
|
||||
.dom('.places-list .place-type')
|
||||
.hasText('Saved place', 'Place type displays Saved place correctly');
|
||||
|
||||
// 6. Click back button in collection list header
|
||||
// 8. Click back button in collection list header
|
||||
await click('.sidebar-header .back-btn');
|
||||
assert.strictEqual(currentURL(), '/lists', 'Goes back to /lists index');
|
||||
|
||||
// 7. Click back button in collections index header
|
||||
// 9. Click back button in collections index header
|
||||
await click('.sidebar-header .back-btn');
|
||||
assert.strictEqual(currentURL(), '/menu', 'Goes back to main menu route');
|
||||
|
||||
// 8. Close sidebar
|
||||
// 10. Close sidebar
|
||||
await click('.sidebar-header .close-btn');
|
||||
assert.strictEqual(currentURL(), '/', 'Sidebar closed and returned home');
|
||||
});
|
||||
|
||||
test('clicking a place inside Saved Places sets returnToRoute and returns gracefully on back click', async function (assert) {
|
||||
await visit('/lists/saved');
|
||||
await waitFor('.places-list');
|
||||
|
||||
await click('.place-item');
|
||||
assert.ok(
|
||||
currentURL().includes('/place/place-456'),
|
||||
'Transitions to the newest saved place details route'
|
||||
);
|
||||
|
||||
await click('.back-btn');
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
'/lists/saved',
|
||||
'Returns gracefully back to the Saved Places view'
|
||||
);
|
||||
});
|
||||
|
||||
test('clicking a place inside a collection sets returnToRoute and returns gracefully on back click', async function (assert) {
|
||||
await visit('/lists/to-go');
|
||||
await waitFor('.places-list');
|
||||
@@ -136,7 +191,7 @@ module('Acceptance | collections navigation', function (hooks) {
|
||||
);
|
||||
});
|
||||
|
||||
test('places inside a collection are sorted by createdAt descending', async function (assert) {
|
||||
test('places inside Saved Places are sorted by createdAt descending', async function (assert) {
|
||||
class SortedMockStorageService extends Service {
|
||||
initialSyncDone = true;
|
||||
savedPlaces = [
|
||||
@@ -203,7 +258,7 @@ module('Acceptance | collections navigation', function (hooks) {
|
||||
this.owner.unregister('service:storage');
|
||||
this.owner.register('service:storage', SortedMockStorageService);
|
||||
|
||||
await visit('/lists/to-go');
|
||||
await visit('/lists/saved');
|
||||
await waitFor('.places-list');
|
||||
|
||||
const placeNames = Array.from(
|
||||
@@ -213,7 +268,7 @@ module('Acceptance | collections navigation', function (hooks) {
|
||||
assert.deepEqual(
|
||||
placeNames,
|
||||
['Newest Place', 'Middle Place', 'Oldest Place'],
|
||||
'Places are ordered by createdAt in descending order'
|
||||
'Saved Places are ordered by createdAt in descending order'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user