dialog-popover-closedby-complex.html (6585B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <meta name="timeout" content="long"> 4 <link rel="author" href="mailto:masonf@chromium.org"> 5 <link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-light-dismiss"> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <script src="/resources/testdriver.js"></script> 9 <script src="/resources/testdriver-actions.js"></script> 10 <script src="/resources/testdriver-vendor.js"></script> 11 <script src="../../popovers/resources/popover-utils.js"></script> 12 13 <div id=unrelated>Unrelated</div> 14 15 <div class=testcase data-description="normal DOM nesting"> 16 <dialog class=dialogA closedby=any style="position: fixed; top: 100px; bottom: auto; padding:0;">Dialog 1 17 <div class=popoverA popover style="top: 150px; bottom: auto; padding:0;">Popover 1 18 <dialog class=dialogB closedby=any style="position: fixed; top: 200px; bottom: auto; padding:0;">Dialog 2 19 <div class=popoverB popover style="top: 250px; bottom: auto; padding:0;">Popover 2</div> 20 </dialog> 21 </div> 22 </dialog> 23 </div> 24 25 <div class=testcase data-description="same structure, but with shadow DOM slots"> 26 <div> 27 <template shadowrootmode=open> 28 <dialog class=dialogA closedby=any style="position: fixed; top: 100px; bottom: auto; padding:0;">Dialog 1 29 <slot></slot> 30 </dialog> 31 </template> 32 <div> 33 <template shadowrootmode=open> 34 <div class=popoverA popover style="top: 150px; bottom: auto; padding:0;">Popover 1 35 <slot></slot> 36 </div> 37 </template> 38 <div> 39 <template shadowrootmode=open> 40 <dialog class=dialogB closedby=any style="position: fixed; top: 200px; bottom: auto; padding:0;">Dialog 2 41 <slot></slot> 42 </dialog> 43 </template> 44 <div class=popoverB popover style="top: 250px; bottom: auto; padding:0;">Popover 2</div> 45 </div> 46 </div> 47 </div> 48 </div> 49 50 <script> 51 function openDialog(dialog,modal) { 52 assert_true(!!dialog); 53 assert_false(dialog.open); 54 if (modal) { 55 dialog.showModal(); 56 } else { 57 dialog.show(); 58 } 59 assert_true(dialog.open); 60 assert_equals(dialog.matches(':modal'),modal); 61 } 62 function assertStates(elements,dialogAExpected,popoverAExpected, 63 dialogBExpected,popoverBExpected) { 64 assert_equals(elements.dialogA.open,dialogAExpected, 65 `dialogA should be ${dialogAExpected ? 'open' : 'closed'}`); 66 assert_equals(elements.popoverA.matches(':popover-open'),popoverAExpected, 67 `popoverA should be ${popoverAExpected ? 'open' : 'closed'}`); 68 assert_equals(elements.dialogB.open,dialogBExpected, 69 `dialogB should be ${dialogBExpected ? 'open' : 'closed'}`); 70 assert_equals(elements.popoverB.matches(':popover-open'),popoverBExpected, 71 `popoverB should be ${popoverBExpected ? 'open' : 'closed'}`); 72 } 73 function findElements(wrapper) { 74 let elements = {}; 75 if (!wrapper) { 76 return elements; 77 } 78 Array.from(wrapper.children).forEach(child => { 79 ['dialogA','popoverA','dialogB','popoverB'].forEach(pattern => { 80 if (child.matches(`.${pattern}`)) { 81 assert_false(elements.hasOwnProperty(pattern),'Multiple elements with the same class'); 82 elements[pattern] = child; 83 } 84 elements = {...elements, ...findElements(child)}; 85 elements = {...elements, ...findElements(child.shadowRoot)}; 86 }); 87 }); 88 return elements; 89 } 90 function openDialogPopoverStack(t,elements,modalA,modalB) { 91 t.add_cleanup(() => { 92 elements.dialogA.close(); 93 elements.popoverA.hidePopover(); 94 elements.dialogB.close(); 95 elements.popoverB.hidePopover(); 96 }); 97 openDialog(elements.dialogA,modalA); 98 elements.popoverA.showPopover(); 99 openDialog(elements.dialogB,modalB); 100 elements.popoverB.showPopover(); 101 assertStates(elements,true,true,true,true); 102 } 103 104 document.querySelectorAll('.testcase').forEach(testcase => { 105 const testDescription = `, ${testcase.dataset.description}`; 106 const elements = findElements(testcase); 107 assert_array_equals(Object.keys(elements).sort(),['dialogA','popoverA','dialogB','popoverB'].sort()); 108 [false,true].forEach(modalA => { 109 [false,true].forEach(modalB => { 110 const modalAString = modalA ? 'modal dialogA' : 'modeless dialogA'; 111 const modalBString = modalB ? 'modal dialogB' : 'modeless dialogB'; 112 promise_test(async (t) => { 113 openDialogPopoverStack(t,elements,modalA,modalB); 114 await clickOn(unrelated); 115 // Clicking outside all is actually a click on a dialog backdrop. 116 // If dialogB is modal, it'll be dialogB, which is nested inside popoverA. 117 // Either way, both popoverB and dialogB should close. 118 assertStates(elements,true,modalB,false,false); 119 await clickOn(unrelated); 120 // Clicking outside again should close the remaining two. 121 assertStates(elements,false,false,false,false); 122 },`clicking outside all with ${modalAString} and ${modalBString}${testDescription}`); 123 124 promise_test(async (t) => { 125 openDialogPopoverStack(t,elements,modalA,modalB); 126 await clickOn(elements.popoverB); 127 // Clicking popoverB should keep everything open. 128 assertStates(elements,true,true,true,true); 129 },`clicking popoverB with ${modalAString} and ${modalBString}${testDescription}`); 130 131 promise_test(async (t) => { 132 openDialogPopoverStack(t,elements,modalA,modalB); 133 await clickOn(elements.dialogB); 134 // Only popoverB should be light dismissed. 135 assertStates(elements,true,true,true,false); 136 },`clicking dialogB with ${modalAString} and ${modalBString}${testDescription}`); 137 138 promise_test(async (t) => { 139 openDialogPopoverStack(t,elements,modalA,modalB); 140 await clickOn(elements.popoverA); 141 // Both dialogB and popoverB should be light dismissed. 142 assertStates(elements,true,true,false,false); 143 },`clicking popoverA with ${modalAString} and ${modalBString}${testDescription}`); 144 145 promise_test(async (t) => { 146 openDialogPopoverStack(t,elements,modalA,modalB); 147 await clickOn(elements.dialogA); 148 // If dialogB is modal, clicking on dialogA is actually clicking on dialogB, 149 // which means popoverB will stay open. 150 assertStates(elements,true,modalB,false,false); 151 await clickOn(elements.dialogA); 152 // The next click on dialogA should light dismiss popoverA. 153 assertStates(elements,true,false,false,false); 154 },`clicking dialogA with ${modalAString} and ${modalBString}${testDescription}`); 155 }); 156 }); 157 }); 158 </script>