<!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>
