blob: f47ec55ed01e13bdd5c4cf8c07271a6c0e83b943 [file] [log] [blame]
<!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>