Backed out changeset 4d64571ef929 (bug 1821732) for popovers related unexpected passe...
[gecko.git] / testing / web-platform / tests / html / semantics / popovers / popover-light-dismiss.html
blob4c31bed1635f68774f4368e834b3793d3f857495
1 <!DOCTYPE html>
2 <meta charset="utf-8" />
3 <title>Popover light dismiss behavior</title>
4 <meta name="timeout" content="long">
5 <link rel="author" href="mailto:masonf@chromium.org">
6 <link rel=help href="https://open-ui.org/components/popover.research.explainer">
7 <script src="/resources/testharness.js"></script>
8 <script src="/resources/testharnessreport.js"></script>
9 <script src="/resources/testdriver.js"></script>
10 <script src="/resources/testdriver-actions.js"></script>
11 <script src="/resources/testdriver-vendor.js"></script>
12 <script src="/resources/declarative-shadow-dom-polyfill.js"></script>
13 <script src="resources/popover-utils.js"></script>
15 <button id=b1t popovertarget='p1'>Popover 1</button>
16 <button id=b1s popovertarget='p1' popovertargetaction=show>Popover 1</button>
17 <span id=outside>Outside all popovers</span>
18 <div popover id=p1>
19 <span id=inside1>Inside popover 1</span>
20 <button id=b2 popovertarget='p2' popovertargetaction=show>Popover 2</button>
21 <span id=inside1after>Inside popover 1 after button</span>
22 <div popover id=p2>
23 <span id=inside2>Inside popover 2</span>
24 </div>
25 </div>
26 <button id=after_p1 tabindex="0">Next control after popover1</button>
27 <style>
28 #p1 {top: 50px;}
29 #p2 {top: 120px;}
30 [popover] {bottom:auto;}
31 [popover]::backdrop {
32 /* This should *not* affect anything: */
33 pointer-events: auto;
35 </style>
36 <script>
37 const popover1 = document.querySelector('#p1');
38 const button1toggle = document.querySelector('#b1t');
39 const button1show = document.querySelector('#b1s');
40 const inside1After = document.querySelector('#inside1after');
41 const button2 = document.querySelector('#b2');
42 const popover2 = document.querySelector('#p2');
43 const outside = document.querySelector('#outside');
44 const inside1 = document.querySelector('#inside1');
45 const inside2 = document.querySelector('#inside2');
46 const afterp1 = document.querySelector('#after_p1');
48 let popover1HideCount = 0;
49 popover1.addEventListener('beforetoggle',(e) => {
50 if (e.newState !== "closed")
51 return;
52 ++popover1HideCount;
53 e.preventDefault(); // 'beforetoggle' should not be cancellable.
54 });
55 let popover2HideCount = 0;
56 popover2.addEventListener('beforetoggle',(e) => {
57 if (e.newState !== "closed")
58 return;
59 ++popover2HideCount;
60 e.preventDefault(); // 'beforetoggle' should not be cancellable.
61 });
62 promise_test(async () => {
63 assert_false(popover1.matches(':popover-open'));
64 popover1.showPopover();
65 assert_true(popover1.matches(':popover-open'));
66 let p1HideCount = popover1HideCount;
67 await clickOn(outside);
68 assert_false(popover1.matches(':popover-open'));
69 assert_equals(popover1HideCount,p1HideCount+1);
70 },'Clicking outside a popover will dismiss the popover');
72 promise_test(async (t) => {
73 const controller = new AbortController();
74 t.add_cleanup(() => controller.abort());
75 function addListener(eventName) {
76 document.addEventListener(eventName,(e) => e.preventDefault(),{signal:controller.signal,capture: true});
78 addListener('pointerdown');
79 addListener('pointerup');
80 addListener('mousedown');
81 addListener('mouseup');
82 assert_false(popover1.matches(':popover-open'));
83 popover1.showPopover();
84 assert_true(popover1.matches(':popover-open'));
85 let p1HideCount = popover1HideCount;
86 await clickOn(outside);
87 assert_false(popover1.matches(':popover-open'),'preventDefault should not prevent light dismiss');
88 assert_equals(popover1HideCount,p1HideCount+1);
89 },'Canceling pointer events should not keep clicks from light dismissing popovers');
91 promise_test(async () => {
92 assert_false(popover1.matches(':popover-open'));
93 popover1.showPopover();
94 await waitForRender();
95 p1HideCount = popover1HideCount;
96 await clickOn(inside1);
97 assert_true(popover1.matches(':popover-open'));
98 assert_equals(popover1HideCount,p1HideCount);
99 popover1.hidePopover();
100 },'Clicking inside a popover does not close that popover');
102 promise_test(async () => {
103 assert_false(popover1.matches(':popover-open'));
104 popover1.showPopover();
105 await waitForRender();
106 assert_true(popover1.matches(':popover-open'));
107 await new test_driver.Actions()
108 .pointerMove(0, 0, {origin: outside})
109 .pointerDown()
110 .send();
111 await waitForRender();
112 assert_true(popover1.matches(':popover-open'),'pointerdown (outside the popover) should not hide the popover');
113 await new test_driver.Actions()
114 .pointerUp()
115 .send();
116 await waitForRender();
117 assert_false(popover1.matches(':popover-open'),'pointerup (outside the popover) should trigger light dismiss');
118 },'Popovers close on pointerup, not pointerdown');
120 promise_test(async (t) => {
121 t.add_cleanup(() => popover1.hidePopover());
122 assert_false(popover1.matches(':popover-open'));
123 popover1.showPopover();
124 assert_true(popover1.matches(':popover-open'));
125 async function testOne(eventName) {
126 document.body.dispatchEvent(new PointerEvent(eventName));
127 document.body.dispatchEvent(new MouseEvent(eventName));
128 document.body.dispatchEvent(new ProgressEvent(eventName));
129 await waitForRender();
130 assert_true(popover1.matches(':popover-open'),`A synthetic "${eventName}" event should not hide the popover`);
132 await testOne('pointerup');
133 await testOne('pointerdown');
134 await testOne('mouseup');
135 await testOne('mousedown');
136 },'Synthetic events can\'t close popovers');
138 promise_test(async (t) => {
139 t.add_cleanup(() => popover1.hidePopover());
140 popover1.showPopover();
141 await clickOn(inside1After);
142 assert_true(popover1.matches(':popover-open'));
143 await sendTab();
144 assert_equals(document.activeElement,afterp1,'Focus should move to a button outside the popover');
145 assert_true(popover1.matches(':popover-open'));
146 },'Moving focus outside the popover should not dismiss the popover');
148 promise_test(async () => {
149 popover1.showPopover();
150 popover2.showPopover();
151 await waitForRender();
152 p1HideCount = popover1HideCount;
153 let p2HideCount = popover2HideCount;
154 await clickOn(inside2);
155 assert_true(popover1.matches(':popover-open'),'popover1 should be open');
156 assert_true(popover2.matches(':popover-open'),'popover2 should be open');
157 assert_equals(popover1HideCount,p1HideCount,'popover1');
158 assert_equals(popover2HideCount,p2HideCount,'popover2');
159 popover1.hidePopover();
160 assert_false(popover1.matches(':popover-open'));
161 assert_false(popover2.matches(':popover-open'));
162 },'Clicking inside a child popover shouldn\'t close either popover');
164 promise_test(async () => {
165 popover1.showPopover();
166 popover2.showPopover();
167 await waitForRender();
168 p1HideCount = popover1HideCount;
169 p2HideCount = popover2HideCount;
170 await clickOn(inside1);
171 assert_true(popover1.matches(':popover-open'));
172 assert_equals(popover1HideCount,p1HideCount);
173 assert_false(popover2.matches(':popover-open'));
174 assert_equals(popover2HideCount,p2HideCount+1);
175 popover1.hidePopover();
176 },'Clicking inside a parent popover should close child popover');
178 promise_test(async () => {
179 await clickOn(button1show);
180 assert_true(popover1.matches(':popover-open'));
181 await waitForRender();
182 p1HideCount = popover1HideCount;
183 await clickOn(button1show);
184 assert_true(popover1.matches(':popover-open'),'popover1 should stay open');
185 assert_equals(popover1HideCount,p1HideCount,'popover1 should not get hidden and reshown');
186 popover1.hidePopover(); // Cleanup
187 assert_false(popover1.matches(':popover-open'));
188 },'Clicking on invoking element, after using it for activation, shouldn\'t close its popover');
190 promise_test(async () => {
191 popover1.showPopover();
192 assert_true(popover1.matches(':popover-open'));
193 assert_false(popover2.matches(':popover-open'));
194 await clickOn(button2);
195 assert_true(popover2.matches(':popover-open'),'button2 should activate popover2');
196 p2HideCount = popover2HideCount;
197 await clickOn(button2);
198 assert_true(popover2.matches(':popover-open'),'popover2 should stay open');
199 assert_equals(popover2HideCount,p2HideCount,'popover2 should not get hidden and reshown');
200 popover1.hidePopover(); // Cleanup
201 assert_false(popover1.matches(':popover-open'));
202 assert_false(popover2.matches(':popover-open'));
203 },'Clicking on invoking element, after using it for activation, shouldn\'t close its popover (nested case)');
205 promise_test(async () => {
206 popover1.showPopover();
207 popover2.showPopover();
208 assert_true(popover1.matches(':popover-open'));
209 assert_true(popover2.matches(':popover-open'));
210 p2HideCount = popover2HideCount;
211 await clickOn(button2);
212 assert_true(popover2.matches(':popover-open'),'popover2 should stay open');
213 assert_equals(popover2HideCount,p2HideCount,'popover2 should not get hidden and reshown');
214 popover1.hidePopover(); // Cleanup
215 assert_false(popover1.matches(':popover-open'));
216 assert_false(popover2.matches(':popover-open'));
217 },'Clicking on invoking element, after using it for activation, shouldn\'t close its popover (nested case, not used for invocation)');
219 promise_test(async () => {
220 popover1.showPopover(); // Directly show the popover
221 assert_true(popover1.matches(':popover-open'));
222 await waitForRender();
223 p1HideCount = popover1HideCount;
224 await clickOn(button1show);
225 assert_true(popover1.matches(':popover-open'),'popover1 should stay open');
226 assert_equals(popover1HideCount,p1HideCount,'popover1 should not get hidden and reshown');
227 popover1.hidePopover(); // Cleanup
228 assert_false(popover1.matches(':popover-open'));
229 },'Clicking on invoking element, even if it wasn\'t used for activation, shouldn\'t close its popover');
231 promise_test(async () => {
232 popover1.showPopover(); // Directly show the popover
233 assert_true(popover1.matches(':popover-open'));
234 await waitForRender();
235 p1HideCount = popover1HideCount;
236 await clickOn(button1toggle);
237 assert_false(popover1.matches(':popover-open'),'popover1 should be hidden by popovertarget');
238 assert_equals(popover1HideCount,p1HideCount+1,'popover1 should get hidden only once by popovertarget');
239 },'Clicking on popovertarget element, even if it wasn\'t used for activation, should hide it exactly once');
241 promise_test(async () => {
242 popover1.showPopover();
243 popover2.showPopover(); // Popover1 is an ancestral element for popover2.
244 assert_true(popover1.matches(':popover-open'));
245 assert_true(popover2.matches(':popover-open'));
246 const drag_actions = new test_driver.Actions();
247 // Drag *from* popover2 *to* popover1 (its ancestor).
248 await drag_actions.pointerMove(0,0,{origin: popover2})
249 .pointerDown({button: drag_actions.ButtonType.LEFT})
250 .pointerMove(0,0,{origin: popover1})
251 .pointerUp({button: drag_actions.ButtonType.LEFT})
252 .send();
253 assert_true(popover1.matches(':popover-open'),'popover1 should be open');
254 assert_true(popover2.matches(':popover-open'),'popover1 should be open');
255 popover1.hidePopover();
256 assert_false(popover2.matches(':popover-open'));
257 },'Dragging from an open popover outside an open popover should leave the popover open');
258 </script>
260 <button id=b3 popovertarget=p3>Popover 3 - button 3
261 <div popover id=p4>Inside popover 4</div>
262 </button>
263 <div popover id=p3>Inside popover 3</div>
264 <div popover id=p5>Inside popover 5
265 <button popovertarget=p3>Popover 3 - button 4 - unused</button>
266 </div>
267 <style>
268 #p3 {top:100px;}
269 #p4 {top:200px;}
270 #p5 {top:200px;}
271 </style>
272 <script>
273 const popover3 = document.querySelector('#p3');
274 const popover4 = document.querySelector('#p4');
275 const popover5 = document.querySelector('#p5');
276 const button3 = document.querySelector('#b3');
277 promise_test(async () => {
278 await clickOn(button3);
279 assert_true(popover3.matches(':popover-open'),'invoking element should open popover');
280 popover4.showPopover();
281 assert_true(popover4.matches(':popover-open'));
282 assert_false(popover3.matches(':popover-open'),'popover3 is unrelated to popover4');
283 popover4.hidePopover(); // Cleanup
284 assert_false(popover4.matches(':popover-open'));
285 },'A popover inside an invoking element doesn\'t participate in that invoker\'s ancestor chain');
287 promise_test(async () => {
288 popover5.showPopover();
289 assert_true(popover5.matches(':popover-open'));
290 assert_false(popover3.matches(':popover-open'));
291 popover3.showPopover();
292 assert_true(popover3.matches(':popover-open'));
293 assert_false(popover5.matches(':popover-open'),'Popover 5 was not invoked from popover3\'s invoker');
294 popover3.hidePopover();
295 assert_false(popover3.matches(':popover-open'));
296 },'An invoking element that was not used to invoke the popover is not part of the ancestor chain');
297 </script>
299 <div popover id=p6>Inside popover 6
300 <div style="height:2000px;background:lightgreen"></div>
301 Bottom of popover6
302 </div>
303 <button popovertarget=p6>Popover 6</button>
304 <style>
305 #p6 {
306 width: 300px;
307 height: 300px;
308 overflow-y: scroll;
310 </style>
311 <script>
312 const popover6 = document.querySelector('#p6');
313 promise_test(async () => {
314 popover6.showPopover();
315 assert_equals(popover6.scrollTop,0,'popover6 should start non-scrolled');
316 await new test_driver.Actions()
317 .scroll(0, 0, 0, 50, {origin: popover6})
318 .send();
319 await waitForRender();
320 assert_true(popover6.matches(':popover-open'),'popover6 should stay open');
321 assert_equals(popover6.scrollTop,50,'popover6 should be scrolled');
322 popover6.hidePopover();
323 },'Scrolling within a popover should not close the popover');
324 </script>
326 <my-element id="myElement">
327 <template shadowrootmode="open">
328 <button id=b7 popovertarget=p7 popovertargetaction=show tabindex="0">Popover7</button>
329 <div popover id=p7 style="top: 100px;">
330 <p>Popover content.</p>
331 <input id="inside7" type="text" placeholder="some text">
332 </div>
333 </template>
334 </my-element>
335 <script>
336 polyfill_declarative_shadow_dom(document.querySelector('#myElement'));
337 const button7 = document.querySelector('#myElement').shadowRoot.querySelector('#b7');
338 const popover7 = document.querySelector('#myElement').shadowRoot.querySelector('#p7');
339 const inside7 = document.querySelector('#myElement').shadowRoot.querySelector('#inside7');
340 promise_test(async () => {
341 button7.click();
342 assert_true(popover7.matches(':popover-open'),'invoking element should open popover');
343 inside7.click();
344 assert_true(popover7.matches(':popover-open'));
345 popover7.hidePopover();
346 },'Clicking inside a shadow DOM popover does not close that popover');
348 promise_test(async () => {
349 button7.click();
350 inside7.click();
351 assert_true(popover7.matches(':popover-open'));
352 await clickOn(outside);
353 assert_false(popover7.matches(':popover-open'));
354 },'Clicking outside a shadow DOM popover should close that popover');
355 </script>
357 <div popover id=p8>
358 <button tabindex="0">Button</button>
359 <span id=inside8after>Inside popover 8 after button</span>
360 </div>
361 <button id=p8invoker popovertarget=p8 tabindex="0">Popover8 invoker (no action)</button>
362 <script>
363 promise_test(async () => {
364 const popover8 = document.querySelector('#p8');
365 const inside8After = document.querySelector('#inside8after');
366 const popover8Invoker = document.querySelector('#p8invoker');
367 assert_false(popover8.matches(':popover-open'));
368 popover8.showPopover();
369 await clickOn(inside8After);
370 assert_true(popover8.matches(':popover-open'));
371 await sendTab();
372 assert_equals(document.activeElement,popover8Invoker,'Focus should move to the invoker element');
373 assert_true(popover8.matches(':popover-open'),'popover should stay open');
374 popover8.hidePopover(); // Cleanup
375 },'Moving focus back to the invoker element should not dismiss the popover');
376 </script>
378 <!-- Convoluted ancestor relationship -->
379 <div popover id=convoluted_p1>Popover 1
380 <button popovertarget=convoluted_p2>Open Popover 2</button>
381 <div popover id=convoluted_p2>Popover 2
382 <button popovertarget=convoluted_p3>Open Popover 3</button>
383 <button popovertarget=convoluted_p2 popovertargetaction=show>Self-linked invoker</button>
384 </div>
385 <div popover id=convoluted_p3>Popover 3
386 <button popovertarget=convoluted_p4>Open Popover 4</button>
387 </div>
388 <div popover id=convoluted_p4><p>Popover 4</p></div>
389 </div>
390 <button onclick="convoluted_p1.showPopover()" tabindex="0">Open convoluted popover</button>
391 <style>
392 #convoluted_p1 {top:50px;}
393 #convoluted_p2 {top:150px;}
394 #convoluted_p3 {top:250px;}
395 #convoluted_p4 {top:350px;}
396 </style>
397 <script>
398 const convPopover1 = document.querySelector('#convoluted_p1');
399 const convPopover2 = document.querySelector('#convoluted_p2');
400 const convPopover3 = document.querySelector('#convoluted_p3');
401 const convPopover4 = document.querySelector('#convoluted_p4');
402 promise_test(async () => {
403 convPopover1.showPopover(); // Programmatically open p1
404 assert_true(convPopover1.matches(':popover-open'));
405 convPopover1.querySelector('button').click(); // Click to invoke p2
406 assert_true(convPopover1.matches(':popover-open'));
407 assert_true(convPopover2.matches(':popover-open'));
408 convPopover2.querySelector('button').click(); // Click to invoke p3
409 assert_true(convPopover1.matches(':popover-open'));
410 assert_true(convPopover2.matches(':popover-open'));
411 assert_true(convPopover3.matches(':popover-open'));
412 convPopover3.querySelector('button').click(); // Click to invoke p4
413 assert_true(convPopover1.matches(':popover-open'));
414 assert_true(convPopover2.matches(':popover-open'));
415 assert_true(convPopover3.matches(':popover-open'));
416 assert_true(convPopover4.matches(':popover-open'));
417 convPopover4.firstElementChild.click(); // Click within p4
418 assert_true(convPopover1.matches(':popover-open'));
419 assert_true(convPopover2.matches(':popover-open'));
420 assert_true(convPopover3.matches(':popover-open'));
421 assert_true(convPopover4.matches(':popover-open'));
422 convPopover1.hidePopover();
423 assert_false(convPopover1.matches(':popover-open'));
424 assert_false(convPopover2.matches(':popover-open'));
425 assert_false(convPopover3.matches(':popover-open'));
426 assert_false(convPopover4.matches(':popover-open'));
427 },'Ensure circular/convoluted ancestral relationships are functional');
429 promise_test(async () => {
430 convPopover1.showPopover(); // Programmatically open p1
431 convPopover1.querySelector('button').click(); // Click to invoke p2
432 assert_true(convPopover1.matches(':popover-open'));
433 assert_true(convPopover2.matches(':popover-open'));
434 assert_false(convPopover3.matches(':popover-open'));
435 assert_false(convPopover4.matches(':popover-open'));
436 convPopover4.showPopover(); // Programmatically open p4
437 assert_true(convPopover1.matches(':popover-open'),'popover1 stays open because it is a DOM ancestor of popover4');
438 assert_false(convPopover2.matches(':popover-open'),'popover2 closes because it isn\'t connected to popover4 via active invokers');
439 assert_true(convPopover4.matches(':popover-open'));
440 convPopover4.firstElementChild.click(); // Click within p4
441 assert_true(convPopover1.matches(':popover-open'),'nothing changes');
442 assert_false(convPopover2.matches(':popover-open'));
443 assert_true(convPopover4.matches(':popover-open'));
444 convPopover1.hidePopover();
445 assert_false(convPopover1.matches(':popover-open'));
446 assert_false(convPopover2.matches(':popover-open'));
447 assert_false(convPopover3.matches(':popover-open'));
448 assert_false(convPopover4.matches(':popover-open'));
449 },'Ensure circular/convoluted ancestral relationships are functional, with a direct showPopover()');
450 </script>
452 <div popover id=p10>Popover</div>
453 <div popover=hint id=p11>Hint</div>
454 <div popover=manual id=p12>Manual</div>
455 <style>
456 #p10 {top:100px;}
457 #p11 {top:200px;}
458 #p12 {top:300px;}
459 </style>
460 <script>
461 if (popoverHintSupported()) {
462 promise_test(async () => {
463 const auto = document.querySelector('#p10');
464 const hint = document.querySelector('#p11');
465 const manual = document.querySelector('#p12');
466 // All three can be open at once, if shown in this order:
467 auto.showPopover();
468 hint.showPopover();
469 manual.showPopover();
470 assert_true(auto.matches(':popover-open'));
471 assert_true(hint.matches(':popover-open'));
472 assert_true(manual.matches(':popover-open'));
473 // Clicking the hint will close the auto, but not the manual.
474 await clickOn(hint);
475 assert_false(auto.matches(':popover-open'),'auto should be hidden');
476 assert_true(hint.matches(':popover-open'),'hint should stay open');
477 assert_true(manual.matches(':popover-open'),'manual does not light dismiss');
478 // Clicking outside should close the hint, but not the manual:
479 await clickOn(outside);
480 assert_false(auto.matches(':popover-open'));
481 assert_false(hint.matches(':popover-open'),'hint should close');
482 assert_true(manual.matches(':popover-open'),'manual does not light dismiss');
483 manual.hidePopover();
484 assert_false(manual.matches(':popover-open'));
485 auto.showPopover();
486 hint.showPopover();
487 assert_true(auto.matches(':popover-open'));
488 assert_true(hint.matches(':popover-open'));
489 // Clicking on the auto should close the hint:
490 await clickOn(auto);
491 assert_true(auto.matches(':popover-open'),'auto should stay open');
492 assert_false(hint.matches(':popover-open'),'hint should light dismiss');
493 auto.hidePopover();
494 assert_false(auto.matches(':popover-open'));
495 },'Light dismiss of mixed popover types including hints');
497 </script>
498 <div popover id=p13>Popover 1
499 <div popover id=p14>Popover 2
500 <div popover id=p15>Popover 3</div>
501 </div>
502 </div>
503 <style>
504 #p13 {top: 100px;}
505 #p14 {top: 200px;}
506 #p15 {top: 300px;}
507 </style>
508 <script>
509 promise_test(async () => {
510 const p13 = document.querySelector('#p13');
511 const p14 = document.querySelector('#p14');
512 const p15 = document.querySelector('#p15');
513 p13.showPopover();
514 p14.showPopover();
515 p15.showPopover();
516 p15.addEventListener('beforetoggle', (e) => {
517 if (e.newState !== "closed")
518 return;
519 p14.hidePopover();
520 },{once:true});
521 assert_true(p13.matches(':popover-open') && p14.matches(':popover-open') && p15.matches(':popover-open'),'all three should be open');
522 p14.hidePopover();
523 assert_true(p13.matches(':popover-open'),'p13 should still be open');
524 assert_false(p14.matches(':popover-open'));
525 assert_false(p15.matches(':popover-open'));
526 p13.hidePopover(); // Cleanup
527 },'Hide the target popover during "hide all popovers until"');
528 </script>
530 <div id=p16 popover>Popover 16
531 <div id=p17 popover>Popover 17</div>
532 <div id=p18 popover>Popover 18</div>
533 </div>
535 <script>
536 promise_test(async () => {
537 p16.showPopover();
538 p18.showPopover();
539 let events = [];
540 const logEvents = (e) => {events.push(`${e.newState==='open' ? 'show' : 'hide'} ${e.target.id}`)};
541 p16.addEventListener('beforetoggle', logEvents);
542 p17.addEventListener('beforetoggle', logEvents);
543 p18.addEventListener('beforetoggle', (e) => {
544 logEvents(e);
545 p17.showPopover();
547 p16.hidePopover();
548 assert_array_equals(events,['hide p18','show p17','hide p16'],'There should not be a hide event for p17');
549 assert_false(p16.matches(':popover-open'));
550 assert_false(p17.matches(':popover-open'));
551 assert_false(p18.matches(':popover-open'));
552 },'Show a sibling popover during "hide all popovers until"');
553 </script>
555 <div id=p19 popover>Popover 19</div>
556 <div id=p20 popover>Popover 20</div>
557 <button id=example2 tabindex="0">Example 2</button>
559 <script>
560 promise_test(async () => {
561 p19.showPopover();
562 let events = [];
563 const logEvents = (e) => {events.push(`${e.newState==='open' ? 'show' : 'hide'} ${e.target.id}`)};
564 p19.addEventListener('beforetoggle', (e) => {
565 logEvents(e);
566 p20.showPopover();
568 p20.addEventListener('beforetoggle', logEvents);
569 p19.hidePopover();
570 assert_array_equals(events,['hide p19','show p20'],'There should not be a second hide event for 19');
571 assert_false(p19.matches(':popover-open'));
572 assert_true(p20.matches(':popover-open'));
573 p20.hidePopover(); // Cleanup
574 },'Show an unrelated popover during "hide popover"');
575 </script>
577 <div id=p21 popover>21
578 <div id=p22 popover>22</div>
579 <div id=p23 popover>23</div>
580 <div id=p24 popover>24</div>
581 </div>
583 <script>
584 promise_test(async () => {
585 p21.showPopover();
586 p22.showPopover();
587 let events = [];
588 const logEvents = (e) => { events.push(`${e.newState === 'open' ? 'show' : 'hide'} ${e.target.id}`) };
589 p22.addEventListener('beforetoggle', (e) => {
590 logEvents(e);
591 p24.showPopover()
593 p23.addEventListener('beforetoggle', logEvents);
594 p24.addEventListener('beforetoggle', logEvents);
595 p23.showPopover();
596 assert_array_equals(events, ['show p23', 'hide p22', 'show p24'], 'hiding p24 does not fire event');
597 assert_false(p22.matches(':popover-open'));
598 assert_true(p23.matches(':popover-open'));
599 assert_false(p24.matches(':popover-open'));
600 p21.hidePopover(); // Cleanup
601 },'Show other auto popover during "hide all popover until"');
602 </script>
604 <div id=p25 popover>
605 <div id=p26 popover>26</div>
606 <div id=p27 popover>27</div>
607 <div id=p28 popover>28</div>
608 </div>
609 <script>
610 promise_test(async () => {
611 p25.showPopover();
612 p26.showPopover();
613 let events = [];
614 const logEvents = (e) => { events.push(`${e.newState === 'open' ? 'show' : 'hide'} ${e.target.id}`) };
615 p26.addEventListener('beforetoggle', (e) => {
616 logEvents(e);
617 p28.showPopover();
619 p27.addEventListener('beforetoggle', logEvents);
620 p28.addEventListener('beforetoggle', (e) => {
621 logEvents(e);
622 p27.showPopover();
624 p27.showPopover();
625 assert_array_equals(events, ['show p27', 'hide p26', 'show p28', 'show p27'], 'Nested showPopover should not fire event for its HideAllPopoversUntil');
626 assert_false(p26.matches(':popover-open'));
627 assert_true(p27.matches(':popover-open'));
628 assert_false(p28.matches(':popover-open'));
629 p25.hidePopover(); // Cleanup
630 }, 'Nested showPopover');
631 </script>