Optimize reviewer list

- add support for adding groups
- add support for adding reviewers without email address
- fix property 'suggestFrom'
  (start searching from specified value instead of value + 1)

Change-Id: I57c145cff94e143a711e4ed039539dcec914f32a
This commit is contained in:
Urs Wolfer
2016-01-22 17:45:15 +01:00
parent 3752568816
commit 940ceb348e
2 changed files with 44 additions and 32 deletions

View File

@@ -46,11 +46,11 @@ limitations under the License.
left: 0;
top: 100%;
}
.account {
.dropdown .reviewer {
cursor: pointer;
padding: .5em .75em;
}
.account[selected] {
.dropdown .reviewer[selected] {
background-color: #ccc;
}
.remove,
@@ -86,19 +86,24 @@ limitations under the License.
<div class="controlsContainer" hidden$="[[!mutable]]">
<div class="autocompleteContainer" hidden$="[[!_showInput]]">
<div class="inputContainer">
<input is="iron-input" type="email" id="input"
<input is="iron-input" id="input"
bind-value="{{_inputVal}}" disabled$="[[disabled]]">
<a href="#" class="cancel" on-tap="_handleCancelTap">×</a>
</div>
<div class="dropdown" hidden$="[[_hideAutocomplete]]">
<template is="dom-repeat" items="[[_autocompleteData]]" as="account">
<div class="account"
<template is="dom-repeat" items="[[_autocompleteData]]" as="reviewer">
<div class="reviewer"
data-index$="[[index]]"
on-mouseenter="_handleMouseEnterItem"
on-tap="_handleItemTap"
selected$="[[_computeSelected(index, _selectedIndex)]]">
<gr-account-label
account="[[account]]" show-email></gr-account-label>
<template is="dom-if" if="[[reviewer.account]]">
<gr-account-label
account="[[reviewer.account]]" show-email></gr-account-label>
</template>
<template is="dom-if" if="[[reviewer.group]]">
<span>[[reviewer.group.name]] (group)</span>
</template>
</div>
</template>
</div>
@@ -239,9 +244,9 @@ limitations under the License.
},
_handleResponse: function(e) {
this._autocompleteData = e.detail.response.map(function(result) {
return result.account;
}).filter(function(account) {
this._autocompleteData = e.detail.response.filter(function(reviewer) {
var account = reviewer.account;
if (!account) return true;
for (var i = 0; i < this._reviewers.length; i++) {
if (account._account_id == this.change.owner._account_id ||
account._account_id == this._reviewers[i]._account_id) {
@@ -302,17 +307,17 @@ limitations under the License.
},
_handleItemTap: function(e) {
var accountEl;
var reviewerEl;
var eventPath = Polymer.dom(e).path;
for (var i = 0; i < eventPath.length; i++) {
var el = eventPath[i];
if (el.classList && el.classList.contains('account')) {
accountEl = el;
if (el.classList && el.classList.contains('reviewer')) {
reviewerEl = el;
break;
}
}
this._selectedIndex =
parseInt(accountEl.getAttribute('data-index'), 10);
parseInt(reviewerEl.getAttribute('data-index'), 10);
this._sendAddRequest();
},
@@ -336,7 +341,7 @@ limitations under the License.
if (this.disabled || val == null || val.trim().length == 0) {
return;
}
if (val.length <= this.suggestFrom) {
if (val.length < this.suggestFrom) {
this._clearInputRequestHandle();
this._hideAutocomplete = true;
this._selectedIndex = -1;
@@ -396,13 +401,15 @@ limitations under the License.
_sendAddRequest: function() {
this._clearInputRequestHandle();
// TODO(andybons): Support groups and non-email types.
var account = this._autocompleteData[this._selectedIndex];
if (account) {
this._inputVal = account.email;
var reviewerID;
var reviewer = this._autocompleteData[this._selectedIndex];
if (reviewer.account) {
reviewerID = reviewer.account._account_id;
} else if (reviewer.group) {
reviewerID = reviewer.group.id;
}
this._autocompleteData = [];
this._send('POST', this._restEndpoint()).then(function(req) {
this._send('POST', this._restEndpoint(), reviewerID).then(function(req) {
this.change.reviewers['CC'] = this.change.reviewers['CC'] || [];
req.response.reviewers.forEach(function(r) {
this.push('change.removable_reviewers', r);
@@ -412,21 +419,20 @@ limitations under the License.
this.$.input.focus();
}.bind(this)).catch(function(err) {
// TODO(andybons): Use the message returned by the server.
alert('Unable to add ' + this._inputVal + ' as a reviewer. ' +
'Are you sure the email is right?');
alert('Unable to add ' + reviewerID + ' as a reviewer.');
throw err;
}.bind(this));
},
_send: function(method, url) {
_send: function(method, url, reviewerID) {
this.disabled = true;
var request = document.createElement('gr-request');
var opts = {
method: method,
url: url,
};
if (method == 'PUT' || method == 'POST') {
opts.body = {reviewer: this._inputVal};
if (reviewerID) {
opts.body = {reviewer: reviewerID};
}
this._xhrPromise = request.send(opts);
var enableEl = function() { this.disabled = false; }.bind(this);

View File

@@ -61,6 +61,12 @@ limitations under the License.
name: "Andrew Bonventre",
email: "andybons@google.com",
}
},
{
"group": {
"id": "c7af6dd375c092ff3b23c0937aa910693dc0c41b",
"name": "andy"
}
}
]),
]
@@ -183,7 +189,7 @@ limitations under the License.
});
});
test('autocomplete starts at >= 4 chars', function() {
test('autocomplete starts at >= 3 chars', function() {
element._inputRequestTimeout = 0;
element._mutable = true;
var genRequestStub = sinon.stub(
@@ -191,9 +197,9 @@ limitations under the License.
'generateRequest',
function() {
assert(false, 'generateRequest should not be called for input ' +
'lengths of less than 4 chars');
'lengths of less than 3 chars');
});
element._inputVal = 'foo';
element._inputVal = 'fo';
flushAsynchronousOperations();
genRequestStub.restore();
});
@@ -215,8 +221,8 @@ limitations under the License.
element._lastAutocompleteRequest.completes.then(function() {
flushAsynchronousOperations();
assert.isFalse(element.$$('.dropdown').hasAttribute('hidden'));
var itemEls = Polymer.dom(element.root).querySelectorAll('.account');
assert.equal(itemEls.length, 2);
var itemEls = Polymer.dom(element.root).querySelectorAll('.reviewer');
assert.equal(itemEls.length, 3);
assert.isTrue(itemEls[0].hasAttribute('selected'));
assert.isFalse(itemEls[1].hasAttribute('selected'));
@@ -236,8 +242,8 @@ limitations under the License.
element._lastAutocompleteRequest.completes.then(function() {
assert.isFalse(element.$$('.dropdown').hasAttribute('hidden'));
var itemEls = Polymer.dom(element.root).querySelectorAll('.account');
assert.equal(itemEls.length, 2);
var itemEls = Polymer.dom(element.root).querySelectorAll('.reviewer');
assert.equal(itemEls.length, 3);
assert.isTrue(itemEls[0].hasAttribute('selected'));
assert.isFalse(itemEls[1].hasAttribute('selected'));
MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'