Make the app work: bind transport fetch (Illegal invocation), emergency phone content, real map assets+POIs, wire map loader into routes, offline icon cache match, multi-day event times, hide empty emergency groups; remove bottom nav bar (home tiles + brand cover navigation)
Some checks failed
ci / check (push) Has been cancelled
Some checks failed
ci / check (push) Has been cancelled
This commit is contained in:
parent
859cb649c8
commit
398f0b097a
11 changed files with 200 additions and 89 deletions
|
|
@ -6,7 +6,7 @@
|
|||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import { mount as boot, renderView } from "../../src/app/main.js";
|
||||
import { ROUTES, normalizePath, routeForPath } from "../../src/ui/router/router.js";
|
||||
import { createLayout, setActiveNav } from "../../src/app/layout.js";
|
||||
import { createLayout } from "../../src/app/layout.js";
|
||||
|
||||
function setupAppHost(): HTMLElement {
|
||||
document.body.innerHTML = '<div id="app" aria-busy="true"></div>';
|
||||
|
|
@ -72,9 +72,9 @@ describe("Stage 2 shell — boot", () => {
|
|||
});
|
||||
|
||||
describe("Stage 2 shell — layout / a11y", () => {
|
||||
it("provides skip link, banner, main, and nav landmarks", () => {
|
||||
it("provides skip link, banner, and main landmarks; no bottom nav bar", () => {
|
||||
setupAppHost();
|
||||
const { root, main, nav } = createLayout();
|
||||
const { root, main } = createLayout();
|
||||
const host = document.getElementById("app");
|
||||
if (!host) throw new Error("host");
|
||||
host.replaceChildren(root);
|
||||
|
|
@ -82,8 +82,9 @@ describe("Stage 2 shell — layout / a11y", () => {
|
|||
expect(document.querySelector('header[role="banner"]')).toBeTruthy();
|
||||
expect(main.id).toBe("main-content");
|
||||
expect(main.getAttribute("tabindex")).toBe("-1");
|
||||
expect(nav.getAttribute("aria-label")).toBe("Primary navigation");
|
||||
expect(nav.getAttribute("role")).toBe("navigation");
|
||||
// Bottom navigation bar removed by product decision (Oct 3):
|
||||
// home tiles + brand-as-home cover navigation.
|
||||
expect(document.querySelector(".side-nav")).toBeNull();
|
||||
});
|
||||
|
||||
it("header status has live region", () => {
|
||||
|
|
@ -114,23 +115,23 @@ describe("Stage 2 shell — navigation / emergency affordance", () => {
|
|||
});
|
||||
|
||||
it("renders home plus four primary destinations, emergency visually prioritized", () => {
|
||||
history.replaceState(null, "", "/");
|
||||
setupAppHost();
|
||||
boot();
|
||||
const links = [...document.querySelectorAll<HTMLAnchorElement>(".side-nav__link")];
|
||||
expect(links.map((a) => a.textContent)).toEqual([
|
||||
"Home",
|
||||
// Home screen tiles are the navigation surface.
|
||||
const tiles = [...document.querySelectorAll<HTMLAnchorElement>(".home-tile")];
|
||||
expect(tiles.map((a) => a.textContent)).toEqual([
|
||||
"Emergency",
|
||||
"Schedule",
|
||||
"Map",
|
||||
"Information",
|
||||
]);
|
||||
const emergency = links[1];
|
||||
expect(emergency?.classList.contains("side-nav__link--emergency")).toBe(true);
|
||||
const home = links[0];
|
||||
expect(home?.getAttribute("data-route")).toBe("/");
|
||||
const emergency = tiles[0];
|
||||
expect(emergency?.classList.contains("home-tile--emergency")).toBe(true);
|
||||
});
|
||||
|
||||
it("all four destinations have 48px touch-target capable links and data-route", () => {
|
||||
history.replaceState(null, "", "/");
|
||||
setupAppHost();
|
||||
boot();
|
||||
for (const r of ROUTES) {
|
||||
|
|
@ -140,29 +141,42 @@ describe("Stage 2 shell — navigation / emergency affordance", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("aria-current reflects active route", () => {
|
||||
const { nav } = createLayout();
|
||||
setActiveNav(nav, "schedule");
|
||||
expect(nav.querySelector('a[data-route="/schedule"]')?.getAttribute("aria-current")).toBe(
|
||||
"page",
|
||||
);
|
||||
expect(nav.querySelector('a[data-route="/emergency"]')?.hasAttribute("aria-current")).toBe(
|
||||
false,
|
||||
);
|
||||
it("brand acts as home control: aria-current on destination pages, off at home", () => {
|
||||
history.replaceState(null, "", "/schedule");
|
||||
document.body.innerHTML = '<div id="app"></div>';
|
||||
setupAppHost();
|
||||
boot();
|
||||
const brand = document.querySelector<HTMLElement>(".app-header__brand");
|
||||
expect(brand?.getAttribute("aria-current")).toBe("page");
|
||||
history.replaceState(null, "", "/");
|
||||
document.body.innerHTML = '<div id="app"></div>';
|
||||
setupAppHost();
|
||||
boot();
|
||||
expect(
|
||||
document.querySelector<HTMLElement>(".app-header__brand")?.hasAttribute("aria-current"),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("emergency is reachable with one tap from every primary destination", () => {
|
||||
for (const start of ROUTES.map((r) => r.path)) {
|
||||
history.replaceState(null, "", start);
|
||||
it("emergency is one tap from home; brand is one tap back from every destination", () => {
|
||||
history.replaceState(null, "", "/");
|
||||
document.body.innerHTML = '<div id="app"></div>';
|
||||
setupAppHost();
|
||||
const { router } = boot();
|
||||
const emergencyLink = document.querySelector<HTMLAnchorElement>('a[data-route="/emergency"]');
|
||||
expect(emergencyLink, "emergency tile on home").toBeTruthy();
|
||||
emergencyLink?.click();
|
||||
expect(router.getPath()).toBe("/emergency");
|
||||
expect(document.querySelector("h1")?.textContent).toBe("Emergency");
|
||||
|
||||
for (const dest of ROUTES.map((r) => r.path)) {
|
||||
history.replaceState(null, "", dest);
|
||||
document.body.innerHTML = '<div id="app"></div>';
|
||||
setupAppHost();
|
||||
const { router } = boot();
|
||||
expect(router.getPath()).toBe(start);
|
||||
const emergencyLink = document.querySelector<HTMLAnchorElement>('a[data-route="/emergency"]');
|
||||
expect(emergencyLink, `emergency reachable from ${start}`).toBeTruthy();
|
||||
emergencyLink?.click();
|
||||
expect(router.getPath()).toBe("/emergency");
|
||||
expect(document.querySelector("h1")?.textContent).toBe("Emergency");
|
||||
const b = boot();
|
||||
const brand = document.querySelector<HTMLAnchorElement>(".app-header__brand");
|
||||
expect(brand, `brand present at ${dest}`).toBeTruthy();
|
||||
brand?.click();
|
||||
expect(b.router.getPath()).toBe("/");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -185,6 +199,7 @@ describe("Stage 2 shell — navigation / emergency affordance", () => {
|
|||
});
|
||||
|
||||
it("clicking a data-route link uses router.navigate (no reload) and updates history", () => {
|
||||
history.replaceState(null, "", "/");
|
||||
setupAppHost();
|
||||
boot();
|
||||
const scheduleLink = document.querySelector<HTMLAnchorElement>('a[data-route="/schedule"]');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue