|
10 | 10 | <div popover>Popover</div> |
11 | 11 |
|
12 | 12 | <script> |
| 13 | +function getPopoverAndSignal(t) { |
| 14 | + const popover = document.querySelector('[popover]'); |
| 15 | + const controller = new AbortController(); |
| 16 | + const signal = controller.signal; |
| 17 | + t.add_cleanup(() => controller.abort()); |
| 18 | + return {popover, signal}; |
| 19 | +} |
13 | 20 | window.onload = () => { |
14 | 21 | for(const method of ["listener","attribute"]) { |
15 | 22 | promise_test(async t => { |
|
22 | 29 | function listener(e) { |
23 | 30 | if (e.type === "beforetoggle") { |
24 | 31 | if (e.newState === "open") { |
25 | | - assert_equals(e.currentState,"closed",'The "beforetoggle" event should be fired before the popover is open'); |
| 32 | + ++showCount; |
| 33 | + assert_equals(e.oldState,"closed",'The "beforetoggle" event should be fired before the popover is open'); |
26 | 34 | assert_true(e.target.matches(':closed'),'The popover should be in the :closed state when the opening event fires.'); |
27 | 35 | assert_false(e.target.matches(':open'),'The popover should *not* be in the :open state when the opening event fires.'); |
28 | | - ++showCount; |
29 | 36 | } else { |
| 37 | + ++hideCount; |
30 | 38 | assert_equals(e.newState,"closed",'Popover toggleevent states should be "open" and "closed"'); |
31 | | - assert_equals(e.currentState,"open",'The "beforetoggle" event should be fired before the popover is closed') |
| 39 | + assert_equals(e.oldState,"open",'The "beforetoggle" event should be fired before the popover is closed') |
32 | 40 | assert_true(e.target.matches(':open'),'The popover should be in the :open state when the hiding event fires.'); |
33 | 41 | assert_false(e.target.matches(':closed'),'The popover should *not* be in the :closed state when the hiding event fires.'); |
34 | | - ++hideCount; |
35 | 42 | } |
36 | 43 | } else { |
37 | | - assert_equals(e.type,"aftertoggle",'Popover events should be "beforetoggle" and "aftertoggle"') |
| 44 | + assert_equals(e.type,"toggle",'Popover events should be "beforetoggle" and "toggle"') |
38 | 45 | if (e.newState === "open") { |
39 | | - assert_equals(e.currentState,"open",'Aftertoggle should be fired after the popover is open'); |
| 46 | + ++afterShowCount; |
40 | 47 | if (document.body.contains(e.target)) { |
41 | 48 | assert_true(e.target.matches(':open'),'The popover should be in the :open state when the after opening event fires.'); |
42 | 49 | assert_false(e.target.matches(':closed'),'The popover should *not* be in the :closed state when the after opening event fires.'); |
43 | 50 | } |
44 | | - ++afterShowCount; |
45 | 51 | } else { |
| 52 | + ++afterHideCount; |
46 | 53 | assert_equals(e.newState,"closed",'Popover toggleevent states should be "open" and "closed"'); |
47 | | - assert_equals(e.currentState,"closed",'Aftertoggle should be fired after the popover is closed'); |
48 | 54 | assert_true(e.target.matches(':closed'),'The popover should be in the :closed state when the after hiding event fires.'); |
49 | 55 | assert_false(e.target.matches(':open'),'The popover should *not* be in the :open state when the after hiding event fires.'); |
50 | | - ++afterHideCount; |
51 | 56 | } |
52 | | - e.preventDefault(); // "aftertoggle" should not be cancelable. |
| 57 | + e.preventDefault(); // "toggle" should not be cancelable. |
53 | 58 | } |
54 | 59 | }; |
55 | 60 | switch (method) { |
56 | 61 | case "listener": |
57 | | - const controller = new AbortController(); |
58 | | - const signal = controller.signal; |
59 | | - t.add_cleanup(() => controller.abort()); |
| 62 | + const {signal} = getPopoverAndSignal(t); |
60 | 63 | // These events bubble. |
61 | 64 | document.addEventListener('beforetoggle', listener, {signal}); |
62 | | - document.addEventListener('aftertoggle', listener, {signal}); |
| 65 | + document.addEventListener('toggle', listener, {signal}); |
63 | 66 | break; |
64 | 67 | case "attribute": |
65 | 68 | assert_false(popover.hasAttribute('onbeforetoggle')); |
66 | 69 | t.add_cleanup(() => popover.removeAttribute('onbeforetoggle')); |
67 | 70 | popover.onbeforetoggle = listener; |
68 | | - assert_false(popover.hasAttribute('onaftertoggle')); |
69 | | - t.add_cleanup(() => popover.removeAttribute('onaftertoggle')); |
70 | | - popover.onaftertoggle = listener; |
| 71 | + assert_false(popover.hasAttribute('ontoggle')); |
| 72 | + t.add_cleanup(() => popover.removeAttribute('ontoggle')); |
| 73 | + popover.ontoggle = listener; |
71 | 74 | break; |
72 | 75 | default: assert_unreached(); |
73 | 76 | } |
|
82 | 85 | assert_equals(0,afterShowCount); |
83 | 86 | assert_equals(0,afterHideCount); |
84 | 87 | await waitForRender(); |
85 | | - assert_equals(1,afterShowCount,'aftertoggle show is fired asynchronously'); |
| 88 | + assert_equals(1,afterShowCount,'toggle show is fired asynchronously'); |
86 | 89 | assert_equals(0,afterHideCount); |
87 | 90 | assert_true(popover.matches(':open')); |
88 | 91 | popover.hidePopover(); |
|
93 | 96 | assert_equals(0,afterHideCount); |
94 | 97 | await waitForRender(); |
95 | 98 | assert_equals(1,afterShowCount); |
96 | | - assert_equals(1,afterHideCount,'aftertoggle hide is fired asynchronously'); |
| 99 | + assert_equals(1,afterHideCount,'toggle hide is fired asynchronously'); |
97 | 100 | // No additional events |
98 | 101 | await waitForRender(); |
99 | 102 | await waitForRender(); |
|
106 | 109 | } |
107 | 110 |
|
108 | 111 | promise_test(async t => { |
109 | | - const popover = document.querySelector('[popover]'); |
110 | | - const controller = new AbortController(); |
111 | | - const signal = controller.signal; |
112 | | - t.add_cleanup(() => controller.abort()); |
| 112 | + const {popover,signal} = getPopoverAndSignal(t); |
113 | 113 | let cancel = true; |
114 | 114 | popover.addEventListener('beforetoggle',(e) => { |
115 | 115 | if (e.newState !== "open") |
|
128 | 128 | }, 'The "beforetoggle" event is cancelable for the "opening" transition'); |
129 | 129 |
|
130 | 130 | promise_test(async t => { |
131 | | - const popover = document.querySelector('[popover]'); |
132 | | - const controller = new AbortController(); |
133 | | - const signal = controller.signal; |
134 | | - t.add_cleanup(() => {controller.abort();}); |
| 131 | + const {popover,signal} = getPopoverAndSignal(t); |
135 | 132 | popover.addEventListener('beforetoggle',(e) => { |
136 | 133 | assert_not_equals(e.newState,"closed",'The "beforetoggle" event was fired for the closing transition'); |
137 | 134 | }, {signal}); |
|
144 | 141 | await waitForRender(); // Check for async events also |
145 | 142 | assert_false(popover.matches(':open')); |
146 | 143 | }, 'The "beforetoggle" event is not fired for element removal'); |
| 144 | + |
| 145 | + promise_test(async t => { |
| 146 | + const {popover,signal} = getPopoverAndSignal(t); |
| 147 | + let events; |
| 148 | + function resetEvents() { |
| 149 | + events = { |
| 150 | + singleShow: false, |
| 151 | + singleHide: false, |
| 152 | + coalescedShow: false, |
| 153 | + coalescedHide: false, |
| 154 | + }; |
| 155 | + } |
| 156 | + function setEvent(type) { |
| 157 | + assert_equals(events[type],false,'event repeated'); |
| 158 | + events[type] = true; |
| 159 | + } |
| 160 | + function assertOnly(type,msg) { |
| 161 | + Object.keys(events).forEach(val => { |
| 162 | + assert_equals(events[val],val===type,`${msg} (${val})`); |
| 163 | + }); |
| 164 | + } |
| 165 | + popover.addEventListener('toggle',(e) => { |
| 166 | + switch (e.newState) { |
| 167 | + case "open": |
| 168 | + switch (e.oldState) { |
| 169 | + case "open": setEvent('coalescedShow'); break; |
| 170 | + case "closed": setEvent('singleShow'); break; |
| 171 | + default: assert_unreached(); |
| 172 | + } |
| 173 | + break; |
| 174 | + case "closed": |
| 175 | + switch (e.oldState) { |
| 176 | + case "closed": setEvent('coalescedHide'); break; |
| 177 | + case "open": setEvent('singleHide'); break; |
| 178 | + default: assert_unreached(); |
| 179 | + } |
| 180 | + break; |
| 181 | + default: assert_unreached(); |
| 182 | + } |
| 183 | + }, {signal}); |
| 184 | + |
| 185 | + resetEvents(); |
| 186 | + assertOnly('none'); |
| 187 | + assert_false(popover.matches(':open')); |
| 188 | + popover.showPopover(); |
| 189 | + await waitForRender(); |
| 190 | + assert_true(popover.matches(':open')); |
| 191 | + assertOnly('singleShow','Single event should have been fired, which is a "show"'); |
| 192 | + |
| 193 | + resetEvents(); |
| 194 | + popover.hidePopover(); |
| 195 | + popover.showPopover(); // Immediate re-show |
| 196 | + await waitForRender(); |
| 197 | + assert_true(popover.matches(':open')); |
| 198 | + assertOnly('coalescedShow','Single coalesced event should have been fired, which is a "show"'); |
| 199 | + |
| 200 | + resetEvents(); |
| 201 | + popover.hidePopover(); |
| 202 | + await waitForRender(); |
| 203 | + assertOnly('singleHide','Single event should have been fired, which is a "hide"'); |
| 204 | + assert_false(popover.matches(':open')); |
| 205 | + |
| 206 | + resetEvents(); |
| 207 | + popover.showPopover(); |
| 208 | + popover.hidePopover(); // Immediate re-hide |
| 209 | + await waitForRender(); |
| 210 | + assertOnly('coalescedHide','Single coalesced event should have been fired, which is a "hide"'); |
| 211 | + assert_false(popover.matches(':open')); |
| 212 | + }, 'The "toggle" event is coalesced'); |
147 | 213 | }; |
148 | 214 | </script> |
0 commit comments