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>
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>
23 <span id=inside2
>Inside popover
2</span>
26 <button id=after_p1
tabindex=
"0">Next control after popover1
</button>
30 [popover] {bottom:auto;}
32 /* This should *not* affect anything: */
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")
53 e
.preventDefault(); // 'beforetoggle' should not be cancellable.
55 let popover2HideCount
= 0;
56 popover2
.addEventListener('beforetoggle',(e
) => {
57 if (e
.newState
!== "closed")
60 e
.preventDefault(); // 'beforetoggle' should not be cancellable.
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
})
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()
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'));
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
})
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');
260 <button id=b3 popovertarget=p3
>Popover
3 - button
3
261 <div popover id=p4
>Inside popover
4</div>
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>
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');
299 <div popover id=p6
>Inside popover
6
300 <div style=
"height:2000px;background:lightgreen"></div>
303 <button popovertarget=p6
>Popover
6</button>
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
})
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');
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">
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 () => {
342 assert_true(popover7
.matches(':popover-open'),'invoking element should open popover');
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 () => {
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');
358 <button tabindex=
"0">Button
</button>
359 <span id=inside8after
>Inside popover
8 after button
</span>
361 <button id=p8invoker popovertarget=p8
tabindex=
"0">Popover8 invoker (no action)
</button>
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'));
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');
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>
385 <div popover id=convoluted_p3
>Popover
3
386 <button popovertarget=convoluted_p4
>Open Popover
4</button>
388 <div popover id=convoluted_p4
><p>Popover
4</p></div>
390 <button onclick=
"convoluted_p1.showPopover()" tabindex=
"0">Open convoluted popover
</button>
392 #convoluted_p1 {top:
50px;}
393 #convoluted_p2 {top:
150px;}
394 #convoluted_p3 {top:
250px;}
395 #convoluted_p4 {top:
350px;}
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()');
452 <div popover id=p10
>Popover
</div>
453 <div popover=hint id=p11
>Hint
</div>
454 <div popover=manual id=p12
>Manual
</div>
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:
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.
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'));
487 assert_true(auto
.matches(':popover-open'));
488 assert_true(hint
.matches(':popover-open'));
489 // Clicking on the auto should close the hint:
491 assert_true(auto
.matches(':popover-open'),'auto should stay open');
492 assert_false(hint
.matches(':popover-open'),'hint should light dismiss');
494 assert_false(auto
.matches(':popover-open'));
495 },'Light dismiss of mixed popover types including hints');
498 <div popover id=p13
>Popover
1
499 <div popover id=p14
>Popover
2
500 <div popover id=p15
>Popover
3</div>
509 promise_test(async () => {
510 const p13
= document
.querySelector('#p13');
511 const p14
= document
.querySelector('#p14');
512 const p15
= document
.querySelector('#p15');
516 p15
.addEventListener('beforetoggle', (e
) => {
517 if (e
.newState
!== "closed")
521 assert_true(p13
.matches(':popover-open') && p14
.matches(':popover-open') && p15
.matches(':popover-open'),'all three should be open');
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"');
530 <div id=p16 popover
>Popover
16
531 <div id=p17 popover
>Popover
17</div>
532 <div id=p18 popover
>Popover
18</div>
536 promise_test(async () => {
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
) => {
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"');
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>
560 promise_test(async () => {
563 const logEvents
= (e
) => {events
.push(`${e.newState==='open' ? 'show' : 'hide'} ${e.target.id}`)};
564 p19
.addEventListener('beforetoggle', (e
) => {
568 p20
.addEventListener('beforetoggle', logEvents
);
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"');
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>
584 promise_test(async () => {
588 const logEvents
= (e
) => { events
.push(`${e.newState === 'open' ? 'show' : 'hide'} ${e.target.id}`) };
589 p22
.addEventListener('beforetoggle', (e
) => {
593 p23
.addEventListener('beforetoggle', logEvents
);
594 p24
.addEventListener('beforetoggle', logEvents
);
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"');
605 <div id=p26 popover
>26</div>
606 <div id=p27 popover
>27</div>
607 <div id=p28 popover
>28</div>
610 promise_test(async () => {
614 const logEvents
= (e
) => { events
.push(`${e.newState === 'open' ? 'show' : 'hide'} ${e.target.id}`) };
615 p26
.addEventListener('beforetoggle', (e
) => {
619 p27
.addEventListener('beforetoggle', logEvents
);
620 p28
.addEventListener('beforetoggle', (e
) => {
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');