Merge "Add method resolveExactIgnoreVisibility for AccountResolver"
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-results.ts b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
index 1752da0..e81a190 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-results.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
@@ -876,6 +876,10 @@
   @state()
   isShowAll: Map<Category, boolean> = new Map();
 
+  /** Maintains the state of which result sections has all results expanded. */
+  @state()
+  allResultsExpanded: Map<Category, boolean> = new Map();
+
   /**
    * This is the current state of whether a section is expanded or not. As long
    * as isSectionExpandedByUser is false this will be computed by a default rule
@@ -1045,6 +1049,9 @@
           padding: var(--spacing-s) var(--spacing-m);
         }
         .categoryHeader {
+          display: flex;
+          justify-content: space-between;
+          align-items: flex-end;
           margin-top: var(--spacing-l);
           margin-left: var(--spacing-l);
           cursor: default;
@@ -1461,6 +1468,8 @@
     const expandedClass = expanded ? 'expanded' : 'collapsed';
 
     const isShowAll = this.isShowAll.get(category) ?? false;
+    const allExpanded = this.allResultsExpanded.get(category) ?? false;
+    const hasExpandableResults = filtered.some(computeIsExpandable);
     const resultCount = filtered.length;
     const empty = resultCount === 0 ? 'empty' : '';
     const resultLimit = isShowAll ? 1000 : 20;
@@ -1472,28 +1481,40 @@
     );
     const icon = iconFor(category);
     return html`
-      <div class=${expandedClass}>
-        <h3
-          class="categoryHeader ${catString} ${empty} heading-3"
+      <div class="${expandedClass} ${catString}">
+        <div
+          class="categoryHeader ${catString} ${empty}"
           @click=${() => this.toggleExpanded(category)}
         >
-          <gr-icon
-            class="expandIcon"
-            icon=${expanded ? 'expand_less' : 'expand_more'}
-          ></gr-icon>
-          <div class="statusIconWrapper">
+          <h3 class="left heading-3">
             <gr-icon
-              icon=${icon.name}
-              ?filled=${icon.filled}
-              class="statusIcon ${catString}"
+              class="expandIcon"
+              icon=${expanded ? 'expand_less' : 'expand_more'}
             ></gr-icon>
-            <span class="title">${catString}</span>
-            <span class="count">${this.renderCount(all, filtered)}</span>
-            <paper-tooltip offset="5"
-              >${CATEGORY_TOOLTIPS.get(category)}</paper-tooltip
+            <div class="statusIconWrapper">
+              <gr-icon
+                icon=${icon.name}
+                ?filled=${icon.filled}
+                class="statusIcon ${catString}"
+              ></gr-icon>
+              <span class="title">${catString}</span>
+              <span class="count">${this.renderCount(all, filtered)}</span>
+              <paper-tooltip offset="5"
+                >${CATEGORY_TOOLTIPS.get(category)}</paper-tooltip
+              >
+            </div>
+          </h3>
+          <div class="right">
+            <gr-button
+              link
+              ?hidden=${!expanded || !hasExpandableResults}
+              @click=${(e: MouseEvent) =>
+                this.toggleResultsExpanded(e, category)}
+            >
+              ${allExpanded ? 'Collapse All' : 'Expand All'}</gr-button
             >
           </div>
-        </h3>
+        </div>
         ${when(expanded, () =>
           this.renderResults(
             all,
@@ -1527,6 +1548,24 @@
     `;
   }
 
+  toggleResultsExpanded(e: MouseEvent, category: Category) {
+    e.preventDefault();
+    // Clicking the header row would otherwise collapse the entire section.
+    e.stopPropagation();
+    const catString = category.toString().toLowerCase();
+    const current = this.allResultsExpanded.get(category) ?? false;
+    const desired = !current;
+    this.allResultsExpanded.set(category, desired);
+    // this.allResultsExpanded stays the same object, but changes its content
+    this.requestUpdate();
+    const rows = this.shadowRoot!.querySelectorAll<GrResultRow>(
+      `.${catString} gr-result-row`
+    );
+    for (const row of rows) {
+      row.toggleExpanded(desired);
+    }
+  }
+
   toggleShowAll(category: Category) {
     const current = this.isShowAll.get(category) ?? false;
     this.isShowAll.set(category, !current);
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-results_test.ts b/polygerrit-ui/app/elements/checks/gr-checks-results_test.ts
index 1e218b2..f0971fc 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-results_test.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-results_test.ts
@@ -417,16 +417,25 @@
           </div>
         </div>
         <div class="body">
-          <div class="expanded">
-            <h3 class="categoryHeader error heading-3">
-              <gr-icon icon="expand_less" class="expandIcon"></gr-icon>
-              <div class="statusIconWrapper">
-                <gr-icon icon="error" filled class="error statusIcon"></gr-icon>
-                <span class="title"> error </span>
-                <span class="count"> (3) </span>
-                <paper-tooltip offset="5"> </paper-tooltip>
+          <div class="error expanded">
+            <div class="categoryHeader error">
+              <h3 class="left heading-3">
+                <gr-icon icon="expand_less" class="expandIcon"></gr-icon>
+                <div class="statusIconWrapper">
+                  <gr-icon
+                    icon="error"
+                    filled
+                    class="error statusIcon"
+                  ></gr-icon>
+                  <span class="title"> error </span>
+                  <span class="count"> (3) </span>
+                  <paper-tooltip offset="5"> </paper-tooltip>
+                </div>
+              </h3>
+              <div class="right">
+                <gr-button link=""> Expand All </gr-button>
               </div>
-            </h3>
+            </div>
             <gr-result-row
               class="FAKEErrorFinderFinderFinderFinderFinderFinderFinder"
             >
@@ -448,17 +457,22 @@
               <tbody></tbody>
             </table>
           </div>
-          <div class="expanded">
-            <h3 class="categoryHeader heading-3 warning">
-              <gr-icon icon="expand_less" class="expandIcon"></gr-icon>
-              <div class="statusIconWrapper">
-                <gr-icon icon="warning" filled class="warning statusIcon">
-                </gr-icon>
-                <span class="title"> warning </span>
-                <span class="count"> (1) </span>
-                <paper-tooltip offset="5"> </paper-tooltip>
+          <div class="expanded warning">
+            <div class="categoryHeader warning">
+              <h3 class="left heading-3">
+                <gr-icon icon="expand_less" class="expandIcon"></gr-icon>
+                <div class="statusIconWrapper">
+                  <gr-icon icon="warning" filled class="warning statusIcon">
+                  </gr-icon>
+                  <span class="title"> warning </span>
+                  <span class="count"> (1) </span>
+                  <paper-tooltip offset="5"> </paper-tooltip>
+                </div>
+              </h3>
+              <div class="right">
+                <gr-button link=""> Expand All </gr-button>
               </div>
-            </h3>
+            </div>
             <gr-result-row class="FAKESuperCheck" isexpandable> </gr-result-row>
             <table class="resultsTable">
               <thead>
@@ -471,28 +485,38 @@
               <tbody></tbody>
             </table>
           </div>
-          <div class="collapsed">
-            <h3 class="categoryHeader heading-3 info">
-              <gr-icon icon="expand_more" class="expandIcon"></gr-icon>
-              <div class="statusIconWrapper">
-                <gr-icon icon="info" class="info statusIcon"></gr-icon>
-                <span class="title"> info </span>
-                <span class="count"> (3) </span>
-                <paper-tooltip offset="5"> </paper-tooltip>
+          <div class="collapsed info">
+            <div class="categoryHeader info">
+              <h3 class="left heading-3">
+                <gr-icon icon="expand_more" class="expandIcon"></gr-icon>
+                <div class="statusIconWrapper">
+                  <gr-icon icon="info" class="info statusIcon"></gr-icon>
+                  <span class="title"> info </span>
+                  <span class="count"> (3) </span>
+                  <paper-tooltip offset="5"> </paper-tooltip>
+                </div>
+              </h3>
+              <div class="right">
+                <gr-button hidden="" link=""> Expand All </gr-button>
               </div>
-            </h3>
+            </div>
           </div>
-          <div class="collapsed">
-            <h3 class="categoryHeader empty heading-3 success">
-              <gr-icon icon="expand_more" class="expandIcon"></gr-icon>
-              <div class="statusIconWrapper">
-                <gr-icon icon="check_circle" class="statusIcon success">
-                </gr-icon>
-                <span class="title"> success </span>
-                <span class="count"> (0) </span>
-                <paper-tooltip offset="5"> </paper-tooltip>
+          <div class="collapsed success">
+            <div class="categoryHeader empty success">
+              <h3 class="left heading-3">
+                <gr-icon icon="expand_more" class="expandIcon"></gr-icon>
+                <div class="statusIconWrapper">
+                  <gr-icon icon="check_circle" class="statusIcon success">
+                  </gr-icon>
+                  <span class="title"> success </span>
+                  <span class="count"> (0) </span>
+                  <paper-tooltip offset="5"> </paper-tooltip>
+                </div>
+              </h3>
+              <div class="right">
+                <gr-button hidden="" link=""> Expand All </gr-button>
               </div>
-            </h3>
+            </div>
           </div>
         </div>
       `,