1<!doctype html> 2<!-- 3@license 4Copyright (c) 2015 The Polymer Project Authors. All rights reserved. 5This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 6The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 7The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 8Code distributed by Google as part of the polymer project is also 9subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 10--> 11<html> 12 13<head> 14 <meta charset="UTF-8"> 15 <title>iron-dropdown basic tests</title> 16 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"> 17 18 <script src="../../webcomponentsjs/webcomponents-lite.js"></script> 19 <script src="../../web-component-tester/browser.js"></script> 20 <script src="../../test-fixture/test-fixture-mocha.js"></script> 21 <script src="../../iron-test-helpers/mock-interactions.js"></script> 22 23 <link rel="import" href="../iron-dropdown.html"> 24 <link rel="import" href="../../test-fixture/test-fixture.html"> 25 26</head> 27<style> 28 body { 29 margin: 0; 30 padding: 0; 31 } 32 33 .container { 34 display: block; 35 position: relative; 36 width: 100px; 37 height: 100px; 38 background-color: yellow; 39 } 40 41 .positioned { 42 position: absolute; 43 top: 40px; 44 left: 40px; 45 } 46 47 .dropdown-content { 48 width: 50px; 49 height: 50px; 50 background-color: orange; 51 } 52 53 .big { 54 width: 3000px; 55 height: 3000px; 56 } 57</style> 58 59<body> 60 61 <test-fixture id="TrivialDropdown"> 62 <template> 63 <iron-dropdown> 64 <div class="dropdown-content"></div> 65 </iron-dropdown> 66 </template> 67 </test-fixture> 68 69 <test-fixture id="NonLockingDropdown"> 70 <template> 71 <iron-dropdown allow-outside-scroll> 72 <div class="dropdown-content">I don't lock scroll!</div> 73 </iron-dropdown> 74 </template> 75 </test-fixture> 76 77 <test-fixture id="AlignedDropdown"> 78 <template> 79 <div class="container"> 80 <iron-dropdown horizontal-align="right" vertical-align="top"> 81 <div class="dropdown-content big"></div> 82 </iron-dropdown> 83 </div> 84 </template> 85 </test-fixture> 86 87 <!-- Absolutely position the dropdown so that it has enough space to move around --> 88 <test-fixture id="OffsetDropdownTopLeft"> 89 <template> 90 <div class="container positioned"> 91 <iron-dropdown> 92 <div class="dropdown-content"></div> 93 </iron-dropdown> 94 </div> 95 </template> 96 </test-fixture> 97 98 <test-fixture id="OffsetDropdownBottomRight"> 99 <template> 100 <div class="container positioned"> 101 <iron-dropdown horizontal-align="right" vertical-align="bottom"> 102 <div class="dropdown-content"></div> 103 </iron-dropdown> 104 </div> 105 </template> 106 </test-fixture> 107 108 <test-fixture id="FocusableContentDropdown"> 109 <template> 110 <iron-dropdown> 111 <div class="dropdown-content" tabindex="0"> 112 <div class="subcontent" tabindex="0"></div> 113 </div> 114 </iron-dropdown> 115 </template> 116 </test-fixture> 117 118 <test-fixture id="RTLDropdownLeft"> 119 <template> 120 <div dir="rtl" class="container"> 121 <iron-dropdown> 122 <div class="dropdown-content"></div> 123 </iron-dropdown> 124 </div> 125 </template> 126 </test-fixture> 127 128 <test-fixture id="RTLDropdownRight"> 129 <template> 130 <div dir="rtl" class="container"> 131 <iron-dropdown horizontal-align="right"> 132 <div class="dropdown-content"></div> 133 </iron-dropdown> 134 </div> 135 </template> 136 </test-fixture> 137 138 <test-fixture id="sizingTarget"> 139 <template> 140 <iron-dropdown> 141 <div class="dropdown-content"> 142 <div class="subcontent"></div> 143 </div> 144 </iron-dropdown> 145 </template> 146 </test-fixture> 147 148 <test-fixture id="EmptyDropdown"> 149 <template> 150 <iron-dropdown></iron-dropdown> 151 </template> 152 </test-fixture> 153 154 <script> 155 function elementIsVisible(element) { 156 var contentRect = element.getBoundingClientRect(); 157 var computedStyle = window.getComputedStyle(element); 158 159 return computedStyle.display !== 'none' && 160 contentRect.width > 0 && 161 contentRect.height > 0; 162 } 163 164 function runAfterOpen(overlay, callback) { 165 overlay.addEventListener('iron-overlay-opened', callback); 166 overlay.open(); 167 } 168 169 function fireWheel(node, deltaX, deltaY) { 170 // IE 11 doesn't support WheelEvent, use CustomEvent. 171 var event = new CustomEvent('wheel', { 172 cancelable: true, 173 bubbles: true 174 }); 175 event.deltaX = deltaX; 176 event.deltaY = deltaY; 177 node.dispatchEvent(event); 178 return event; 179 } 180 181 function dispatchScroll(target, scrollLeft, scrollTop) { 182 target.scrollLeft = scrollLeft; 183 target.scrollTop = scrollTop; 184 target.dispatchEvent(new CustomEvent('scroll', { bubbles:true } )); 185 } 186 187 suite('<iron-dropdown>', function() { 188 var dropdown; 189 var content; 190 191 suite('basic', function() { 192 setup(function() { 193 dropdown = fixture('TrivialDropdown'); 194 content = Polymer.dom(dropdown).querySelector('.dropdown-content'); 195 }); 196 197 test('effectively hides the dropdown content', function() { 198 expect(elementIsVisible(content)).to.be.equal(false); 199 }); 200 201 test('shows dropdown content when opened', function(done) { 202 runAfterOpen(dropdown, function() { 203 expect(elementIsVisible(content)).to.be.equal(true); 204 done(); 205 }); 206 }); 207 208 test('hides dropdown content when outside is clicked', function(done) { 209 runAfterOpen(dropdown, function() { 210 expect(elementIsVisible(content)).to.be.equal(true); 211 dropdown.addEventListener('iron-overlay-closed', function() { 212 expect(elementIsVisible(content)).to.be.equal(false); 213 done(); 214 }); 215 MockInteractions.tap(dropdown.parentNode); 216 }); 217 }); 218 219 suite('when content is focusable', function() { 220 setup(function() { 221 dropdown = fixture('FocusableContentDropdown'); 222 content = Polymer.dom(dropdown).querySelector('.dropdown-content'); 223 }); 224 test('focuses the content when opened', function(done) { 225 runAfterOpen(dropdown, function() { 226 expect(document.activeElement).to.be.equal(content); 227 done(); 228 }); 229 }); 230 231 test('focuses a configured focus target', function(done) { 232 var focusableChild = Polymer.dom(content).querySelector('div[tabindex]'); 233 dropdown.focusTarget = focusableChild; 234 235 runAfterOpen(dropdown, function() { 236 expect(document.activeElement).to.not.be.equal(content); 237 expect(document.activeElement).to.be.equal(focusableChild); 238 done(); 239 }); 240 }); 241 }); 242 243 suite('when dropdown is empty', function() { 244 test('keeps the sizingTarget default value', function() { 245 dropdown = fixture('EmptyDropdown'); 246 expect(dropdown.sizingTarget).to.be.equal(dropdown); 247 }); 248 }); 249 250 suite('correct animationConfig setup', function() { 251 test('as objects', function() { 252 dropdown.openAnimationConfig = { 253 name: 'open-animation' 254 }; 255 dropdown.closeAnimationConfig = { 256 name: 'close-animation' 257 }; 258 259 dropdown.opened = true; 260 261 assert.deepEqual(dropdown.openAnimationConfig, { 262 name: 'open-animation', 263 node: content 264 }, 'open animation ok'); 265 266 assert.deepEqual(dropdown.closeAnimationConfig, { 267 name: 'close-animation', 268 node: content 269 }, 'close animation ok'); 270 271 assert.deepEqual(dropdown.animationConfig, { 272 open: dropdown.openAnimationConfig, 273 close: dropdown.closeAnimationConfig 274 }, 'animationConfig ok'); 275 }); 276 277 test('as arrays', function() { 278 dropdown.openAnimationConfig = [{ 279 name: 'open-animation-1' 280 }, { 281 name: 'open-animation-2' 282 }]; 283 dropdown.closeAnimationConfig = [{ 284 name: 'close-animation-1' 285 }, { 286 name: 'close-animation-2' 287 }]; 288 289 dropdown.opened = true; 290 291 assert.deepEqual(dropdown.openAnimationConfig, [{ 292 name: 'open-animation-1', 293 node: content 294 }, { 295 name: 'open-animation-2', 296 node: content 297 }], 'open animation ok'); 298 299 assert.deepEqual(dropdown.closeAnimationConfig, [{ 300 name: 'close-animation-1', 301 node: content 302 }, { 303 name: 'close-animation-2', 304 node: content 305 }], 'close animation ok'); 306 307 assert.deepEqual(dropdown.animationConfig, { 308 open: dropdown.openAnimationConfig, 309 close: dropdown.closeAnimationConfig 310 }, 'animationConfig ok'); 311 }); 312 }); 313 314 }); 315 316 suite('locking scroll', function() { 317 318 var bigDiv, scrollTarget; 319 suiteSetup(function() { 320 bigDiv = document.createElement('div'); 321 bigDiv.classList.add('big'); 322 document.body.appendChild(bigDiv); 323 // Need to discover if html or body is scrollable. 324 // Here we are sure the page is scrollable. 325 document.documentElement.scrollTop = 1; 326 if (document.documentElement.scrollTop === 1) { 327 document.documentElement.scrollTop = 0; 328 scrollTarget = document.documentElement; 329 } else { 330 scrollTarget = document.body; 331 } 332 }); 333 334 suiteTeardown(function() { 335 document.body.removeChild(bigDiv); 336 }); 337 338 setup(function() { 339 dropdown = fixture('TrivialDropdown'); 340 }); 341 342 teardown(function() { 343 dispatchScroll(scrollTarget, 0, 0); 344 }); 345 346 test('should lock, only once', function(done) { 347 var openCount = 0; 348 runAfterOpen(dropdown, function() { 349 expect(Polymer.IronDropdownScrollManager._lockingElements.length) 350 .to.be.equal(1); 351 expect(Polymer.IronDropdownScrollManager.elementIsScrollLocked(document.body)) 352 .to.be.equal(true); 353 expect(fireWheel(document.body, 0, 10).defaultPrevented).to.be.equal(true); 354 355 if (openCount === 0) { 356 // This triggers a second `pushScrollLock` with the same element, however 357 // that should not add the element to the `_lockingElements` stack twice 358 dropdown.close(); 359 dropdown.open(); 360 } else { 361 done(); 362 } 363 openCount++; 364 }); 365 }); 366 367 test('should lock scroll', function(done) { 368 runAfterOpen(dropdown, function() { 369 dispatchScroll(scrollTarget, 10, 10); 370 assert.equal(scrollTarget.scrollTop, 0, 'scrollTop ok'); 371 assert.equal(scrollTarget.scrollLeft, 0, 'scrollLeft ok'); 372 done(); 373 }); 374 }); 375 376 test('can be disabled with `allowOutsideScroll`', function(done) { 377 dropdown.allowOutsideScroll = true; 378 runAfterOpen(dropdown, function() { 379 dispatchScroll(scrollTarget, 10, 10); 380 assert.equal(scrollTarget.scrollTop, 10, 'scrollTop ok'); 381 assert.equal(scrollTarget.scrollLeft, 10, 'scrollLeft ok'); 382 done(); 383 }); 384 }); 385 386 }); 387 388 suite('non locking scroll', function() { 389 390 setup(function() { 391 dropdown = fixture('NonLockingDropdown'); 392 }); 393 394 test('can be disabled with `allowOutsideScroll`', function(done) { 395 runAfterOpen(dropdown, function() { 396 expect(Polymer.IronDropdownScrollManager.elementIsScrollLocked(document.body)) 397 .to.be.equal(false); 398 expect(fireWheel(document.body, 0, 10).defaultPrevented).to.be.equal(false); 399 done(); 400 }); 401 }); 402 }); 403 404 suite('aligned dropdown', function() { 405 var parent; 406 var parentRect; 407 var dropdownRect; 408 409 setup(function() { 410 parent = fixture('AlignedDropdown'); 411 dropdown = parent.querySelector('iron-dropdown'); 412 parentRect = parent.getBoundingClientRect(); 413 }); 414 415 test('can be re-aligned to the right and the top', function(done) { 416 runAfterOpen(dropdown, function() { 417 dropdownRect = dropdown.getBoundingClientRect(); 418 assert.equal(dropdownRect.top, parentRect.top, 'top ok'); 419 assert.equal(dropdownRect.left, 0, 'left ok'); 420 assert.equal(dropdownRect.bottom, window.innerHeight, 'bottom ok'); 421 assert.equal(dropdownRect.right, parentRect.right, 'right ok'); 422 done(); 423 }); 424 }); 425 426 test('can be re-aligned to the bottom', function(done) { 427 dropdown.verticalAlign = 'bottom'; 428 runAfterOpen(dropdown, function() { 429 dropdownRect = dropdown.getBoundingClientRect(); 430 assert.equal(dropdownRect.top, 0, 'top ok'); 431 assert.equal(dropdownRect.left, 0, 'left ok'); 432 assert.equal(dropdownRect.bottom, parentRect.bottom, 'bottom ok'); 433 assert.equal(dropdownRect.right, parentRect.right, 'right ok'); 434 done(); 435 }); 436 }); 437 438 test('handles parent\'s stacking context', function(done) { 439 // This will create a new stacking context. 440 parent.style.transform = 'translateZ(0)'; 441 runAfterOpen(dropdown, function() { 442 dropdownRect = dropdown.getBoundingClientRect(); 443 assert.equal(dropdownRect.top, parentRect.top, 'top ok'); 444 assert.equal(dropdownRect.left, 0, 'left ok'); 445 assert.equal(dropdownRect.bottom, window.innerHeight, 'bottom ok'); 446 assert.equal(dropdownRect.right, parentRect.right, 'right ok'); 447 done(); 448 }); 449 }); 450 }); 451 452 suite('when align is left/top, with an offset', function() { 453 var dropdownRect; 454 var offsetDropdownRect; 455 setup(function() { 456 var parent = fixture('OffsetDropdownTopLeft'); 457 dropdown = parent.querySelector('iron-dropdown'); 458 }); 459 460 test('can be offset towards the bottom right', function(done) { 461 runAfterOpen(dropdown, function() { 462 dropdownRect = dropdown.getBoundingClientRect(); 463 dropdown.verticalOffset = 10; 464 dropdown.horizontalOffset = 10; 465 // Force refit instead of waiting for requestAnimationFrame. 466 dropdown.refit(); 467 offsetDropdownRect = dropdown.getBoundingClientRect(); 468 // verticalAlign is top, so a positive offset moves down. 469 assert.equal(dropdownRect.top + 10, offsetDropdownRect.top, 'top ok'); 470 // horizontalAlign is left, so a positive offset moves to the right. 471 assert.equal(dropdownRect.left + 10, offsetDropdownRect.left, 'left ok'); 472 done(); 473 }); 474 }); 475 476 test('can be offset towards the top left', function(done) { 477 runAfterOpen(dropdown, function() { 478 dropdownRect = dropdown.getBoundingClientRect(); 479 dropdown.verticalOffset = -10; 480 dropdown.horizontalOffset = -10; 481 // Force refit instead of waiting for requestAnimationFrame. 482 dropdown.refit(); 483 offsetDropdownRect = dropdown.getBoundingClientRect(); 484 // verticalAlign is top, so a negative offset moves up. 485 assert.equal(dropdownRect.top - 10, offsetDropdownRect.top, 'top ok'); 486 // horizontalAlign is left, so a negative offset moves to the left. 487 assert.equal(dropdownRect.left - 10, offsetDropdownRect.left, 'left ok'); 488 done(); 489 }); 490 }); 491 }); 492 493 suite('when align is right/bottom, with an offset', function() { 494 var dropdownRect; 495 var offsetDropdownRect; 496 setup(function() { 497 var parent = fixture('OffsetDropdownBottomRight'); 498 dropdown = parent.querySelector('iron-dropdown'); 499 }); 500 501 test('can be offset towards the top left', function(done) { 502 runAfterOpen(dropdown, function() { 503 dropdownRect = dropdown.getBoundingClientRect(); 504 dropdown.verticalOffset = 10; 505 dropdown.horizontalOffset = 10; 506 // Force refit instead of waiting for requestAnimationFrame. 507 dropdown.refit(); 508 offsetDropdownRect = dropdown.getBoundingClientRect(); 509 // verticalAlign is bottom, so a positive offset moves up. 510 assert.equal(dropdownRect.bottom - 10, offsetDropdownRect.bottom, 'bottom ok'); 511 // horizontalAlign is right, so a positive offset moves to the left. 512 assert.equal(dropdownRect.right - 10, offsetDropdownRect.right, 'right ok'); 513 done(); 514 }); 515 }); 516 517 test('can be offset towards the bottom right', function(done) { 518 runAfterOpen(dropdown, function() { 519 dropdownRect = dropdown.getBoundingClientRect(); 520 dropdown.verticalOffset = -10; 521 dropdown.horizontalOffset = -10; 522 // Force refit instead of waiting for requestAnimationFrame. 523 dropdown.refit(); 524 offsetDropdownRect = dropdown.getBoundingClientRect(); 525 // verticalAlign is bottom, so a negative offset moves down. 526 assert.equal(dropdownRect.bottom + 10, offsetDropdownRect.bottom, 'bottom ok'); 527 // horizontalAlign is right, so a positive offset moves to the right. 528 assert.equal(dropdownRect.right + 10, offsetDropdownRect.right, 'right ok'); 529 done(); 530 }); 531 }); 532 }); 533 534 suite('RTL', function() { 535 var dropdownRect; 536 537 test('with horizontalAlign=left', function(done) { 538 var parent = fixture('RTLDropdownLeft'); 539 dropdown = parent.querySelector('iron-dropdown'); 540 runAfterOpen(dropdown, function() { 541 // In RTL, if `horizontalAlign` is "left", that's the same as 542 // being right-aligned in LTR. So the dropdown should be in the top 543 // right corner. 544 dropdownRect = dropdown.getBoundingClientRect(); 545 expect(dropdownRect.top).to.be.equal(0); 546 expect(dropdownRect.right).to.be.equal(100); 547 done(); 548 }); 549 }); 550 551 test('with horizontalAlign=right', function(done) { 552 var parent = fixture('RTLDropdownRight'); 553 dropdown = parent.querySelector('iron-dropdown'); 554 runAfterOpen(dropdown, function() { 555 // In RTL, if `horizontalAlign` is "right", that's the same as 556 // being left-aligned in LTR. So the dropdown should be in the top 557 // left corner. 558 dropdownRect = dropdown.getBoundingClientRect(); 559 expect(dropdownRect.top).to.be.equal(0); 560 expect(dropdownRect.left).to.be.equal(0); 561 done(); 562 }); 563 }); 564 }); 565 566 suite('sizing target', function() { 567 setup(function() { 568 dropdown = fixture('sizingTarget'); 569 content = Polymer.dom(dropdown).querySelector('.dropdown-content'); 570 }); 571 572 test('sizingTarget is the content element by default', function(done) { 573 runAfterOpen(dropdown, function() { 574 expect(dropdown.sizingTarget).to.be.equal(content); 575 expect(content.style.maxHeight).to.be.not.empty; 576 expect(content.style.maxWidth).to.be.not.empty; 577 done(); 578 }); 579 }); 580 581 test('sizingTarget can be set to a child element', function(done) { 582 var subcontent = Polymer.dom(dropdown).querySelector('.subcontent'); 583 dropdown.sizingTarget = subcontent; 584 585 runAfterOpen(dropdown, function() { 586 expect(dropdown.sizingTarget).to.be.equal(subcontent); 587 expect(subcontent.style.maxHeight).to.be.not.empty; 588 expect(subcontent.style.maxWidth).to.be.not.empty; 589 done(); 590 }); 591 }); 592 }); 593 }); 594 </script> 595</body> 596 597</html> 598