Allows topic editing in the change view
Adds an editable text label to the gr-change-metadata for modifying the
topic of a change. Introduces the gr-editable-label as a UI element for
editable text labels. Editing the topic is only allowed if the user is
logged in and has the topic-edit permission.
Bug: Issue 4102
Change-Id: I42e8f65bd538ec50f12d29ef886395b6a70edbb7
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
index 98e8e9a..fe0107b 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
@@ -18,6 +18,8 @@
<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-date-formatter/gr-date-formatter.html">
+<link rel="import" href="../../shared/gr-editable-label/gr-editable-label.html">
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../gr-reviewer-list/gr-reviewer-list.html">
<dom-module id="gr-change-metadata">
@@ -117,7 +119,13 @@
</section>
<section>
<span class="title">Topic</span>
- <span class="value">[[change.topic]]</span>
+ <span class="value">
+ <gr-editable-label
+ value="{{change.topic}}"
+ label-if-empty="(No topic)"
+ read-only="[[!_canEditTopic(mutable, change)]]"
+ on-edited="_handleTopicEdited"></gr-editable-label>
+ </span>
</section>
<section class="strategy" hidden$="[[_computeHideStrategy(change)]]" hidden>
<span class="title">Strategy</span>
@@ -141,6 +149,7 @@
</span>
</section>
</template>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-change-metadata.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
index 825b88b..b359cac 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
@@ -102,5 +102,14 @@
});
return result;
},
+
+ _handleTopicEdited: function(e, topic) {
+ if (!topic.length) { topic = null; }
+ this.$.restAPI.setChangeTopic(this.change.id, topic);
+ },
+
+ _canEditTopic: function(mutable, change) {
+ return mutable && change.actions.topic && change.actions.topic.enabled;
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html
new file mode 100644
index 0000000..3a61cbe7
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html
@@ -0,0 +1,55 @@
+<!--
+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.
+-->
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<dom-module id="gr-editable-label">
+ <template>
+ <style>
+ input {
+ font: inherit;
+ width: 7.5em;
+ }
+ label {
+ color: #777;
+ }
+ label.editable {
+ cursor: pointer;
+ }
+ .save {
+ color: #999;
+ font-size: 1em;
+ line-height: 1;
+ padding: 0 .15em;
+ text-decoration: none;
+ }
+ </style>
+ <input
+ is="iron-input"
+ hidden$="[[!editing]]"
+ on-keydown="_handleInputKeydown"
+ bind-value="{{_inputText}}">
+ <gr-button
+ link
+ hidden$="[[!editing]]"
+ class="save"
+ on-tap="_toggleEdit">✓</gr-button>
+ <label
+ hidden$="[[editing]]"
+ class$="[[_computeLabelClass(readOnly)]]"
+ on-tap="_toggleEdit">[[_computeLabel(value, labelIfEmpty)]]</label>
+ </template>
+ <script src="gr-editable-label.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js
new file mode 100644
index 0000000..d0bb3f6
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js
@@ -0,0 +1,97 @@
+// 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';
+
+ Polymer({
+ is: 'gr-editable-label',
+
+ /**
+ * Fired when the value is changed.
+ *
+ * @event edited
+ */
+
+ properties: {
+ editing: {
+ type: Boolean,
+ value: false,
+ },
+ value: {
+ type: String,
+ notify: true,
+ value: null,
+ },
+ labelIfEmpty: {
+ type: String,
+ value: null,
+ },
+ readOnly: {
+ type: Boolean,
+ value: false,
+ },
+ _inputText: String,
+ },
+
+ _computeLabel: function(value, labelIfEmpty) {
+ if ((!value || !value.length) && labelIfEmpty) {
+ return labelIfEmpty;
+ }
+ return value;
+ },
+
+ _toggleEdit: function() {
+ if (this.readOnly) { return; }
+
+ if (this.editing) {
+ this.value = this._inputText;
+ } else {
+ this._inputText = this.value;
+ }
+
+ this.editing = !this.editing;
+
+ this.async(function() {
+ if (this.editing) {
+ var input = this.$$('input');
+ input.focus();
+ input.setSelectionRange(0, input.value.length)
+ } else {
+ this.fire('edited', this.value);
+ }
+ });
+ },
+
+ _cancelEdit: function() {
+ if (!this.editing) { return; }
+
+ this.editing = false;
+ this._inputText = this.value;
+ },
+
+ _handleInputKeydown: function(e) {
+ if (e.keyCode === 13) { // Enter key
+ e.preventDefault();
+ this._toggleEdit();
+ } else if (e.keyCode === 27) { // Escape key
+ e.preventDefault();
+ this._cancelEdit();
+ }
+ },
+
+ _computeLabelClass: function(readOnly) {
+ return readOnly ? '' : 'editable';
+ },
+ });
+})();
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html
new file mode 100644
index 0000000..dc4102a
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-editable-label</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="../../../bower_components/iron-test-helpers/mock-interactions.js"></script>
+
+<link rel="import" href="gr-editable-label.html">
+
+<test-fixture id="basic">
+ <template>
+ <gr-editable-label
+ value="value text"
+ label-if-empty="label text"></gr-editable-label>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-linked-text tests', function() {
+ var element;
+ var awaitChange;
+
+ setup(function() {
+ element = fixture('basic');
+ });
+
+ test('basic functionality', function(done) {
+ var editedStub = sinon.stub();
+ element.addEventListener('edited', editedStub);
+
+ var input = element.$$('input');
+ var button = element.$$('gr-button');
+ var label = element.$$('label');
+
+ // The input and button are hidden:
+ assert.isNotNull(input.getAttribute('hidden'));
+ assert.isNotNull(button.getAttribute('hidden'));
+
+ // The label is visible:
+ assert.isNull(label.getAttribute('hidden'));
+
+ assert.equal(label.textContent, 'value text');
+
+ setTimeout(function() {
+ // The input and button are visible:
+ assert.isNull(input.getAttribute('hidden'));
+ assert.isNull(button.getAttribute('hidden'));
+
+ // The label is hidden:
+ assert.isNotNull(label.getAttribute('hidden'));
+
+ assert.equal(input.value, 'value text');
+
+ input.set('_inputText', 'new text');
+
+ assert.isFalse(editedStub.called);
+
+ setTimeout(function() {
+ assert.isTrue(editedStub.called);
+ done();
+ });
+
+ MockInteractions.keyDownOn(input, 13);
+ });
+
+ MockInteractions.tap(label);
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 9604ded..162c954 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -761,5 +761,10 @@
return {baseImage: baseImage, revisionImage: revisionImage};
}.bind(this));
},
+
+ setChangeTopic: function(changeNum, topic) {
+ return this.send('PUT', '/changes/' + encodeURIComponent(changeNum) +
+ '/topic', {topic: topic});
+ },
});
})();
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 781f457..171ec07 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -60,6 +60,7 @@
'shared/gr-cursor-manager/gr-cursor-manager_test.html',
'shared/gr-date-formatter/gr-date-formatter_test.html',
'shared/gr-js-api-interface/gr-js-api-interface_test.html',
+ 'shared/gr-editable-label/gr-editable-label_test.html',
'shared/gr-linked-text/gr-linked-text_test.html',
'shared/gr-rest-api-interface/gr-rest-api-interface_test.html',
'shared/gr-storage/gr-storage_test.html',