imperative-slot-api.html (11892B)
1 <!DOCTYPE html> 2 <title>Shadow DOM: Imperative Slot API</title> 3 <meta name="author" title="Yu Han" href="mailto:yuzhehan@chromium.org"> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <script src="resources/shadow-dom.js"></script> 7 8 <div id="test_basic"> 9 <div id="host1"></div> 10 <div id="host2"></div> 11 <div id="host3"></div> 12 </div> 13 <script> 14 test(() => { 15 let tTree = createTestTree(test_basic); 16 const shadow1 = tTree.host1.attachShadow({ mode: 'open', slotAssignment: 'manual'}); 17 assert_not_equals(shadow1, null, 'slot assignment manual should work'); 18 assert_equals(shadow1.slotAssignment, "manual", 'slotAssignment should return "manual"'); 19 const shadow2 = tTree.host2.attachShadow({ mode: 'open', slotAssignment: 'named'}); 20 assert_not_equals(shadow2, null, 'slot assignment named should work'); 21 assert_equals(shadow2.slotAssignment, "named", 'slotAssignment should return "named"'); 22 assert_throws_js(TypeError, () => { 23 tTree.host3.attachShadow({ mode: 'open', slotAssignment: 'exceptional' })}, 24 'others should throw exception'); 25 }, 'attachShadow can take slotAssignment parameter.'); 26 </script> 27 28 <div id="test_assign"> 29 <div id="host"> 30 <template id="shadow_root" data-mode="open" data-slot-assignment="manual"> 31 <slot id="s1"></slot> 32 <slot id="s2"></slot> 33 <slot id="s3"></slot> 34 </template> 35 <div id="c1"></div> 36 <div id="c2"></div> 37 <div id="c3"></div> 38 <div id="nested"> 39 <div id="ns1"></div> 40 </div> 41 </div> 42 <div id="c4"></div> 43 <div id="host4"> 44 <template id="shadow_root4" data-mode="open" data-slot-assignment="manual"> 45 <slot id="s4" name="s4"></slot> 46 </template> 47 </div> 48 </div> 49 <script> 50 test(() => { 51 let tTree = createTestTree(test_assign); 52 tTree.s1.assign(c1,c2); // Should work 53 assert_throws_js(TypeError, () => { 54 tTree.s1.assign([c1,c2]) 55 }, 'sequence not allowed'); 56 assert_throws_js(TypeError, () => { 57 tTree.s1.assign([]) 58 }, 'including empty sequences'); 59 }, 'slot.attach() should take variadic not sequence.'); 60 61 test(() => { 62 let tTree = createTestTree(test_assign); 63 assert_array_equals(tTree.s2.assignedElements(), []); 64 assert_equals(tTree.c1.assignedSlot, null); 65 66 tTree.s1.assign(tTree.c1); 67 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]); 68 assert_equals(tTree.c1.assignedSlot, tTree.s1); 69 70 tTree.s2.assign(tTree.c2, tTree.c3); 71 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]); 72 assert_array_equals(tTree.s2.assignedNodes(), [tTree.c2, tTree.c3]); 73 }, 'Imperative slot API can assign nodes in manual slot assignment.'); 74 75 test(() => { 76 let tTree = createTestTree(test_assign); 77 78 tTree.s1.assign(tTree.c2, tTree.c3, tTree.c1); 79 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c2, tTree.c3, tTree.c1]); 80 assert_equals(tTree.c1.assignedSlot, tTree.s1); 81 assert_equals(tTree.c2.assignedSlot, tTree.s1); 82 assert_equals(tTree.c3.assignedSlot, tTree.s1); 83 84 tTree.s1.assign(tTree.c1, tTree.c2); 85 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]); 86 assert_equals(tTree.c1.assignedSlot, tTree.s1); 87 assert_equals(tTree.c2.assignedSlot, tTree.s1); 88 assert_equals(tTree.c3.assignedSlot, null); 89 90 tTree.s1.assign(tTree.c3, tTree.c2, tTree.c1); 91 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c3, tTree.c2, tTree.c1]); 92 assert_equals(tTree.c1.assignedSlot, tTree.s1); 93 assert_equals(tTree.c2.assignedSlot, tTree.s1); 94 assert_equals(tTree.c3.assignedSlot, tTree.s1); 95 }, 'Order of slottables is preserved in manual slot assignment.'); 96 97 test(() => { 98 let tTree = createTestTree(test_assign); 99 100 tTree.s1.assign(tTree.c2, tTree.c3, tTree.c1); 101 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c2, tTree.c3, tTree.c1]); 102 103 tTree.s2.assign(tTree.c2); 104 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c3, tTree.c1]); 105 assert_array_equals(tTree.s2.assignedNodes(), [tTree.c2]); 106 assert_equals(tTree.c1.assignedSlot, tTree.s1); 107 assert_equals(tTree.c2.assignedSlot, tTree.s2); 108 assert_equals(tTree.c3.assignedSlot, tTree.s1); 109 110 tTree.s3.assign(tTree.c3); 111 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]); 112 assert_array_equals(tTree.s2.assignedNodes(), [tTree.c2]); 113 assert_array_equals(tTree.s3.assignedNodes(), [tTree.c3]); 114 assert_equals(tTree.c1.assignedSlot, tTree.s1); 115 assert_equals(tTree.c2.assignedSlot, tTree.s2); 116 assert_equals(tTree.c3.assignedSlot, tTree.s3); 117 }, 'Previously assigned slottable is moved to new slot when it\'s reassigned.'); 118 119 test(() => { 120 let tTree = createTestTree(test_assign); 121 122 tTree.s1.assign(tTree.c1); 123 tTree.s2.assign(tTree.c2, tTree.c1); 124 tTree.s3.assign(tTree.c1, tTree.c3); 125 126 assert_array_equals(tTree.s1.assignedNodes(), []); 127 assert_array_equals(tTree.s2.assignedNodes(), [tTree.c2]); 128 assert_array_equals(tTree.s3.assignedNodes(), [tTree.c1, tTree.c3]); 129 assert_equals(tTree.c1.assignedSlot, tTree.s3); 130 assert_equals(tTree.c2.assignedSlot, tTree.s2); 131 assert_equals(tTree.c3.assignedSlot, tTree.s3); 132 }, 'Order and assignment of nodes are preserved during multiple assignment in a row.'); 133 134 test(() => { 135 let tTree = createTestTree(test_assign); 136 137 // tTree.c4 is invalid for tTree.host slot assignment. 138 // No exception should be thrown here. 139 tTree.s1.assign(tTree.c1, tTree.c4, tTree.c2); 140 141 // All observable assignments should skip c4. 142 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]); 143 assert_equals(tTree.c1.assignedSlot, tTree.s1); 144 assert_equals(tTree.c2.assignedSlot, tTree.s1); 145 assert_equals(tTree.c4.assignedSlot, null); 146 147 // Moving c4 into place should reveal the assignment. 148 tTree.host.append(tTree.c4); 149 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c4, tTree.c2]); 150 assert_equals(tTree.c1.assignedSlot, tTree.s1); 151 assert_equals(tTree.c2.assignedSlot, tTree.s1); 152 assert_equals(tTree.c4.assignedSlot, tTree.s1); 153 154 // Moving c4 into a different shadow host and back should 155 // also not break the assignment. 156 tTree.host4.append(tTree.c4) 157 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]); 158 assert_equals(tTree.c4.assignedSlot, null); 159 tTree.host.append(tTree.c4); 160 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c4, tTree.c2]); 161 assert_equals(tTree.c4.assignedSlot, tTree.s1); 162 }, 'Assigning invalid nodes should be allowed.'); 163 164 test(() => { 165 let tTree = createTestTree(test_assign); 166 167 tTree.s1.assign(tTree.c1, tTree.c2, tTree.c3); 168 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2, tTree.c3]); 169 170 tTree.host4.append(tTree.s1); 171 assert_array_equals(tTree.s1.assignedNodes(), []); 172 }, 'Moving a slot to a new host, the slot loses its previously assigned slottables.'); 173 174 test(() => { 175 let tTree = createTestTree(test_assign); 176 177 tTree.s1.assign(tTree.c1, tTree.c2, tTree.c3); 178 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2, tTree.c3]); 179 180 tTree.shadow_root.insertBefore(tTree.s2, tTree.s1); 181 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2, tTree.c3]); 182 assert_array_equals(tTree.s2.assignedNodes(), []); 183 184 tTree.shadow_root.insertBefore(tTree.s4, tTree.s1); 185 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2, tTree.c3]); 186 assert_array_equals(tTree.s4.assignedNodes(), []); 187 188 tTree.ns1.append(tTree.s1); 189 assert_array_equals(tTree.s1.assignedNodes(), []); 190 }, 'Moving a slot\'s tree order position within a shadow host has no impact on its assigned slottables.'); 191 192 test(() => { 193 let tTree = createTestTree(test_assign); 194 195 tTree.s1.assign(tTree.c1, tTree.c2, tTree.c3); 196 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2, tTree.c3]); 197 198 tTree.host4.append(tTree.c1); 199 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c2, tTree.c3]); 200 assert_array_equals(tTree.s4.assignedNodes(), []); 201 assert_equals(tTree.c1.assignedSlot, null); 202 203 tTree.s4.assign(tTree.c1); 204 assert_array_equals(tTree.s4.assignedNodes(), [tTree.c1]); 205 assert_equals(tTree.c1.assignedSlot, tTree.s4); 206 }, 'Appending slottable to different host, it loses slot assignment. It can be re-assigned within a new host.'); 207 208 test(() => { 209 let tTree = createTestTree(test_assign); 210 211 tTree.s1.assign(tTree.c1); 212 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]); 213 214 tTree.shadow_root4.insertBefore(tTree.s1, tTree.s4); 215 assert_array_equals(tTree.s1.assignedNodes(), []); 216 // Trigger slot assignment on previous shadow root. 217 assert_array_equals(tTree.s2.assignedNodes(), []); 218 219 tTree.shadow_root.insertBefore(tTree.s1, tTree.s2); 220 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]); 221 }, 'Previously assigned node should not be assigned if slot moved to a new shadow root. The node is re-assigned when moved back.'); 222 223 test(() => { 224 let tTree = createTestTree(test_assign); 225 226 tTree.s1.assign(tTree.c1, tTree.c1, tTree.c1); 227 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1]); 228 229 tTree.s1.assign(tTree.c1, tTree.c1, tTree.c2, tTree.c2, tTree.c1); 230 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]); 231 }, 'Assignment with the same node in parameters should be ignored, first one wins.'); 232 233 test(() => { 234 let tTree = createTestTree(test_assign); 235 236 tTree.s1.assign(tTree.c1, tTree.c2, tTree.c3); 237 tTree.s1.remove(); 238 239 assert_equals(tTree.c1.assignedSlot, null); 240 assert_equals(tTree.c2.assignedSlot, null); 241 assert_equals(tTree.c3.assignedSlot, null); 242 }, 'Removing a slot from DOM resets its slottable\'s slot assignment.'); 243 244 test(() => { 245 let tTree = createTestTree(test_assign); 246 247 const isolatedDocNode = document.implementation.createHTMLDocument("").body; 248 isolatedDocNode.appendChild(tTree.c1); 249 const isolatedDocNode2 = document.implementation.createHTMLDocument("").body; 250 isolatedDocNode2.appendChild(tTree.s1); 251 252 tTree.s1.assign(tTree.c1, tTree.c2); 253 assert_array_equals(tTree.s1.assignedNodes(), [], 's1 not inside shadow root'); 254 assert_equals(tTree.c1.assignedSlot, null); 255 assert_equals(tTree.c2.assignedSlot, null); 256 257 tTree.shadow_root.appendChild(tTree.s1); 258 tTree.host.appendChild(tTree.c1); 259 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]); 260 assert_equals(tTree.c1.assignedSlot, tTree.s1); 261 assert_equals(tTree.c2.assignedSlot, tTree.s1); 262 }, 'Nodes can be assigned even if slots or nodes aren\'t in the same tree.'); 263 264 test(() => { 265 let tTree = createTestTree(test_assign); 266 267 tTree.s1.assign(tTree.c1, tTree.c2); 268 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]); 269 assert_equals(tTree.c1.assignedSlot, tTree.s1); 270 assert_equals(tTree.c2.assignedSlot, tTree.s1); 271 272 const isolatedDocNode = document.implementation.createHTMLDocument("").body; 273 isolatedDocNode.appendChild(tTree.c1); 274 const isolatedDocNode2 = document.implementation.createHTMLDocument("").body; 275 isolatedDocNode2.appendChild(tTree.s1); 276 277 assert_array_equals(tTree.s1.assignedNodes(), [], 's1 not inside shadow root'); 278 assert_equals(tTree.c1.assignedSlot, null); 279 assert_equals(tTree.c2.assignedSlot, null); 280 281 tTree.shadow_root.appendChild(tTree.s1); 282 tTree.host.appendChild(tTree.c1); 283 assert_array_equals(tTree.s1.assignedNodes(), [tTree.c1, tTree.c2]); 284 assert_equals(tTree.c1.assignedSlot, tTree.s1); 285 assert_equals(tTree.c2.assignedSlot, tTree.s1); 286 }, 'Removing a node from the document does not break manually assigned slot linkage.'); 287 288 test(() => { 289 const inputValues = [ 290 ['Attr', document.createAttribute('bar')], 291 ['Comment', document.createComment('bar')], 292 ['DocumentFragment', document.createDocumentFragment()], 293 ['DocumentType', document.implementation.createDocumentType('html', '', '')] 294 ]; 295 for (const [label, input] of inputValues) { 296 assert_throws_js(TypeError, () => { 297 const slot = document.createElement('slot'); 298 slot.assign(input); 299 }, label); 300 } 301 }, 'throw TypeError if the passed values are neither Element nor Text'); 302 303 </script>