Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement toggle-visibility CSS property. #36264

Merged
merged 1 commit into from
Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions css/css-toggle/toggle-visibility-z-ordering-001.tentative.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!DOCTYPE HTML>
<meta charset="UTF-8">
<title>CSS Toggles: toggle-visibility</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/#toggle-visibility-property">
<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
<style>

body {
toggle-root: inactive-toggle 1 at 0;
}

#container {
toggle-visibility: toggle inactive-toggle;
background: green;
width: 100px;
height: 100px;
}

#child, #sibling {
height: 100px;
width: 100px;
background: red;
}

#sibling {
margin-top: -100px;
}

</style>

<body>

<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>

<div id="container">
<div id="child"></div>
</div>
<div id="sibling"></div>
44 changes: 44 additions & 0 deletions css/css-toggle/toggle-visibility-z-ordering-002.tentative.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!DOCTYPE HTML>
<meta charset="UTF-8">
<title>CSS Toggles: toggle-visibility</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/#toggle-visibility-property">
<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
<style>

body {
toggle-root: active-toggle 1 at 1;
}

#container {
toggle-visibility: toggle active-toggle;
background: red;
width: 100px;
height: 100px;
}

#child, #sibling {
height: 100px;
width: 100px;
}

#child {
background: green;
}

#sibling {
background: red;
margin-top: -100px;
}

</style>

<body>

<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>

<div id="container">
<div id="child"></div>
</div>
<div id="sibling"></div>
267 changes: 267 additions & 0 deletions css/css-toggle/toggle-visibility.tentative.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
<!DOCTYPE HTML>
<meta charset="UTF-8">
<title>CSS Toggles: toggle-visibility</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/#toggle-visibility-property">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/toggle-helpers.js"></script>
<style id="style">

</style>

<body>

<div id="container"></div>
<script>

let style = document.getElementById("style");
let container = document.getElementById("container");

promise_test(async function() {
style.innerText = `
#container { toggle-group: t self; }
#t, #t2 { toggle-root: t 1 at 0 group; }
#trigger, #t2 { toggle-trigger: t; }
#vis { toggle-visibility: toggle t; }
#child { height: 100px; }
`;
container.innerHTML = `
<div id="t"></div>
<div id="vis">
<div id="child"></div>
</div>
<div id="trigger"></div>
<div id="t2"></div>
`;

let t = document.getElementById("t");
let trigger = document.getElementById("trigger");
let t2 = document.getElementById("t2");
let vis = document.getElementById("vis");

await wait_for_toggle_creation(t);

const kVisibleHeight = 100;
const kHiddenHeight = 0;

assert_equals(vis.clientHeight, kHiddenHeight, "before first click");
trigger.click();
assert_equals(vis.clientHeight, kVisibleHeight, "after first click");
trigger.click();
assert_equals(vis.clientHeight, kHiddenHeight, "after second click");
t.toggles.get("t").value = 1;
assert_equals(vis.clientHeight, kVisibleHeight, "after toggle value setter");
t2.click();
assert_equals(vis.clientHeight, kHiddenHeight, "after click on other element in group");
}, "showing and hiding in response to toggle changes");

const action_tests = [
{
name: "focus",
markup: `<input type="checkbox" id="check">`,
action: () => {
document.getElementById("check").focus();
},
},
{
name: "scrollIntoView",
markup: `<input type="checkbox" id="check">`,
action: () => {
document.getElementById("check").scrollIntoView();
},
},
];

for (let test of action_tests) {
promise_test(async function() {
style.innerText = `
#container { toggle-group: tab self; }
#tab { toggle: tab 1 at 0 group; }
#panel { toggle-visibility: toggle tab; }
`;
container.innerHTML = `
<div id="tab">Tab</div>
<div id="panel">
${test.markup}
</div>
`;

let tab = document.getElementById("tab");
let panel = document.getElementById("panel");

await wait_for_toggle_creation(tab);

let tab_toggle = tab.toggles.get("tab");

assert_equals(tab_toggle.value, 0, "toggle value before focus");
assert_equals(panel.clientHeight, 0, "panel height before focus");

test.action();

assert_equals(tab_toggle.value, 1, "toggle value after focus");
assert_not_equals(panel.clientHeight, 0, "panel height after focus");
}, `${test.name} inside hidden element changes a toggle to show it`);
}

