Add a hovercard for accounts

Following this spec:
https://www.gerritcodereview.com/design-docs/attention-set-solution-1-user-interface.html

Disabling the tooltip, removing it in change 257776.

Screenshot: https://imgur.com/a/6TvzFBT

Change-Id: I14b57021b06a3f5450e0d168036c28e09ee61668
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 a74ec5f..57337fb 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
@@ -167,7 +167,7 @@
     return NaN;
   }
 
-  _computeReviewerTooltip(reviewer, change) {
+  _computeVoteableText(reviewer, change) {
     if (!change || !change.labels) { return ''; }
     const maxScores = [];
     const maxPermitted = this._getMaxPermittedScores(change);
@@ -181,11 +181,7 @@
         maxScores.push(`${label}`);
       }
     }
-    if (maxScores.length) {
-      return 'Votable: ' + maxScores.join(', ');
-    } else {
-      return '';
-    }
+    return maxScores.join(', ');
   }
 
   _reviewersChanged(changeRecord, owner) {
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.js b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.js
index bf7db12..c5df61d 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.js
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.js
@@ -44,7 +44,7 @@
     </style>
     <div class="container">
       <template is="dom-repeat" items="[[_displayedReviewers]]" as="reviewer">
-        <gr-account-chip class="reviewer" account="[[reviewer]]" on-remove="_handleRemove" additional-text="[[_computeReviewerTooltip(reviewer, change)]]" removable="[[_computeCanRemoveReviewer(reviewer, mutable)]]">
+        <gr-account-chip class="reviewer" account="[[reviewer]]" on-remove="_handleRemove" voteable-text="[[_computeVoteableText(reviewer, change)]]" removable="[[_computeCanRemoveReviewer(reviewer, mutable)]]">
         </gr-account-chip>
       </template>
       <gr-button class="hiddenReviewers" link="" hidden\$="[[!_hiddenReviewerCount]]" on-click="_handleViewAll">and [[_hiddenReviewerCount]] more</gr-button>
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 627fa10..32e2e9b6 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
@@ -329,13 +329,13 @@
       },
     };
     assert.strictEqual(
-        element._computeReviewerTooltip({_account_id: 1}, change),
-        'Votable: Bar');
+        element._computeVoteableText({_account_id: 1}, change),
+        'Bar');
     assert.strictEqual(
-        element._computeReviewerTooltip({_account_id: 7}, change),
-        'Votable: Foo: +2, Bar, FooBar');
+        element._computeVoteableText({_account_id: 7}, change),
+        'Foo: +2, Bar, FooBar');
     assert.strictEqual(
-        element._computeReviewerTooltip({_account_id: 2}, change),
+        element._computeVoteableText({_account_id: 2}, change),
         '');
   });
 
@@ -347,7 +347,7 @@
       },
     };
     assert.strictEqual(
-        element._computeReviewerTooltip({_account_id: 1}, change), '');
+        element._computeVoteableText({_account_id: 1}, change), '');
   });
 });
 </script>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js
index 4ac540d..22fd1c20 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js
@@ -56,7 +56,7 @@
   static get properties() {
     return {
       account: Object,
-      additionalText: String,
+      voteableText: String,
       disabled: {
         type: Boolean,
         value: false,
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.js b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.js
index 7f219e5..14bbd57 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.js
@@ -81,7 +81,7 @@
       }
     </style>
     <div class\$="container [[_getBackgroundClass(transparentBackground)]]">
-      <gr-account-link account="[[account]]" additional-text="[[additionalText]]">
+      <gr-account-link account="[[account]]" voteable-text="[[voteableText]]">
       </gr-account-link>
       <gr-button id="remove" link="" hidden\$="[[!removable]]" hidden="" tabindex="-1" aria-label="Remove" class\$="remove [[_getBackgroundClass(transparentBackground)]]" on-click="_handleRemoveTap">
         <iron-icon icon="gr-icons:close"></iron-icon>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
index dc0a276..c6f8aa6 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
@@ -20,6 +20,7 @@
 import '../../../scripts/bundled-polymer.js';
 import '../../../styles/shared-styles.js';
 import '../gr-avatar/gr-avatar.js';
+import '../gr-hovercard-account/gr-hovercard-account.js';
 import '../gr-limited-text/gr-limited-text.js';
 import '../gr-rest-api-interface/gr-rest-api-interface.js';
 import '../../../scripts/util.js';
@@ -55,7 +56,7 @@
         reflectToAttribute: true,
         computed: '_computeAccountTitle(account, additionalText)',
       },
-      additionalText: String,
+      voteableText: String,
       hasTooltip: {
         type: Boolean,
         reflectToAttribute: true,
@@ -136,7 +137,7 @@
 
   _computeHasTooltip(account) {
     // If an account has loaded to fire this method, then set to true.
-    return !!account;
+    return false;
   }
 }
 
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.js b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.js
index f25c159..9e2ac83 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.js
@@ -44,6 +44,7 @@
       }
     </style>
     <span>
