Merge "Add a utility for processing the output of HighlightJS"
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.ts b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.ts
index 4db07ce..5da6fa8 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.ts
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.ts
@@ -106,34 +106,39 @@
         gr-button {
           min-width: 42px;
           box-sizing: border-box;
+          --vote-text-color: var(--vote-chip-unselected-text-color);
+        }
+        gr-button.iron-selected {
+          --vote-text-color: var(--vote-chip-selected-text-color);
         }
         gr-button::part(paper-button) {
+          padding: 0 var(--spacing-m);
           background-color: var(
             --button-background-color,
             var(--table-header-background-color)
           );
-          padding: 0 var(--spacing-m);
+          border-color: var(--vote-chip-unselected-outline-color);
+        }
+        gr-button.iron-selected::part(paper-button) {
+          border-color: transparent;
+        }
+        gr-button {
+          --button-background-color: var(--vote-chip-unselected-color);
         }
         gr-button[vote='max'].iron-selected {
-          --button-background-color: var(--vote-color-approved);
+          --button-background-color: var(--vote-chip-selected-positive-color);
         }
         gr-button[vote='positive'].iron-selected {
-          --button-background-color: var(--vote-color-recommended);
-        }
-        gr-button[vote='min'].iron-selected {
-          --button-background-color: var(--vote-color-rejected);
-        }
-        gr-button[vote='negative'].iron-selected {
-          --button-background-color: var(--vote-color-disliked);
+          --button-background-color: var(--vote-chip-selected-positive-color);
         }
         gr-button[vote='neutral'].iron-selected {
-          --button-background-color: var(--vote-color-neutral);
+          --button-background-color: var(--vote-chip-selected-neutral-color);
         }
