Public: anyone can read this; the crew can change it.
The browse view's and the studio change page's merge boxes draw §5's components, and the merge-* classes leave their markup #4410
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/4399-the-browse-view-s-and-the-studio-change → main 1 commit fork
4 files
changed +63
−40
src/web/pages/browse.ts +20 −22
| @@ -805,30 +805,28 @@ function timelineOf( | ||
| 805 | 805 | function mergeElsewhere(scope: RepoScope): Html { |
| 806 | 806 | const host = |
| 807 | 807 | scope.origin === undefined ? "the origin" : hostLabel(scope.origin.webUrl); |
| 808 | // One §5 row in a toned card: `.setting-list` lets the sentence wrap and | |
| 809 | // drops the button under it on a phone. | |
| 808 | 810 | return card( |
| 809 | html`${icon(scope.origin ? originIcon(scope.origin.kind) : "globe")} | |
| 810 | <div class="grow"> | |
| 811 | <div class="merge-headline">Merge on ${host}.</div> | |
| 812 | <p class="merge-sub"> | |
| 813 | You are reading this pull request live. Merging, reviewing and | |
| 814 | commenting all happen where it lives — mirror the repository and | |
| 815 | comments come back through this forge instead. | |
| 816 | </p> | |
| 817 | </div> | |
| 818 | ${when( | |
| 819 | scope.originUrl !== "", | |
| 820 | () => | |
| 811 | html`<ul class="list setting-list"> | |
| 812 | ${row({ | |
| 813 | lead: icon(scope.origin ? originIcon(scope.origin.kind) : "globe"), | |
| 814 | title: `Merge on ${host}.`, | |
| 815 | meta: html`You are reading this pull request live. Merging, reviewing | |
| 816 | and commenting all happen where it lives — mirror the repository and | |
| 817 | comments come back through this forge instead.`, | |
| 818 | trail: when(scope.originUrl !== "", () => | |
| 821 | 819 | // Outline: the header's Mirror button is this page's one accent. |
| 822 | html`<span class="merge-form" | |
| 823 | >${btn("Open on the origin", { | |
| 824 | href: scope.originUrl, | |
| 825 | variant: "outline", | |
| 826 | icon: "external", | |
| 827 | external: true, | |
| 828 | })}</span | |
| 829 | >`, | |
| 830 | )}`, | |
| 831 | { tone: "attn", extra: "merge-box" }, | |
| 820 | btn("Open on the origin", { | |
| 821 | href: scope.originUrl, | |
| 822 | variant: "outline", | |
| 823 | icon: "external", | |
| 824 | external: true, | |
| 825 | }), | |
| 826 | ), | |
| 827 | })} | |
| 828 | </ul>`, | |
| 829 | { tone: "attn", pad: false, extra: "mt" }, | |
| 832 | 830 | ); |
| 833 | 831 | } |
| 834 | 832 |
src/web/studio/change.ts +19 −18
| @@ -110,25 +110,26 @@ export function pullContext(ctx: Ctx): { repo: Repo; pull: Pull } | Response { | ||
| 110 | 110 | */ |
| 111 | 111 | export function wallCard(wall: Wall): Html { |
| 112 | 112 | return card( |
| 113 | html`${icon("shield")} | |
| 114 | <div class="grow"> | |
| 115 | <div class="merge-headline"> | |
| 116 | ${wall.unsound !== undefined | |
| 113 | html`<p class="muted small"> | |
| 114 | ${wall.unsound !== undefined | |
| 115 | ? "An app built on it does not boot. A person decides this one." | |
| 116 | : "A cell loosened is a decision undone, so the crew leaves this to a person: read the cells, then land or say what is off."} | |
| 117 | </p> | |
| 118 | <ul class="wall-cells"> | |
| 119 | ${(wall.unsound ?? wall.loosened.map(wallSentence)).map( | |
| 120 | (line) => html`<li>${line}</li>`, | |
| 121 | )} | |
| 122 | </ul>`, | |
| 123 | { | |
| 124 | tone: "attn", | |
| 125 | extra: "wall mt", | |
| 126 | head: html`${icon("shield")} | |
| 127 | <strong | |
| 128 | >${wall.unsound !== undefined | |
| 117 | 129 | ? "This pull's contract does not read." |
| 118 | : `This pull changes the policy: ${wall.loosened.length === 1 ? "one door opens" : `${wall.loosened.length} doors open`} wider.`} | |
| 119 | </div> | |
| 120 | <p class="merge-sub"> | |
| 121 | ${wall.unsound !== undefined | |
| 122 | ? "An app built on it does not boot. A person decides this one." | |
| 123 | : "A cell loosened is a decision undone, so the crew leaves this to a person: read the cells, then land or say what is off."} | |
| 124 | </p> | |
| 125 | <ul class="wall-cells"> | |
| 126 | ${(wall.unsound ?? wall.loosened.map(wallSentence)).map( | |
| 127 | (line) => html`<li>${line}</li>`, | |
| 128 | )} | |
| 129 | </ul> | |
| 130 | </div>`, | |
| 131 | { tone: "attn", extra: "merge-box wall" }, | |
| 130 | : `This pull changes the policy: ${wall.loosened.length === 1 ? "one door opens" : `${wall.loosened.length} doors open`} wider.`}</strong | |
| 131 | >`, | |
| 132 | }, | |
| 132 | 133 | ); |
| 133 | 134 | } |
| 134 | 135 |
test/policy.test.ts +7
| @@ -244,6 +244,13 @@ describe("a pull at the wall", () => { | ||
| 244 | 244 | "This pull changes the policy: one door opens wider.", |
| 245 | 245 | ); |
| 246 | 246 | expect(body).toContain("note.create as anon: was no, now yes"); |
| 247 | // The wall is §5's card, its headline in the head: no merge-* class. | |
| 248 | for (const name of ["merge-box", "merge-form", "merge-headline", "merge-sub"]) | |
| 249 | expect(body).not.toContain(name); | |
| 250 | expect(body).toMatch( | |
| 251 | /<div class="card-head">[\s\S]*?<strong\s*>This pull changes the policy: one door opens wider\.<\/strong/, | |
| 252 | ); | |
| 253 | expect(body).toContain("A cell loosened is a decision undone"); | |
| 247 | 254 | const quiet = await openPull(w.app, w.alice, w.repo, { |
| 248 | 255 | title: "Only the signed-in read", |
| 249 | 256 | baseBranch: "main", |
test/web-browse.test.ts +17
| @@ -475,6 +475,23 @@ describe("browsing an origin repository on the web", () => { | ||
| 475 | 475 | expect(pull).toContain("Merge on"); |
| 476 | 476 | }); |
| 477 | 477 | |
| 478 | test("a live pull's merge box is §5's card and row, with no merge-* class", async () => { | |
| 479 | const pull = await bodyOf(alice, `${browse}/pulls/9`); | |
| 480 | for (const name of ["merge-box", "merge-form", "merge-headline", "merge-sub"]) | |
| 481 | expect(pull).not.toContain(name); | |
| 482 | const box = | |
| 483 | (pull.match(/<div\s+class="card mt"[^>]*data-tone="attn"[\s\S]*?<\/ul>/)?.[0] ?? "") | |
| 484 | .replace(/\s+/g, " "); | |
| 485 | expect(box).toContain('<ul class="list setting-list">'); | |
| 486 | expect(box).toContain(`class="row-title" >Merge on ${new URL(fake.url).host}.</span`); | |
| 487 | expect(box).toContain( | |
| 488 | "You are reading this pull request live. Merging, reviewing and commenting all happen where it lives — mirror the repository and comments come back through this forge instead.", | |
| 489 | ); | |
| 490 | expect(box).toContain('class="row-trail"'); | |
| 491 | expect(box).toContain("Open on the origin"); | |
| 492 | expect(box).not.toContain("<form"); | |
| 493 | }); | |
| 494 | ||
| 478 | 495 | // ---- the where strip ---- |
| 479 | 496 | |
| 480 | 497 | test("a live browse shows origin filled, workbench and machine hollow", async () => { |
new above
offline