Public: anyone can read this; the crew can change it.
The home page's tabs draw §5's tabs, and .home-tabs leaves the markup #4230
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/4224-the-home-page-s-tabs-draw-5-s-tabs-and-h → main 1 commit fork
2 files
changed +26
−14
src/web/studio/home_tabs.ts +16 −9
| @@ -1,19 +1,26 @@ | ||
| 1 | 1 | /** |
| 2 | 2 | * The home's three tabs on a phone — Feed, Your apps, Asked — drawn on each |
| 3 | 3 | * of the three pages, so each is a tap from the others and never only the |
| 4 | * logo away (#599, #851). Hidden on a desk, where the feed's own column and | |
| 5 | * links do the same work (`feed_style.ts`). | |
| 4 | * logo away (#599, #851). Drawn with §5's tabs; the old `.home-tabs` rules in | |
| 5 | * `feed_style.ts` go in part two (#4224). | |
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | import { html, raw, type Html } from "../html.ts"; | |
| 8 | import { html, type Html } from "../html.ts"; | |
| 9 | import { tabs } from "../ui.ts"; | |
| 9 | 10 | |
| 10 | 11 | export type HomeTab = "feed" | "apps" | "asked"; |
| 11 | 12 | |
| 13 | /** §5's `tabs()` in a `.mb` block: link tabs, so `aria-current`. */ | |
| 12 | 14 | export function homeTabs(current: HomeTab): Html { |
| 13 | const tab = (id: HomeTab, href: string, label: string) => | |
| 14 | html`<a href="${href}" ${current === id ? raw('aria-current="page"') : null}>${label}</a>`; | |
| 15 | return html`<nav class="home-tabs seg" aria-label="Home"> | |
| 16 | ${tab("feed", "/", "Feed")} ${tab("apps", "/apps", "Your apps")} | |
| 17 | ${tab("asked", "/asked", "Asked")} | |
| 18 | </nav>`; | |
| 15 | const item = (id: HomeTab, href: string, label: string) => ({ | |
| 16 | label, | |
| 17 | href, | |
| 18 | current: current === id, | |
| 19 | }); | |
| 20 | return html`<div class="mb"> | |
| 21 | ${tabs( | |
| 22 | [item("feed", "/", "Feed"), item("apps", "/apps", "Your apps"), item("asked", "/asked", "Asked")], | |
| 23 | "Home", | |
| 24 | )} | |
| 25 | </div>`; | |
| 19 | 26 | } |
test/home-feed.test.ts +10 −5
| @@ -145,24 +145,29 @@ describe("home is the feed", () => { | ||
| 145 | 145 | // Your apps: a narrow column, and a tab on a phone. |
| 146 | 146 | expect(text).toContain('id="live-apps"'); |
| 147 | 147 | expect(text).toContain('href="/apps"'); |
| 148 | expect(text).toContain('class="home-tabs seg"'); | |
| 148 | expect(text).toContain('class="tabs"'); | |
| 149 | expect(text).not.toContain("home-tabs"); | |
| 149 | 150 | // Nothing stands between the composer and the first post: the crew's |
| 150 | 151 | // missing key sits beside the timeline (after it on a phone), and |
| 151 | 152 | // "What you asked for" is a tab there. |
| 152 | 153 | const note = text.indexOf('id="crew-key-note"'); |
| 153 | 154 | if (note !== -1) expect(note).toBeGreaterThan(feed); |
| 154 | expect(text).toMatch(/class="home-tabs seg"[\s\S]*?href="\/asked"/); | |
| 155 | expect(text).toMatch(/class="tabs"[\s\S]*?href="\/asked"/); | |
| 155 | 156 | // The old home's shelf rows and cost line are /apps's now. |
| 156 | 157 | expect(text).not.toContain("spent today across"); |
| 157 | 158 | const apps = await (await get("/apps", aliceCookie)).text(); |
| 158 | 159 | expect(apps).toContain("spent today across"); |
| 159 | 160 | // Each of the three pages carries the three tabs, so a phone has a way |
| 160 | 161 | // back that is not only the logo. |
| 161 | expect(apps).toMatch(/class="home-tabs seg"[\s\S]*?href="\/apps" aria-current="page"/); | |
| 162 | expect(apps).toMatch(/class="tabs"[\s\S]*?href="\/apps"\s+aria-current="page"/); | |
| 162 | 163 | const asked = await (await get("/asked", aliceCookie)).text(); |
| 163 | expect(asked).toMatch(/class="home-tabs seg"[\s\S]*?href="\/asked" aria-current="page"/); | |
| 164 | expect(asked).toMatch(/class="tabs"[\s\S]*?href="\/asked"\s+aria-current="page"/); | |
| 164 | 165 | expect(asked).toContain('href="/"'); |
| 165 | expect(text).toMatch(/class="home-tabs seg"[\s\S]*?href="\/" aria-current="page"/); | |
| 166 | expect(text).toMatch(/class="tabs"[\s\S]*?href="\/"\s+aria-current="page"/); | |
| 167 | for (const page of [apps, asked]) { | |
| 168 | expect(page).not.toContain("home-tabs"); | |
| 169 | for (const w of ["Feed", "Your apps", "Asked"]) expect(page).toContain(w); | |
| 170 | } | |
| 166 | 171 | }); |
| 167 | 172 | |
| 168 | 173 | test("a reader's timeline has the public repository's posts and nothing private or configured", async () => { |
new above
offline