Add filter for checks state in checks table

Change-Id: I5affb9f2c0d4e8f312c4b96f05b214897eee1804
diff --git a/gr-checks/gr-checks-view.html b/gr-checks/gr-checks-view.html
index e0bd67d..9e0910d 100644
--- a/gr-checks/gr-checks-view.html
+++ b/gr-checks/gr-checks-view.html
@@ -49,7 +49,6 @@
       header {
         display: flex;
         margin: 1rem 1rem 0;
-        justify-content: space-between;
       }
 
       table {
@@ -88,31 +87,54 @@
         padding-left: 1em;
       }
 
-      .configure-button {
-        float:right;
-        margin-top: 10px;
+      .configure-button-container {
+        text-align: right;
+        flex: 1;
+      }
+
+      .filter {
+        margin-left: 10px;
       }
 
       #listOverlay {
         width: 75%;
       }
+
+      .container {
+        display: flex;
+        width: 100%;
+      }
     </style>
 
     <header>
-      <template is="dom-if" if="[[_patchSetDropdownItems.length]]">
-        <gr-dropdown-list
-          class="patch-set-dropdown"
-          items="[[_patchSetDropdownItems]]"
-          on-value-change="_handlePatchSetChanged"
-          value="[[_currentPatchSet]]">
-        </gr-dropdown-list>
-      </template>
-      <template is="dom-if" if="[[_createCheckerCapability]]">
-        <gr-button class="configure-button" on-click="_handleConfigureClicked"> Configure </gr-button>
-      </template>
+      <div class="container">
+        <template is="dom-if" if="[[_patchSetDropdownItems.length]]">
+          <gr-dropdown-list
+            class="patch-set-dropdown"
+            items="[[_patchSetDropdownItems]]"
+            on-value-change="_handlePatchSetChanged"
+            value="[[_currentPatchSet]]">
+          </gr-dropdown-list>
+        </template>
+        <template is="dom-if" if="[[_hasResults(_status)]]">
+          <div class="filter">
+            <span> Filter By: </span>
+            <gr-dropdown-list
+              class="check-state-filter"
+              items="[[_statuses]]"
+              on-value-change="_handleStatusFilterChanged"
+              value="[[_currentStatus]]">
+            </gr-dropdown-list>
+          </div>
+        </template>
+        <template is="dom-if" if="[[_createCheckerCapability]]">
+          <div class="configure-button-container">
+            <gr-button on-click="_handleConfigureClicked"> Configure </gr-button>
+          </div>
+        </template>
+      </div>
     </header>
 
-
     <template is="dom-if" if="[[_isLoading(_status)]]">
       <div class="no-content">
         <p>Loading...</p>
@@ -150,26 +172,26 @@
           </tr>
         </thead>
         <tbody>
-          <template is="dom-repeat" items="[[_checks]]" as="check">
-            <tr>
-              <gr-checks-item on-retry-check="_handleRetryCheck" on-toggle-check-message="_toggleCheckMessage"
-                check="[[check]]">
-              </gr-checks-item>
-            </tr>
-            <template is="dom-if" if="[[check.showCheckMessage]]">
+          <template is="dom-repeat" items="[[_visibleChecks]]" as="check">
               <tr>
-                <td colspan="10" class="check-message-heading">
-                  <span class="message-heading">
-                    Message
-                  </span>
-                </td>
+                <gr-checks-item on-retry-check="_handleRetryCheck" on-toggle-check-message="_toggleCheckMessage"
+                  check="[[check]]">
+                </gr-checks-item>
               </tr>
-              <tr>
-                <td colspan="10" class="check-message">
-                  <span class="message"> [[check.message]] </span>
-                </td>
-              </tr>
-            </template>
+              <template is="dom-if" if="[[check.showCheckMessage]]">
+                <tr>
+                  <td colspan="10" class="check-message-heading">
+                    <span class="message-heading">
+                      Message
+                    </span>
+                  </td>
+                </tr>
+                <tr>
+                  <td colspan="10" class="check-message">
+                    <span class="message"> [[check.message]] </span>
+                  </td>
+                </tr>
+              </template>
           </template>
         </tbody>
       </table>
diff --git a/gr-checks/gr-checks-view.js b/gr-checks/gr-checks-view.js
index 8d4e3f8..4ca7ebc 100644
--- a/gr-checks/gr-checks-view.js
+++ b/gr-checks/gr-checks-view.js
@@ -13,6 +13,9 @@
     Statuses.NOT_RELEVANT,
   ];
 
