Merge "Allow for deletion of votes from changes"

This commit is contained in:
David Pursehouse
2016-10-06 02:06:05 +00:00
committed by Gerrit Code Review
7 changed files with 144 additions and 7 deletions

View File

@@ -16,7 +16,7 @@ limitations under the License.
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/rest-client-behavior.html">
<link rel="import" href="../../shared/gr-account-link/gr-account-link.html">
<link rel="import" href="../../shared/gr-account-chip/gr-account-chip.html">
<link rel="import" href="../../shared/gr-label/gr-label.html">
<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
<link rel="import" href="../../shared/gr-editable-label/gr-editable-label.html">
@@ -45,12 +45,13 @@ limitations under the License.
}
.labelValueContainer .approved,
.labelValueContainer .notApproved {
display: inline-block;
display: inline-flex;
padding: .1em .3em;
border-radius: 3px;
}
.labelValue {
display: inline-block;
padding-right: .3em;
}
.approved {
background-color: #d4ffd4;
@@ -150,7 +151,7 @@ limitations under the License.
<span class="title">[[labelName]]</span>
<span class="value">
<template is="dom-repeat"
items="[[_computeLabelValues(labelName, change.labels)]]"
items="[[_computeLabelValues(labelName, change.labels.*)]]"
as="label">
<div class="labelValueContainer">
<span class$="[[label.className]]">
@@ -160,7 +161,13 @@ limitations under the License.
class="labelValue">
[[label.value]]
</gr-label>
<gr-account-link account="[[label.account]]"></gr-account-link>
<gr-account-chip
account="[[label.account]]"
data-account-id$="[[label.account._account_id]]"
label-name="[[labelName]]"
removable="[[_computeCanDeleteVote(label.account, mutable)]]"
transparent-background
on-remove="_onDeleteVote"></gr-account-chip>
</span>
</div>
</template>

View File

@@ -55,8 +55,9 @@
return Object.keys(labels).sort();
},
_computeLabelValues: function(labelName, labels) {
_computeLabelValues: function(labelName, _labels) {
var result = [];
var labels = _labels.base;
var t = labels[labelName];
if (!t) { return result; }
var approvals = t.all || [];
@@ -101,5 +102,46 @@
_computeShowReviewersByState: function(serverConfig) {
return !!serverConfig.note_db_enabled;
},
/**
* A user is able to delete a vote iff the mutable property is true and the
* reviewer that left the vote exists in the list of removable_reviewers
* received from the backend.
*
* @param {!Object} reviewer An object describing the reviewer that left the
* vote.
* @param {boolean} mutable this.mutable describes whether the
* change-metadata section is modifiable by the current user.
*/
_computeCanDeleteVote: function(reviewer, mutable) {
if (!mutable) { return false; }
for (var i = 0; i < this.change.removable_reviewers.length; i++) {
if (this.change.removable_reviewers[i]._account_id ===
reviewer._account_id) {
return true;
}
}
return false;
},
_onDeleteVote: function(e) {
e.preventDefault();
var target = Polymer.dom(e).rootTarget;
var labelName = target.labelName;
var accountID = parseInt(target.getAttribute('data-account-id'), 10);
this._xhrPromise =
this.$.restAPI.deleteVote(this.change.id, accountID, labelName)
.then(function(response) {
if (!response.ok) { return response; }
var labels = this.change.labels[labelName].all || [];
for (var i = 0; i < labels.length; i++) {
if (labels[i]._account_id === accountID) {
this.splice(['change.labels', labelName, 'all'], i, 1);
break;
}
}
}.bind(this));
},
});
})();

View File

@@ -77,5 +77,74 @@ limitations under the License.
element.serverConfig = {note_db_enabled: true};
assert.isTrue(hasCc());
});
suite('remove reviewer votes', function() {
var sandbox;
setup(function() {
sandbox = sinon.sandbox.create();
sandbox.stub(element, '_computeValueTooltip').returns('');
sandbox.stub(element, '_computeTopicReadOnly').returns(true);
element.change = {
status: 'NEW',
submit_type: 'CHERRY_PICK',
labels: {
test: {
all: [{_account_id: 1, name: 'bojack', value: 1}],
default_value: 0,
},
},
removable_reviewers: [],
};
});
teardown(function() {
sandbox.restore();
});
test('_computeCanDeleteVote hides delete button', function() {
flushAsynchronousOperations();
var button = element.$$('gr-account-chip').$$('gr-button');
assert.isTrue(button.hasAttribute('hidden'));
element.mutable = true;
assert.isTrue(button.hasAttribute('hidden'));
});
test('_computeCanDeleteVote shows delete button', function() {
element.change.removable_reviewers = [
{
_account_id: 1,
name: 'bojack',
}
];
element.mutable = true;
flushAsynchronousOperations();
var button = element.$$('gr-account-chip').$$('gr-button');
assert.isFalse(button.hasAttribute('hidden'));
});
test('deletes votes', function(done) {
sandbox.stub(element.$.restAPI, 'deleteVote')
.returns(Promise.resolve({'ok': true}));
element.change.removable_reviewers = [
{
_account_id: 1,
name: 'bojack',
}
];
element.mutable = true;
flushAsynchronousOperations();
var button = element.$$('gr-account-chip').$$('gr-button');
MockInteractions.tap(button);
flushAsynchronousOperations();
var spliceStub = sinon.stub(element, 'splice',
function(path, index, length) {
assert.deepEqual(path, ['change.labels', 'test', 'all']);
assert.equal(index, 0);
assert.equal(length, 1);
spliceStub.restore();
done();
});
});
});
});
</script>

View File

@@ -53,12 +53,17 @@ limitations under the License.
padding: 0;
text-decoration: none;
}
.transparentBackground,
gr-button.transparentBackground {
background-color: transparent;
}
</style>
<div class="container">
<div class$="container [[_getBackgroundClass(transparentBackground)]]">
<gr-account-link account="[[account]]"></gr-account-link>
<gr-button
hidden$="[[!removable]]" hidden
class="remove" on-tap="_handleRemoveTap">×</gr-button>
class$="remove [[_getBackgroundClass(transparentBackground)]]"
on-tap="_handleRemoveTap">×</gr-button>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>

View File

@@ -28,6 +28,10 @@
type: Boolean,
reflectToAttribute: true,
},
transparentBackground: {
type: Boolean,
value: false,
},
},
ready: function() {
@@ -36,6 +40,10 @@
}.bind(this));
},
_getBackgroundClass: function(transparent) {
return transparent ? 'transparentBackground' : '';
},
_handleRemoveTap: function(e) {
e.preventDefault();
this.fire('remove', {account: this.account});

View File

@@ -47,5 +47,6 @@ limitations under the License.
</span>
</span>
</template>
<script src="../../../scripts/util.js"></script>
<script src="gr-account-label.js"></script>
</dom-module>

View File

@@ -865,5 +865,10 @@
deleteAccountSSHKey: function(id) {
return this.send('DELETE', '/accounts/self/sshkeys/' + id);
},
deleteVote: function(changeID, account, label) {
return this.send('DELETE', '/changes/' + changeID +
'/reviewers/' + account + '/votes/' + encodeURIComponent(label));
},
});
})();