4
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
5
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
6
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
7
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
8
Code distributed by Google as part of the polymer project is also
9
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
15
<title>iron-overlay-behavior tests</title>
17
<meta charset="utf-8">
18
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
19
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
21
<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
23
<script src="../../web-component-tester/browser.js"></script>
24
<link rel="import" href="../../iron-test-helpers/iron-test-helpers.html">
25
<link rel="import" href="test-overlay.html">
26
<link rel="import" href="test-overlay2.html">
27
<link rel="import" href="test-buttons.html">
28
<link rel="import" href="test-menu-button.html">
30
<style is="custom-style">
31
iron-overlay-backdrop {
32
/* For quicker tests */
33
--iron-overlay-backdrop: {
43
<test-fixture id="basic">
51
<test-fixture id="opened">
59
<test-fixture id="autofocus">
63
<button autofocus>button</button>
68
<test-fixture id="focusables">
70
<test-overlay tabindex="-1">
71
<h2>Focusables (no tabindex)</h2>
73
<input class="focusable1" placeholder="1 (nested)">
75
<button class="focusable2">1</button>
76
<button disabled> disabled button</button>
77
<div tabindex="-1">not focusable</div>
78
<button class="focusable3">2</button>
80
<test-overlay tabindex="-1">
81
<h2>Focusables (with tabindex)</h2>
82
<div tabindex="-1">not focusable</div>
83
<div tabindex="3" class="focusable3">3</div>
84
<div tabindex="4" class="focusable4">4</div>
85
<div tabindex="5" class="focusable5">5</div>
87
<div tabindex="1" class="focusable1">1 (nested)</div>
88
<div tabindex="6" class="focusable6">6 (nested)</div>
90
<div tabindex="2" class="focusable2">2</div>
95
<test-fixture id="backdrop">
97
<test-overlay with-backdrop>
103
<test-fixture id="multiple">
105
<test-overlay class="overlay-1">
108
<test-overlay class="overlay-2">
111
<test-overlay2 class="overlay-3">
117
<test-fixture id="composed">
121
<button>Button</button>
126
<test-buttons id="buttons"></test-buttons>
127
<input id="focusInput" placeholder="focus input">
131
HTMLImports.whenReady(function() {
132
// Enable document-wide tap recognizer.
133
Polymer.Gestures.add(document, 'tap', null);
136
function runAfterOpen(overlay, callback) {
137
overlay.addEventListener('iron-overlay-opened', callback);
141
function runAfterClose(overlay, callback) {
142
overlay.addEventListener('iron-overlay-closed', callback);
146
suite('basic overlay', function() {
150
overlay = fixture('basic');
153
test('overlay starts hidden', function() {
154
assert.isFalse(overlay.opened, 'overlay starts closed');
155
assert.equal(getComputedStyle(overlay).display, 'none', 'overlay starts hidden');
158
test('overlay open/close events', function(done) {
161
overlay.addEventListener('iron-overlay-opened', function() {
163
overlay.opened = false;
166
overlay.addEventListener('iron-overlay-closed', function() {
168
assert.equal(nevents, 2, 'opened and closed events fired');
172
overlay.opened = true;
175
test('open() refits overlay only once', function(done) {
176
var spy = sinon.spy(overlay, 'refit');
177
runAfterOpen(overlay, function() {
178
assert.equal(spy.callCount, 1, 'overlay did refit only once');
183
test('open overlay refits on iron-resize', function(done) {
184
runAfterOpen(overlay, function() {
185
var spy = sinon.spy(overlay, 'refit');
186
overlay.fire('iron-resize');
188
requestAnimationFrame(function() {
189
assert.isTrue(spy.called, 'overlay did refit');
195
test('closed overlay does not refit on iron-resize', function(done) {
196
var spy = sinon.spy(overlay, 'refit');
197
overlay.fire('iron-resize');
199
requestAnimationFrame(function() {
200
assert.isFalse(spy.called, 'overlay should not refit');
205
test('open() triggers iron-resize', function(done) {
207
// Ignore iron-resize triggered by window resize.
208
window.addEventListener('resize', function() { callCount--; }, true);
209
overlay.addEventListener('iron-resize', function() { callCount++; });
210
runAfterOpen(overlay, function() {
211
assert.equal(callCount, 1, 'iron-resize called once before iron-overlay-opened');
216
test('close() triggers iron-resize', function(done) {
217
runAfterOpen(overlay, function() {
218
var spy = sinon.stub();
219
overlay.addEventListener('iron-resize', spy);
220
runAfterClose(overlay, function() {
221
assert.equal(spy.callCount, 1, 'iron-resize called once before iron-overlay-closed');
227
test('closed overlay does not trigger iron-resize when its content changes', function() {
228
// Ignore iron-resize triggered by window resize.
230
window.addEventListener('resize', function() { callCount--; }, true);
231
overlay.addEventListener('iron-resize', function() { callCount++; });
232
Polymer.dom(overlay).appendChild(document.createElement('div'));
234
assert.equal(callCount, 0, 'iron-resize should not be called');
237
test('open overlay triggers iron-resize when its content changes', function(done) {
238
runAfterOpen(overlay, function() {
239
var spy = sinon.stub();
240
overlay.addEventListener('iron-resize', spy);
241
Polymer.dom(overlay).appendChild(document.createElement('div'));
243
assert.equal(spy.callCount, 1, 'iron-resize should be called once');
248
test('close an overlay quickly after open', function(done) {
249
// first, open the overlay
251
overlay.async(function() {
252
// during the opening transition, close the overlay
254
// wait for any exceptions to be thrown until the transition is done
255
this.async(function() {
261
test('clicking an overlay does not close it', function(done) {
262
runAfterOpen(overlay, function() {
263
var spy = sinon.stub();
264
overlay.addEventListener('iron-overlay-closed', spy);
265
MockInteractions.tap(overlay);
266
overlay.async(function() {
267
assert.isFalse(spy.called, 'iron-overlay-closed should not fire');
273
test('open overlay on mousedown does not close it', function(done) {
274
var btn = document.createElement('button');
275
btn.addEventListener('mousedown', overlay.open.bind(overlay));
276
document.body.appendChild(btn);
277
// It triggers mousedown, mouseup, and click.
278
MockInteractions.tap(btn);
279
document.body.removeChild(btn);
281
assert.isTrue(overlay.opened, 'overlay opened');
282
overlay.async(function() {
283
assert.isTrue(overlay.opened, 'overlay is still open');
288
test('clicking outside fires iron-overlay-canceled', function(done) {
289
runAfterOpen(overlay, function() {
290
overlay.addEventListener('iron-overlay-canceled', function(event) {
291
assert.equal(event.detail.target, document.body, 'detail contains original click event');
294
MockInteractions.tap(document.body);
298
test('clicking outside closes the overlay', function(done) {
299
runAfterOpen(overlay, function() {
300
overlay.addEventListener('iron-overlay-closed', function(event) {
301
assert.isTrue(event.detail.canceled, 'overlay is canceled');
304
MockInteractions.tap(document.body);
308
test('iron-overlay-canceled event can be prevented', function(done) {
309
runAfterOpen(overlay, function() {
310
overlay.addEventListener('iron-overlay-canceled', function(event) {
311
event.preventDefault();
313
var spy = sinon.stub();
314
overlay.addEventListener('iron-overlay-closed', spy);
315
MockInteractions.tap(document.body);
316
Polymer.Base.async(function() {
317
assert.isTrue(overlay.opened, 'overlay is still open');
318
assert.isFalse(spy.called, 'iron-overlay-closed not fired');
324
test('cancel an overlay with esc key', function(done) {
325
runAfterOpen(overlay, function() {
326
overlay.addEventListener('iron-overlay-canceled', function(event) {
327
assert.equal(event.detail.type, 'keydown');
330
MockInteractions.pressAndReleaseKeyOn(document, 27);
334
test('close an overlay with esc key', function(done) {
335
runAfterOpen(overlay, function() {
336
overlay.addEventListener('iron-overlay-closed', function(event) {
337
assert.isTrue(event.detail.canceled, 'overlay is canceled');
340
MockInteractions.pressAndReleaseKeyOn(document, 27);
344
test('no-cancel-on-outside-click property', function(done) {
345
overlay.noCancelOnOutsideClick = true;
346
runAfterOpen(overlay, function() {
347
var spy = sinon.stub();
348
overlay.addEventListener('iron-overlay-closed', spy);
349
MockInteractions.tap(document.body);
350
Polymer.Base.async(function() {
351
assert.isFalse(spy.called, 'iron-overlay-closed should not fire');
357
test('no-cancel-on-esc-key property', function(done) {
358
overlay.noCancelOnEscKey = true;
359
runAfterOpen(overlay, function() {
360
var spy = sinon.stub();
361
overlay.addEventListener('iron-overlay-closed', spy);
362
MockInteractions.pressAndReleaseKeyOn(document, 27);
363
Polymer.Base.async(function() {
364
assert.isFalse(spy.called, 'iron-overlay-cancel should not fire');
370
test('with-backdrop sets tabindex=-1 and removes it', function() {
371
overlay.withBackdrop = true;
372
assert.equal(overlay.getAttribute('tabindex'), '-1', 'tabindex is -1');
373
overlay.withBackdrop = false;
374
assert.isFalse(overlay.hasAttribute('tabindex'), 'tabindex removed');
377
test('with-backdrop does not override tabindex if already set', function() {
378
overlay.setAttribute('tabindex', '1');
379
overlay.withBackdrop = true;
380
assert.equal(overlay.getAttribute('tabindex'), '1', 'tabindex is 1');
381
overlay.withBackdrop = false;
382
assert.equal(overlay.getAttribute('tabindex'), '1', 'tabindex is still 1');
387
suite('keyboard event listener', function() {
390
var preventKeyDown = function(event) {
391
event.preventDefault();
392
event.stopPropagation();
395
suiteSetup(function() {
396
// Worst case scenario: listener with useCapture = true that prevents & stops propagation
397
// added before the overlay is initialized.
398
document.addEventListener('keydown', preventKeyDown, true);
402
overlay = fixture('basic');
405
suiteTeardown(function() {
406
document.removeEventListener('keydown', preventKeyDown, true);
409
test('cancel an overlay with esc key even if event is prevented by other listeners', function(done) {
410
runAfterOpen(overlay, function() {
411
overlay.addEventListener('iron-overlay-canceled', function(event) {
414
MockInteractions.pressAndReleaseKeyOn(document, 27);
419
suite('opened overlay', function() {
423
overlay = fixture('opened');
426
test('overlay open by default', function(done) {
427
overlay.addEventListener('iron-overlay-opened', function() {
428
assert.isTrue(overlay.opened, 'overlay starts opened');
429
assert.notEqual(getComputedStyle(overlay).display, 'none', 'overlay starts showing');
434
test('overlay positioned & sized properly', function(done) {
435
overlay.addEventListener('iron-overlay-opened', function() {
436
var s = getComputedStyle(overlay);
437
assert.closeTo(parseFloat(s.left), (window.innerWidth - overlay.offsetWidth) / 2, 1, 'centered horizontally');
438
assert.closeTo(parseFloat(s.top), (window.innerHeight - overlay.offsetHeight) / 2, 1, 'centered vertically');
444
suite('focus handling', function() {
448
overlay = fixture('autofocus');
451
test('node with autofocus is focused', function(done) {
452
runAfterOpen(overlay, function() {
453
assert.equal(Polymer.dom(overlay).querySelector('[autofocus]'), document.activeElement, '<button autofocus> is focused');
458
test('no-auto-focus will not focus node with autofocus', function(done) {
459
overlay.noAutoFocus = true;
460
runAfterOpen(overlay, function() {
461
assert.notEqual(Polymer.dom(overlay).querySelector('[autofocus]'), document.activeElement, '<button autofocus> not focused after opened');
464
// In Safari the element with autofocus will immediately receive focus when displayed for the first time http://jsbin.com/woroci/2/
465
// Ensure this is not the case for overlay.
466
assert.notEqual(Polymer.dom(overlay).querySelector('[autofocus]'), document.activeElement, '<button autofocus> not immediately focused');
469
test('no-cancel-on-outside-click property; focus stays on overlay when click outside', function(done) {
470
overlay.noCancelOnOutsideClick = true;
471
runAfterOpen(overlay, function() {
472
MockInteractions.tap(document.body);
473
Polymer.Base.async(function() {
474
assert.equal(Polymer.dom(overlay).querySelector('[autofocus]'), document.activeElement, '<button autofocus> is focused');
480
test('with-backdrop traps the focus within the overlay', function(done) {
481
var focusSpy = sinon.stub();
482
var button = document.createElement('button');
483
document.body.appendChild(button);
484
button.addEventListener('focus', focusSpy, true);
486
overlay.withBackdrop = true;
487
runAfterOpen(overlay, function() {
488
// Try to steal the focus
489
MockInteractions.focus(button);
490
assert.equal(Polymer.dom(overlay).querySelector('[autofocus]'), document.activeElement, '<button autofocus> is focused');
491
assert.equal(focusSpy.callCount, 0, 'button in body did not get the focus');
492
document.body.removeChild(button);
499
suite('focusable nodes', function() {
500
var overlay, overlayWithTabIndex;
503
var f = fixture('focusables');
505
overlayWithTabIndex = f[1];
508
test('_focusableNodes returns nodes that are focusable', function() {
509
var focusableNodes = overlay._focusableNodes;
510
assert.equal(focusableNodes.length, 3, '3 nodes are focusable');
511
assert.equal(focusableNodes[0], Polymer.dom(overlay).querySelector('.focusable1'));
512
assert.equal(focusableNodes[1], Polymer.dom(overlay).querySelector('.focusable2'));
513
assert.equal(focusableNodes[2], Polymer.dom(overlay).querySelector('.focusable3'));
516
test('_focusableNodes includes overlay if it has a valid tabindex', function() {
517
overlay.setAttribute('tabindex', '0');
518
var focusableNodes = overlay._focusableNodes;
519
assert.equal(focusableNodes.length, 4, '4 focusable nodes');
520
assert.notEqual(focusableNodes.indexOf(overlay), -1, 'overlay is included');
523
test('_focusableNodes respects the tabindex order', function() {
524
var focusableNodes = overlayWithTabIndex._focusableNodes;
525
assert.equal(focusableNodes.length, 6, '6 nodes are focusable');
526
assert.equal(focusableNodes[0], Polymer.dom(overlayWithTabIndex).querySelector('.focusable1'));
527
assert.equal(focusableNodes[1], Polymer.dom(overlayWithTabIndex).querySelector('.focusable2'));
528
assert.equal(focusableNodes[2], Polymer.dom(overlayWithTabIndex).querySelector('.focusable3'));
529
assert.equal(focusableNodes[3], Polymer.dom(overlayWithTabIndex).querySelector('.focusable4'));
530
assert.equal(focusableNodes[4], Polymer.dom(overlayWithTabIndex).querySelector('.focusable5'));
531
assert.equal(focusableNodes[5], Polymer.dom(overlayWithTabIndex).querySelector('.focusable6'));
534
test('with-backdrop: TAB & Shift+TAB wrap focus', function(done) {
535
overlay.withBackdrop = true;
536
var focusableNodes = overlay._focusableNodes;
537
runAfterOpen(overlay, function() {
538
Polymer.Base.async(function() {
539
// Go to last element.
540
MockInteractions.focus(focusableNodes[focusableNodes.length-1]);
541
// Simulate TAB & focus out of overlay.
542
MockInteractions.pressAndReleaseKeyOn(document, 9);
543
MockInteractions.focus(document.body);
545
assert.equal(focusableNodes[0], document.activeElement, 'focus wrapped to first focusable');
546
// Simulate Shift+TAB & focus out of overlay.
547
MockInteractions.pressAndReleaseKeyOn(document, 9, ['shift']);
548
MockInteractions.focus(document.body);
549
assert.equal(focusableNodes[focusableNodes.length-1], document.activeElement, 'focus wrapped to last focusable');
555
test('with-backdrop: TAB & Shift+TAB wrap focus respecting tabindex', function(done) {
556
overlayWithTabIndex.withBackdrop = true;
557
var focusableNodes = overlayWithTabIndex._focusableNodes;
558
runAfterOpen(overlayWithTabIndex, function() {
559
Polymer.Base.async(function() {
560
// Go to last element.
561
MockInteractions.focus(focusableNodes[focusableNodes.length-1]);
562
// Simulate TAB & focus out of overlay.
563
MockInteractions.pressAndReleaseKeyOn(document, 9);
564
MockInteractions.focus(document.body);
565
assert.equal(focusableNodes[0], document.activeElement, 'focus wrapped to first focusable');
566
// Simulate Shift+TAB & focus out of overlay.
567
MockInteractions.pressAndReleaseKeyOn(document, 9, ['shift']);
568
MockInteractions.focus(document.body);
569
assert.equal(focusableNodes[focusableNodes.length-1], document.activeElement, 'focus wrapped to last focusable');
577
suite('Polymer.IronOverlayManager.deepActiveElement', function() {
579
test('handles document.body', function () {
580
document.body.focus();
581
assert.equal(Polymer.IronOverlayManager.deepActiveElement, document.body);
584
test('handles light dom', function () {
585
var focusable = document.getElementById('focusInput');
587
assert.equal(Polymer.IronOverlayManager.deepActiveElement, focusable, 'input is handled');
591
test('handles shadow dom', function () {
592
var focusable = document.getElementById('buttons').$.button0;
594
assert.equal(Polymer.IronOverlayManager.deepActiveElement, focusable);
600
suite('restore-focus-on-close', function() {
604
overlay = fixture('autofocus');
605
overlay.restoreFocusOnClose = true;
608
teardown(function () {
609
// No matter what, return the focus to body!
610
document.body.focus();
613
test('does not return focus on close by default (restore-focus-on-close=false)', function(done) {
614
overlay.restoreFocusOnClose = false;
615
var focusable = document.getElementById('focusInput');
617
runAfterOpen(overlay, function() {
618
runAfterClose(overlay, function() {
619
assert.notEqual(Polymer.IronOverlayManager.deepActiveElement, focusable, 'focus is not restored to focusable');
625
test('overlay returns focus on close', function(done) {
626
var focusable = document.getElementById('focusInput');
628
runAfterOpen(overlay, function() {
629
runAfterClose(overlay, function() {
630
assert.equal(Polymer.IronOverlayManager.deepActiveElement, focusable, 'focus restored to focusable');
636
test('overlay returns focus on close (ShadowDOM)', function(done) {
637
var focusable = document.getElementById('buttons').$.button0;
639
runAfterOpen(overlay, function() {
640
runAfterClose(overlay, function() {
641
assert.equal(Polymer.IronOverlayManager.deepActiveElement, focusable, 'focus restored to focusable');
647
test('overlay does not return focus to elements contained in another overlay', function(done) {
648
var overlay2 = fixture('basic');
649
// So it doesn't interfere with focus changes.
650
overlay2.noAutoFocus = true;
651
var focusable = document.createElement('input');
652
runAfterOpen(overlay2,function () {
653
Polymer.dom(overlay2).appendChild(focusable);
655
runAfterOpen(overlay, function() {
656
runAfterClose(overlay, function() {
657
assert.notEqual(Polymer.IronOverlayManager.deepActiveElement, focusable, 'focus not restored to focusable inside overlay2');
664
test('overlay does not return focus to elements that are not in the body anymore', function(done) {
665
var focusable = document.createElement('input');
666
document.body.appendChild(focusable);
668
var focusSpy = sinon.spy(focusable, 'focus');
669
runAfterOpen(overlay, function() {
670
document.body.removeChild(focusable);
671
runAfterClose(overlay, function() {
672
assert.isFalse(focusSpy.called, 'focus not called');
680
suite('overlay with backdrop', function() {
684
overlay = fixture('backdrop');
687
test('backdrop is opened when overlay is opened', function(done) {
688
assert.isDefined(overlay.backdropElement, 'backdrop is defined');
689
runAfterOpen(overlay, function() {
690
assert.isTrue(overlay.backdropElement.opened, 'backdrop is opened');
691
assert.isDefined(overlay.backdropElement.parentNode, 'backdrop is inserted in the DOM');
696
test('backdrop appears behind the overlay', function(done) {
697
runAfterOpen(overlay, function() {
698
styleZ = parseInt(window.getComputedStyle(overlay).zIndex, 10);
699
backdropStyleZ = parseInt(window.getComputedStyle(overlay.backdropElement).zIndex, 10);
700
assert.isTrue(styleZ > backdropStyleZ, 'overlay has higher z-index than backdrop');
705
test('backdrop is removed when overlay is closed', function(done) {
706
runAfterOpen(overlay, function() {
707
runAfterClose(overlay, function() {
708
assert.isFalse(overlay.backdropElement.opened, 'backdrop is closed');
709
assert.isNotOk(overlay.backdropElement.parentNode, 'backdrop is removed from the DOM');
710
assert.lengthOf(document.querySelectorAll('iron-overlay-backdrop'), 0, 'no backdrop elements on body');
716
test('backdrop is removed when the element is removed from DOM', function(done) {
717
runAfterOpen(overlay, function() {
718
Polymer.dom(overlay).parentNode.removeChild(overlay);
719
// Ensure detached is executed.
721
assert.isFalse(overlay.backdropElement.opened, 'backdrop is closed');
722
assert.isNotOk(overlay.backdropElement.parentNode, 'backdrop is removed from the DOM');
723
assert.lengthOf(document.querySelectorAll('iron-overlay-backdrop'), 0, 'no backdrop elements on body');
724
assert.isNotOk(overlay._manager.currentOverlay(), 'currentOverlay ok');
729
test('manager.getBackdrops() immediately updated on opened changes', function() {
730
overlay.opened = true;
731
assert.equal(Polymer.IronOverlayManager.getBackdrops().length, 1, 'overlay added to manager backdrops');
732
overlay.opened = false;
733
assert.equal(Polymer.IronOverlayManager.getBackdrops().length, 0, 'overlay removed from manager backdrops');
736
test('updating with-backdrop to false closes backdrop', function(done) {
737
runAfterOpen(overlay, function() {
738
overlay.withBackdrop = false;
739
assert.isFalse(overlay.backdropElement.opened, 'backdrop is closed');
740
assert.isNotObject(overlay.backdropElement.parentNode, 'backdrop is removed from document');
745
test('backdrop is removed when toggling overlay opened', function(done) {
747
assert.isOk(overlay.backdropElement.parentNode, 'backdrop is immediately inserted in the document');
748
runAfterClose(overlay, function() {
749
assert.isFalse(overlay.backdropElement.opened, 'backdrop is closed');
750
assert.isNotOk(overlay.backdropElement.parentNode, 'backdrop is removed from document');
756
suite('multiple overlays', function() {
757
var overlay1, overlay2;
760
var f = fixture('multiple');
765
test('new overlays appear on top', function(done) {
766
runAfterOpen(overlay1, function() {
767
runAfterOpen(overlay2, function() {
768
var styleZ = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
769
var styleZ1 = parseInt(window.getComputedStyle(overlay2).zIndex, 10);
770
assert.isTrue(styleZ1 > styleZ, 'overlay2 has higher z-index than overlay1');
776
test('ESC closes only the top overlay', function(done) {
777
runAfterOpen(overlay1, function() {
778
runAfterOpen(overlay2, function() {
779
MockInteractions.pressAndReleaseKeyOn(document, 27);
780
assert.isFalse(overlay2.opened, 'overlay2 was closed');
781
assert.isTrue(overlay1.opened, 'overlay1 is still opened');
787
test('close an overlay in proximity to another overlay', function(done) {
788
// Open and close a separate overlay.
792
// Open the overlay we care about.
795
// Immediately close the first overlay.
796
// Wait for infinite recursion, otherwise we win.
797
runAfterClose(overlay2, function() {
804
suite('Manager overlays in sync', function() {
805
var overlay1, overlay2;
809
var f = fixture('multiple');
812
overlays = Polymer.IronOverlayManager._overlays;
815
test('no duplicates after attached', function(done) {
816
overlay1 = document.createElement('test-overlay');
817
overlay1.addEventListener('iron-overlay-opened',function() {
818
assert.equal(overlays.length, 1, 'correct count after open and attached');
819
document.body.removeChild(overlay1);
822
overlay1.opened = true;
823
assert.equal(overlays.length, 1, 'immediately updated');
824
document.body.appendChild(overlay1);
827
test('open twice handled', function() {
829
assert.equal(overlays.length, 1, '1 overlay after open');
831
assert.equal(overlays.length, 1, '1 overlay after second open');
834
test('close handled', function() {
837
assert.equal(overlays.length, 0, '0 overlays after close');
840
test('open/close brings overlay on top', function() {
843
assert.equal(overlays.indexOf(overlay1), 0, 'overlay1 at index 0');
844
assert.equal(overlays.indexOf(overlay2), 1, 'overlay2 at index 1');
847
assert.equal(overlays.indexOf(overlay1), 1, 'overlay1 moved at index 1');
848
assert.isAbove(parseInt(overlay1.style.zIndex), parseInt(overlay2.style.zIndex), 'overlay1 on top of overlay2');
852
suite('z-ordering', function() {
854
var originalMinimumZ;
855
var overlay1, overlay2;
858
var f = fixture('multiple');
861
originalMinimumZ = Polymer.IronOverlayManager._minimumZ;
864
teardown(function() {
865
Polymer.IronOverlayManager._minimumZ = originalMinimumZ;
869
test('default z-index is greater than 100', function(done) {
870
runAfterOpen(overlay1, function() {
871
var styleZ = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
872
assert.isTrue(styleZ > 100, 'overlay1 z-index is <= 100');
877
test('ensureMinimumZ() effects z-index', function(done) {
878
Polymer.IronOverlayManager.ensureMinimumZ(1000);
880
runAfterOpen(overlay1, function() {
881
var styleZ = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
882
assert.isTrue(styleZ > 1000, 'overlay1 z-index is <= 1000');
887
test('ensureMinimumZ() never decreases the minimum z-index', function(done) {
888
Polymer.IronOverlayManager.ensureMinimumZ(1000);
889
Polymer.IronOverlayManager.ensureMinimumZ(500);
891
runAfterOpen(overlay1, function() {
892
var styleZ = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
893
assert.isTrue(styleZ > 1000, 'overlay1 z-index is <= 1000');
900
suite('multiple overlays with backdrop', function() {
901
var overlay1, overlay2, overlay3;
904
var f = fixture('multiple');
908
overlay1.withBackdrop = overlay2.withBackdrop = overlay3.withBackdrop = true;
911
test('multiple overlays share the same backdrop', function() {
912
assert.isTrue(overlay1.backdropElement === overlay2.backdropElement, 'overlay1 and overlay2 have the same backdrop element');
913
assert.isTrue(overlay1.backdropElement === overlay3.backdropElement, 'overlay1 and overlay3 have the same backdrop element');
916
test('only one iron-overlay-backdrop in the DOM', function() {
918
overlay1.opened = overlay2.opened = overlay3.opened = true;
919
assert.lengthOf(document.querySelectorAll('iron-overlay-backdrop'), 1, 'only one backdrop element in the DOM');
922
test('iron-overlay-backdrop is removed from the DOM when all overlays with backdrop are closed', function(done) {
923
// Open & close them all.
924
overlay1.opened = overlay2.opened = overlay3.opened = true;
925
overlay1.opened = overlay2.opened = overlay3.opened = false;
926
Polymer.Base.async(function() {
927
assert.lengthOf(document.querySelectorAll('iron-overlay-backdrop'), 0, 'backdrop element removed from the DOM');
932
test('newest overlay appear on top', function(done) {
933
runAfterOpen(overlay1, function() {
934
runAfterOpen(overlay2, function() {
935
var styleZ = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
936
var style1Z = parseInt(window.getComputedStyle(overlay2).zIndex, 10);
937
var bgStyleZ = parseInt(window.getComputedStyle(overlay1.backdropElement).zIndex, 10);
938
assert.isTrue(style1Z > styleZ, 'overlay2 has higher z-index than overlay1');
939
assert.isTrue(styleZ > bgStyleZ, 'overlay1 has higher z-index than backdrop');
945
test('click events handled only by top overlay', function(done) {
946
var btn = document.createElement('button');
947
btn.addEventListener('tap', overlay2.close.bind(overlay2));
948
Polymer.dom(overlay2).appendChild(btn);
949
runAfterOpen(overlay1, function() {
950
runAfterOpen(overlay2, function() {
951
MockInteractions.tap(btn);
952
assert.isFalse(overlay2.opened, 'overlay2 closed');
953
assert.isTrue(overlay1.opened, 'overlay1 still opened');
959
test('updating with-backdrop updates z-index', function(done) {
960
runAfterOpen(overlay1, function() {
961
runAfterOpen(overlay2, function() {
962
overlay1.withBackdrop = false;
963
var styleZ = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
964
var style1Z = parseInt(window.getComputedStyle(overlay2).zIndex, 10);
965
var bgStyleZ = parseInt(window.getComputedStyle(overlay1.backdropElement).zIndex, 10);
966
assert.isTrue(style1Z > bgStyleZ, 'overlay2 has higher z-index than backdrop');
967
assert.isTrue(styleZ < bgStyleZ, 'overlay1 has lower z-index than backdrop');
975
suite('overlay in composed tree', function() {
976
test('click on overlay content does not close it', function(done) {
977
var composed = fixture('composed');
979
MockInteractions.tap(composed.$.trigger);
980
composed.$.dropdown.addEventListener('iron-overlay-opened', function() {
981
// Tap on button inside overlay.
982
MockInteractions.tap(Polymer.dom(composed).querySelector('button'));
983
Polymer.Base.async(function(){
984
assert.isTrue(composed.$.dropdown.opened, 'overlay still opened');
992
suite('always-on-top', function() {
993
var overlay1, overlay2;
996
var f = fixture('multiple');
999
overlay1.alwaysOnTop = true;
1002
test('stays on top', function(done) {
1003
runAfterOpen(overlay1, function() {
1004
runAfterOpen(overlay2, function() {
1005
var zIndex1 = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
1006
var zIndex2 = parseInt(window.getComputedStyle(overlay2).zIndex, 10);
1007
assert.isAbove(zIndex1, zIndex2, 'overlay1 on top');
1008
assert.equal(Polymer.IronOverlayManager.currentOverlay(), overlay1, 'currentOverlay ok');
1014
test('stays on top also if another overlay is with-backdrop', function(done) {
1015
overlay2.withBackdrop = true;
1016
runAfterOpen(overlay1, function() {
1017
runAfterOpen(overlay2, function() {
1018
var zIndex1 = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
1019
var zIndex2 = parseInt(window.getComputedStyle(overlay2).zIndex, 10);
1020
assert.isAbove(zIndex1, zIndex2, 'overlay1 on top');
1021
assert.equal(Polymer.IronOverlayManager.currentOverlay(), overlay1, 'currentOverlay ok');
1027
test('last overlay with always-on-top wins', function(done) {
1028
overlay2.alwaysOnTop = true;
1029
runAfterOpen(overlay1, function() {
1030
runAfterOpen(overlay2, function() {
1031
var zIndex1 = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
1032
var zIndex2 = parseInt(window.getComputedStyle(overlay2).zIndex, 10);
1033
assert.isAbove(zIndex2, zIndex1, 'overlay2 on top');
1034
assert.equal(Polymer.IronOverlayManager.currentOverlay(), overlay2, 'currentOverlay ok');
1042
suite('animations', function() {
1044
test('overlay animations correctly triggered', function(done) {
1045
var overlay = fixture('basic');
1046
overlay.animated = true;
1048
overlay.addEventListener('simple-overlay-open-animation-start', function() {
1049
// Since animated overlay will transition center + 300px to center,
1050
// we should not find the element at the center when the open animation starts.
1051
var centerElement = document.elementFromPoint(window.innerWidth/2, window.innerHeight/2);
1052
assert.notEqual(centerElement, overlay, 'overlay should not be centered already');
1059
suite('a11y', function() {
1061
test('overlay has aria-hidden=true when opened', function() {
1062
var overlay = fixture('basic');
1063
assert.equal(overlay.getAttribute('aria-hidden'), 'true', 'overlay has aria-hidden="true"');
1065
assert.isFalse(overlay.hasAttribute('aria-hidden'), 'overlay does not have aria-hidden attribute');
1067
assert.equal(overlay.getAttribute('aria-hidden'), 'true', 'overlay has aria-hidden="true"');