Add link to changes with same topic

Adds a component (gr-linked-chip) that consists of a link and a remove
button, and employs this component to allow the topic to be both
editable and a link.

Feature: Issue 4956
Change-Id: I6d8c90ecec9502a5efdddeb60ab334760bbfdda1
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 83e182a..ce17b3a 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
@@ -20,6 +20,7 @@
 <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">
+<link rel="import" href="../../shared/gr-linked-chip/gr-linked-chip.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">
 
@@ -145,11 +146,20 @@
     <section>
       <span class="title">Topic</span>
       <span class="value">
-        <gr-editable-label
-            value="{{change.topic}}"
-            placeholder="[[_computeTopicPlaceholder(_topicReadOnly)]]"
-            read-only="[[_topicReadOnly]]"
-            on-changed="_handleTopicChanged"></gr-editable-label>
+        <template is="dom-if" if="[[change.topic]]">
+          <gr-linked-chip
+              text="[[change.topic]]"
+              href="[[_computeTopicHref(change.topic)]]"
+              removable
+              on-remove="_handleTopicRemoved"></gr-linked-chip>
+        </template>
+        <template is="dom-if" if="[[!change.topic]]">
+          <gr-editable-label
+              value="{{change.topic}}"
+              placeholder="[[_computeTopicPlaceholder(_topicReadOnly)]]"
+              read-only="[[_topicReadOnly]]"
+              on-changed="_handleTopicChanged"></gr-editable-label>
+        </template>
       </span>
     </section>
     <section class="strategy" hidden$="[[_computeHideStrategy(change)]]" hidden>
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 01ef473..e458068 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
@@ -172,5 +172,14 @@
       }
       return output;
     },
+
+    _computeTopicHref: function(topic) {
+      return '/q/topic:' + encodeURIComponent(encodeURIComponent(topic)) +
+          '+(status:open OR status:merged)';
+    },
+
+    _handleTopicRemoved: function() {
+      this.set(['change', 'topic'], '');
+    },
   });
 })();
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
index 7fa4744..04cc212 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
@@ -173,6 +173,14 @@
         element._handleTopicChanged({}, 'the new topic');
         assert.isTrue(topicStub.calledWith('the id', 'the new topic'));
       });
+
+      test('clicking x on topic chip removes topic', function() {
+        sandbox.stub(element, '_handleTopicChanged');
+        flushAsynchronousOperations();
+        var remove = element.$$('gr-linked-chip').$.remove;
+        MockInteractions.tap(remove);
+        assert.equal(element.change.topic, '');
+      });
     });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html
new file mode 100644
index 0000000..a3d2769
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html
@@ -0,0 +1,66 @@
+<!--
+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="../gr-button/gr-button.html">
+<dom-module id="gr-linked-chip">
+  <template>
+    <style>
+      :host {
+        display: block;
+        overflow: hidden;
+      }
+      .container {
+        align-items: center;
+        background: #eee;
+        border-radius: .75em;
+        display: inline-flex;
+        padding: 0 .5em;
+      }
+      gr-button.remove,
+      gr-button.remove:hover,
+      gr-button.remove:focus {
+        border-color: transparent;
+        color: #333;
+      }
+      gr-button.remove {
+        background: #eee;
+        color: #666;
+        font-size: 1.7em;
+        font-weight: normal;
+        height: .6em;
+        line-height: .6em;
+        margin-left: .15em;
+        padding: 0;
+        text-decoration: none;
+      }
+      .transparentBackground,
+      gr-button.transparentBackground {
+        background-color: transparent;
+      }
+    </style>
+    <div class$="container [[_getBackgroundClass(transparentBackground)]]">
+      <a href$="[[href]]">[[text]]</a>
+      <gr-button
+          id="remove"
+          hidden$="[[!removable]]"
+          hidden
+          class$="remove [[_getBackgroundClass(transparentBackground)]]"
+          on-tap="_handleRemoveTap">×</gr-button>
+    </div>
+  </template>
+  <script src="gr-linked-chip.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js
new file mode 100644
index 0000000..c6a5e4e
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js
@@ -0,0 +1,42 @@
+// 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-linked-chip',
+
+    properties: {
+      href: String,
+      removable: {
+        type: Boolean,
+        value: false,
+      },
+      text: String,
+      transparentBackground: {
+        type: Boolean,
+        value: false,
+      },
+    },
+
+    _getBackgroundClass: function(transparent) {
+      return transparent ? 'transparentBackground' : '';
+    },
+
+    _handleRemoveTap: function(e) {
+      e.preventDefault();
+      this.fire('remove');
+    },
+  });
+})();
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html
new file mode 100644
index 0000000..5e2cac5
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html
@@ -0,0 +1,56 @@
+<!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-linked-chip</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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="gr-linked-chip.html">
+
+<test-fixture id="basic">
+  <template>
+    <gr-linked-chip></gr-linked-chip>
+  </template>
+</test-fixture>
+
+<script>
+  suite('gr-linked-chip tests', function() {
+    var element;
+    var sandbox;
+
+    setup(function() {
+      element = fixture('basic');
+      sandbox = sinon.sandbox.create();
+    });
+
+    teardown(function() {
+      sandbox.restore();
+    });
+
+    test('remove fired', function() {
+      var spy = sandbox.spy();
+      element.addEventListener('remove', spy);
+      flushAsynchronousOperations();
+      MockInteractions.tap(element.$.remove);
+      assert.isTrue(spy.called);
+    });
+  });
+</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 a98b00b..e0ee6f9 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
@@ -68,7 +68,7 @@
     REVIEWER_UPDATES: 19,
 
     // Set the submittable boolean.
-    SUBMITTABLE: 20
+    SUBMITTABLE: 20,
   };
 
   Polymer({
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 7574d18..48ddaf2 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -94,6 +94,7 @@
     'shared/gr-js-api-interface/gr-change-actions-js-api_test.html',
     'shared/gr-js-api-interface/gr-change-reply-js-api_test.html',
     'shared/gr-js-api-interface/gr-js-api-interface_test.html',
+    'shared/gr-linked-chip/gr-linked-chip_test.html',
     'shared/gr-linked-text/gr-linked-text_test.html',
     'shared/gr-rest-api-interface/gr-rest-api-interface_test.html',
     'shared/gr-select/gr-select_test.html',