1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
|
<!DOCTYPE HTML>
<meta charset="UTF-8">
<title>CSS Toggles: CSSToggle and CSSToggleMap API</title>
<link rel="author" title="L. David Baron" href="https://dbaron.org/">
<link rel="author" title="Google" href="http://www.google.com/">
<link rel="help" href="https://tabatkins.github.io/css-toggle/#dom">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/toggle-helpers.js"></script>
<div id="test"></div>
<script>
let container = document.getElementById("test");
promise_test(async function() {
container.innerHTML = `
<style>
:toggle(mytoggle) { --t:4; }
:toggle(newtoggle) { --t:7; }
:toggle(newname) { --t:9; }
</style>
<div>
<div id="a" style="toggle-root: mytoggle 2 at 1 self"></div>
</div>
<div>
<div id="b"></div>
</div>
`;
let a = document.getElementById("a");
let b = document.getElementById("b");
assert_equals(a.toggles.size, 0, "a.toggles.size before creation");
assert_equals(b.toggles.size, 0, "b.toggles.size before creation");
await wait_for_toggle_creation(a);
assert_equals(a.toggles.size, 1, "a.toggles.size after creation");
assert_equals(b.toggles.size, 0, "b.toggles.size after creation");
let t = a.toggles.get("mytoggle");
for (let item of a.toggles) {
assert_equals(item[0], "mytoggle", "iteration of a.toggles");
assert_equals(item[1], t, "iteration of a.toggles");
}
for (let item of a.toggles.entries()) {
assert_equals(item[0], "mytoggle", "iteration of a.toggles.entries()");
assert_equals(item[1], t, "iteration of a.toggles.entries()");
}
for (let item of a.toggles.keys()) {
assert_equals(item, "mytoggle", "iteration of a.toggles.keys()");
}
for (let item of a.toggles.values()) {
assert_equals(item, t, "iteration of a.toggles.values()");
}
assert_equals(Object.getPrototypeOf(a.toggles), CSSToggleMap.prototype);
assert_equals(Object.getPrototypeOf(t), CSSToggle.prototype);
let computed = (element) => getComputedStyle(element).getPropertyValue("--t");
assert_equals(computed(a), "4", "computed style before move");
assert_equals(computed(b), "", "computed style before move");
b.toggles.set("newtoggle", t);
assert_equals(a.toggles.size, 0, "a.toggles.size after move");
assert_equals(b.toggles.size, 1, "b.toggles.size after move");
assert_equals(computed(a), "", "computed style after move");
assert_equals(computed(b), "7", "computed style after move");
assert_equals(a.toggles.get("mytoggle"), undefined, 'a.toggles.get("mytoggle") after move');
assert_equals(a.toggles.get("newtoggle"), undefined, 'a.toggles.get("newtoggle") after move');
assert_equals(b.toggles.get("mytoggle"), undefined, 'b.toggles.get("mytoggle") after move');
assert_equals(b.toggles.get("newtoggle"), t, 'b.toggles.get("newtoggle") after move');
b.toggles.set("newname", t);
assert_equals(a.toggles.size, 0, "a.toggles.size after rename");
assert_equals(b.toggles.size, 1, "b.toggles.size after rename");
assert_equals(computed(a), "", "computed style after rename");
assert_equals(computed(b), "9", "computed style after rename");
assert_equals(a.toggles.get("mytoggle"), undefined, 'a.toggles.get("mytoggle") after rename');
assert_equals(a.toggles.get("newtoggle"), undefined, 'a.toggles.get("newtoggle") after rename');
assert_equals(a.toggles.get("newname"), undefined, 'a.toggles.get("newname") after rename');
assert_equals(b.toggles.get("mytoggle"), undefined, 'b.toggles.get("mytoggle") after rename');
assert_equals(b.toggles.get("newtoggle"), undefined, 'b.toggles.get("newtoggle") after rename');
assert_equals(b.toggles.get("newname"), t, 'b.toggles.get("newname") after rename');
assert_throws_dom("SyntaxError", () => { a.toggles.set("none", t); },
"setting toggle_name to 'none'");
assert_equals(a.toggles.size, 0, "a.toggles.size after failed set");
assert_equals(b.toggles.size, 1, "b.toggles.size after failed set");
assert_equals(b.toggles.get("newname"), t, "b.toggles.get after failed set");
assert_throws_dom("SyntaxError", () => { let t = new CSSToggle({ "states": [] }); },
"toggle constructor with empty list of states");
assert_throws_dom("SyntaxError", () => { let t = new CSSToggle({ "states": ["one"] }); },
"toggle constructor with only one state");
assert_throws_dom("SyntaxError", () => { let t = new CSSToggle({ "states": ["one", "two", "one"] }); },
"toggle constructor with duplicate states");
let c = new CSSToggle({ "states": ["one", "two", "three"] });
assert_throws_dom("SyntaxError", () => { c.states = []; },
"toggle states setter with empty list of states");
assert_throws_dom("SyntaxError", () => { c.states = ["one"]; },
"toggle states setter with only one state");
assert_throws_dom("SyntaxError", () => { c.states = ["one", "two", "one"]; },
"toggle states setter with duplicate states");
// TODO(https://crbug.com/1250716): Should the toggle on a be
// re-created at some point? If so, when?
}, "CSSToggleMap basic API usage and moving toggle");
promise_test(async function() {
container.innerHTML = `
<style>
:toggle(mytoggle 0) { --t:0; }
:toggle(mytoggle 1) { --t:1; }
:toggle(mytoggle 2) { --t:2; }
:toggle(mytoggle 3) { --t:3; }
:toggle(mytoggle 4) { --t:4; }
</style>
<div id="a" style="toggle: mytoggle 2 at 1 self"></div>
<div id="b"></div>
`;
let a = document.getElementById("a");
let b = document.getElementById("b");
let computed = (elt) => getComputedStyle(elt).getPropertyValue("--t");
await wait_for_toggle_creation(a);
let t = a.toggles.get("mytoggle");
assert_equals(computed(a), "1", "initial state of toggle");
assert_equals(t.value, 1, "CSSToggle.value in initial state");
assert_equals(t.valueAsNumber, 1, "CSSToggle.valueAsNumber in initial state");
assert_equals(t.valueAsString, null, "CSSToggle.valueAsString in initial state");
assert_equals(t.states, 2, "CSSToggle.states in initial state");
assert_equals(t.group, false, "CSSToggle.group in initial state");
assert_equals(t.scope, "narrow", "CSSToggle.scope in initial state");
assert_equals(t.cycle, "cycle", "CSSToggle.cycle in initial state");
a.click();
assert_equals(computed(a), "2", "state of toggle after click");
assert_equals(t.value, 2, "CSSToggle.value after click");
assert_equals(t.valueAsNumber, 2, "CSSToggle.valueAsNumber after click");
assert_equals(t.valueAsString, null, "CSSToggle.valueAsString after click");
t.value = 1;
assert_equals(computed(a), "1", "state after setting value");
assert_equals(t.value, 1, "CSSToggle.value after setting value");
assert_equals(t.valueAsNumber, 1, "CSSToggle.valueAsNumber after setting value");
assert_equals(t.valueAsString, null, "CSSToggle.valueAsString after setting value");
t.states = ["zero", "one", "two", "three", "four"]
assert_equals(computed(a), "1", "state after setting states");
assert_equals(t.value, 1, "CSSToggle.value after setting states");
assert_equals(t.valueAsNumber, 1, "CSSToggle.valueAsNumber after setting states");
assert_equals(t.valueAsString, "one", "CSSToggle.valueAsString after setting states");
t.value = "three";
assert_equals(computed(a), "3", "state after changing value with new states");
assert_equals(t.value, "three", "CSSToggle.value after changing value with new states");
assert_equals(t.valueAsNumber, 3, "CSSToggle.valueAsNumber after changing value with new states");
assert_equals(t.valueAsString, "three", "CSSToggle.valueAsString after changing value with new states");
// dynamic changes to group are tested in a separate test below
assert_equals(computed(a), "3", "a state before changing scope");
assert_equals(computed(b), "", "b state before changing scope");
t.scope = "wide";
assert_equals(computed(a), "3", "a state after changing scope");
assert_equals(computed(b), "3", "b state after changing scope");
t.scope = "narrow";
assert_equals(computed(a), "3", "a state after changing scope again");
assert_equals(computed(b), "", "b state after changing scope again");
t.value = 4;
assert_equals(computed(a), "4", "state after changing value again")
a.click();
assert_equals(computed(a), "0", "state after cycling with initial cycle")
t.value = 4;
t.cycle = "cycle-on";
assert_equals(computed(a), "4", "state after changing cycle to cycle-on, with toggle-root still set")
a.click();
assert_equals(computed(a), "0", "state after cycling with cycle-on, with toggle-root still set")
// now remove the toggle-root property so that it no longer overrides the
// toggle when changing a toggle.
a.style.toggleRoot = "";
t.value = 4;
t.cycle = "cycle-on";
assert_equals(computed(a), "4", "state after changing cycle to cycle-on")
a.click();
assert_equals(computed(a), "1", "state after cycling with cycle-on")
t.value = 4;
t.cycle = "sticky";
assert_equals(computed(a), "4", "state after changing cycle to sticky")
a.click();
assert_equals(computed(a), "4", "state after cycling with sticky")
t.value = 4;
t.cycle = "cycle";
assert_equals(computed(a), "4", "state after changing cycle to cycle")
a.click();
assert_equals(computed(a), "0", "state after cycling with cycle")
}, "CSSToggle basic API usage on existing toggle");
promise_test(async function() {
container.innerHTML = `
<style>
:toggle(grouptoggle 0) { --g:0; }
:toggle(grouptoggle 1) { --g:1; }
:toggle(grouptoggle 2) { --g:2; }
</style>
<!-- use the document-wide implicit toggle group -->
<div id="a" style="toggle: grouptoggle 2 at 1 self cycle-on"></div>
<div id="b" style="toggle: grouptoggle 2 at 1 self cycle-on"></div>
`;
let a = document.getElementById("a");
let b = document.getElementById("b");
let computed = (elt) => getComputedStyle(elt).getPropertyValue("--g");
await wait_for_toggle_creation(a);
await wait_for_toggle_creation(b);
let ta = a.toggles.get("grouptoggle");
let tb = b.toggles.get("grouptoggle");
assert_equals(computed(a), "1", "initial state of a");
assert_equals(computed(b), "1", "initial state of b");
assert_equals(ta.group, false, "initial group of a");
assert_equals(tb.group, false, "initial group of b");
// Remove the toggle-root property so it doesn't override the group
// on the toggle.
a.style.toggleRoot = "";
b.style.toggleRoot = "";
ta.group = true;
a.click();
assert_equals(computed(a), "2", "state of a after first click");
assert_equals(computed(b), "1", "state of b after first click");
tb.group = true;
assert_equals(computed(a), "2", "state of a after both in group");
assert_equals(computed(b), "1", "state of b after both in group");
a.click();
assert_equals(computed(a), "1", "state of a after second click");
assert_equals(computed(b), "0", "state of b after second click");
b.click();
assert_equals(computed(a), "0", "state of a after third click");
assert_equals(computed(b), "1", "state of b after third click");
ta.group = false;
a.click();
assert_equals(computed(a), "1", "state of a after fourth click");
assert_equals(computed(b), "1", "state of b after fourth click");
ta.group = true;
tb.group = true;
a.click();
assert_equals(computed(a), "2", "state of a after fifth click");
assert_equals(computed(b), "0", "state of b after fifth click");
// Put the toggle-root property back so that it overrides the group
// on the toggle, but only when changing *that* toggle.
b.style.toggleRoot = "grouptoggle 2 at 1 self cycle-on";
b.click();
assert_equals(computed(a), "2", "state of a after sixth click");
assert_equals(computed(b), "1", "state of b after sixth click");
a.click();
assert_equals(computed(a), "1", "state of a after seventh click");
assert_equals(computed(b), "0", "state of b after seventh click");
}, "CSSToggle usage of group setter on existing toggle");
promise_test(async function() {
container.innerHTML = `
<style>
:toggle(d 0) { --d:0; }
:toggle(d 1) { --d:1; }
:toggle(d 2) { --d:2; }
:toggle(e 0) { --e:0; }
:toggle(e 1) { --e:1; }
:toggle(e 2) { --e:2; }
:toggle(f 0) { --f:0; }
:toggle(f 1) { --f:1; }
:toggle(f 2) { --f:2; }
</style>
<div id="a"></div>
<!-- TODO(https://crbug.com/1250716): This toggle-trigger should probably
also work if I set it dynamically right before the first click. -->
<div id="b" style="toggle-trigger: d next, e prev"></div>
`;
let a = document.getElementById("a");
let b = document.getElementById("b");
assert_equals(a.toggles.size, 0, "a.toggles.size before creation");
assert_equals(b.toggles.size, 0, "b.toggles.size before creation");
let computed = (elt, prop) => getComputedStyle(elt).getPropertyValue(`--${prop}`);
assert_equals(computed(a, "d"), "", "initial computed(a, d)");
assert_equals(computed(a, "e"), "", "initial computed(a, e)");
assert_equals(computed(b, "d"), "", "initial computed(b, d)");
assert_equals(computed(b, "e"), "", "initial computed(b, e)");
let t1 = new CSSToggle({states: 2, cycle: "cycle-on"});
let t2 = new CSSToggle({states: 2, value: 1, scope: "narrow"});
a.toggles.set("d", t1);
assert_equals(a.toggles.size, 1, "step 2 a.toggles.size");
assert_equals(b.toggles.size, 0, "step 2 b.toggles.size");
assert_equals(computed(a, "d"), "0", "step 2 computed(a, d)");
assert_equals(computed(b, "d"), "0", "step 2 computed(b, d)");
assert_equals(computed(a, "e"), "", "step 2 computed(a, e)");
assert_equals(computed(b, "e"), "", "step 2 computed(b, e)");
b.toggles.set("e", t2);
assert_equals(a.toggles.size, 1, "step 3 a.toggles.size");
assert_equals(b.toggles.size, 1, "step 3 b.toggles.size");
assert_equals(computed(a, "d"), "0", "step 3 computed(a, d)");
assert_equals(computed(b, "d"), "0", "step 3 computed(b, d)");
assert_equals(computed(a, "e"), "", "step 3 computed(a, e)");
assert_equals(computed(b, "e"), "1", "step 3 computed(b, e)");
b.click();
assert_equals(computed(a, "d"), "1", "step 4 computed(a, d)");
assert_equals(computed(b, "d"), "1", "step 4 computed(b, d)");
assert_equals(computed(a, "e"), "", "step 4 computed(a, e)");
assert_equals(computed(b, "e"), "0", "step 4 computed(b, e)");
a.toggles.set("f", t2);
assert_equals(a.toggles.size, 2, "step 5 a.toggles.size");
assert_equals(b.toggles.size, 0, "step 5 b.toggles.size");
assert_equals(computed(a, "d"), "1", "step 5 computed(a, d)");
assert_equals(computed(b, "d"), "1", "step 5 computed(b, d)");
assert_equals(computed(a, "e"), "", "step 5 computed(a, e)");
assert_equals(computed(b, "e"), "", "step 5 computed(b, e)");
assert_equals(computed(a, "f"), "0", "step 5 computed(a, f)");
assert_equals(computed(b, "f"), "", "step 5 computed(b, f)");
b.click();
assert_equals(computed(a, "d"), "2", "step 6 computed(a, d)");
assert_equals(computed(b, "d"), "2", "step 6 computed(b, d)");
assert_equals(computed(a, "e"), "", "step 6 computed(a, e)");
assert_equals(computed(b, "e"), "", "step 6 computed(b, e)");
assert_equals(computed(a, "f"), "0", "step 6 computed(a, f)");
assert_equals(computed(b, "f"), "", "step 6 computed(b, f)");
}, "dynamic creation of CSSToggle and their use");
</script>
|