
The iron-overlay-behavior tries to prevent the user from tabbing outside of the overlay by recording the first and last child elements which appear to be focusable. It intercepts [Tab] and [Shift+Tab] keystrokes to wrap the tab-order of its controls. However, the way it determines the first and last such element is 1) not composable with child elements that are Polymer components and 2) not composable with dynamically changing children. Because gr-reply-dialog is both 1) built with Polymer components and 2) partially dynamically generated, the tab order is broken when the overlay behavior tries to intercept and constrain it. The workaround here is to remove the iron-overlay-behavior's guesswork and manually set the first and last focusable inputs with knowledge of what those elements would be in gr-reply-dialog. gr-overlay's #open method now returns a promise that resolves when the overlay is actually displayed. Bug: Issue 4165 Change-Id: Ib1cdea4c5e99f821db699c89743240caab751f03
77 lines
2.2 KiB
JavaScript
77 lines
2.2 KiB
JavaScript
// Copyright (C) 2016 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
(function() {
|
|
'use strict';
|
|
|
|
var AWAIT_MAX_ITERS = 10;
|
|
var AWAIT_STEP = 5;
|
|
|
|
Polymer({
|
|
is: 'gr-overlay',
|
|
|
|
behaviors: [
|
|
Polymer.IronOverlayBehavior,
|
|
],
|
|
|
|
detached: function() {
|
|
// For good measure.
|
|
Gerrit.KeyboardShortcutBehavior.enabled = true;
|
|
},
|
|
|
|
open: function() {
|
|
return new Promise(function(resolve) {
|
|
Gerrit.KeyboardShortcutBehavior.enabled = false;
|
|
Polymer.IronOverlayBehaviorImpl.open.apply(this, arguments);
|
|
this._awaitOpen(resolve);
|
|
}.bind(this));
|
|
},
|
|
|
|
close: function() {
|
|
Gerrit.KeyboardShortcutBehavior.enabled = true;
|
|
Polymer.IronOverlayBehaviorImpl.close.apply(this, arguments);
|
|
},
|
|
|
|
cancel: function() {
|
|
Gerrit.KeyboardShortcutBehavior.enabled = true;
|
|
Polymer.IronOverlayBehaviorImpl.cancel.apply(this, arguments);
|
|
},
|
|
|
|
/**
|
|
* Override the focus stops that iron-overlay-behavior tries to find.
|
|
*/
|
|
setFocusStops: function(stops) {
|
|
this.__firstFocusableNode = stops.start;
|
|
this.__lastFocusableNode = stops.end;
|
|
},
|
|
|
|
/**
|
|
* NOTE: (wyatta) Slightly hacky way to listen to the overlay actually
|
|
* opening. Eventually replace with a direct way to listen to the overlay.
|
|
*/
|
|
_awaitOpen: function(fn) {
|
|
var iters = 0;
|
|
function step() {
|
|
this.async(function() {
|
|
if (this.style.display !== 'none') {
|
|
fn.call(this);
|
|
} else if (iters++ < AWAIT_MAX_ITERS) {
|
|
step.call(this);
|
|
}
|
|
}.bind(this), AWAIT_STEP);
|
|
};
|
|
step.call(this);
|
|
},
|
|
});
|
|
})();
|