feat(web): add the local location view at /location
The screen a player stands on between activities: name, region, scene artwork with hotspots pinned by percentage, a four-button action bar and a context sidebar covering identity, danger, encounters, interactions and rewards. It owns no knowledge of any particular place. Hotspots and actions are routed by interaction type: HUNT and MAP hand off to the existing hunt and map screens, and everything that reveals text goes through the server-authoritative interaction endpoint. A second location therefore renders by supplying different content, which the Südtor case in the page spec exercises. The shell drops its generic area rail on /location, where the screen's own sidebar says the same thing better, and Ort joins the navigation as its first entry. Root and unknown routes now land on the location rather than the map: arriving somewhere should mean arriving at a place. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ describe('App', () => {
|
||||
imports: [AppShellComponent],
|
||||
providers: [
|
||||
provideRouter([
|
||||
{ path: 'location', children: [] },
|
||||
{ path: 'world', children: [] },
|
||||
{ path: 'hunt', children: [] },
|
||||
]),
|
||||
@@ -65,6 +66,55 @@ describe('App', () => {
|
||||
expect(element.textContent).not.toContain('Shop');
|
||||
});
|
||||
|
||||
it('offers Ort as the first navigation entry, marked active on /location', async () => {
|
||||
const fixture = TestBed.createComponent(AppShellComponent);
|
||||
const router = TestBed.inject(Router);
|
||||
await router.navigateByUrl('/location');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const element = fixture.nativeElement as HTMLElement;
|
||||
const entries = element.querySelectorAll<HTMLButtonElement>('[data-navigation]');
|
||||
expect([...entries].map((entry) => entry.dataset['navigation'])).toEqual([
|
||||
'location',
|
||||
'world',
|
||||
'hunt',
|
||||
'quests',
|
||||
'inventory',
|
||||
'character',
|
||||
]);
|
||||
|
||||
const locationButton = element.querySelector<HTMLButtonElement>(
|
||||
'[data-navigation="location"]',
|
||||
);
|
||||
expect(locationButton?.disabled).toBe(false);
|
||||
expect(locationButton?.getAttribute('aria-label')).toBe('Ort');
|
||||
expect(locationButton?.getAttribute('aria-current')).toBe('page');
|
||||
expect(
|
||||
element.querySelector('[data-navigation="world"]')?.getAttribute('aria-current'),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('drops the shell context rail on /location, where the screen brings its own', async () => {
|
||||
const fixture = TestBed.createComponent(AppShellComponent);
|
||||
const router = TestBed.inject(Router);
|
||||
await router.navigateByUrl('/location');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(fixture.nativeElement.querySelector('app-context-panel')).toBeNull();
|
||||
});
|
||||
|
||||
it('keeps the shell context rail on the map', async () => {
|
||||
const fixture = TestBed.createComponent(AppShellComponent);
|
||||
const router = TestBed.inject(Router);
|
||||
await router.navigateByUrl('/world');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(fixture.nativeElement.querySelector('app-context-panel')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('marks Jagd as the active navigation entry while on /hunt', async () => {
|
||||
const fixture = TestBed.createComponent(AppShellComponent);
|
||||
const router = TestBed.inject(Router);
|
||||
@@ -104,11 +154,19 @@ describe('App', () => {
|
||||
expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain('320');
|
||||
});
|
||||
|
||||
it('redirects root and unknown routes to the world shell', () => {
|
||||
it('lands root and unknown routes on the place the character is standing in', () => {
|
||||
expect(routes.find((route) => route.path === '')).toMatchObject({
|
||||
pathMatch: 'full',
|
||||
redirectTo: 'world',
|
||||
redirectTo: 'location',
|
||||
});
|
||||
expect(routes.find((route) => route.path === '**')).toMatchObject({ redirectTo: 'world' });
|
||||
expect(routes.find((route) => route.path === '**')).toMatchObject({ redirectTo: 'location' });
|
||||
});
|
||||
|
||||
it('keeps the map and hunt routes where they were', () => {
|
||||
const shellChildren = routes.find((route) => route.children)?.children ?? [];
|
||||
|
||||
expect(shellChildren.map((route) => route.path)).toEqual(
|
||||
expect.arrayContaining(['location', 'world', 'hunt']),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user