Add reply dropdown to publish drafts
One notable thing that I intentionally did was name the event coming from the dropdown more simply. This makes it easier to add a handler within the html like `on-send` instead of `on-gr-reply-dropdown-send`. After a bit of thought I think we should simplify all event names for this purpose. Feature: Issue 3649 Change-Id: I2460db54b5d85243988c22f572c00043c5597210
This commit is contained in:
parent
e4cc4a944f
commit
f8b026d227
@ -8,6 +8,7 @@ bower_components(
|
||||
'//lib/js:iron-autogrow-textarea',
|
||||
'//lib/js:iron-dropdown',
|
||||
'//lib/js:iron-input',
|
||||
'//lib/js:iron-selector',
|
||||
'//lib/js:page',
|
||||
'//lib/js:polymer',
|
||||
],
|
||||
|
@ -41,7 +41,7 @@ limitations under the License.
|
||||
:host([constrained]) main {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 980px;
|
||||
max-width: var(--max-constrained-width);
|
||||
}
|
||||
header,
|
||||
footer {
|
||||
|
@ -20,28 +20,50 @@ limitations under the License.
|
||||
<link rel="import" href="gr-date-formatter.html">
|
||||
<link rel="import" href="gr-file-list.html">
|
||||
<link rel="import" href="gr-messages-list.html">
|
||||
<link rel="import" href="gr-reply-dropdown.html">
|
||||
|
||||
<dom-module id="gr-change-view">
|
||||
<template>
|
||||
<style>
|
||||
.container {
|
||||
margin: 1em 0;
|
||||
padding-top: 1em;
|
||||
}
|
||||
.container:not(.loading) {
|
||||
background-color: var(--view-background-color);
|
||||
}
|
||||
.container.loading {
|
||||
color: #666;
|
||||
padding: 0;
|
||||
}
|
||||
h2 {
|
||||
padding: 0 var(--default-horizontal-margin);
|
||||
.headerContainer {
|
||||
height: 4.1em;
|
||||
}
|
||||
.header {
|
||||
background-color: var(--view-background-color);
|
||||
display: flex;
|
||||
max-width: var(--max-constrained-width);
|
||||
padding: 1em var(--default-horizontal-margin);
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
.header.pinned {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
transition: box-shadow 250ms linear;
|
||||
}
|
||||
.header h2 {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
section {
|
||||
margin: 10px 0;
|
||||
padding: 10px var(--default-horizontal-margin);
|
||||
}
|
||||
section:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
@ -72,66 +94,73 @@ limitations under the License.
|
||||
<gr-ajax id="commentsXHR"
|
||||
url="[[_computeCommentsPath(changeNum)]]"
|
||||
last-response="{{comments}}"></gr-ajax>
|
||||
<template is="dom-if" if="{{_loading}}">
|
||||
<div class="container loading">Loading...</div>
|
||||
</template>
|
||||
<template is="dom-if" if="{{!_loading}}">
|
||||
<div class="container">
|
||||
<h2>
|
||||
<a href$="[[_computeChangePath(change._number)]]">[[change._number]]</a><span>:</span>
|
||||
<span>[[change.subject]]</span>
|
||||
</h2>
|
||||
<section class="changeInfo">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Owner</td>
|
||||
<td>[[change.owner.name]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Reviewers</td>
|
||||
<td>
|
||||
<template is="dom-repeat"
|
||||
items="[[_computeReviewers(change.labels.Code-Review.all, change.owner)]]"
|
||||
as="reviewer">
|
||||
<div>[[reviewer.name]]</div>
|
||||
</template>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Project</td>
|
||||
<td>[[change.project]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Branch</td>
|
||||
<td>[[change.branch]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Topic</td>
|
||||
<td>[[change.topic]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Strategy</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Updated</td>
|
||||
<td>
|
||||
<gr-date-formatter
|
||||
date-str="[[change.updated]]"></gr-date-formatter>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
<section class="summary">[[_computeCurrentRevisionMessage(change)]]</section>
|
||||
<gr-file-list change-num="[[changeNum]]"
|
||||
patch-num="[[_computePatchNum(change.current_revision)]]"
|
||||
revision="[[change.current_revision]]"
|
||||
comments="[[comments]]"></gr-file-list>
|
||||
<gr-messages-list change-num="[[changeNum]]"
|
||||
messages="[[change.messages]]"
|
||||
comments="[[comments]]"></gr-messages-list>
|
||||
<div class="container loading" hidden$="{{!_loading}}">Loading...</div>
|
||||
<div class="container" hidden$="{{_loading}}">
|
||||
<div class="headerContainer">
|
||||
<div class="header">
|
||||
<h2>
|
||||
<a href$="[[_computeChangePath(change._number)]]">[[change._number]]</a><span>:</span>
|
||||
<span>[[change.subject]]</span>
|
||||
</h2>
|
||||
<gr-reply-dropdown id="replyDropdown"
|
||||
change-num="[[changeNum]]"
|
||||
patch-num="[[_computePatchNum(change.current_revision)]]"
|
||||
labels="[[change.labels]]"
|
||||
permitted-labels="[[change.permitted_labels]]"
|
||||
on-send="_handleReplySent"
|
||||
hidden$="[[!_loggedIn]]">Reply</gr-reply-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<section class="changeInfo">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Owner</td>
|
||||
<td>[[change.owner.name]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Reviewers</td>
|
||||
<td>
|
||||
<template is="dom-repeat"
|
||||
items="[[_computeReviewers(change.labels, change.owner)]]"
|
||||
as="reviewer">
|
||||
<div>[[reviewer.name]]</div>
|
||||
</template>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Project</td>
|
||||
<td>[[change.project]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Branch</td>
|
||||
<td>[[change.branch]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Topic</td>
|
||||
<td>[[change.topic]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Strategy</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="changeInfo-label">Updated</td>
|
||||
<td>
|
||||
<gr-date-formatter
|
||||
date-str="[[change.updated]]"></gr-date-formatter>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
<section class="summary">[[_computeCurrentRevisionMessage(change)]]</section>
|
||||
<gr-file-list change-num="[[changeNum]]"
|
||||
patch-num="[[_computePatchNum(change.current_revision)]]"
|
||||
revision="[[change.current_revision]]"
|
||||
comments="[[comments]]"></gr-file-list>
|
||||
<gr-messages-list change-num="[[changeNum]]"
|
||||
messages="[[change.messages]]"
|
||||
comments="[[comments]]"></gr-messages-list>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
(function() {
|
||||
@ -159,13 +188,51 @@ limitations under the License.
|
||||
observer: '_paramsChanged',
|
||||
},
|
||||
changeNum: Number,
|
||||
|
||||
_loggedIn: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
_loading: Boolean,
|
||||
_headerContainerEl: Object,
|
||||
_headerEl: Object,
|
||||
},
|
||||
|
||||
keyBindings: {
|
||||
'a u': '_handleKey',
|
||||
},
|
||||
|
||||
ready: function() {
|
||||
app.accountReady.then(function() {
|
||||
this._loggedIn = app.loggedIn;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
attached: function() {
|
||||
window.addEventListener('scroll',
|
||||
this._handleBodyScroll.bind(this));
|
||||
},
|
||||
|
||||
_handleBodyScroll: function(e) {
|
||||
var containerEl = this._headerContainerEl ||
|
||||
this.$$('.headerContainer');
|
||||
|
||||
// Calculate where the header is relative to the window.
|
||||
var top = containerEl.offsetTop;
|
||||
for (var offsetParent = containerEl.offsetParent;
|
||||
offsetParent;
|
||||
offsetParent = offsetParent.offsetParent) {
|
||||
top += offsetParent.offsetTop;
|
||||
}
|
||||
|
||||
var el = this._headerEl || this.$$('.header');
|
||||
el.classList.toggle('pinned', window.scrollY >= top);
|
||||
},
|
||||
|
||||
_handleReplySent: function(e) {
|
||||
this._reload();
|
||||
},
|
||||
|
||||
_paramsChanged: function(value) {
|
||||
this.changeNum = value.changeNum;
|
||||
if (!this.changeNum) {
|
||||
@ -173,8 +240,7 @@ limitations under the License.
|
||||
this.comments = null;
|
||||
return;
|
||||
}
|
||||
this.$.detailXHR.generateRequest();
|
||||
this.$.commentsXHR.generateRequest();
|
||||
this._reload();
|
||||
},
|
||||
|
||||
_computeChangePath: function(changeNum) {
|
||||
@ -211,7 +277,9 @@ limitations under the License.
|
||||
change.revisions[change.current_revision].commit.message;
|
||||
},
|
||||
|
||||
_computeReviewers: function(reviewers, owner) {
|
||||
_computeReviewers: function(labels, owner) {
|
||||
var reviewers =
|
||||
(labels['Code-Review'] && labels['Code-Review'].all) || [];
|
||||
if (reviewers.length == 1) { return reviewers; }
|
||||
return reviewers.filter(function(reviewer) {
|
||||
return reviewer._account_id != owner._account_id;
|
||||
@ -220,10 +288,10 @@ limitations under the License.
|
||||
|
||||
_handleKey: function(e) {
|
||||
if (util.shouldSupressKeyboardShortcut(e)) { return; }
|
||||
|
||||
e.preventDefault();
|
||||
switch(e.detail.combo) {
|
||||
case 'a':
|
||||
this._showReplyField();
|
||||
this.$.replyDropdown.open();
|
||||
break;
|
||||
case 'u':
|
||||
page.show('/');
|
||||
@ -231,8 +299,9 @@ limitations under the License.
|
||||
}
|
||||
},
|
||||
|
||||
_showReplyField: function() {
|
||||
// TODO(andybons): Implement reply field.
|
||||
_reload: function() {
|
||||
this.$.detailXHR.generateRequest();
|
||||
this.$.commentsXHR.generateRequest();
|
||||
},
|
||||
|
||||
});
|
||||
|
@ -203,7 +203,7 @@ limitations under the License.
|
||||
},
|
||||
patchNum: String,
|
||||
|
||||
_xhrPromise: Object,
|
||||
_xhrPromise: Object, // Used for testing.
|
||||
_editDraft: String,
|
||||
},
|
||||
|
||||
|
253
polygerrit-ui/app/elements/gr-reply-dropdown.html
Normal file
253
polygerrit-ui/app/elements/gr-reply-dropdown.html
Normal file
@ -0,0 +1,253 @@
|
||||
<!--
|
||||
Copyright (C) 2015 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.
|
||||
-->
|
||||
|
||||
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
|
||||
<link rel="import" href="../bower_components/iron-dropdown/iron-dropdown.html">
|
||||
<link rel="import" href="../bower_components/iron-selector/iron-selector.html">
|
||||
<link rel="import" href="gr-request.html">
|
||||
|
||||
<dom-module id="gr-reply-dropdown">
|
||||
<style>
|
||||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
:host([disabled]) {
|
||||
pointer-events: none;
|
||||
}
|
||||
:host([disabled]) .dropdown-content {
|
||||
opacity: .5;
|
||||
}
|
||||
.dropdown-content {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 1px 5px rgba(0, 0, 0, .3);
|
||||
}
|
||||
button {
|
||||
font: inherit;
|
||||
background-color: #f1f2f3;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 2px;
|
||||
padding: .2em .5em;
|
||||
}
|
||||
section {
|
||||
border-top: 1px solid #ddd;
|
||||
padding: .5em .75em;
|
||||
}
|
||||
.textareaContainer {
|
||||
position: relative;
|
||||
}
|
||||
.message {
|
||||
border: none;
|
||||
}
|
||||
.label:first-of-type {
|
||||
margin-bottom: .25em;
|
||||
}
|
||||
.labelName {
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
width: 6em;
|
||||
margin-right: .5em;
|
||||
}
|
||||
iron-selector {
|
||||
display: inline-flex;
|
||||
}
|
||||
iron-selector > button {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-left: none;
|
||||
padding: .25em 0;
|
||||
cursor: pointer;
|
||||
width: 3em;
|
||||
text-align: center;
|
||||
}
|
||||
iron-selector > button:first-of-type {
|
||||
border-top-left-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
iron-selector > button:last-of-type {
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
iron-selector > button.iron-selected {
|
||||
background-color: #ddd;
|
||||
}
|
||||
.actionsContainer {
|
||||
display: flex;
|
||||
}
|
||||
.action:link,
|
||||
.action:visited {
|
||||
color: #00e;
|
||||
}
|
||||
.danger {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<button id="trigger" on-tap="_showPopupTapHandler">Reply</button>
|
||||
<iron-dropdown id="dropdown"
|
||||
vertical-align="top"
|
||||
vertical-offset="25"
|
||||
horizontal-align="right">
|
||||
<div class="dropdown-content">
|
||||
<section class="textareaContainer">
|
||||
<iron-autogrow-textarea
|
||||
id="textarea"
|
||||
class="message"
|
||||
placeholder="Say something..."
|
||||
disabled="{{disabled}}"
|
||||
rows="1"
|
||||
bind-value="{{_draft}}"></iron-autogrow-textarea>
|
||||
</section>
|
||||
<section>
|
||||
<template is="dom-repeat"
|
||||
items="[[_computeLabelArray(permittedLabels)]]" as="label">
|
||||
<span class="labelName">[[label]]</span>
|
||||
<iron-selector data-label$="[[label]]"
|
||||
selected="[[_computeIndexOfDefaultLabelValue(labels, permittedLabels, label)]]">
|
||||
<template is="dom-repeat"
|
||||
items="[[_computePermittedLabelValues(permittedLabels, label)]]"
|
||||
as="value">
|
||||
<button data-value$="[[value]]">[[value]]</button>
|
||||
</template>
|
||||
</iron-selector>
|
||||
</template>
|
||||
</section>
|
||||
<section class="actionsContainer">
|
||||
<a class="action send" href="#" on-tap="_sendTapHandler">Send</a>
|
||||
<div class="danger">
|
||||
<a class="action cancel" href="#" on-tap="_cancelTapHandler">Cancel</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</iron-dropdown>
|
||||
</template>
|
||||
<script>
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
Polymer({
|
||||
is: 'gr-reply-dropdown',
|
||||
|
||||
/**
|
||||
* Fired when a reply is successfully sent.
|
||||
*
|
||||
* @event send
|
||||
*/
|
||||
|
||||
properties: {
|
||||
changeNum: String,
|
||||
patchNum: String,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
reflectToAttribute: true,
|
||||
},
|
||||
labels: Object,
|
||||
permittedLabels: Object,
|
||||
|
||||
_xhrPromise: Object, // Used for testing.
|
||||
_draft: String,
|
||||
},
|
||||
|
||||
get opened() {
|
||||
return this.$.dropdown.opened;
|
||||
},
|
||||
|
||||
open: function() {
|
||||
this.$.dropdown.open();
|
||||
this.async(function() {
|
||||
this.$.textarea.textarea.focus();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.$.dropdown.close();
|
||||
},
|
||||
|
||||
_computeLabelArray: function(labelsObj) {
|
||||
return Object.keys(labelsObj).sort();
|
||||
},
|
||||
|
||||
_computeIndexOfDefaultLabelValue: function(labels, permittedLabels,
|
||||
labelName) {
|
||||
for (var i = 0; i < permittedLabels[labelName].length; i++) {
|
||||
var val = parseInt(permittedLabels[labelName][i], 10);
|
||||
if (val == labels[labelName].default_value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_computePermittedLabelValues: function(permittedLabels, label) {
|
||||
return permittedLabels[label];
|
||||
},
|
||||
|
||||
_showPopupTapHandler: function(e) {
|
||||
e.preventDefault();
|
||||
this.open();
|
||||
},
|
||||
|
||||
_cancelTapHandler: function(e) {
|
||||
e.preventDefault();
|
||||
this.$.dropdown.close();
|
||||
},
|
||||
|
||||
_sendTapHandler: function(e) {
|
||||
e.preventDefault();
|
||||
var obj = {
|
||||
drafts: 'PUBLISH_ALL_REVISIONS',
|
||||
labels: {},
|
||||
};
|
||||
for (var label in this.permittedLabels) {
|
||||
var selectorEl = this.$$('iron-selector[data-label="' + label + '"]');
|
||||
var selectedVal = selectorEl.selectedItem.getAttribute('data-value');
|
||||
selectedVal = parseInt(selectedVal, 10);
|
||||
obj.labels[label] = selectedVal;
|
||||
}
|
||||
if (this._draft != null) {
|
||||
obj.message = this._draft;
|
||||
}
|
||||
this.disabled = true;
|
||||
this._send(obj).then(function(req) {
|
||||
this.fire('send', {bubbles: false});
|
||||
this._draft = '';
|
||||
this.disabled = false;
|
||||
this.$.dropdown.close();
|
||||
}.bind(this)).catch(function(err) {
|
||||
alert('Oops. Something went wrong. Check the console and bug the ' +
|
||||
'PolyGerrit team for assistance.');
|
||||
throw err;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_send: function(payload) {
|
||||
var xhr = document.createElement('gr-request');
|
||||
this._xhrPromise = xhr.send({
|
||||
method: 'POST',
|
||||
url: Changes.baseURL(this.changeNum, this.patchNum) + '/review',
|
||||
body: payload,
|
||||
});
|
||||
|
||||
return this._xhrPromise;
|
||||
},
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</dom-module>
|
@ -87,3 +87,7 @@ Changes.listChangesOptionsToHex = function() {
|
||||
}
|
||||
return v.toString(16);
|
||||
};
|
||||
|
||||
Changes.baseURL = function(changeNum, patchNum) {
|
||||
return '/changes/' + changeNum + '/revisions/' + patchNum;
|
||||
};
|
||||
|
@ -22,5 +22,6 @@ limitations under the License.
|
||||
--default-text-color: #000;
|
||||
--view-background-color: #fff;
|
||||
--default-horizontal-margin: 1.25rem;
|
||||
--max-constrained-width: 980px;
|
||||
}
|
||||
</style>
|
||||
|
@ -22,6 +22,7 @@ limitations under the License.
|
||||
<script src="../../bower_components/web-component-tester/browser.js"></script>
|
||||
<script src="../../bower_components/page/page.js"></script>
|
||||
<script src="../scripts/changes.js"></script>
|
||||
<script src="../scripts/fake-app.js"></script>
|
||||
<script src="../scripts/util.js"></script>
|
||||
|
||||
<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
|
||||
@ -41,7 +42,7 @@ limitations under the License.
|
||||
element = fixture('basic');
|
||||
});
|
||||
|
||||
test('keyboard shortcuts', function(done) {
|
||||
test('keyboard shortcuts', function() {
|
||||
var showStub = sinon.stub(page, 'show');
|
||||
|
||||
MockInteractions.pressAndReleaseKeyOn(element, 85); // 'u'
|
||||
@ -49,13 +50,11 @@ limitations under the License.
|
||||
'Should navigate to /');
|
||||
showStub.restore();
|
||||
|
||||
// TODO(andybons): Actually test the effect once _showReplyField is
|
||||
// implemented.
|
||||
sinon.stub(element, '_showReplyField', function() {
|
||||
element._showReplyField.restore();
|
||||
done();
|
||||
});
|
||||
MockInteractions.pressAndReleaseKeyOn(element, 65); // 'a'
|
||||
var dropdownEl = element.$.replyDropdown;
|
||||
assert.isTrue(dropdownEl.opened);
|
||||
dropdownEl.close();
|
||||
assert.isFalse(dropdownEl.opened);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -269,6 +269,7 @@ limitations under the License.
|
||||
}
|
||||
|
||||
test('jump to line', function() {
|
||||
window.scrollTo(0, 0);
|
||||
element._jumpToLine(-12849);
|
||||
assert.equal(window.scrollY, 0);
|
||||
element._jumpToLine('sup');
|
||||
|
160
polygerrit-ui/app/test/gr-reply-dropdown-test.html
Normal file
160
polygerrit-ui/app/test/gr-reply-dropdown-test.html
Normal file
@ -0,0 +1,160 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2015 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.
|
||||
-->
|
||||
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||
<title>gr-reply-dropdown</title>
|
||||
|
||||
<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
|
||||
<script src="../../bower_components/web-component-tester/browser.js"></script>
|
||||
<script src="../scripts/changes.js"></script>
|
||||
<script src="../scripts/util.js"></script>
|
||||
|
||||
<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
|
||||
<link rel="import" href="../elements/gr-reply-dropdown.html">
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<gr-reply-dropdown></gr-reply-dropdown>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('gr-reply-dropdown tests', function() {
|
||||
var element;
|
||||
var server;
|
||||
|
||||
setup(function() {
|
||||
element = fixture('basic');
|
||||
element.changeNum = 42;
|
||||
element.patchNum = 1;
|
||||
element.labels = {
|
||||
Verified: {
|
||||
values: {
|
||||
'-1': 'Fails',
|
||||
' 0': 'No score',
|
||||
'+1': 'Verified'
|
||||
},
|
||||
default_value: 0
|
||||
},
|
||||
'Code-Review': {
|
||||
values: {
|
||||
'-2': 'Do not submit',
|
||||
'-1': 'I would prefer that you didn\'t submit this',
|
||||
' 0': 'No score',
|
||||
'+1': 'Looks good to me, but someone else must approve',
|
||||
'+2': 'Looks good to me, approved'
|
||||
},
|
||||
default_value: 0
|
||||
}
|
||||
};
|
||||
element.permittedLabels = {
|
||||
'Code-Review': [
|
||||
'-1',
|
||||
' 0',
|
||||
'+1'
|
||||
],
|
||||
Verified: [
|
||||
'-1',
|
||||
' 0',
|
||||
'+1'
|
||||
]
|
||||
};
|
||||
|
||||
server = sinon.fakeServer.create();
|
||||
server.respondWith(
|
||||
'POST',
|
||||
'/changes/42/revisions/1/review',
|
||||
[
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
')]}\'\n' +
|
||||
'{' +
|
||||
'"labels": {' +
|
||||
'"Code-Review": -1,' +
|
||||
'"Verified": -1' +
|
||||
'}' +
|
||||
'}'
|
||||
]
|
||||
);
|
||||
|
||||
// Allow the elements created by dom-repeat to be stamped.
|
||||
flushAsynchronousOperations();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
server.restore();
|
||||
});
|
||||
|
||||
test('open close', function() {
|
||||
assert.isFalse(element.opened);
|
||||
MockInteractions.tap(element.$.trigger);
|
||||
assert.isTrue(element.opened);
|
||||
element.close();
|
||||
assert.isFalse(element.opened);
|
||||
element.open();
|
||||
assert.isTrue(element.opened);
|
||||
MockInteractions.tap(element.$$('.cancel'));
|
||||
assert.isFalse(element.opened);
|
||||
});
|
||||
|
||||
test('label picker', function(done) {
|
||||
assert.isFalse(element.opened);
|
||||
MockInteractions.tap(element.$.trigger);
|
||||
assert.isTrue(element.opened);
|
||||
for (var label in element.permittedLabels) {
|
||||
assert.ok(element.$$('iron-selector[data-label="' + label + '"]'),
|
||||
label);
|
||||
}
|
||||
element._draft = 'I wholeheartedly disapprove';
|
||||
MockInteractions.tap(element.$$(
|
||||
'iron-selector[data-label="Code-Review"] > button[data-value="-1"]'));
|
||||
MockInteractions.tap(element.$$(
|
||||
'iron-selector[data-label="Verified"] > button[data-value="-1"]'));
|
||||
|
||||
// This is needed on non-Blink engines most likely due to the ways in
|
||||
// which the dom-repeat elements are stamped.
|
||||
element.async(function() {
|
||||
MockInteractions.tap(element.$$('.send'));
|
||||
assert.isTrue(element.disabled);
|
||||
|
||||
server.respond();
|
||||
|
||||
element._xhrPromise.then(function(req) {
|
||||
assert.isFalse(element.disabled,
|
||||
'Element should be enabled when done sending reply.');
|
||||
assert.isFalse(element.opened);
|
||||
assert.equal(req.status, 200);
|
||||
assert.equal(req.url, '/changes/42/revisions/1/review');
|
||||
var reqObj = JSON.parse(req.xhr.requestBody);
|
||||
assert.deepEqual(reqObj, {
|
||||
drafts: 'PUBLISH_ALL_REVISIONS',
|
||||
labels: {
|
||||
'Code-Review': -1,
|
||||
'Verified': -1
|
||||
},
|
||||
message: 'I wholeheartedly disapprove'
|
||||
});
|
||||
assert.equal(req.response.labels['Code-Review'], -1);
|
||||
assert.equal(req.response.labels['Verified'], -1);
|
||||
assert.isFalse(element.opened);
|
||||
done();
|
||||
});
|
||||
}, 1);
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
@ -41,7 +41,7 @@ limitations under the License.
|
||||
test('value is propagated to _inputVal', function() {
|
||||
element.value = 'foo';
|
||||
assert.equal(element._inputVal, 'foo');
|
||||
})
|
||||
});
|
||||
|
||||
test('tap on search button triggers nav', function(done) {
|
||||
sinon.stub(element, '_preventDefaultAndNavigateToInputVal', function() {
|
||||
|
@ -31,10 +31,11 @@ limitations under the License.
|
||||
'gr-diff-comment-test.html',
|
||||
'gr-diff-comment-thread-test.html',
|
||||
'gr-diff-view-test.html',
|
||||
'gr-reply-dropdown-test.html',
|
||||
'gr-search-bar-test.html',
|
||||
].forEach(function(file) {
|
||||
testFiles.push(file);
|
||||
testFiles.push(file + '?dom=shadow');
|
||||
testFiles.push(file);
|
||||
testFiles.push(file + '?dom=shadow');
|
||||
});
|
||||
|
||||
WCT.loadSuites(testFiles);
|
||||
|
Loading…
Reference in New Issue
Block a user