diff --git a/app-drawer/app-drawer.html b/app-drawer/app-drawer.html index 19313aa7..f97d4eac 100644 --- a/app-drawer/app-drawer.html +++ b/app-drawer/app-drawer.html @@ -254,10 +254,6 @@ _boundEscKeydownHandler: null, - _firstTabStop: null, - - _lastTabStop: null, - attached: function() { // Only transition the drawer after its first render (e.g. app-drawer-layout // may need to set the initial opened state which should not be transitioned). @@ -273,8 +269,6 @@ this.$.scrim.addEventListener('transitionend', this._transitionend.bind(this)); this.$.contentContainer.addEventListener('transitionend', this._transitionend.bind(this)); - this.addEventListener('keydown', this._tabKeydownHandler.bind(this)) - // Only listen for horizontal track so you can vertically scroll inside the drawer. this.listen(this, 'track', '_track'); this.setScrollDirection('y'); @@ -285,6 +279,9 @@ detached: function() { document.removeEventListener('keydown', this._boundEscKeydownHandler); + if (document.$blockingElements) { + document.$blockingElements.remove(this); + } }, /** @@ -566,6 +563,9 @@ } else { document.removeEventListener('keydown', this._boundEscKeydownHandler); document.body.style.overflow = ''; + if (document.$blockingElements) { + document.$blockingElements.remove(this); + } } // Don't fire the event on initial load. @@ -579,29 +579,8 @@ if (this.noFocusTrap) { return; } - - // NOTE: Unless we use /deep/ (which we shouldn't since it's deprecated), this will - // not select focusable elements inside shadow roots. - var focusableElementsSelector = [ - 'a[href]:not([tabindex="-1"])', - 'area[href]:not([tabindex="-1"])', - 'input:not([disabled]):not([tabindex="-1"])', - 'select:not([disabled]):not([tabindex="-1"])', - 'textarea:not([disabled]):not([tabindex="-1"])', - 'button:not([disabled]):not([tabindex="-1"])', - 'iframe:not([tabindex="-1"])', - '[tabindex]:not([tabindex="-1"])', - '[contentEditable=true]:not([tabindex="-1"])' - ].join(','); - var focusableElements = Polymer.dom(this).querySelectorAll(focusableElementsSelector); - - if (focusableElements.length > 0) { - this._firstTabStop = focusableElements[0]; - this._lastTabStop = focusableElements[focusableElements.length - 1]; - } else { - // Reset saved tab stops when there are no focusable elements in the drawer. - this._firstTabStop = null; - this._lastTabStop = null; + if (document.$blockingElements) { + document.$blockingElements.push(this); } // Focus on app-drawer if it has non-zero tabindex. Otherwise, focus the first focusable @@ -610,29 +589,22 @@ var tabindex = this.getAttribute('tabindex'); if (tabindex && parseInt(tabindex, 10) > -1) { this.focus(); - } else if (this._firstTabStop) { - this._firstTabStop.focus(); - } - }, - - _tabKeydownHandler: function(event) { - if (this.noFocusTrap) { - return; - } - - var TAB_KEYCODE = 9; - if (this._drawerState === this._DRAWER_STATE.OPENED && event.keyCode === TAB_KEYCODE) { - if (event.shiftKey) { - if (this._firstTabStop && Polymer.dom(event).localTarget === this._firstTabStop) { - event.preventDefault(); - this._lastTabStop.focus(); - } - } else { - if (this._lastTabStop && Polymer.dom(event).localTarget === this._lastTabStop) { - event.preventDefault(); - this._firstTabStop.focus(); - } - } + } else { + // NOTE: Unless we use /deep/ (which we shouldn't since it's deprecated), this will + // not select focusable elements inside shadow roots. + var selector = [ + 'a[href]:not([tabindex="-1"])', + 'area[href]:not([tabindex="-1"])', + 'input:not([disabled]):not([tabindex="-1"])', + 'select:not([disabled]):not([tabindex="-1"])', + 'textarea:not([disabled]):not([tabindex="-1"])', + 'button:not([disabled]):not([tabindex="-1"])', + 'iframe:not([tabindex="-1"])', + '[tabindex]:not([tabindex="-1"])', + '[contentEditable=true]:not([tabindex="-1"])' + ].join(','); + var firstTabStop = Polymer.dom(this).querySelectorAll(selector)[0]; + firstTabStop && firstTabStop.focus(); } }, diff --git a/app-drawer/demo/right-drawer.html b/app-drawer/demo/right-drawer.html index 1872e210..3f0ed6b6 100644 --- a/app-drawer/demo/right-drawer.html +++ b/app-drawer/demo/right-drawer.html @@ -17,6 +17,8 @@ app-drawer demo + + diff --git a/app-drawer/test/app-drawer.html b/app-drawer/test/app-drawer.html index 1fceb53f..23b389c4 100644 --- a/app-drawer/test/app-drawer.html +++ b/app-drawer/test/app-drawer.html @@ -17,6 +17,10 @@ + + + + @@ -69,7 +73,7 @@ function fireKeydownEvent(target, keyCode, shiftKey) { var e = new CustomEvent('keydown', { bubbles: true, - cancelable: true + cancelable: true }); e.keyCode = keyCode; e.shiftKey = !!shiftKey; @@ -285,7 +289,7 @@ assert.isFalse(drawer.opened); assert.equal(listenerSpy.callCount, 4, 'should fire after swiping even if opened state unchanged'); - + drawer.fire('track', { state: 'start' }); drawer.fire('track', { state: 'track', dx: 200, ddx: 200 }); drawer.fire('track', { state: 'end', dx: 200, ddx: 0 }); @@ -337,7 +341,7 @@ assert.isFalse(drawer.opened); assert.equal(listenerSpy.callCount, 3, 'should fire after flinging'); - + drawer.fire('track', { state: 'start' }); drawer.fire('track', { state: 'track', dx: 200, ddx: 200 }); drawer.fire('track', { state: 'end', dx: 200, ddx: 0 }); @@ -653,36 +657,14 @@ var root = Polymer.dom(focusDrawer.root); var drawer = root.querySelector('app-drawer'); var input = Polymer.dom(drawer).querySelector('input'); - var div = Polymer.dom(drawer).querySelector('div[tabindex]'); var inputFocusSpy = sinon.spy(input, 'focus'); - var divFocusSpy = sinon.spy(div, 'focus'); Polymer.RenderStatus.afterNextRender(drawer, function() { drawer.transitionDuration = 0; drawer.opened = true; assert.isTrue(inputFocusSpy.called); - - var e = fireKeydownEvent(input, 9); - - assert.isFalse(e.defaultPrevented, 'should not prevent default'); - - input.focus(); - inputFocusSpy.reset(); - e = fireKeydownEvent(input, 9, true /* shiftKey */); - - assert.isTrue(divFocusSpy.called); - assert.isTrue(e.defaultPrevented, 'should prevent default'); - - e = fireKeydownEvent(div, 9, true /* shiftKey */); - - assert.isFalse(e.defaultPrevented, 'should not prevent default'); - - div.focus(); - e = fireKeydownEvent(div, 9); - - assert.isTrue(inputFocusSpy.called); - assert.isTrue(e.defaultPrevented, 'should prevent default'); + assert.equal(document.$blockingElements.top, drawer, 'drawer is the top blocking element'); done(); }); }); @@ -712,7 +694,7 @@ var drawer = root.querySelector('app-drawer'); var input = Polymer.dom(drawer).querySelector('input'); var inputFocusSpy = sinon.spy(input, 'focus'); - + Polymer.RenderStatus.afterNextRender(drawer, function() { drawer.transitionDuration = 0; drawer.noFocusTrap = true; diff --git a/bower.json b/bower.json index cda2346e..0122df55 100644 --- a/bower.json +++ b/bower.json @@ -38,7 +38,9 @@ "paper-tabs": "PolymerElements/paper-tabs#^1.0.0", "test-fixture": "PolymerElements/test-fixture#^1.0.0", "web-component-tester": "^4.0.0", - "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" + "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0", + "blockingElements": "PolymerLabs/blockingElements#issue-2", + "inert": "WICG/inert" }, "ignore": [], "private": true