feat(voyage): implement drill-down + back-nav + URL-parameter project

Step 15 (v4.3 Sesjon 3 — Wave 3) — wires the dashboard fleet-tiles
to a drill-down view with breadcrumb update + back-to-dashboard
navigation + browser back/forward restoration via popstate.

renderArtifactDetail(artifactKey) renders the chosen artifact into
the #voyage-detail slot using renderPageShell + renderArtifact:
  - brief / plan / review → markdown render
  - progress              → JSON pretty-print in <pre>
  - research              → list of all research-briefs
  - missing entry         → "Artifact mangler" placeholder

Click delegation on .fleet-tile[data-artifact] triggers detail render
+ pushDetailURL (?artifact=<key>); data-action=back-to-dashboard
returns to the dashboard view + pushDashboardURL. Topbar breadcrumb
gets a third segment for detail views.

URL-parameter deep-linking: at page-load, ?project=<basePath>
surfaces a guide-panel hint explaining that webkitdirectory requires
a user-gesture; the hint links to the same Last prosjektmappe button
that wireProjectLoader already exposes. popstate handler restores
the view-state on browser back/forward (no-op when no project loaded).

Test additions (4): renderArtifactDetail function, URLSearchParams
presence, data-action=back-to-dashboard attribute, popstate listener.
This commit is contained in:
Kjell Tore Guttormsen 2026-05-10 16:46:13 +02:00
commit 946eb7ab0f
2 changed files with 186 additions and 0 deletions

View file

@ -176,3 +176,27 @@ test('voyage-playground.html declares dashboard status vocabulary (v4.3 Step 14)
assert.match(text, /'missing'/, 'status missing required');
assert.match(text, /'stale'/, 'status stale required');
});
test('voyage-playground.html declares renderArtifactDetail JS function (v4.3 Step 15 drill-down)', () => {
const text = readFileSync(HTML, 'utf-8');
assert.match(text, /function renderArtifactDetail\b/, 'renderArtifactDetail function required for drill-down');
});
test('voyage-playground.html declares URLSearchParams routing (v4.3 Step 15)', () => {
const text = readFileSync(HTML, 'utf-8');
// Presence-only: URLSearchParams already used at line 810 for project-key
// derivation; Step 15 adds ?project= dashboard/detail routing.
assert.match(text, /URLSearchParams/, 'URLSearchParams required for ?project= routing');
});
test('voyage-playground.html declares data-action="back-to-dashboard" (v4.3 Step 15 back-nav)', () => {
const text = readFileSync(HTML, 'utf-8');
// Stricter than Step 14 wording — must appear as data-action attribute
// somewhere in the JS template, not only in HTML comments.
assert.match(text, /data-action="back-to-dashboard"/, 'data-action="back-to-dashboard" required for return-nav handler');
});
test('voyage-playground.html declares popstate handler (v4.3 Step 15 back/forward)', () => {
const text = readFileSync(HTML, 'utf-8');
assert.match(text, /'popstate'/, 'popstate listener required for browser back/forward');
});