Keep a place, and come back to it
Restores the commit reverted off the routing branch, which is where it was committed by mistake. The change is unmodified; only its branch is. The map had no memory. Every visit started at the same overview, and a system worth returning to had to be found again by name each time. A mark on the readout and on a body's panel now keeps it, a Bookmarks tab lists what has been kept, and choosing one goes there — a star by flying into its system, a body by opening its page. Local storage, not an account. This map asks nobody to sign in, and a list of stars somebody liked is not worth a server. Every read of that store is defensive, because it is a string a person can edit, another tab can write, and a browser can refuse to hand over at all: a bad entry is skipped rather than losing the rest, duplicates are collapsed since two entries for one place would each toggle the other's control, the list is bounded so a hand-edited store cannot decide how much this renders, and where storage is denied outright the bookmarks still work for the visit — they just do not outlive it. The name is stored alongside the id rather than looked up, so the list reads before the catalogues have loaded, and a bookmark to something a later catalogue no longer holds still says what it was instead of decaying into a bare number. The tab is offered even when it is empty, and says what the mark does: a tab that appears only once you have already found the feature is a tab that never taught anyone anything. Choosing a kept place hands the panel back to the readout, the same move as choosing a search result and for the same reason. That behaviour is what the end-to-end spec caught missing — the readout it asserted on did not exist, because the panel just used was still covering it. Verified: build clean, 587/587 unit, 11/11 end-to-end, design detector clean, screenshots at 1440x900 and 390x844. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi
This commit is contained in:
@@ -3,6 +3,7 @@ import { Router } from '@angular/router';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { DataLoaderService } from '../../core/data/data-loader.service';
|
||||
import { BookmarksStore } from '../../shared/state/bookmarks.store';
|
||||
import { DEFAULT_HUD_DISPLAY, HudDisplay, HudDockComponent } from './hud-dock.component';
|
||||
|
||||
class EmptyDataLoaderService {
|
||||
@@ -46,6 +47,7 @@ describe('HudDockComponent', () => {
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
localStorage.clear();
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [HudDockComponent],
|
||||
providers: [
|
||||
@@ -56,9 +58,10 @@ describe('HudDockComponent', () => {
|
||||
fixture = TestBed.createComponent(HudDockComponent);
|
||||
});
|
||||
|
||||
it('offers only the search when it has nothing to read out and no layers', () => {
|
||||
it('offers the search and what has been kept, when it has nothing else', () => {
|
||||
// Bookmarks are always offered: it is the only place that says the map can keep anything.
|
||||
fixture.detectChanges();
|
||||
expect(tabNames()).toEqual(['Search']);
|
||||
expect(tabNames()).toEqual(['Search', 'Bookmarks']);
|
||||
expect(host().querySelector('[role="tabpanel"]')).toBeNull();
|
||||
});
|
||||
|
||||
@@ -66,11 +69,11 @@ describe('HudDockComponent', () => {
|
||||
setReadout();
|
||||
fixture.componentRef.setInput('display', DEFAULT_HUD_DISPLAY);
|
||||
fixture.detectChanges();
|
||||
expect(tabNames()).toEqual(['Search', 'Readout', 'Display']);
|
||||
expect(tabNames()).toEqual(['Search', 'Readout', 'Bookmarks', 'Display']);
|
||||
|
||||
fixture.componentRef.setInput('routing', true);
|
||||
fixture.detectChanges();
|
||||
expect(tabNames()).toEqual(['Search', 'Readout', 'Routes', 'Display']);
|
||||
expect(tabNames()).toEqual(['Search', 'Readout', 'Routes', 'Bookmarks', 'Display']);
|
||||
});
|
||||
|
||||
it('opens the default tab on mount and renders the readout from its inputs', () => {
|
||||
@@ -141,6 +144,80 @@ describe('HudDockComponent', () => {
|
||||
expect(pressed).toEqual(['Labels=true', 'Orbits=true', 'Grid=false', 'Deep sky=true', 'Sky=true', 'Systems=true', 'Jump links=false']);
|
||||
});
|
||||
|
||||
it('says how to keep a place, rather than showing an empty list', () => {
|
||||
fixture.componentRef.setInput('defaultTab', 'bookmarks');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(host().querySelector('#dock-panel-bookmarks ul')).toBeNull();
|
||||
expect(host().textContent).toContain('Nothing kept yet');
|
||||
});
|
||||
|
||||
it('lists what has been kept, newest first, and says which kind each is', () => {
|
||||
const bookmarks = TestBed.inject(BookmarksStore);
|
||||
bookmarks.toggle({ kind: 'star', id: 7, name: 'Sirius' });
|
||||
bookmarks.toggle({ kind: 'body', id: 'earth', name: 'Earth' });
|
||||
fixture.componentRef.setInput('defaultTab', 'bookmarks');
|
||||
fixture.detectChanges();
|
||||
|
||||
const rows = [...host().querySelectorAll('#dock-panel-bookmarks li')].map((li) => li.textContent?.replace(/\s+/g, ' ').trim());
|
||||
expect(rows[0]).toContain('Earth');
|
||||
expect(rows[0]).toContain('Body');
|
||||
expect(rows[1]).toContain('Sirius');
|
||||
expect(rows[1]).toContain('System');
|
||||
});
|
||||
|
||||
it('hands back the place that was chosen, whole', () => {
|
||||
const bookmarks = TestBed.inject(BookmarksStore);
|
||||
bookmarks.toggle({ kind: 'star', id: 7, name: 'Sirius' });
|
||||
fixture.componentRef.setInput('defaultTab', 'bookmarks');
|
||||
fixture.detectChanges();
|
||||
const chosen: unknown[] = [];
|
||||
fixture.componentInstance.bookmarkChosen.subscribe((bookmark) => chosen.push(bookmark));
|
||||
|
||||
host().querySelector<HTMLButtonElement>('#dock-panel-bookmarks li button')?.click();
|
||||
|
||||
expect(chosen).toEqual([{ kind: 'star', id: 7, name: 'Sirius' }]);
|
||||
});
|
||||
|
||||
it('forgets one without disturbing the rest', () => {
|
||||
const bookmarks = TestBed.inject(BookmarksStore);
|
||||
bookmarks.toggle({ kind: 'star', id: 7, name: 'Sirius' });
|
||||
bookmarks.toggle({ kind: 'body', id: 'earth', name: 'Earth' });
|
||||
fixture.componentRef.setInput('defaultTab', 'bookmarks');
|
||||
fixture.detectChanges();
|
||||
|
||||
host().querySelector<HTMLButtonElement>('[aria-label="Forget Earth"]')?.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(bookmarks.bookmarks().map((bookmark) => bookmark.name)).toEqual(['Sirius']);
|
||||
expect(host().textContent).not.toContain('Earth');
|
||||
});
|
||||
|
||||
it('keeps the star the readout is about, and says so on the control', () => {
|
||||
const bookmarks = TestBed.inject(BookmarksStore);
|
||||
setReadout();
|
||||
fixture.componentRef.setInput('title', 'Sirius');
|
||||
fixture.componentRef.setInput('keepableStarId', 7);
|
||||
fixture.componentRef.setInput('defaultTab', 'readout');
|
||||
fixture.detectChanges();
|
||||
|
||||
const keep = host().querySelector<HTMLButtonElement>('[aria-label="Keep Sirius"]');
|
||||
expect(keep?.getAttribute('aria-pressed')).toBe('false');
|
||||
keep?.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(bookmarks.has('star', 7)).toBe(true);
|
||||
expect(host().querySelector('[aria-label="Forget Sirius"]')?.getAttribute('aria-pressed')).toBe('true');
|
||||
});
|
||||
|
||||
it('offers nothing to keep where the readout is a scale rather than a place', () => {
|
||||
setReadout();
|
||||
fixture.componentRef.setInput('defaultTab', 'readout');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(host().querySelector('[aria-label^="Keep"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('opens the search on "/" from anywhere but a text field', () => {
|
||||
setReadout();
|
||||
fixture.componentRef.setInput('defaultTab', 'readout');
|
||||
|
||||
Reference in New Issue
Block a user