-        gr-button[vote='positive'].iron-selected::part(paper-button) {
-          border-color: var(--vote-outline-recommended);
+        gr-button[vote='negative'].iron-selected {
+          --button-background-color: var(--vote-chip-selected-negative-color);
         }
-        gr-button[vote='negative'].iron-selected::part(paper-button) {
-          border-color: var(--vote-outline-disliked);
+        gr-button[vote='min'].iron-selected {
+          --button-background-color: var(--vote-chip-selected-negative-color);
         }
         gr-button > gr-tooltip-content {
           margin: 0px -10px;
@@ -228,6 +233,7 @@
           data-value="${value}"
           aria-label="${value}"
           voteChip
+          flatten
         >
           <gr-tooltip-content
             has-tooltip
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.ts b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.ts
index a6f9b78..958b11a 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.ts
@@ -358,6 +358,7 @@
             title="bad"
             vote="min"
             votechip=""
+            flatten=""
           >
             <gr-tooltip-content light-tooltip="" has-tooltip="" title="bad">
               -1
@@ -372,6 +373,7 @@
             tabindex="0"
             vote="neutral"
             votechip=""
+            flatten=""
           >
             <gr-tooltip-content light-tooltip="" has-tooltip="">
               0
@@ -389,6 +391,7 @@
             title="good"
             vote="max"
             votechip=""
+            flatten=""
           >
             <gr-tooltip-content light-tooltip="" has-tooltip="" title="good">
               +1
diff --git a/polygerrit-ui/app/elements/change/gr-message-scores/gr-message-scores.ts b/polygerrit-ui/app/elements/change/gr-message-scores/gr-message-scores.ts
index 1c235d8..f3abe13 100644
--- a/polygerrit-ui/app/elements/change/gr-message-scores/gr-message-scores.ts
+++ b/polygerrit-ui/app/elements/change/gr-message-scores/gr-message-scores.ts
@@ -114,7 +114,8 @@
     if (
       this.flagsService.isEnabled(KnownExperimentId.SUBMIT_REQUIREMENTS_UI) &&
       score.label &&
-      triggerVotes.includes(score.label)
+      triggerVotes.includes(score.label) &&
+      !score.value?.includes(VOTE_RESET_TEXT)
     ) {
       const labels = this.change?.labels ?? {};
       return html`<gr-trigger-vote
diff --git a/polygerrit-ui/app/elements/change/gr-message-scores/gr-message-scores_test.ts b/polygerrit-ui/app/elements/change/gr-message-scores/gr-message-scores_test.ts
index 7398909..d9c1db1 100644
--- a/polygerrit-ui/app/elements/change/gr-message-scores/gr-message-scores_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-message-scores/gr-message-scores_test.ts
@@ -17,8 +17,12 @@
 
 import '../../../test/common-test-setup-karma';
 import './gr-message-scores';
-import {createChangeMessage} from '../../../test/test-data-generators';
-import {queryAll} from '../../../test/test-utils';
+import {
+  createChange,
+  createChangeMessage,
+  createDetailedLabelInfo,
+} from '../../../test/test-data-generators';
+import {queryAll, stubFlags} from '../../../test/test-utils';
 import {GrMessageScores} from './gr-message-scores';
 
 const basicFixture = fixtureFromElement('gr-message-scores');
@@ -133,4 +137,38 @@
     const scoreChips = element.shadowRoot?.querySelectorAll('.score');
     assert.equal(scoreChips?.length, 0);
   });
+
+  test('reset vote', async () => {
+    stubFlags('isEnabled').returns(true);
+    element = basicFixture.instantiate();
+    element.change = {
+      ...createChange(),
+      labels: {
+        'Commit-Queue': createDetailedLabelInfo(),
+        'Auto-Submit': createDetailedLabelInfo(),
+      },
+    };
+    element.message = {
+      ...createChangeMessage(),
+      author: {},
+      expanded: false,
+      message: 'Patch Set 10: Auto-Submit+1 -Commit-Queue',
+    };
+    element.labelExtremes = {
+      'Commit-Queue': {max: 2, min: 0},
+      'Auto-Submit': {max: 1, min: 0},
+    };
+    await element.updateComplete;
+    const triggerChips =
+      element.shadowRoot?.querySelectorAll('gr-trigger-vote');
+    assert.equal(triggerChips?.length, 1);
+    expect(triggerChips?.[0]).shadowDom.equal(`<div class="container">
+      <span class="label">Auto-Submit</span>
+    </div>`);
+    const scoreChips = element.shadowRoot?.querySelectorAll('.score');
+    assert.equal(scoreChips?.length, 1);
+    expect(scoreChips?.[0]).dom.equal(`<span class="removed score">
+    Commit-Queue 0 (vote reset)
+    </span>`);
+  });
 });
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
index e024566..c5b509c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
@@ -520,7 +520,7 @@
     const patchNum = this.patchRange?.patchNum;
     if (!path || !patchNum || patchNum === EditPatchSetNum) return;
     this.checksSubscription = this.getChecksModel()
-      .allResultsLatest$.pipe(
+      .allResults$.pipe(
         map(results =>
           results.filter(result => {
             if (result.patchset !== patchNum) return false;
@@ -575,6 +575,7 @@
   private createCheckEl(check: RunResult) {
     const pointer = check.codePointers?.[0];
     assertIsDefined(pointer, 'code pointer of check result in diff');
+    const line = pointer.range.end_line || pointer.range.start_line || 'LOST';
     const el = document.createElement('gr-diff-check-result');
     // This is what gr-diff expects, even though this is a check, not a comment.
     el.className = 'comment-thread';
@@ -582,9 +583,9 @@
     el.result = check;
     // These attributes are the "interface" between comments/checks and gr-diff.
     // <gr-comment-thread> does not care about them and is not affected by them.
-    el.setAttribute('slot', `${Side.RIGHT}-${pointer.range.end_line}`);
+    el.setAttribute('slot', `${Side.RIGHT}-${line}`);
     el.setAttribute('diff-side', `${Side.RIGHT}`);
-    el.setAttribute('line-num', `${pointer.range.end_line || 'LOST'}`);
+    el.setAttribute('line-num', `${line}`);
     el.setAttribute('range', `${JSON.stringify(pointer.range)}`);
     this.$.diff.appendChild(el);
     return el;
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts b/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts
index 2da1b19..c28e83b 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts
@@ -54,6 +54,10 @@
   @property({type: Boolean, reflect: true})
   link = false;
 
+  // If flattened then the button will not be shown as raised.
+  @property({type: Boolean, reflect: true})
+  flatten = false;
+
   @property({type: Boolean, reflect: true})
   loading = false;
 
@@ -190,7 +194,7 @@
 
   override render() {
     return html`<paper-button
-      ?raised="${!this.link}"
+      ?raised="${!this.link && !this.flatten}"
       ?disabled="${this.disabled || this.loading}"
       role="button"
       tabindex="-1"
diff --git a/polygerrit-ui/app/models/checks/checks-model.ts b/polygerrit-ui/app/models/checks/checks-model.ts
index d23b7dc..7b3b95f 100644
--- a/polygerrit-ui/app/models/checks/checks-model.ts
+++ b/polygerrit-ui/app/models/checks/checks-model.ts
@@ -353,6 +353,16 @@
       .filter(r => r !== undefined)
   );
 
+  public allResults$ = select(
+    combineLatest([
+      this.checksSelectedPatchsetNumber$,
+      this.allResultsSelected$,
+      this.allResultsLatest$,
+    ]),
+    ([selectedPs, selected, latest]) =>
+      selectedPs ? [...selected, ...latest] : latest
+  );
+
   constructor(
     readonly routerModel: RouterModel,
     readonly changeModel: ChangeModel,
diff --git a/polygerrit-ui/app/styles/themes/app-theme.ts b/polygerrit-ui/app/styles/themes/app-theme.ts
index d9c102b..4746d6d 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.ts
+++ b/polygerrit-ui/app/styles/themes/app-theme.ts
@@ -252,6 +252,15 @@
     --vote-outline-recommended: var(--green-700);
     --vote-color-rejected: var(--red-300);
 
+    /* vote chip background colors */
+    --vote-chip-unselected-outline-color: var(--gray-500);
+    --vote-chip-unselected-color: white;
+    --vote-chip-selected-positive-color: var(--green-300);
+    --vote-chip-selected-neutral-color: var(--gray-300);
+    --vote-chip-selected-negative-color: var(--red-300);
+    --vote-chip-unselected-text-color: black;
+    --vote-chip-selected-text-color: black;
+
     --outline-color-focus: var(--gray-900);
 
     /* misc colors */
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.ts b/polygerrit-ui/app/styles/themes/dark-theme.ts
index 79dec45..e1365a4 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.ts
+++ b/polygerrit-ui/app/styles/themes/dark-theme.ts
@@ -136,6 +136,15 @@
       --vote-outline-recommended: var(--green-200);
       --vote-color-rejected: var(--red-200);
 
+      /* vote chip background colors */
+      --vote-chip-unselected-outline-color: var(--gray-500);
+      --vote-chip-unselected-color: var(--grey-800);
+      --vote-chip-selected-positive-color: var(--green-200);
+      --vote-chip-selected-neutral-color: var(--gray-300);
+      --vote-chip-selected-negative-color: var(--red-200);
+      --vote-chip-unselected-text-color: white;
+      --vote-chip-selected-text-color: black;
+
       --outline-color-focus: var(--gray-100);
 
       /* misc colors */