Submit requirements - Collapsed hovercard

Layout is similar to gr-hovercard-run.
Screenshot: https://imgur.com/a/ihnfoAh

Change-Id: I41f5f1fcf7ecce8ebedb0dc546afbb0d08e390cb
diff --git a/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard.ts b/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard.ts
index 150090f..096cb0d 100644
--- a/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard.ts
+++ b/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard.ts
@@ -17,15 +17,68 @@
 
 import '../../shared/gr-hovercard/gr-hovercard-shared-style';
 import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {customElement} from '@polymer/decorators';
+import {customElement, property} from '@polymer/decorators';
 import {hovercardBehaviorMixin} from '../../shared/gr-hovercard/gr-hovercard-behavior';
 import {htmlTemplate} from './gr-submit-requirement-hovercard_html';
+import {
+  AccountInfo,
+  SubmitRequirementResultInfo,
+  SubmitRequirementStatus,
+} from '../../../api/rest-api';
+import {
+  extractAssociatedLabels,
+  iconForStatus,
+} from '../../../utils/label-util';
+import {ParsedChangeInfo} from '../../../types/types';
+import {Label} from '../gr-change-requirements/gr-change-requirements';
 
 @customElement('gr-submit-requirement-hovercard')
 export class GrHovercardRun extends hovercardBehaviorMixin(PolymerElement) {
   static get template() {
     return htmlTemplate;
   }
+
+  @property({type: Object})
+  requirement?: SubmitRequirementResultInfo;
+
+  @property({type: Object})
+  change?: ParsedChangeInfo;
+
+  @property({type: Object})
+  account?: AccountInfo;
+
+  @property({type: Boolean})
+  mutable?: boolean;
+
+  @property({type: Array, computed: 'computeLabels(change, requirement)'})
+  _labels: Label[] = [];
+
+  computeLabels(
+    change?: ParsedChangeInfo,
+    requirement?: SubmitRequirementResultInfo
+  ) {
+    if (!requirement) return [];
+    const requirementLabels = extractAssociatedLabels(requirement);
+    const labels = change?.labels ?? {};
+
+    const allLabels: Label[] = [];
+
+    for (const label of Object.keys(labels)) {
+      if (requirementLabels.includes(label)) {
+        allLabels.push({
+          labelName: label,
+          icon: '',
+          style: '',
+          labelInfo: labels[label],
+        });
+      }
+    }
+    return allLabels;
+  }
+
+  computeIcon(status: SubmitRequirementStatus) {
+    return iconForStatus(status);
+  }
 }
 
 declare global {
diff --git a/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard_html.ts b/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard_html.ts
index 397486e..da7b780 100644
--- a/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard_html.ts
@@ -23,8 +23,92 @@
       max-width: 356px;
       padding: var(--spacing-xl) 0 var(--spacing-m) 0;
     }
+    section.label {
+      display: table-row;
+    }
+    .label-title {
+      min-width: 10em;
+      padding-top: var(--spacing-s);
+    }
+    .label-value {
+      padding-top: var(--spacing-s);
+    }
+    .label-title,
+    .label-value {
+      display: table-cell;
+      vertical-align: top;
+    }
+    .row {
+      display: flex;
+      margin-top: var(--spacing-s);
+    }
+    .title {
+      color: var(--deemphasized-text-color);
+      margin-right: var(--spacing-m);
+    }
+    div.section {
+      margin: 0 var(--spacing-xl) var(--spacing-m) var(--spacing-xl);
+      display: flex;
+    }
+    div.sectionIcon iron-icon {
+      position: relative;
+      top: 2px;
+      width: 20px;
+      height: 20px;
+    }
+    iron-icon.check {
+      color: var(--success-foreground);
+    }
+    iron-icon.close {
+      color: var(--warning-foreground);
+    }
   </style>
   <div id="container" role="tooltip" tabindex="-1">
-    <slot></slot>
+    <div class="section">
+      <div class="sectionIcon">
+        <iron-icon
+          class$="[[computeIcon(requirement.status)]]"
+          icon="gr-icons:[[computeIcon(requirement.status)]]"
+        ></iron-icon>
+      </div>
+      <div class="sectionContent">
+        <h3 class="name heading-3">
+          <span>[[requirement.name]]</span>
+        </h3>
+      </div>
+    </div>
+    <div class="section">
+      <div class="sectionIcon">
+        <iron-icon class="small" icon="gr-icons:info-outline"></iron-icon>
+      </div>
+      <div class="sectionContent">
+        <div class="row">
+          <div class="title">Status</div>
+          <div>[[requirement.status]]</div>
+        </div>
+      </div>
+    </div>
+    <div class="section">
+      <template is="dom-repeat" items="[[_labels]]">
+        <section class="label">
+          <div class="label-title">
+            <gr-limited-text
+              class="name"
+              limit="25"
+              text="[[item.labelName]]"
+            ></gr-limited-text>
+          </div>
+          <div class="label-value">
+            <gr-label-info
+              change="{{change}}"
+              account="[[account]]"
+              mutable="[[mutable]]"
+              label="[[item.labelName]]"
+              label-info="[[item.labelInfo]]"
+            ></gr-label-info>
+          </div>
+        </section>
+      </template>
+    </div>
   </div>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts b/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