+      <gr-hovercard-account account="[[account]]" voteable-text="[[voteableText]]"></gr-hovercard-account>
       <template is="dom-if" if="[[!hideAvatar]]">
         <gr-avatar account="[[account]]" image-size="32"></gr-avatar>
       </template>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
index abc88d0..e0d5583 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
@@ -41,7 +41,7 @@
 
   static get properties() {
     return {
-      additionalText: String,
+      voteableText: String,
       account: Object,
     };
   }
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.js b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.js
index 189e2cd..4f1ea44 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.js
@@ -33,7 +33,7 @@
     </style>
     <span>
       <a href\$="[[_computeOwnerLink(account)]]" tabindex="-1">
-        <gr-account-label account="[[account]]" additional-text="[[additionalText]]"></gr-account-label>
+        <gr-account-label account="[[account]]" voteable-text="[[voteableText]]"></gr-account-label>
       </a>
     </span>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.js b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.js
new file mode 100644
index 0000000..f5893e0
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.js
@@ -0,0 +1,49 @@
+/**
+ * @license
+ * Copyright (C) 2020 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.
+ */
+import '../../../scripts/bundled-polymer.js';
+
+import '../../../styles/shared-styles.js';
+import '../gr-avatar/gr-avatar.js';
+import '../gr-button/gr-button.js';
+import {hovercardBehaviorMixin} from '../gr-hovercard/gr-hovercard-behavior.js';
+import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
+import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
+import {PolymerElement} from '@polymer/polymer/polymer-element.js';
+import {htmlTemplate} from './gr-hovercard-account_html.js';
+
+/** @extends Polymer.Element */
+class GrHovercardAccount extends GestureEventListeners(
+    hovercardBehaviorMixin(LegacyElementMixin(
+        PolymerElement))) {
+  static get template() { return htmlTemplate; }
+
+  static get is() { return 'gr-hovercard-account'; }
+
+  static get properties() {
+    return {
+      account: Object,
+      voteableText: String,
+      attention: {
+        type: Boolean,
+        value: false,
+        reflectToAttribute: true,
+      },
+    };
+  }
+}
+
+customElements.define(GrHovercardAccount.is, GrHovercardAccount);
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.js b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.js
new file mode 100644
index 0000000..97f4e72
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.js
@@ -0,0 +1,86 @@
+/**
+ * @license
+ * Copyright (C) 2020 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.
+ */
+import '../gr-hovercard/gr-hovercard-shared-style.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+
+export const htmlTemplate = html`
+    <style include="gr-hovercard-shared-style">
+      .top,
+      .attention,
+      .status,
+      .voteable {
+        padding: var(--spacing-s) var(--spacing-l);
+      }
+      .top {
+        display: flex;
+        padding-top: var(--spacing-xl);
+        min-width: 300px;
+      }
+      gr-avatar {
+        height: 48px;
+        width: 48px;
+        margin-right: var(--spacing-l);
+      }
+      .title,
+      .email {
+        color: var(--deemphasized-text-color);
+      }
+      .action {
+        border-top: 1px solid var(--border-color);
+        padding: var(--spacing-s) var(--spacing-l);
+        --gr-button: {
+          padding: var(--spacing-s) 0;
+        };
+      }
+      :host(:not([attention])) .attention {
+        display: none;
+      }
+      .attention {
+        background-color: var(--emphasis-color);
+      }
+      .attention iron-icon {
+        vertical-align: top;
+      }
+    </style>
+    <div id="container" role="tooltip" tabindex="-1">
+      <div class="top">
+        <div class="avatar">
+          <gr-avatar account="[[account]]" image-size="56"></gr-avatar>
+        </div>
+        <div class="account">
+          <h3 class="name">[[account.name]]</h3>
+          <div class="email">[[account.email]]</div>
+        </div>
+      </div>
+      <template is="dom-if" if="[[account.status]]">
+        <div class="status">
+          <span class="title">Status:</span>
+          <span class="value">[[account.status]]</span>
+        </div>
+      </template>
+      <template is="dom-if" if="[[voteableText]]">
+        <div class="voteable">
+          <span class="title">Voteable:</span>
+          <span class="value">[[voteableText]]</span>
+        </div>
+      </template>
+      <div class="attention">
+        <iron-icon icon="gr-icons:attention"></iron-icon>
+        <span>It is this user's turn to take action.</span>
+      </div>
+    </div>
+`;
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.html b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.html
new file mode 100644
index 0000000..7a5f4c6
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2020 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-hovercard-account</title>
+
+<script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/components/wct-browser-legacy/browser.js"></script>
+<script type="module" src="../../../test/test-pre-setup.js"></script>
+<script type="module" src="../../../test/common-test-setup.js"></script>
+<script src="../../../node_modules/iron-test-helpers/mock-interactions.js" type="module"></script>
+
+<script type="module" src="./gr-hovercard-account.js"></script>
+
+<script type="module">
+  import '../../../test/test-pre-setup.js';
+  import '../../../test/common-test-setup.js';
+  import './gr-hovercard-account.js';
+
+  void (0);
+</script>
+
+<test-fixture id="basic">
+  <template>
+    <gr-hovercard-account class="hovered"></gr-hovercard-account>
+  </template>
+</test-fixture>
+
+
+<script type="module">
+  import '../../../test/test-pre-setup.js';
+  import '../../../test/common-test-setup.js';
+  import './gr-hovercard-account.js';
+
+  suite('gr-hovercard-account tests', () => {
+    let element;
+    const ACCOUNT = {
+      email: 'kermit@gmail.com',
+      username: 'kermit',
+      name: 'Kermit The Frog',
+      _account_id: '31415926535',
+    };
+
+    setup(() => {
+      element = fixture('basic');
+      element.account = Object.assign({}, ACCOUNT);
+    });
+
+    test('account name is shown', () => {
+      assert.equal(element.shadowRoot.querySelector('.name').innerText,
+          'Kermit The Frog');
+    });
+
+    test('account status is not shown if the property is not set', () => {
+      assert.isNull(element.shadowRoot.querySelector('.status'));
+    });
+
+    test('account status is displayed', () => {
+      element.account = Object.assign({status: 'OOO'}, ACCOUNT);
+      flushAsynchronousOperations();
+      assert.equal(element.shadowRoot.querySelector('.status .value').innerText,
+          'OOO');
+    });
+
+    test('voteable div is not shown if the property is not set', () => {
+      assert.isNull(element.shadowRoot.querySelector('.voteable'));
+    });
+
+    test('voteable div is displayed', () => {
+      element.voteableText = 'CodeReview: +2';
+      flushAsynchronousOperations();
+      assert.equal(element.shadowRoot.querySelector('.voteable .value').innerText,
+          element.voteableText);
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-behavior.js b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-behavior.js
index 08571e4..a77f5f7 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-behavior.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-behavior.js
@@ -211,7 +211,7 @@
 
     // Reset and remove the hovercard from the DOM
     this.style.cssText = '';
-    this.$.hovercard.setAttribute('tabindex', -1);
+    this.$.container.setAttribute('tabindex', -1);
 
     // Remove the hovercard from the container, given that it is still a child
     // of the container.
@@ -351,4 +351,4 @@
   _forChanged() {
     this._target = this.target;
   }
-};
\ No newline at end of file
+};
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-shared-style.js b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-shared-style.js
index 5fdd8fa..bb81cfd 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-shared-style.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-shared-style.js
@@ -17,7 +17,7 @@
 
 /** The shared styles for all hover cards. */
 const GrHoverCardSharedStyle = document.createElement('dom-module');
-GrHoverCardSharedStyle .innerHTML =
+GrHoverCardSharedStyle.innerHTML =
   `<template>
     <style include="shared-styles">
       :host {
@@ -26,13 +26,20 @@
         position: absolute;
         transition: opacity 200ms;
         visibility: hidden;
-        z-index: 100;
+        z-index: 200;
       }
       :host(.hovered) {
         visibility: visible;
         opacity: 1;
       }
+      /* You have to use a <div class="container"> in your hovercard in order
+         to pick up this consistent styling. */
+      #container {
+        background: var(--dialog-background-color);
+        border-radius: var(--border-radius);
+        box-shadow: var(--elevation-level-5);
+      }
     </style>
   </template>`;
 
-GrHoverCardSharedStyle.register('gr-hovercard-shared-style');
\ No newline at end of file
+GrHoverCardSharedStyle.register('gr-hovercard-shared-style');
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.js b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.js
index 14eb923..69fd4c5 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.js
@@ -18,13 +18,11 @@
 
 export const htmlTemplate = html`
     <style include="gr-hovercard-shared-style">
-      #hovercard {
-        background: var(--dialog-background-color);
-        box-shadow: var(--elevation-level-2);
+      #container {
         padding: var(--spacing-l);
       }
     </style>
-    <div id="hovercard" role="tooltip" tabindex="-1">
+    <div id="container" role="tooltip" tabindex="-1">
       <slot></slot>
     </div>
 `;
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 45e5af6..26fc512 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -189,6 +189,7 @@
     'shared/gr-editable-label/gr-editable-label_test.html',
     'shared/gr-formatted-text/gr-formatted-text_test.html',
     'shared/gr-hovercard/gr-hovercard_test.html',
+    'shared/gr-hovercard-account/gr-hovercard-account_test.html',
     'shared/gr-js-api-interface/gr-annotation-actions-context_test.html',
     'shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html',
     'shared/gr-js-api-interface/gr-change-actions-js-api_test.html',