Prompt to confirm to add large group as reviewer
Depending on server configuration, a group with a certain number of members must be marked as explicitly confirmed by the user before it may be added as a reviewer. This change introduces an overlay on top of the reply dialog to prompt the user for this confirmation in advance of submission. Change-Id: I0a61819e785c9a80aaa12ebae347907e41b24aac
This commit is contained in:
committed by
Andrew Bonventre
parent
44cf1b5d03
commit
a008aa9428
@@ -24,6 +24,11 @@
|
|||||||
},
|
},
|
||||||
change: Object,
|
change: Object,
|
||||||
placeholder: String,
|
placeholder: String,
|
||||||
|
pendingConfirmation: {
|
||||||
|
type: Object,
|
||||||
|
value: null,
|
||||||
|
notify: true,
|
||||||
|
},
|
||||||
readonly: Boolean,
|
readonly: Boolean,
|
||||||
|
|
||||||
filter: {
|
filter: {
|
||||||
@@ -52,10 +57,22 @@
|
|||||||
var account = Object.assign({}, reviewer.account, {_pendingAdd: true});
|
var account = Object.assign({}, reviewer.account, {_pendingAdd: true});
|
||||||
this.push('accounts', account);
|
this.push('accounts', account);
|
||||||
} else if (reviewer.group) {
|
} else if (reviewer.group) {
|
||||||
|
if (reviewer.confirm) {
|
||||||
|
this.pendingConfirmation = reviewer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
var group = Object.assign({}, reviewer.group,
|
var group = Object.assign({}, reviewer.group,
|
||||||
{_pendingAdd: true, _group: true});
|
{_pendingAdd: true, _group: true});
|
||||||
this.push('accounts', group);
|
this.push('accounts', group);
|
||||||
}
|
}
|
||||||
|
this.pendingConfirmation = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
confirmGroup: function(group) {
|
||||||
|
group = Object.assign(
|
||||||
|
{}, group, {confirmed: true, _pendingAdd: true, _group: true});
|
||||||
|
this.push('accounts', group);
|
||||||
|
this.pendingConfirmation = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_computeChipClass: function(account) {
|
_computeChipClass: function(account) {
|
||||||
|
|||||||
@@ -199,5 +199,38 @@ limitations under the License.
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('large group confirmations', function() {
|
||||||
|
assert.isNull(element.pendingConfirmation);
|
||||||
|
assert.deepEqual(element.additions(), []);
|
||||||
|
|
||||||
|
var group = makeGroup();
|
||||||
|
var reviewer = {
|
||||||
|
group: group,
|
||||||
|
count: 10,
|
||||||
|
confirm: true,
|
||||||
|
};
|
||||||
|
element._handleAdd({
|
||||||
|
detail: {
|
||||||
|
value: reviewer,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(element.pendingConfirmation, reviewer);
|
||||||
|
assert.deepEqual(element.additions(), []);
|
||||||
|
|
||||||
|
element.confirmGroup(group);
|
||||||
|
assert.isNull(element.pendingConfirmation);
|
||||||
|
assert.deepEqual(element.additions(), [
|
||||||
|
{
|
||||||
|
group: {
|
||||||
|
id: group.id,
|
||||||
|
_group: true,
|
||||||
|
_pendingAdd: true,
|
||||||
|
confirmed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ limitations under the License.
|
|||||||
<link rel="import" href="../../../behaviors/rest-client-behavior.html">
|
<link rel="import" href="../../../behaviors/rest-client-behavior.html">
|
||||||
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
||||||
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
|
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
|
||||||
|
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
|
||||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||||
<link rel="import" href="../gr-account-list/gr-account-list.html">
|
<link rel="import" href="../gr-account-list/gr-account-list.html">
|
||||||
|
|
||||||
@@ -64,6 +65,19 @@ limitations under the License.
|
|||||||
gr-account-list {
|
gr-account-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
#reviewerConfirmationOverlay {
|
||||||
|
padding: 1em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.reviewerConfirmationButtons {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
.groupName {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.groupSize {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
.textareaContainer {
|
.textareaContainer {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -135,9 +149,32 @@ limitations under the License.
|
|||||||
id="reviewers"
|
id="reviewers"
|
||||||
accounts="[[_reviewers]]"
|
accounts="[[_reviewers]]"
|
||||||
change="[[change]]"
|
change="[[change]]"
|
||||||
|
pending-confirmation="{{_reviewerPendingConfirmation}}"
|
||||||
placeholder="Add reviewer...">
|
placeholder="Add reviewer...">
|
||||||
</gr-account-list>
|
</gr-account-list>
|
||||||
</div>
|
</div>
|
||||||
|
<gr-overlay
|
||||||
|
id="reviewerConfirmationOverlay"
|
||||||
|
on-iron-overlay-canceled="_cancelPendingReviewer"
|
||||||
|
with-backdrop>
|
||||||
|
<div class="reviewerConfirmation">
|
||||||
|
Group
|
||||||
|
<span class="groupName">
|
||||||
|
{{_reviewerPendingConfirmation.group.name}}
|
||||||
|
</span>
|
||||||
|
has
|
||||||
|
<span class="groupSize">
|
||||||
|
{{_reviewerPendingConfirmation.count}}
|
||||||
|
</span>
|
||||||
|
members.
|
||||||
|
<br>
|
||||||
|
Are you sure you want to add them all?
|
||||||
|
</div>
|
||||||
|
<div class="reviewerConfirmationButtons">
|
||||||
|
<gr-button on-tap="_confirmPendingReviewer">Yes</gr-button>
|
||||||
|
<gr-button on-tap="_cancelPendingReviewer">No</gr-button>
|
||||||
|
</div>
|
||||||
|
</gr-overlay>
|
||||||
</section>
|
</section>
|
||||||
<section class="textareaContainer">
|
<section class="textareaContainer">
|
||||||
<iron-autogrow-textarea
|
<iron-autogrow-textarea
|
||||||
|
|||||||
@@ -54,6 +54,10 @@
|
|||||||
_account: Object,
|
_account: Object,
|
||||||
_owners: Array,
|
_owners: Array,
|
||||||
_reviewers: Array,
|
_reviewers: Array,
|
||||||
|
_reviewerPendingConfirmation: {
|
||||||
|
type: Object,
|
||||||
|
observer: '_reviewerPendingConfirmationUpdated',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
FocusTarget: FocusTarget,
|
FocusTarget: FocusTarget,
|
||||||
@@ -130,15 +134,17 @@
|
|||||||
var newReviewers = this.$.reviewers.additions();
|
var newReviewers = this.$.reviewers.additions();
|
||||||
newReviewers.forEach(function(reviewer) {
|
newReviewers.forEach(function(reviewer) {
|
||||||
var reviewerId;
|
var reviewerId;
|
||||||
|
var confirmed;
|
||||||
if (reviewer.account) {
|
if (reviewer.account) {
|
||||||
reviewerId = reviewer.account._account_id;
|
reviewerId = reviewer.account._account_id;
|
||||||
} else if (reviewer.group) {
|
} else if (reviewer.group) {
|
||||||
reviewerId = reviewer.group.id;
|
reviewerId = reviewer.group.id;
|
||||||
|
confirmed = reviewer.group.confirmed;
|
||||||
}
|
}
|
||||||
if (!obj.reviewers) {
|
if (!obj.reviewers) {
|
||||||
obj.reviewers = [];
|
obj.reviewers = [];
|
||||||
}
|
}
|
||||||
obj.reviewers.push({reviewer: reviewerId});
|
obj.reviewers.push({reviewer: reviewerId, confirmed: confirmed});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.disabled = true;
|
this.disabled = true;
|
||||||
@@ -262,5 +268,23 @@
|
|||||||
return this.$.restAPI.saveChangeReview(this.change._number, this.patchNum,
|
return this.$.restAPI.saveChangeReview(this.change._number, this.patchNum,
|
||||||
review);
|
review);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_reviewerPendingConfirmationUpdated: function(reviewer) {
|
||||||
|
if (reviewer === null) {
|
||||||
|
this.$.reviewerConfirmationOverlay.close();
|
||||||
|
} else {
|
||||||
|
this.$.reviewerConfirmationOverlay.open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_confirmPendingReviewer: function() {
|
||||||
|
this.$.reviewers.confirmGroup(this._reviewerPendingConfirmation.group);
|
||||||
|
this.focusOn(FocusTarget.REVIEWERS);
|
||||||
|
},
|
||||||
|
|
||||||
|
_cancelPendingReviewer: function() {
|
||||||
|
this._reviewerPendingConfirmation = null;
|
||||||
|
this.focusOn(FocusTarget.REVIEWERS);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -144,5 +144,92 @@ limitations under the License.
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getActiveElement() {
|
||||||
|
return Polymer.IronOverlayManager.deepActiveElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVisible(el) {
|
||||||
|
assert.ok(el);
|
||||||
|
return getComputedStyle(el).getPropertyValue('display') != 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function overlayObserver(mode) {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
function listener() {
|
||||||
|
element.removeEventListener('iron-overlay-' + mode, listener);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
element.addEventListener('iron-overlay-' + mode, listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test('reviewer confirmation', function(done) {
|
||||||
|
var yesButton =
|
||||||
|
element.$$('.reviewerConfirmationButtons gr-button:first-child');
|
||||||
|
var noButton =
|
||||||
|
element.$$('.reviewerConfirmationButtons gr-button:last-child');
|
||||||
|
|
||||||
|
element._reviewerPendingConfirmation = null;
|
||||||
|
flushAsynchronousOperations();
|
||||||
|
assert.isFalse(isVisible(element.$.reviewerConfirmationOverlay));
|
||||||
|
|
||||||
|
// Cause the confirmation dialog to display.
|
||||||
|
var observer = overlayObserver('opened');
|
||||||
|
var group = {
|
||||||
|
id: 'id',
|
||||||
|
name: 'name',
|
||||||
|
count: 10,
|
||||||
|
};
|
||||||
|
element._reviewerPendingConfirmation = {
|
||||||
|
group: group,
|
||||||
|
};
|
||||||
|
|
||||||
|
observer.then(function() {
|
||||||
|
assert.isTrue(isVisible(element.$.reviewerConfirmationOverlay));
|
||||||
|
observer = overlayObserver('closed');
|
||||||
|
MockInteractions.tap(noButton); // close the overlay
|
||||||
|
return observer;
|
||||||
|
}).then(function() {
|
||||||
|
assert.isFalse(isVisible(element.$.reviewerConfirmationOverlay));
|
||||||
|
|
||||||
|
// We should be focused on account entry input.
|
||||||
|
assert.equal(getActiveElement().id, 'input');
|
||||||
|
|
||||||
|
// No reviewer should have been added.
|
||||||
|
assert.deepEqual(element.$.reviewers.additions(), []);
|
||||||
|
|
||||||
|
// Reopen confirmation dialog.
|
||||||
|
observer = overlayObserver('opened');
|
||||||
|
element._reviewerPendingConfirmation = {
|
||||||
|
group: group,
|
||||||
|
};
|
||||||
|
return observer;
|
||||||
|
}).then(function() {
|
||||||
|
assert.isTrue(isVisible(element.$.reviewerConfirmationOverlay));
|
||||||
|
observer = overlayObserver('closed');
|
||||||
|
MockInteractions.tap(yesButton); // confirm the group
|
||||||
|
return observer;
|
||||||
|
}).then(function() {
|
||||||
|
assert.isFalse(isVisible(element.$.reviewerConfirmationOverlay));
|
||||||
|
assert.deepEqual(
|
||||||
|
element.$.reviewers.additions(),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
group: {
|
||||||
|
id: 'id',
|
||||||
|
name: 'name',
|
||||||
|
count: 10,
|
||||||
|
confirmed: true,
|
||||||
|
_group: true,
|
||||||
|
_pendingAdd: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// We should be focused on account entry input.
|
||||||
|
assert.equal(getActiveElement().id, 'input');
|
||||||
|
}).then(done);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user