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