| <!DOCTYPE html> |
| <meta charset="utf-8"> |
| <link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org"> |
| <link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| |
| <script> |
| function ensureShadowDom(host) { |
| host.querySelectorAll('my-element').forEach(host => { |
| if (host.shadowRoot) |
| return; // Declarative Shadow DOM is enabled |
| const template = host.firstElementChild; |
| assert_true(template instanceof HTMLTemplateElement); |
| const shadow = host.attachShadow({mode: 'open'}); |
| shadow.appendChild(template.content); |
| template.remove(); |
| }) |
| } |
| function findPopups(root) { |
| let popups = []; |
| if (!root) |
| return popups; |
| if (root instanceof HTMLPopupElement) |
| popups.push(root); |
| popups.push(...findPopups(root.shadowRoot)); |
| root.childNodes.forEach(child => { |
| popups.push(...findPopups(child)); |
| }) |
| return popups; |
| } |
| function getPopupReferences(testId) { |
| const testRoot = document.querySelector(`#${testId}`); |
| assert_true(!!testRoot); |
| ensureShadowDom(testRoot); |
| return findPopups(testRoot); |
| } |
| function popupVisible(popup) { |
| return !!(popup.offsetWidth || popup.offsetHeight || popup.getClientRects().length); |
| } |
| function showPopup(testId,popupNum) { |
| getPopupReferences(testId)[popupNum].show(); |
| } |
| </script> |
| |
| <div id=test1> |
| <button onclick='showPopup("test1",0)'>Test1 Popup</button> |
| <my-element> |
| <template shadowroot=open> |
| <popup> |
| <p>This should show, even though it is inside shadow DOM.</p> |
| </popup> |
| </template> |
| </my-element> |
| </div> |
| |
| <script> |
| test(function() { |
| const popup = getPopupReferences('test1')[0]; |
| popup.show(); |
| assert_true(popup.open); |
| assert_true(popupVisible(popup)); |
| }, "Popups located inside shadow DOM can still be shown"); |
| </script> |
| |
| |
| <div id=test2> |
| <button id=t2b1 onclick='showPopup("test2",0)'>Test 2 Popup 1</button> |
| <popup anchor=t2b1 style="top: 400px;"> |
| <p>Popup 1</p> |
| <button id=t2b2 onclick='showPopup("test2",1)'>Test 2 Popup 2</button> |
| <my-element> |
| <template shadowroot=open> |
| <popup anchor=t2b2 style="top: 400px;"> |
| <p>This popup can never be visible:</p> |
| <p>Hiding this popup will hide *all* open popups,</p> |
| <p>because t2b2 doesn't exist in this context.</p> |
| <p>And since popup 1 is not shown, it is display:none,</p> |
| <p>which means no child content is shown at all.</p> |
| </popup> |
| </template> |
| </my-element> |
| </popup> |
| </div> |
| |
| <script> |
| test(function() { |
| const [popup1,popup2] = getPopupReferences('test2'); |
| popup1.show(); |
| assert_true(popup1.open); |
| assert_true(popupVisible(popup1)); |
| popup2.show(); |
| assert_false(popup1.open); // P1 was closed by P2 |
| assert_true(popup2.open); // P2 thinks it is open |
| assert_false(popupVisible(popup1)); // But neither is visible |
| assert_false(popupVisible(popup2)); |
| }, "anchor references do not cross shadow boundaries"); |
| </script> |
| |
| |
| <div id=test3> |
| <my-element> |
| <template shadowroot=open> |
| <button id=t3b1 onclick='showPopup("test3",0)'>Test 3 Popup 1</button> |
| <popup anchor=t3b1> |
| <p>This popup will be hidden when popup2 shows.</p> |
| <slot></slot> |
| </popup> |
| </template> |
| <button id=t3b2 onclick='showPopup("test3",1)'>Test 3 Popup 2</button> |
| </my-element> |
| <popup anchor=t3b2>Popup 2</popup> |
| </div> |
| |
| <script> |
| test(function() { |
| const [popup1,popup2] = getPopupReferences('test3'); |
| popup1.show(); |
| assert_true(popup1.open); |
| assert_true(popupVisible(popup1)); |
| // Showing popup2 will close popup1, since it is not a DOM |
| // tree ancestor of popup2's anchor button. |
| popup2.show(); |
| assert_true(popup2.open); |
| assert_true(popupVisible(popup2)); |
| assert_false(popup1.open); |
| assert_false(popupVisible(popup1)); |
| popup2.hide(); |
| }, "anchor references use the DOM tree not the flat tree"); |
| </script> |
| |
| |
| <div id=test4> |
| <button id=t4b1 onclick='showPopup("test4",0)'>Test 4 Popup 1</button> |
| <popup anchor=t4b1> |
| <p>This should not get hidden when popup2 opens.</p> |
| <my-element> |
| <template shadowroot=open> |
| <button id=t4b2 onclick='showPopup("test4",1)'>Test 4 Popup 2</button> |
| <popup anchor=t4b2> |
| <p>This should not hide popup1.</p> |
| </popup> |
| </template> |
| </my-element> |
| </popup> |
| </div> |
| |
| <script> |
| test(function() { |
| const [popup1,popup2] = getPopupReferences('test4'); |
| popup1.show(); |
| popup2.show(); |
| // Both 1 and 2 should be open at this point. |
| assert_true(popup1.open); |
| assert_true(popupVisible(popup1)); |
| assert_true(popup2.open); |
| assert_true(popupVisible(popup2)); |
| // This should hide both of them. |
| popup1.hide(); |
| assert_false(popup2.open); |
| assert_false(popupVisible(popup2)); |
| }, "The popup stack is preserved across shadow-inclusive ancestors"); |
| </script> |