Merge "Link to developer mailing list from contributing docs"
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
index d50e0b3..6df33db 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
@@ -30,6 +30,9 @@
         display: table-row;
         border-bottom: 1px solid #eee;
       }
+      :host(:hover) {
+        background-color: #f5fafd;
+      }
       :host([selected]) {
         background-color: #ebf5fb;
       }
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 4918b03..755b660 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
@@ -131,20 +131,20 @@
           <gr-account-link account="[[change.owner]]"></gr-account-link>
         </span>
       </section>
+      <section class="assignee">
+        <span class="title">Assignee</span>
+        <span class="value">
+          <gr-account-list
+              max-count="1"
+              id="assigneeValue"
+              placeholder="Add assignee..."
+              accounts="{{_assignee}}"
+              change="[[change]]"
+              readonly="[[_computeAssigneeReadOnly(mutable, change)]]"
+              allow-any-user></gr-account-list>
+        </span>
+      </section>
       <template is="dom-if" if="[[_showReviewersByState]]">
-        <section class="assignee">
-          <span class="title">Assignee</span>
-          <span class="value">
-            <gr-account-list
-                max-count="1"
-                id="assigneeValue"
-                placeholder="Add assignee..."
-                accounts="{{_assignee}}"
-                change="[[change]]"
-                readonly="[[_computeAssigneeReadOnly(mutable, change)]]"
-                allow-any-user></gr-account-list>
-          </span>
-        </section>
         <section>
           <span class="title">Reviewers</span>
           <span class="value">
@@ -165,19 +165,6 @@
         </section>
       </template>
       <template is="dom-if" if="[[!_showReviewersByState]]">
-        <section class="assignee">
-          <span class="title">Assignee</span>
-          <span class="value">
-            <gr-account-list
-                max-count="1"
-                id="assigneeValue"
-                placeholder="Add assignee..."
-                accounts="{{_assignee}}"
-                change="[[change]]"
-                readonly="[[!mutable]]"
-                allow-any-user></gr-account-list>
-          </span>
-        </section>
         <section>
           <span class="title">Reviewers</span>
           <span class="value">
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index dc40fdd..2737f18 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -457,7 +457,7 @@
           }
           for (const entry of change[key]) {
             if (entry._account_id === owner._account_id) {
-              return;
+              continue;
             }
             switch (key) {
               case 'REVIEWER':
@@ -537,11 +537,14 @@
             .then(() => {
               return this.send(this._includeComments);
             })
-            .then(this._purgeReviewersPendingRemove.bind(this));
+            .then(keepReviewers => {
+              this._purgeReviewersPendingRemove(false, keepReviewers);
+            });
         return;
       }
-      this.send(this._includeComments)
-          .then(this._purgeReviewersPendingRemove.bind(this));
+      this.send(this._includeComments).then(keepReviewers => {
+        this._purgeReviewersPendingRemove(false, keepReviewers);
+      });
     },
 
     _saveReview(review, opt_errFn) {
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
index ef0d9dc..86219e7 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
@@ -614,8 +614,9 @@
       const reviewer2 = makeAccount();
       const cc1 = makeAccount();
       const cc2 = makeAccount();
+      const cc3 = makeAccount();
       element._reviewers = [reviewer1, reviewer2];
-      element._ccs = [cc1, cc2];
+      element._ccs = [cc1, cc2, cc3];
 
       const mutations = [];
 
@@ -633,6 +634,7 @@
       reviewers.fire('remove', {account: reviewer1});
       ccs.$.entry.fire('add', {value: {account: reviewer1}});
       ccs.fire('remove', {account: cc1});
+      ccs.fire('remove', {account: cc3});
       reviewers.$.entry.fire('add', {value: {account: cc1}});
 
       // Add to other field without removing from former field.
@@ -647,9 +649,10 @@
         return result;
       };
 
-      // Send and purge and verify moves without deletions.
+      // Send and purge and verify moves, delete cc3.
       element.send()
-          .then(element._purgeReviewersPendingRemove.bind(element))
+          .then(keepReviewers =>
+              element._purgeReviewersPendingRemove(false, keepReviewers))
           .then(() => {
             assert.deepEqual(
                 mutations, [
@@ -657,6 +660,7 @@
                   mapReviewer(cc2),
                   mapReviewer(reviewer1, 'CC'),
                   mapReviewer(reviewer2, 'CC'),
+                  {account: cc3, state: 'REMOVED'},
                 ]);
             done();
           });
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html
index 9a84c95..c77c783 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html
@@ -33,6 +33,9 @@
       .autocompleteContainer {
         position: relative;
       }
