oficina / slides Public

Clone
git clone https://amadan.net/oficina/slides

Plain git — no account needed to clone.

Download

Download this file

1package layout_test
2
3import (
4 "flag"
5
6 "amadan.net/oficina/slides/internal/slides/deck"
7)
8
9// update rewrites the golden paint lists instead of comparing against
10// them:
11//
12// go test ./internal/slides/layout -update
13//
14// Read the diff before committing a regenerated golden. A paint list
15// is coordinates, and a coordinate that moved because a font metric
16// was read differently looks exactly like one that moved because a
17// stylesheet changed — the difference is in the diff and nowhere else.
18var update = flag.Bool("update", false, "rewrite the golden paint lists")
19
20// txt is one paragraph of unmarked text — the shape most fixture blocks
21// want.
22func txt(s string) deck.Text { return deck.Text{{{T: s}}} }
23
24// paras is several unmarked paragraphs: list items, code lines, a quote
25// that runs on.
26func paras(ss ...string) deck.Text {
27 t := make(deck.Text, 0, len(ss))
28 for _, s := range ss {
29 t = append(t, []deck.Run{{T: s}})
30 }
31 return t
32}
33
34// box is a canvas block's frame, spelled positionally so a fixture
35// slide reads as a picture rather than as four labelled fields.
36func box(x, y, w, h float64) *deck.Box { return &deck.Box{X: x, Y: y, W: w, H: h} }
37
38func intp(n int) *int { return &n }
39
40// fixture is the deck the golden tests lay out: one slide per flow
41// template, one canvas slide, one canvas slide set to reflow, and every
42// block type in §2.4 somewhere among them.
43//
44// Hand-built rather than driven through the core: the goldens are about
45// what the renderer does with a view, and a literal deck is that view
46// with the wasm boundary taken out of the loop. internal/slides/sample.go
47// is the same deck said in ops, and this fixture follows its shape.
48func fixture() *deck.Deck {
49 return &deck.Deck{
50 Meta: deck.Meta{Title: "Renderer fixture", Theme: "paper", Aspect: "16:9"},
51 Assets: map[string]deck.Asset{
52 // Refs are what the object store minted: a hex SHA-256,
53 // abbreviated here so a golden stays readable. What the
54 // <img> carries is the REF, never the id — the id is the
55 // deck's own name for the asset and means nothing to the
56 // route that serves the bytes.
57 "asset0001aaa": {Ref: "aa01bb02cc03dd04", Name: "chart.png", Width: 1600, Height: 900, Alt: "A bar chart with no bars"},
58 "asset0002bbb": {Ref: "ee05ff06aa07bb08", Name: "texture.png", Width: 64, Height: 64},
59 },
60 Slides: []deck.Slide{
61 // 0 — title
62 {
63 ID: "slide001aaaa", Layout: "flow", Template: "title",
64 Background: deck.Background{Kind: "theme"},
65 Notes: txt("The title template: one heading and one line under it."),
66 Blocks: []deck.Block{
67 {ID: "b001aaaaaaaa", Type: "heading", Region: "heading",
68 Props: map[string]any{"level": float64(1)}, Text: txt("Renderer fixture")},
69 {ID: "b002aaaaaaaa", Type: "text", Region: "body",
70 Props: map[string]any{"size": "l"}, Text: txt("One section per slide, one element per block.")},
71 },
72 },
73 // 1 — title-body, with every inline mark and both list styles
74 {
75 ID: "slide002aaaa", Layout: "flow", Template: "title-body",
76 Background: deck.Background{Kind: "theme"},
77 Blocks: []deck.Block{
78 {ID: "b011aaaaaaaa", Type: "heading", Region: "heading",
79 Props: map[string]any{"level": float64(1)}, Text: txt("Text and marks")},
80 {ID: "b012aaaaaaaa", Type: "text", Region: "body",
81 Props: map[string]any{"size": "m", "align": "start"},
82 Text: deck.Text{{
83 {T: "Marks: "},
84 {T: "bold", B: true},
85 {T: ", "},
86 {T: "italic", I: true},
87 {T: ", "},
88 {T: "code", C: true},
89 {T: ", "},
90 {T: "highlight", H: true},
91 {T: ", "},
92 {T: "all four", B: true, I: true, C: true, H: true},
93 {T: " and a "},
94 {T: "link", Href: "https://amadan.net/oficina/slides"},
95 {T: " <not markup>."},
96 }}},
97 {ID: "b013aaaaaaaa", Type: "list", Region: "body",
98 Props: map[string]any{"style": "bullet", "size": "m"},
99 Text: paras("One item per paragraph", "Bullets or numbers")},
100 {ID: "b014aaaaaaaa", Type: "list", Region: "body",
101 Props: map[string]any{"style": "number", "size": "s", "indent": float64(1)},
102 Text: paras("First", "Second")},
103 },
104 },
105 // 2 — two-column, and a columns block whose children must not
106 // also render at the top level
107 {
108 ID: "slide003aaaa", Layout: "flow", Template: "two-column",
109 Background: deck.Background{Kind: "theme"},
110 Blocks: []deck.Block{
111 {ID: "b021aaaaaaaa", Type: "heading", Region: "heading",
112 Props: map[string]any{"level": float64(2)}, Text: txt("Regions, not coordinates")},
113 {ID: "b022aaaaaaaa", Type: "text", Region: "body",
114 Text: txt("A flow slide names regions and blocks stack inside the one they are in.")},
115 {ID: "b023aaaaaaaa", Type: "columns", Region: "body",
116 Props: map[string]any{"count": float64(2), "children": []any{"b025aaaaaaaa", "b024aaaaaaaa"}}},
117 {ID: "b024aaaaaaaa", Type: "text", Region: "body", Text: txt("Second column, first in the array.")},
118 {ID: "b025aaaaaaaa", Type: "text", Region: "body", Text: txt("First column, second in the array.")},
119 {ID: "b026aaaaaaaa", Type: "text", Region: "aside",
120 Text: txt("The aside is the second column.")},
121 },
122 },
123 // 3 — section, on a slide background the deck set by colour
124 {
125 ID: "slide004aaaa", Layout: "flow", Template: "section",
126 Background: deck.Background{Kind: "color", Value: "#2b1030"},
127 Blocks: []deck.Block{
128 {ID: "b031aaaaaaaa", Type: "heading", Region: "heading",
129 Props: map[string]any{"level": float64(1)}, Text: txt("Part two")},
130 },
131 },
132 // 4 — image-full: the alt that comes from the asset
133 {
134 ID: "slide005aaaa", Layout: "flow", Template: "image-full",
135 Background: deck.Background{Kind: "image", Value: "asset0002bbb"},
136 Blocks: []deck.Block{
137 {ID: "b041aaaaaaaa", Type: "image", Region: "media",
138 Props: map[string]any{"asset": "asset0001aaa", "fit": "cover", "radius": float64(8)}},
139 {ID: "b042aaaaaaaa", Type: "heading", Region: "heading",
140 Props: map[string]any{"level": float64(2)}, Text: txt("A picture would go here")},
141 },
142 },
143 // 5 — quote, with and without an attribution
144 {
145 ID: "slide006aaaa", Layout: "flow", Template: "quote",
146 Background: deck.Background{Kind: "theme"},
147 Blocks: []deck.Block{
148 {ID: "b051aaaaaaaa", Type: "quote", Region: "body",
149 Props: map[string]any{"cite": "The design spec"},
150 Text: txt("A deck is a document, not a drawing.")},
151 {ID: "b052aaaaaaaa", Type: "quote", Region: "body",
152 Text: txt("Said by nobody in particular.")},
153 },
154 },
155 // 6 — blank, carrying the block types the other slides do not
156 {
157 ID: "slide007aaaa", Layout: "flow", Template: "blank",
158 Background: deck.Background{Kind: "theme"},
159 Blocks: []deck.Block{
160 {ID: "b061aaaaaaaa", Type: "code", Region: "body",
161 Props: map[string]any{"language": "go", "size": "m"},
162 Text: paras("func main() {", "\tfmt.Println(\"hi <there>\")", "}")},
163 {ID: "b062aaaaaaaa", Type: "table", Region: "body",
164 Props: map[string]any{
165 "rows": float64(2), "cols": float64(2), "header": true,
166 "cells": []any{
167 []any{[]any{map[string]any{"t": "Region"}}},
168 []any{[]any{map[string]any{"t": "Holds"}}},
169 []any{[]any{map[string]any{"t": "heading"}}},
170 []any{[]any{map[string]any{"t": "the title"}}},
171 },
172 }},
173 {ID: "b063aaaaaaaa", Type: "divider", Region: "body"},
174 {ID: "b064aaaaaaaa", Type: "spacer", Region: "body",
175 Props: map[string]any{"size": "l"}},
176 {ID: "b065aaaaaaaa", Type: "shape", Region: "body",
177 Props: map[string]any{"kind": "rect", "fill": "#e8e2d6", "stroke": "#8a7f6a", "strokeWidth": float64(2), "radius": float64(12)},
178 Text: txt("A labelled rectangle")},
179 {ID: "b066aaaaaaaa", Type: "shape", Region: "body",
180 Props: map[string]any{"kind": "ellipse", "fill": "#cfd9e8", "stroke": "#5a6f8a", "strokeWidth": float64(2)}},
181 {ID: "b067aaaaaaaa", Type: "shape", Region: "body",
182 Props: map[string]any{"kind": "line", "stroke": "#5a6f8a", "strokeWidth": float64(3)}},
183 {ID: "b068aaaaaaaa", Type: "shape", Region: "body",
184 Props: map[string]any{"kind": "arrow", "stroke": "currentColor"}},
185 {ID: "b069aaaaaaaa", Type: "image", Region: "body",
186 Props: map[string]any{"asset": "asset0002bbb", "fit": "contain", "decorative": true, "alt": ""}},
187 {ID: "b070aaaaaaaa", Type: "image", Region: "body",
188 Props: map[string]any{"asset": "asset0002bbb", "fit": "contain"}},
189 },
190 },
191 // 7 — canvas, with a phone override on one block
192 {
193 ID: "slide008aaaa", Layout: "canvas", Template: "blank", Phone: "inherit",
194 Background: deck.Background{Kind: "theme"},
195 Blocks: []deck.Block{
196 {ID: "b081aaaaaaaa", Type: "heading", Box: box(0.06, 0.08, 0.88, 0.16), Z: 3,
197 Props: map[string]any{"level": float64(1)}, Text: txt("Canvas: put it where you want it")},
198 {ID: "b082aaaaaaaa", Type: "shape", Box: box(0.08, 0.34, 0.36, 0.44), Z: 1,
199 Props: map[string]any{"kind": "rect", "fill": "#e8e2d6", "stroke": "#8a7f6a", "strokeWidth": float64(2), "radius": float64(12)}},
200 {ID: "b083aaaaaaaa", Type: "text", Box: box(0.58, 0.36, 0.36, 0.4), Z: 4,
201 Phone: deck.PhoneOverride{Order: intp(0), Size: "s", Box: box(0.05, 0.5, 0.9, 0.45)},
202 Text: txt("Every box is a fraction of the 16:9 frame.")},
203 },
204 },
205 // 8 — canvas with phone: reflow, whose z order is not its
206 // array order, and one block hidden on phones
207 {
208 ID: "slide009aaaa", Layout: "canvas", Template: "blank", Phone: "reflow", Transition: "fade",
209 Background: deck.Background{Kind: "theme"},
210 Notes: txt("The phone layer only ever adds to the desktop layout."),
211 Blocks: []deck.Block{
212 {ID: "b091aaaaaaaa", Type: "heading", Box: box(0.06, 0.1, 0.88, 0.16), Z: 1,
213 Props: map[string]any{"level": float64(1)}, Text: txt("On a phone this one reflows")},
214 {ID: "b092aaaaaaaa", Type: "shape", Box: box(0.64, 0.32, 0.3, 0.44), Z: 2,
215 Phone: deck.PhoneOverride{Hidden: true},
216 Props: map[string]any{"kind": "rect", "fill": "#e0d7e8", "stroke": "#75608a", "strokeWidth": float64(2), "radius": float64(12)},
217 Text: txt("Desktop only")},
218 {ID: "b093aaaaaaaa", Type: "text", Box: box(0.06, 0.32, 0.52, 0.44), Z: 2,
219 Text: txt("Setting phone to reflow lays the blocks out as one column.")},
220 {ID: "b094aaaaaaaa", Type: "text", Box: box(0.06, 0.8, 0.88, 0.12), Z: 3,
221 Text: txt("A footnote, last by z.")},
222 },
223 },
224 },
225 }
226}
227