| <!DOCTYPE html> |
| <meta charset="utf-8"> |
| <title>Popover combined with dialog/fullscreen behavior</title> |
| <link rel=author href="mailto:[email protected]"> |
| <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="/common/top-layer.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'); |
| ex.showPopover(); // Should not throw |
| ex.removeAttribute('open'); |
| ex.hidePopover(); |
| 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'); |
| ex.show(); // Should not throw |
| ex.close(); |
| ex.showPopover(); |
| } |
| 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')); |
| ex.showPopover(); // Should not throw |
| ex.close(); |
| assert_false(ex.hasAttribute('open')); |
| ex.hidePopover(); |
| 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')); |
| ex.hidePopover(); |
| } 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(ex); |
| 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> |