+      .hiddenReviewers {
+        margin-top: .3em;
+      }
       .inputContainer {
         display: flex;
         margin-top: .25em;
@@ -56,12 +59,17 @@
         }
       }
     </style>
-    <template is="dom-repeat" items="[[_reviewers]]" as="reviewer">
+    <template is="dom-repeat" items="[[_displayedReviewers]]" as="reviewer">
       <gr-account-chip class="reviewer" account="[[reviewer]]"
           on-remove="_handleRemove"
           removable="[[_computeCanRemoveReviewer(reviewer, mutable)]]">
       </gr-account-chip>
     </template>
+    <gr-button
+        class="hiddenReviewers"
+        link
+        hidden$="[[!_hiddenReviewerCount]]"
+        on-tap="_handleViewAll">and [[_hiddenReviewerCount]] more</gr-button>
     <div class="controlsContainer" hidden$="[[!mutable]]">
       <gr-button
           link
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
index 52b90ee..0e97b6d 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
@@ -14,6 +14,8 @@
 (function() {
   'use strict';
 
+  var MAX_REVIEWERS_DISPLAYED = 10;
+
   Polymer({
     is: 'gr-reviewer-list',
 
@@ -43,6 +45,10 @@
         value: false,
       },
 
+      _displayedReviewers: {
+        type: Array,
+        value: function() { return []; },
+      },
       _reviewers: {
         type: Array,
         value: function() { return []; },
@@ -55,6 +61,11 @@
         type: String,
         computed: '_computeAddLabel(ccsOnly)',
       },
+      _hiddenReviewerCount: {
+        type: Number,
+        computed: '_computeHiddenCount(_reviewers, _displayedReviewers)',
+      },
+
 
       // Used for testing.
       _lastAutocompleteRequest: Object,
@@ -82,6 +93,12 @@
       this._reviewers = result.filter(function(reviewer) {
         return reviewer._account_id != owner._account_id;
       });
+      this._displayedReviewers =
+          this._reviewers.slice(0, MAX_REVIEWERS_DISPLAYED);
+    },
+
+    _computeHiddenCount: function(reviewers, displayedReviewers) {
+      return reviewers.length - displayedReviewers.length;
     },
 
     _computeCanRemoveReviewer: function(reviewer, mutable) {
@@ -138,6 +155,10 @@
       this.fire('show-reply-dialog', {value: value});
     },
 
+    _handleViewAll: function(e) {
+      this._displayedReviewers = this._reviewers;
+    },
+
     _removeReviewer: function(id) {
       return this.$.restAPI.removeChangeReviewer(this.change._number, id);
     },
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html
index 7c8a76f..c3110eb 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html
@@ -187,5 +187,58 @@
       assert.isTrue(fireStub.lastCall.calledWith('show-reply-dialog',
           {value: {ccsOnly: true}}));
     });
+
+    test('no show all reviewers button with 10 reviewers', function() {
+      let reviewers = [];
+      for (var i = 0; i < 10; i++) {
+        reviewers.push(
+          {email: i+'reviewer@google.com', name: 'reviewer-' + i });
+      }
+      element.ccsOnly = true;
+
+      element.change = {
+        owner: {
+          _account_id: 1,
+        },
+        reviewers: {
+          'CC': reviewers,
+        },
+      };
+      flushAsynchronousOperations();
+      assert.equal(element._hiddenReviewerCount, 0);
+      assert.equal(element._displayedReviewers.length, 10);
+      assert.equal(element._reviewers.length, 10);
+      assert.isTrue(element.$$('.hiddenReviewers').hidden);
+    });
+
+    test('show all reviewers button', function() {
+      let reviewers = [];
+      for (var i = 0; i < 100; i++) {
+        reviewers.push(
+          {email: i+'reviewer@google.com', name: 'reviewer-' + i });
+      }
+      element.ccsOnly = true;
+
+      element.change = {
+        owner: {
+          _account_id: 1,
+        },
+        reviewers: {
+          'CC': reviewers,
+        },
+      };
+      flushAsynchronousOperations();
+      assert.equal(element._hiddenReviewerCount, 90);
+      assert.equal(element._displayedReviewers.length, 10);
+      assert.equal(element._reviewers.length, 100);
+      assert.isFalse(element.$$('.hiddenReviewers').hidden);
+
+      MockInteractions.tap(element.$$('.hiddenReviewers'));
+      flushAsynchronousOperations();
+      assert.equal(element._hiddenReviewerCount, 0);
+      assert.equal(element._displayedReviewers.length, 100);
+      assert.equal(element._reviewers.length, 100);
+      assert.isTrue(element.$$('.hiddenReviewers').hidden);
+    });
   });
 </script>