• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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