index ee6fa96..8e82cb3 100644
--- a/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
+++ b/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
@@ -25,9 +25,11 @@
   SubmitRequirementResultInfo,
   SubmitRequirementStatus,
 } from '../../../api/rest-api';
-import {assertNever, unique} from '../../../utils/common-util';
-import {extractAssociatedLabels} from '../../../utils/label-util';
-import {Label} from '../gr-change-requirements/gr-change-requirements';
+import {unique} from '../../../utils/common-util';
+import {
+  extractAssociatedLabels,
+  iconForStatus,
+} from '../../../utils/label-util';
 
 @customElement('gr-submit-requirements')
 export class GrSubmitRequirements extends GrLitElement {
@@ -80,10 +82,10 @@
           width: var(--line-height-small);
           height: var(--line-height-small);
         }
-        iron-icon.satisfied {
+        iron-icon.check {
           color: var(--success-foreground);
         }
-        iron-icon.unsatisfied {
+        iron-icon.close {
           color: var(--warning-foreground);
         }
         .testing {
@@ -112,8 +114,11 @@
       ${submit_requirements.map(
         requirement => html`<section>
           <gr-submit-requirement-hovercard
-            >${this.renderLabels(requirement)}
-          </gr-submit-requirement-hovercard>
+            .requirement="${requirement}"
+            .change="${this.change}"
+            .account="${this.account}"
+            .mutable="${this.mutable}"
+          ></gr-submit-requirement-hovercard>
           <div class="status">${this.renderStatus(requirement.status)}</div>
           <div class="title">
             <gr-limited-text
@@ -131,26 +136,10 @@
   }
 
   renderStatus(status: SubmitRequirementStatus) {
-    let grIcon: string;
-    switch (status) {
-      case SubmitRequirementStatus.SATISFIED:
-        grIcon = 'gr-icons:check';
-        break;
-      case SubmitRequirementStatus.UNSATISFIED:
-        grIcon = 'gr-icons:close';
-        break;
-      case SubmitRequirementStatus.OVERRIDDEN:
-        grIcon = 'gr-icons:warning';
-        break;
-      case SubmitRequirementStatus.NOT_APPLICABLE:
-        grIcon = 'gr-icons:info';
-        break;
-      default:
-        assertNever(status, `Unsupported status: ${status}`);
-    }
+    const icon = iconForStatus(status);
     return html`<iron-icon
-      class=${status.toLowerCase()}
-      icon="${grIcon}"
+      class="${icon}"
+      icon="gr-icons:${icon}"
     ></iron-icon>`;
   }
 
@@ -175,33 +164,6 @@
     );
   }
 
-  renderLabels(requirement: SubmitRequirementResultInfo) {
-    const requirementLabels = extractAssociatedLabels(requirement);
-    const labels = this.change?.labels ?? {};
-
-    const allLabels: Label[] = [];
-
-    for (const label of Object.keys(labels)) {
-      if (requirementLabels.includes(label)) {
-        allLabels.push({
-          labelName: label,
-          icon: '',
-          style: '',
-          labelInfo: labels[label],
-        });
-      }
-    }
-    return allLabels.map(
-      label => html`<gr-label-info
-        .change="${this.change}"
-        .account="${this.account}"
-        .mutable="${this.mutable}"
-        label="${label.labelName}"
-        .labelInfo="${label.labelInfo}"
-      ></gr-label-info>`
-    );
-  }
-
   renderTriggerVotes(submitReqs: SubmitRequirementResultInfo[]) {
     const labels = this.change?.labels ?? {};
     const allLabels = Object.keys(labels);
diff --git a/polygerrit-ui/app/utils/label-util.ts b/polygerrit-ui/app/utils/label-util.ts
index 230c408..e46e4fc 100644
--- a/polygerrit-ui/app/utils/label-util.ts
+++ b/polygerrit-ui/app/utils/label-util.ts
@@ -14,7 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {SubmitRequirementResultInfo} from '../api/rest-api';
+import {
+  SubmitRequirementResultInfo,
+  SubmitRequirementStatus,
+} from '../api/rest-api';
 import {
   AccountInfo,
   ApprovalInfo,
@@ -24,7 +27,7 @@
   LabelNameToInfoMap,
   VotingRangeInfo,
 } from '../types/common';
-import {unique} from './common-util';
+import {assertNever, unique} from './common-util';
 
 // Name of the standard Code-Review label.
 export const CODE_REVIEW = 'Code-Review';
@@ -132,3 +135,18 @@
   }
   return labels.filter(unique);
 }
+
+export function iconForStatus(status: SubmitRequirementStatus) {
+  switch (status) {
+    case SubmitRequirementStatus.SATISFIED:
+      return 'check';
+    case SubmitRequirementStatus.UNSATISFIED:
+      return 'close';
+    case SubmitRequirementStatus.OVERRIDDEN:
+      return 'warning';
+    case SubmitRequirementStatus.NOT_APPLICABLE:
+      return 'info';
+    default:
+      assertNever(status, `Unsupported status: ${status}`);
+  }
+}