+  const STATE_ALL = 'ALL';
+  const CheckStateFilters = [STATE_ALL, ...StatusPriorityOrder];
+
   const CHECKS_POLL_INTERVAL_MS = 60 * 1000;
 
   /**
@@ -50,11 +53,16 @@
       isConfigured: Function,
       /** @type {function(string, string): !Promise<!Object>} */
       pluginRestApi: Object,
-      _checks: Object,
+      _checks: Array,
       _status: {
         type: Object,
         value: LoadingStatus.LOADING,
       },
+      _visibleChecks: {
+        type: Array,
+        computed: '_computeVisibleChecks(_checks, _currentStatus)',
+      },
+      _statuses: Array,
       pollChecksInterval: Number,
       visibilityChangeListenerAdded: {
         type: Boolean,
@@ -72,6 +80,10 @@
       _currentPatchSet: {
         type: Number,
       },
+      _currentStatus: {
+        type: String,
+        value: STATE_ALL,
+      },
     },
 
     observers: [
@@ -80,6 +92,12 @@
 
     attached() {
       this.pluginRestApi = this.plugin.restApi();
+      this._statuses = CheckStateFilters.map(state => {
+        return {
+          text: state,
+          value: state,
+        };
+      });
       this._initCreateCheckerCapability();
     },
 
@@ -100,6 +118,13 @@
           .sort((a, b) => b.value - a.value);
     },
 
+    _computeVisibleChecks(checks, status) {
+      if (!checks) return;
+      return checks.filter(check =>
+        status === STATE_ALL || check.state === status
+      );
+    },
+
     _handleRevisionUpdate(revision) {
       this._currentPatchSet = revision._number;
     },
@@ -111,6 +136,12 @@
       this._currentPatchSet = patchSet;
     },
 
+    _handleStatusFilterChanged(e) {
+      const status = e.detail.value;
+      if (status === this._currentStatus) return;
+      this._currentStatus = status;
+    },
+
     _handleCheckersListResize() {
       // Force polymer to recalculate position of overlay when length of
       // checkers changes
diff --git a/gr-checks/gr-checks-view_test.html b/gr-checks/gr-checks-view_test.html
index 16fa6c4..c35a940 100644
--- a/gr-checks/gr-checks-view_test.html
+++ b/gr-checks/gr-checks-view_test.html
@@ -97,6 +97,8 @@
   };
 
   const CHECKS_POLL_INTERVAL_MS = 60 * 1000;
+  const STATE_ALL = 'ALL';
+  const Statuses = window.Gerrit.Checks.Statuses;
 
   suite('gr-checks-view tests', () => {
     let element;
@@ -319,7 +321,9 @@
 
     suite('with checks', () => {
       setup(done => {
-        getChecksResolve([CHECK1, CHECK1, CHECK1]);
+        const CHECK3 = Object.assign({}, CHECK1, {state: 'FAILED'});
+        const CHECK4 = Object.assign({}, CHECK1, {state: 'FAILED'});
+        getChecksResolve([CHECK1, CHECK2, CHECK3, CHECK4]);
         flush(done);
       });
 
@@ -329,7 +333,7 @@
 
       test('renders a table of all the checks', () => {
         const tbody = element.$$('table > tbody');
-        assert.lengthOf(tbody.querySelectorAll('gr-checks-item'), 3);
+        assert.lengthOf(tbody.querySelectorAll('gr-checks-item'), 4);
       });
 
       suite('create checker capability false', () => {
@@ -369,7 +373,7 @@
 
       suite('patchset navigation', () => {
         test('renders the dropdown', () => {
-          assert.isNotNull(element.querySelector('gr-dropdown-list'));
+          assert.isNotNull(element.querySelector('.patch-set-dropdown'));
         });
 
         test('when patchset updated it fetches new checks', done => {
@@ -400,6 +404,25 @@
           });
         });
       });
+
+      suite('check state filter', () => {
+        test('renders the filter dropdown', () => {
+          assert.isNotNull(element.querySelector('.check-state-filter'));
+        });
+
+        test('default filter is all', () => {
+          assert.equal(element._currentStatus, STATE_ALL);
+        });
+
+        test('updating filter status filters checks', done => {
+          assert.equal(element._visibleChecks.length, 4);
+          element._currentStatus = Statuses.RUNNING;
+          flush(() => {
+            assert.equal(element._visibleChecks.length, 2);
+            done();
+          });
+        });
+      });
     });
   });