summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-combinations.html
blob: 8db327d7d1f00952f0b84807410c512f82de0ede (plain)
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
<!DOCTYPE html>
<meta charset="utf-8">
<title>Popover combined with dialog/fullscreen behavior</title>
<link rel=author href="mailto:masonf@chromium.org">
<link rel=help href="https://open-ui.org/components/popover.research.explainer">
<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/popover-utils.js"></script>

<button id=visible>Visible button</button>
<div id=examples>
  <dialog popover>Popover Dialog</dialog>
  <dialog popover open style="top:50px;">Open Non-modal Popover Dialog</dialog>
  <div    popover class=fullscreen>Fullscreen Popover</div>
  <dialog popover class=fullscreen>Fullscreen Popover Dialog</dialog>
  <dialog popover open class=fullscreen style="top:200px;">Fullscreen Open Non-modal Popover Dialog</dialog>
</div>

<style>
  [popover] {
    inset:auto;
    top:0;
    left:0;
  }
  [popover].fullscreen.visible {
    display:block;
  }
</style>

<script>
const isDialog = (ex) => ex instanceof HTMLDialogElement;
const isFullscreen = (ex) => ex.classList.contains('fullscreen');
function ensureIsOpenPopover(ex,message) {
  // Because :popover-open will eventually support <dialog>, this does extra work to
  // verify we're dealing with an :popover-open Popover. Note that this will also throw
  // if this is an element with the `popover` attribute that has been made
  // visible via an explicit `display:block` style rule.
  message = message || 'Error';
  assert_true(ex.matches(':popover-open'),`${message}: Popover doesn\'t match :popover-open`);
  ex.hidePopover(); // Shouldn't throw if this is a showing popover
  ex.showPopover(); // Show it again to avoid state change
  assert_true(ex.matches(':popover-open'),`${message}: Sanity`);
}
window.onload = () => requestAnimationFrame(() => requestAnimationFrame(() => {
  const examples = Array.from(document.querySelectorAll('#examples>*'));
  examples.forEach(ex => {
    promise_test(async (t) => {
      t.add_cleanup(() => ex.remove());
      // Test initial conditions
      if (ex.hasAttribute('open')) {
        assert_true(isDialog(ex));
        assert_true(isElementVisible(ex),'Open dialog should be visible by default');
        assert_throws_dom("InvalidStateError",() => ex.showPopover(),'Calling showPopover on an already-showing element should throw InvalidStateError');
        ex.removeAttribute('open');
        assert_false(isElementVisible(ex),'Removing the open attribute should hide the dialog');
      } else {
        ex.showPopover(); // Should not throw
        ensureIsOpenPopover(ex,'showPopover should work');
        ex.hidePopover(); // Should not throw
        assert_false(ex.matches(':popover-open'),'hidePopover should work');
      }
      assert_false(isElementVisible(ex));

      // Start with popover, try the other API
      ex.showPopover();
      ensureIsOpenPopover(ex);
      let tested_something=false;
      if (isDialog(ex)) {
        tested_something=true;
        assert_throws_dom("InvalidStateError",() => ex.showModal(),'Calling showModal() on an already-showing Popover should throw InvalidStateError');
        assert_throws_dom("InvalidStateError",() => ex.show(),'Calling show() on an already-showing Popover should throw InvalidStateError');
      }
      if (isFullscreen(ex)) {
        tested_something=true;
        let requestSucceeded = false;
        await blessTopLayer(ex);
        await ex.requestFullscreen()
          .then(() => {requestSucceeded = true;}) // We should not hit this.
          .catch((exception) => {
            // This exception is expected.
            assert_equals(exception.name,'TypeError',`Invalid exception from requestFullscreen() (${exception.message})`);
          });
        assert_false(requestSucceeded,'requestFullscreen() should not succeed when the element is an already-showing Popover');
      }
      assert_true(tested_something);
      ensureIsOpenPopover(ex);
      ex.hidePopover();

      // Start with the other API, then try popover
      if (isDialog(ex)) {
        ex.show();
        assert_true(ex.hasAttribute('open'));
        assert_throws_dom("InvalidStateError",() => ex.showPopover(),'Calling showPopover() on an already-showing non-modal dialog should throw InvalidStateError');
        ex.close();
        assert_false(ex.hasAttribute('open'));
        ex.showModal();
        assert_true(ex.hasAttribute('open'));
        assert_throws_dom("InvalidStateError",() => ex.showPopover(),'Calling showPopover() on an already-showing modal dialog should throw InvalidStateError');
        ex.close();
        assert_false(ex.hasAttribute('open'));
      } else if (isFullscreen(ex)) {
        let requestSucceeded = false;
        await blessTopLayer(visible);
        await ex.requestFullscreen()
          .then(() => {
            assert_throws_dom("InvalidStateError",() => ex.showPopover(),'Calling showPopover() on an already-fullscreen element should throw InvalidStateError');
          });
        await document.exitFullscreen()
        .then(() => assert_true(true));
      }

      // Finally, try invoking these combined popovers via a declarative invoker
      const button = document.createElement('button');
      t.add_cleanup(() => button.remove());
      document.body.appendChild(button);
      button.popoverTargetElement = ex;
      button.popoverTargetAction = "toggle";
      assert_false(ex.matches(':popover-open'));
      await clickOn(button);
      ensureIsOpenPopover(ex,'Invoking element should be able to invoke all popovers');
      ex.hidePopover();
      if (isDialog(ex)) {
        ex.showModal();
        assert_true(ex.hasAttribute('open'));
      } else if (isFullscreen(ex)) {
        // Popover fullscreen isn't visible by default, so explicitly add
        // display:block, so that calls to "clickOn" can succeed.
        ex.classList.add('visible');
        await blessTopLayer(visible);
        await ex.requestFullscreen();
      } else {
        assert_unreached('Not a dialog or fullscreen');
      }
      ex.appendChild(button); // Add button to the element, so it's visible to click
      await clickOn(button);
      assert_false(ex.matches(':popover-open'),'The invoker click should have failed on the already-open dialog/fullscreen');
      if (isDialog(ex)) {
        ex.close();
      } else {
        await document.exitFullscreen()
      }
    }, `Popover combination: ${ex.textContent}`);
  });
}));
</script>