|
1 | 1 | <!DOCTYPE HTML> |
2 | | -<link rel="help" href="https://w3c.github.io/pointerevents/#firing-events-using-the-pointerevent-interface"> |
3 | | -<title>Enter/leave events fired to parent after child is removed from slot</title> |
| 2 | +<link rel="help" |
| 3 | + href="https://w3c.github.io/pointerevents/#firing-events-using-the-pointerevent-interface"> |
| 4 | +<title>Pointerevents fired after a slotted element is removed</title> |
4 | 5 | <meta name="variant" content="?mouse"> |
5 | 6 | <script src="/resources/testharness.js"></script> |
6 | 7 | <script src="/resources/testharnessreport.js"></script> |
|
9 | 10 | <script src="/resources/testdriver-vendor.js"></script> |
10 | 11 | <script src="pointerevent_support.js"></script> |
11 | 12 |
|
12 | | -<template id="template"> |
13 | | - <style> |
14 | | - div { |
15 | | - width: 100px; |
16 | | - height: 100px; |
17 | | - } |
18 | | - </style> |
19 | | - <div id="parent"> |
20 | | - <slot id="slot">slot</slot> |
21 | | - </div> |
22 | | -</template> |
23 | | - |
24 | 13 | <style> |
25 | | - div, my-elem { |
| 14 | + div { |
26 | 15 | width: 100px; |
27 | 16 | height: 100px; |
28 | 17 | display: block; |
29 | 18 | } |
30 | 19 | </style> |
31 | 20 |
|
32 | | -<my-elem id="host"> |
33 | | - <div id="child">child</div> |
34 | | -</my-elem> |
35 | | -<div id="done">done</div> |
| 21 | +<div id="host"> |
| 22 | + <template id="template" shadowrootmode="open"> |
| 23 | + <style> |
| 24 | + div { |
| 25 | + width: 100px; |
| 26 | + height: 100px; |
| 27 | + } |
| 28 | + </style> |
| 29 | + <div id="parent"> |
| 30 | + <slot id="slot"> |
| 31 | + <div></div> |
| 32 | + </slot> |
| 33 | + </div> |
| 34 | + </template> |
| 35 | + |
| 36 | + <div id="filler"></div> |
| 37 | +</div> |
| 38 | +<div id="done"></div> |
36 | 39 |
|
37 | 40 | <script> |
38 | 41 | "use strict"; |
39 | 42 |
|
40 | | - customElements.define( |
41 | | - "my-elem", |
42 | | - class extends HTMLElement { |
43 | | - constructor() { |
44 | | - super(); |
45 | | - let content = document.getElementById("template").content; |
46 | | - const shadowRoot = this.attachShadow({ mode: "open" }); |
47 | | - shadowRoot.appendChild(content.cloneNode(true)); |
48 | | - } |
49 | | - }, |
50 | | - ); |
51 | | - |
52 | 43 | const pointer_type = location.search.substring(1); |
53 | 44 |
|
54 | | - const shadow_host = document.getElementById("host"); |
55 | | - const parent = shadow_host.shadowRoot.getElementById("parent"); |
| 45 | + const host = document.getElementById("host"); |
| 46 | + const parent = host.shadowRoot.getElementById("parent"); |
56 | 47 | const slot = parent.firstElementChild; |
57 | | - const slotted_child = document.getElementById("child"); |
| 48 | + const filler = document.getElementById("filler"); |
58 | 49 | const done = document.getElementById("done"); |
59 | 50 |
|
60 | 51 | let event_log = []; |
61 | | - let elem_to_remove; |
62 | 52 |
|
63 | 53 | function logEvent(e) { |
64 | 54 | if (e.eventPhase == e.AT_TARGET) { |
65 | 55 | event_log.push(e.type + "@" + e.target.id); |
66 | 56 | } |
67 | 57 | } |
68 | 58 |
|
69 | | - function removeChildFromSlot() { |
70 | | - elem_to_remove.remove(); |
71 | | - event_log.push("(child-removed)"); |
72 | | - } |
73 | | - |
74 | | - function restoreChildInSlot() { |
75 | | - if (!slotted_child.parentElement) { |
76 | | - shadow_host.appendChild(slotted_child); |
77 | | - } |
78 | | - if (!slot.parentElement) { |
79 | | - parent.appendChild(slot); |
| 59 | + const modifier_methods = { |
| 60 | + "remove-slot": { |
| 61 | + "remover": () => { slot.remove(); event_log.push("(removed)"); }, |
| 62 | + "restorer": () => { parent.appendChild(slot); } |
| 63 | + }, |
| 64 | + "remove-filler": { |
| 65 | + "remover": () => { filler.remove(); event_log.push("(removed)"); }, |
| 66 | + "restorer": () => { host.appendChild(filler); } |
| 67 | + }, |
| 68 | + "change-slotname": { |
| 69 | + "remover": () => { filler.slot = "xyz"; event_log.push("(removed)"); }, |
| 70 | + "restorer": () => { filler.slot = ""; } |
80 | 71 | } |
81 | 72 | } |
82 | 73 |
|
83 | 74 | function setup() { |
84 | 75 | const events = ["pointerover", "pointerout", |
85 | 76 | "pointerenter", "pointerleave", "pointerdown", "pointerup"]; |
86 | | - let targets = [shadow_host, parent, slot, slotted_child]; |
| 77 | + let targets = [host, parent, slot, filler]; |
87 | 78 | for (let i = 0; i < targets.length; i++) { |
88 | 79 | events.forEach(event => targets[i].addEventListener(event, logEvent)); |
89 | 80 | } |
90 | 81 | } |
91 | 82 |
|
92 | | - function addPromiseTest(remover_event, tested_elem_to_remove, |
| 83 | + function addPromiseTest(remover_event, removal_type, |
93 | 84 | expected_events) { |
94 | | - assert_true(["slot", "slotted-child"].includes(tested_elem_to_remove), |
95 | | - "Unexpcted tested_elem_to_remove param"); |
| 85 | + assert_true(Object.keys(modifier_methods).includes(removal_type), |
| 86 | + "[sanity check] Unknown removal_type param"); |
96 | 87 |
|
97 | | - const test_name = `Pointer events from ${pointer_type} `+ |
98 | | - `received before/after ${tested_elem_to_remove} removal `+ |
| 88 | + const test_name = `${pointer_type} events with ${removal_type} ` + |
99 | 89 | `at ${remover_event}`; |
100 | 90 |
|
101 | 91 | promise_test(async test => { |
102 | 92 | event_log = []; |
103 | | - elem_to_remove = (tested_elem_to_remove == "slot" ? slot : slotted_child); |
104 | 93 |
|
105 | | - restoreChildInSlot(); |
106 | | - child.addEventListener(remover_event, removeChildFromSlot, |
107 | | - { once: true }); |
| 94 | + filler.addEventListener(remover_event, |
| 95 | + modifier_methods[removal_type].remover, |
| 96 | + { once: true }); |
| 97 | + test.add_cleanup(modifier_methods[removal_type].restorer); |
| 98 | + |
108 | 99 | // TODO([email protected]): It would be more robust if we could remove |
109 | 100 | // the event listener above through `test.add_cleanup()` but strangely the |
110 | | - // cleanup call fails after the test that removes the slotted-child! This |
| 101 | + // cleanup call fails after the test that removes the filler! This |
111 | 102 | // happens even if we make the shadow DOM construction dynamic inside this |
112 | 103 | // `promise_test`!!! |
113 | 104 |
|
114 | 105 | let done_click_promise = getEvent("click", done); |
115 | 106 |
|
116 | 107 | let actions = new test_driver.Actions() |
117 | 108 | .addPointer("TestPointer", pointer_type) |
118 | | - .pointerMove(-30, -30, {origin: shadow_host}) |
| 109 | + .pointerMove(-30, -30, {origin: host}) |
119 | 110 | .pointerDown() |
120 | 111 | .pointerUp() |
121 | | - .pointerMove(30, 30, {origin: shadow_host}) |
| 112 | + .pointerMove(30, 30, {origin: host}) |
122 | 113 | .pointerDown() |
123 | 114 | .pointerUp() |
124 | 115 | .pointerMove(0, 0, {origin: done}) |
|
128 | 119 | await actions.send(); |
129 | 120 | await done_click_promise; |
130 | 121 |
|
131 | | - let removal_in_event_log = event_log.indexOf("(child-removed)"); |
132 | | - let removal_in_expected_list = expected_events.indexOf("(child-removed)"); |
133 | | - assert_true(removal_in_event_log != -1 && removal_in_expected_list != -1, |
134 | | - "(child-removed) expected in both lists"); |
| 122 | + let removal_in_event_log = event_log.indexOf("(removed)"); |
| 123 | + assert_true(removal_in_event_log != -1, |
| 124 | + "(removed) in event log"); |
| 125 | + |
| 126 | + let removal_in_expected_list = expected_events.indexOf("(removed)"); |
| 127 | + assert_true(removal_in_expected_list != -1, |
| 128 | + "[sanity check] (removed) in expected events"); |
135 | 129 |
|
136 | 130 | assert_equals(event_log.slice(0, removal_in_event_log).toString(), |
137 | 131 | expected_events.slice(0, removal_in_expected_list).toString(), |
|
146 | 140 |
|
147 | 141 | addPromiseTest( |
148 | 142 | "pointerdown", |
149 | | - "slot", |
| 143 | + "remove-slot", |
150 | 144 | [ |
151 | | - "pointerover@child", |
152 | | - "pointerenter@host", "pointerenter@parent", "pointerenter@slot", "pointerenter@child", |
153 | | - "pointerdown@child", "(child-removed)", |
| 145 | + "pointerover@filler", |
| 146 | + "pointerenter@host", "pointerenter@parent", "pointerenter@slot", "pointerenter@filler", |
| 147 | + "pointerdown@filler", "(removed)", |
| 148 | + "pointerout@filler", "pointerleave@filler", |
154 | 149 | "pointerover@parent", "pointerover@host", "pointerup@parent", "pointerup@host", |
155 | 150 | "pointerdown@parent", "pointerdown@host", "pointerup@parent", "pointerup@host", |
156 | 151 | "pointerout@parent", "pointerout@host", |
|
159 | 154 | ); |
160 | 155 | addPromiseTest( |
161 | 156 | "pointerdown", |
162 | | - "slotted-child", |
| 157 | + "remove-filler", |
| 158 | + [ |
| 159 | + "pointerover@filler", |
| 160 | + "pointerenter@host", "pointerenter@parent", "pointerenter@slot", "pointerenter@filler", |
| 161 | + "pointerdown@filler", "(removed)", |
| 162 | + "pointerover@slot", "pointerup@slot", |
| 163 | + "pointerdown@slot", "pointerup@slot", |
| 164 | + "pointerout@slot", |
| 165 | + "pointerleave@slot", "pointerleave@parent", "pointerleave@host" |
| 166 | + ] |
| 167 | + ); |
| 168 | + addPromiseTest( |
| 169 | + "pointerdown", |
| 170 | + "change-slotname", |
163 | 171 | [ |
164 | | - "pointerover@child", |
165 | | - "pointerenter@host", "pointerenter@parent", "pointerenter@slot", "pointerenter@child", |
166 | | - "pointerdown@child", "(child-removed)", |
167 | | - "pointerleave@slot", |
| 172 | + "pointerover@filler", |
| 173 | + "pointerenter@host", "pointerenter@parent", "pointerenter@slot", "pointerenter@filler", |
| 174 | + "pointerdown@filler", "(removed)", |
| 175 | + "pointerout@filler", "pointerleave@filler", |
168 | 176 | "pointerover@parent", "pointerover@host", "pointerup@parent", "pointerup@host", |
169 | 177 | "pointerdown@parent", "pointerdown@host", "pointerup@parent", "pointerup@host", |
170 | 178 | "pointerout@parent", "pointerout@host", |
|
173 | 181 | ); |
174 | 182 | addPromiseTest( |
175 | 183 | "pointerup", |
176 | | - "slot", |
| 184 | + "remove-slot", |
177 | 185 | [ |
178 | | - "pointerover@child", |
179 | | - "pointerenter@host", "pointerenter@parent", "pointerenter@slot", "pointerenter@child", |
180 | | - "pointerdown@child", "pointerup@child", "(child-removed)", |
| 186 | + "pointerover@filler", |
| 187 | + "pointerenter@host", "pointerenter@parent", "pointerenter@slot", "pointerenter@filler", |
| 188 | + "pointerdown@filler", "pointerup@filler", "(removed)", |
| 189 | + "pointerout@filler", "pointerleave@filler", |
181 | 190 | "pointerover@parent", "pointerover@host", |
182 | 191 | "pointerdown@parent", "pointerdown@host", "pointerup@parent", "pointerup@host", |
183 | 192 | "pointerout@parent", "pointerout@host", |
|
186 | 195 | ); |
187 | 196 | addPromiseTest( |
188 | 197 | "pointerup", |
189 | | - "slotted-child", |
| 198 | + "remove-filler", |
| 199 | + [ |
| 200 | + "pointerover@filler", |
| 201 | + "pointerenter@host", "pointerenter@parent", "pointerenter@slot", "pointerenter@filler", |
| 202 | + "pointerdown@filler", "pointerup@filler", "(removed)", |
| 203 | + "pointerover@slot", |
| 204 | + "pointerdown@slot", "pointerup@slot", |
| 205 | + "pointerout@slot", |
| 206 | + "pointerleave@slot", "pointerleave@parent", "pointerleave@host" |
| 207 | + ] |
| 208 | + ); |
| 209 | + addPromiseTest( |
| 210 | + "pointerup", |
| 211 | + "change-slotname", |
190 | 212 | [ |
191 | | - "pointerover@child", |
192 | | - "pointerenter@host", "pointerenter@parent", "pointerenter@slot", "pointerenter@child", |
193 | | - "pointerdown@child", "pointerup@child", "(child-removed)", |
194 | | - "pointerleave@slot", |
| 213 | + "pointerover@filler", |
| 214 | + "pointerenter@host", "pointerenter@parent", "pointerenter@slot", "pointerenter@filler", |
| 215 | + "pointerdown@filler", "pointerup@filler", "(removed)", |
| 216 | + "pointerout@filler", "pointerleave@filler", |
195 | 217 | "pointerover@parent", "pointerover@host", |
196 | 218 | "pointerdown@parent", "pointerdown@host", "pointerup@parent", "pointerup@host", |
197 | 219 | "pointerout@parent", "pointerout@host", |
|
0 commit comments