const scope_tests = [
`
<div class="root-inactive">
<div class="vis expect-hidden"></div>
</div>
<div class="vis expect-hidden"></div>
`,
`
<div class="root-inactive-self">
<div class="vis expect-hidden"></div>
</div>
<div class="vis expect-shown"></div>
`,
`
<div class="root-active">
<div class="vis expect-shown"></div>
</div>
<div class="vis expect-shown"></div>
`,
`
<div class="root-active-self">
<div class="vis expect-shown"></div>
</div>
<div class="vis expect-shown"></div>
`,
];

for (let test of scope_tests) {
promise_test(async function() {
container.innerHTML = test;
style.innerText = `
.root-inactive { toggle-root: t 1 at 0; }
.root-active { toggle-root: t 1 at 1; }
.root-inactive-self { toggle-root: t 1 at 0 self; }
.root-active-self { toggle-root: t 1 at 1 self; }

.vis {
toggle-visibility: toggle t;
}

.vis::before {
content: "";
display: block;
height: 100px;
width: 100px;
}
`;

await wait_for_toggle_creation(container.querySelector('[class^="root-"]'));

for (let e of container.querySelectorAll('.expect-hidden')) {
assert_equals(e.clientHeight, 0);
}
for (let e of container.querySelectorAll('.expect-shown')) {
assert_equals(e.clientHeight, 100);
}
}, `scope test: ${test}`);
}

async function wait_for_content_visibility_auto() {
return new Promise(resolve => {
requestAnimationFrame(() => {
requestAnimationFrame(resolve);
});
});
}

promise_test(async function() {
style.innerText = `
#t { toggle-root: t 1 at 0; }
#vis {
toggle-visibility: toggle t;
content-visibility: auto;
min-height: 50px;
}
#child { height: 100px; }
#spacer { height: 300vh; }
`;
// TODO (dbaron): Why doesn't padding-top on #container have the same effect
// as this spacer?
container.innerHTML = `
<div id="spacer"></div>
<div id="t"></div>
<div id="vis">
<div id="child"></div>
</div>
`;

let t = document.getElementById("t");
let vis = document.getElementById("vis");
let spacer = document.getElementById("spacer");

await wait_for_toggle_creation(t);

let toggle = t.toggles.get("t");

const kVisibleHeight = 100;
const kHiddenHeight = 50;

await wait_for_content_visibility_auto();
assert_equals(vis.clientHeight, kHiddenHeight, "with toggle inactive and offscreen");
toggle.value = 1;
await wait_for_content_visibility_auto();
assert_equals(vis.clientHeight, kHiddenHeight, "with toggle active and offscreen");
spacer.remove();
await wait_for_content_visibility_auto();
assert_equals(vis.clientHeight, kVisibleHeight, "with toggle active and onscreen");
toggle.value = 0;
await wait_for_content_visibility_auto();
assert_equals(vis.clientHeight, kHiddenHeight, "with toggle inactive and onscreen");
toggle.value = 1;
await wait_for_content_visibility_auto();
assert_equals(vis.clientHeight, kVisibleHeight, "with toggle active and onscreen (2)");
toggle.value = 0;
t.before(spacer);
await wait_for_content_visibility_auto();
assert_equals(vis.clientHeight, kHiddenHeight, "with toggle inactive and offscreen (2)");
spacer.remove();
await wait_for_content_visibility_auto();
assert_equals(vis.clientHeight, kHiddenHeight, "with toggle inactive and onscreen (2)");
toggle.value = 1;
await wait_for_content_visibility_auto();
assert_equals(vis.clientHeight, kVisibleHeight, "with toggle active and onscreen (3)");
t.before(spacer);
await wait_for_content_visibility_auto();
assert_equals(vis.clientHeight, kHiddenHeight, "with toggle active and offscreen (2)");
}, "interaction of toggle-visibility and content-visibility: auto");

promise_test(async function() {
style.innerText = `
#t { toggle-root: t 1 at 0; }
#vis {
toggle-visibility: toggle t;
content-visibility: hidden;
}
#child { height: 100px; }
`;
container.innerHTML = `
<div id="t"></div>
<div id="vis">
<div id="child"></div>
</div>
`;

let t = document.getElementById("t");
let vis = document.getElementById("vis");

await wait_for_toggle_creation(t);

let toggle = t.toggles.get("t");

const kVisibleHeight = 100;
const kHiddenHeight = 0;

assert_equals(vis.clientHeight, kHiddenHeight, "with toggle inactive and offscreen");
toggle.value = 1;
assert_equals(vis.clientHeight, kHiddenHeight, "with toggle active and offscreen");
}, "interaction of toggle-visibility and content-visibility: hidden");

</script>