You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by fj...@apache.org on 2019/06/02 00:37:59 UTC

[incubator-druid] branch master updated: Web console: Truncate and allow easy copy of large values in query table (#7816)

This is an automated email from the ASF dual-hosted git repository.

fjy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 9d3dfd1  Web console: Truncate and allow easy copy of large values in query table (#7816)
9d3dfd1 is described below

commit 9d3dfd168aa0b4167ec3f9ce04e7c6c5180d5193
Author: Vadim Ogievetsky <va...@gmail.com>
AuthorDate: Sat Jun 1 17:37:54 2019 -0700

    Web console: Truncate and allow easy copy of large values in query table (#7816)
    
    * table-column-selection -> table-column-selector
    
    * null-table-cell -> table-cell
    
    * make action icon
    
    * made copyable
    
    * updated snapshots
    
    * more tests
    
    * normalizing tests
    
    * tslint fixes
    
    * fix up snapshot desciptions
    
    * nicer edit icons
    
    * remove unused
    
    * better language
    
    * remove unused
    
    * undo unwanted change
    
    * normalize new lines
    
    * truncate long arrays also
    
    * removed double space
---
 web-console/package-lock.json                      | 21 ++----
 web-console/package.json                           |  4 +-
 web-console/script/mkcomp                          | 40 ++++++++++-
 .../bootstrap/react-table-custom-pagination.tsx    |  2 +-
 .../__snapshots__/action-cell.spec.tsx.snap        |  4 +-
 .../src/components/action-cell/action-cell.scss    |  6 --
 .../components/action-cell/action-cell.spec.tsx    | 10 ++-
 .../src/components/action-cell/action-cell.tsx     |  9 +--
 .../__snapshots__/action-icon.spec.tsx.snap        | 23 ++++++
 .../action-icon.scss}                              | 15 ++--
 .../action-icon.spec.tsx}                          | 16 ++---
 .../action-icon.tsx}                               | 30 ++++----
 .../__snapshots__/array-input.spec.tsx.snap        |  2 +-
 .../components/array-input/array-input.spec.tsx    | 23 +++---
 .../src/components/array-input/array-input.tsx     |  1 -
 .../__snapshots__/auto-form.spec.tsx.snap          |  2 +-
 .../src/components/auto-form/auto-form.spec.tsx    |  9 ++-
 .../__snapshots__/center-message.spec.tsx.snap     |  2 +-
 .../center-message/center-message.spec.tsx         |  4 +-
 .../__snapshots__/clearable-input.spec.tsx.snap    |  2 +-
 .../clearable-input/clearable-input.spec.tsx       |  7 +-
 .../__snapshots__/external-link.spec.tsx.snap      |  2 +-
 .../external-link/external-link.spec.tsx           |  8 +--
 .../__snapshots__/header-bar.spec.tsx.snap         |  2 +-
 .../src/components/header-bar/header-bar.spec.tsx  |  6 +-
 web-console/src/components/index.ts                |  4 +-
 .../__snapshots__/json-collapse.spec.tsx.snap      |  2 +-
 .../json-collapse/json-collapse.spec.tsx           | 10 ++-
 ...apse.spec.tsx.snap => json-input.spec.tsx.snap} |  2 +-
 ...{json-collapse.spec.tsx => json-input.spec.tsx} |  8 +--
 .../loader/__snapshots__/loader.spec.tsx.snap      |  2 +-
 web-console/src/components/loader/loader.spec.tsx  | 13 ++--
 .../__snapshots__/menu-checkbox.spec.tsx.snap      |  2 +-
 .../menu-checkbox/menu-checkbox.spec.tsx           |  8 +--
 .../__snapshots__/null-table-cell.spec.tsx.snap    |  3 -
 .../null-table-cell/null-table-cell.spec.tsx       | 35 ----------
 .../components/null-table-cell/null-table-cell.tsx | 47 -------------
 .../__snapshots__/rule-editor.spec.tsx.snap        |  2 +-
 .../components/rule-editor/rule-editor.spec.tsx    |  8 +--
 .../__snapshots__/show-json.spec.tsx.snap          |  2 +-
 .../src/components/show-json/show-json.spec.tsx    |  9 ++-
 web-console/src/components/show-json/show-json.tsx | 25 ++++---
 .../show-log/__snapshots__/show-log.spec.tsx.snap  |  2 +-
 .../src/components/show-log/show-log.spec.tsx      |  7 +-
 web-console/src/components/show-log/show-log.tsx   | 27 ++++----
 .../__snapshots__/sql-control.spec.tsx.snap        |  2 +-
 .../components/sql-control/sql-control.spec.tsx    |  8 +--
 .../__snapshots__/table-cell.spec.tsx.snap         | 67 ++++++++++++++++++
 .../table-cell.scss}                               |  8 ++-
 .../src/components/table-cell/table-cell.spec.tsx  | 80 +++++++++++++++++++++
 .../src/components/table-cell/table-cell.tsx       | 81 ++++++++++++++++++++++
 .../table-column-selector.spec.tsx.snap}           |  4 +-
 .../table-column-selector.scss}                    |  4 +-
 .../table-column-selector.spec.tsx}                | 10 +--
 .../table-column-selector.tsx}                     | 16 ++---
 .../__snapshots__/view-control-bar.spec.tsx.snap   |  2 +-
 .../view-control-bar/view-control-bar.scss         |  2 +-
 .../view-control-bar/view-control-bar.spec.tsx     |  6 +-
 .../__snapshots__/about-dialog.spec.tsx.snap       |  2 +-
 .../src/dialogs/about-dialog/about-dialog.spec.tsx | 10 ++-
 .../src/dialogs/about-dialog/about-dialog.tsx      |  2 +-
 .../async-action-dialog.spec.tsx.snap              |  2 +-
 .../async-action-dialog.spec.tsx                   | 12 ++--
 .../__snapshots__/compaction-dialog.spec.tsx.snap  |  6 +-
 .../compaction-dialog/compaction-dialog.spec.tsx   | 10 ++-
 .../compaction-dialog/compaction-dialog.tsx        |  2 +-
 .../coordinator-dynamic-config.spec.tsx.snap       |  2 +-
 .../coordinator-dynamic-config.spec.tsx            | 10 ++-
 .../coordinator-dynamic-config.tsx                 |  3 +-
 .../__snapshots__/history-dialog.spec.tsx.snap     |  2 +-
 .../dialogs/history-dialog/history-dialog.spec.tsx | 12 ++--
 .../src/dialogs/history-dialog/history-dialog.tsx  |  4 +-
 .../__snapshots__/lookup-edit-dialog.spec.tsx.snap |  2 +-
 .../lookup-edit-dialog/lookup-edit-dialog.spec.tsx | 15 ++--
 .../overload-dynamic-config.spec.tsx.snap          |  2 +-
 .../overload-dynamic-config.spec.tsx               | 11 ++-
 .../overlord-dynamic-config.tsx                    |  3 +-
 .../__snapshots__/query-plan-dialog.spec.tsx.snap  |  2 +-
 .../query-plan-dialog/query-plan-dialog.spec.tsx   | 10 ++-
 .../__snapshots__/retention-dialog.spec.tsx.snap   |  2 +-
 .../retention-dialog.array.spec.ts                 |  2 +-
 .../retention-dialog/retention-dialog.spec.tsx     | 10 ++-
 .../dialogs/retention-dialog/retention-dialog.tsx  |  2 +-
 .../__snapshots__/snitch-dialog.spec.tsx.snap      |  2 +-
 .../dialogs/snitch-dialog/snitch-dialog.spec.tsx   | 11 ++-
 .../__snapshots__/spec-dialog.spec.tsx.snap        |  2 +-
 .../src/dialogs/spec-dialog/spec-dialog.spec.tsx   |  9 ++-
 .../supervisor-table-action-dialog.spec.tsx.snap   |  2 +-
 .../supervisor-table-action-dialog.spec.tsx        | 12 ++--
 .../supervisor-table-action-dialog.tsx             |  2 +-
 .../table-action-dialog.spec.tsx.snap              |  2 +-
 .../table-action-dialog.spec.tsx                   | 12 ++--
 .../task-table-action-dialog.spec.tsx.snap         |  2 +-
 .../task-table-action-dialog.spec.tsx              | 12 ++--
 .../task-table-action-dialog.tsx                   |  3 +-
 web-console/src/utils/druid-expression.ts          |  1 -
 .../src/utils/table-column-selection-handler.tsx   |  2 +-
 .../__snapshots__/datasource-view.spec.tsx.snap    |  4 +-
 .../views/datasource-view/datasource-view.spec.tsx |  6 +-
 .../src/views/datasource-view/datasource-view.tsx  | 11 +--
 .../__snapshots__/home-view.spec.tsx.snap          |  2 +-
 web-console/src/views/home-view/home-view.spec.tsx | 11 +--
 .../__snapshots__/load-data-view.spec.tsx.snap     |  2 +-
 .../views/load-data-view/load-data-view.spec.tsx   | 10 +--
 .../src/views/load-data-view/load-data-view.tsx    | 20 +++---
 .../__snapshots__/lookups-view.spec.tsx.snap       |  4 +-
 .../src/views/lookups-view/lookups-view.spec.tsx   | 10 +--
 .../src/views/lookups-view/lookups-view.tsx        | 10 +--
 .../__snapshots__/segments-view.spec.tsx.snap      |  4 +-
 .../src/views/segments-view/segments-view.spec.tsx | 10 +--
 .../src/views/segments-view/segments-view.tsx      |  6 +-
 .../__snapshots__/servers-view.spec.tsx.snap       |  4 +-
 .../src/views/servers-view/servers-view.spec.tsx   |  8 +--
 .../src/views/servers-view/servers-view.tsx        |  6 +-
 .../sql-view/__snapshots__/sql-view.spec.tsx.snap  |  2 +-
 web-console/src/views/sql-view/sql-view.spec.tsx   | 10 +--
 web-console/src/views/sql-view/sql-view.tsx        |  4 +-
 .../__snapshots__/tasks-view.spec.tsx.snap         |  6 +-
 .../src/views/task-view/tasks-view.spec.tsx        | 10 +--
 web-console/src/views/task-view/tasks-view.tsx     | 10 +--
 web-console/tsconfig.json                          |  5 --
 121 files changed, 631 insertions(+), 516 deletions(-)

diff --git a/web-console/package-lock.json b/web-console/package-lock.json
index 61aeca8..ce1e783 100644
--- a/web-console/package-lock.json
+++ b/web-console/package-lock.json
@@ -1395,9 +1395,9 @@
       }
     },
     "awesome-code-style": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/awesome-code-style/-/awesome-code-style-1.3.2.tgz",
-      "integrity": "sha512-4AHAhzX9jCnqsCcgdSSDGY8ggBoB+x4vcvDElAN5J81nNajGfFHUPrJ6RY7vB1eTzwXBAWLqaEo+FLCgKemfoA==",
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/awesome-code-style/-/awesome-code-style-1.3.4.tgz",
+      "integrity": "sha512-J1mSLB1Z+40x0iF8JwWVKqrciqk0k4V/gUHU8DgVYmmDqMkcW4D+gXi1PZJC0cvHnRm9VHhgcRp+uma9E+FA1g==",
       "dev": true,
       "requires": {
         "stylelint-config-recommended": "^2.2.0",
@@ -9351,15 +9351,6 @@
         "prop-types": "^15.7.2"
       }
     },
-    "react-copy-to-clipboard": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.1.tgz",
-      "integrity": "sha512-ELKq31/E3zjFs5rDWNCfFL4NvNFQvGRoJdAKReD/rUPA+xxiLPQmZBZBvy2vgH7V0GE9isIQpT9WXbwIVErYdA==",
-      "requires": {
-        "copy-to-clipboard": "^3",
-        "prop-types": "^15.5.8"
-      }
-    },
     "react-dom": {
       "version": "16.8.6",
       "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz",
@@ -12183,9 +12174,9 @@
       }
     },
     "tsutils": {
-      "version": "3.12.0",
-      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.12.0.tgz",
-      "integrity": "sha512-64KxDOb3+5ZVbz6NDZlCtOHstLk9+W96Y7d5Z/s5ge92gLaunxDeXYahvB7Rhl1dbaa3ifyq/W53o4mshIV1Tw==",
+      "version": "3.13.0",
+      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.13.0.tgz",
+      "integrity": "sha512-wRtEjVU8Su72sDIDoqno5Scwt8x4eaF0teKO3m4hu8K1QFPnIZMM88CLafs2tapUeWnY9SwwO3bWeOt2uauBcg==",
       "dev": true,
       "requires": {
         "tslib": "^1.8.1"
diff --git a/web-console/package.json b/web-console/package.json
index d61a955..ae61883 100644
--- a/web-console/package.json
+++ b/web-console/package.json
@@ -46,6 +46,7 @@
     "axios": "^0.19.0",
     "brace": "^0.11.1",
     "classnames": "^2.2.6",
+    "copy-to-clipboard": "^3.2.0",
     "d3-array": "^2.2.0",
     "druid-console": "^0.0.2",
     "es6-shim": "^0.35.5",
@@ -56,7 +57,6 @@
     "numeral": "^2.0.6",
     "react": "^16.8.6",
     "react-ace": "^7.0.1",
-    "react-copy-to-clipboard": "^5.0.1",
     "react-dom": "^16.8.6",
     "react-router": "^5.0.0",
     "react-router-dom": "^5.0.0",
@@ -82,7 +82,7 @@
     "@types/react-splitter-layout": "^3.0.0",
     "@types/react-table": "6.8.3",
     "autoprefixer": "^9.5.1",
-    "awesome-code-style": "^1.3.2",
+    "awesome-code-style": "^1.3.4",
     "css-loader": "^2.1.1",
     "enzyme": "^3.9.0",
     "enzyme-adapter-react-16": "^1.13.2",
diff --git a/web-console/script/mkcomp b/web-console/script/mkcomp
index 5ef5d74..a2b9bb5 100755
--- a/web-console/script/mkcomp
+++ b/web-console/script/mkcomp
@@ -43,12 +43,13 @@ if (!/^([a-z-])+$/.test(name)) {
   process.exit();
 }
 
-let path = `./src/${what}s/`;
+let path = `./src/${what}s/${name}/`;
 fs.ensureDirSync(path);
 console.log('Making path:', path);
 
+const spaceName = name.replace(/-/g, ' ');
 const camelName = name.replace(/(^|-)[a-z]/g, (s) => s.replace('-', '').toUpperCase());
-const year = (new Date()).getFullYear();
+const snakeName = camelName[0].toLowerCase() + camelName.substr(1);
 
 function writeFile(path, data) {
   try {
@@ -133,3 +134,38 @@ writeFile(path + name + '.scss',
 
 }
 `);
+
+// Make the spec test file
+writeFile(path + name + '.spec.tsx',
+  `/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 * as React from 'react';
+import { render } from 'react-testing-library';
+
+import { ${camelName} } from './${name}';
+
+describe('${spaceName}', () => {
+  it('action cell snapshot', () => {
+    const ${snakeName} = <${camelName}
+    />;
+    const { container, getByText } = render(${snakeName});
+    expect(container.firstChild).toMatchSnapshot();
+  });
+});
+`);
diff --git a/web-console/src/bootstrap/react-table-custom-pagination.tsx b/web-console/src/bootstrap/react-table-custom-pagination.tsx
index 7225fb9..ee5f12f 100644
--- a/web-console/src/bootstrap/react-table-custom-pagination.tsx
+++ b/web-console/src/bootstrap/react-table-custom-pagination.tsx
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-import {Button, Classes, HTMLSelect} from '@blueprintjs/core';
+import { Button, Classes, HTMLSelect } from '@blueprintjs/core';
 import * as React from 'react';
 
 import './react-table-custom-pagination.scss';
diff --git a/web-console/src/components/action-cell/__snapshots__/action-cell.spec.tsx.snap b/web-console/src/components/action-cell/__snapshots__/action-cell.spec.tsx.snap
index e1c819e..4641a23 100644
--- a/web-console/src/components/action-cell/__snapshots__/action-cell.spec.tsx.snap
+++ b/web-console/src/components/action-cell/__snapshots__/action-cell.spec.tsx.snap
@@ -1,11 +1,11 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe action cell action cell snapshot 1`] = `
+exports[`action cell matches snapshot 1`] = `
 <div
   class="action-cell"
 >
   <span
-    class="bp3-icon bp3-icon-search-template"
+    class="bp3-icon bp3-icon-search-template action-icon"
     icon="search-template"
   >
     <svg
diff --git a/web-console/src/components/action-cell/action-cell.scss b/web-console/src/components/action-cell/action-cell.scss
index db722e0..69109be 100644
--- a/web-console/src/components/action-cell/action-cell.scss
+++ b/web-console/src/components/action-cell/action-cell.scss
@@ -19,12 +19,6 @@
 .action-cell {
   & > * {
     margin-right: 10px;
-    color: #48aff0;
-    cursor: pointer;
-
-    &:hover {
-      color: #4891d2;
-    }
 
     &:last-child {
       margin-right: 0;
diff --git a/web-console/src/components/action-cell/action-cell.spec.tsx b/web-console/src/components/action-cell/action-cell.spec.tsx
index 94d6471..3536894 100644
--- a/web-console/src/components/action-cell/action-cell.spec.tsx
+++ b/web-console/src/components/action-cell/action-cell.spec.tsx
@@ -16,20 +16,18 @@
  * limitations under the License.
  */
 
-
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import { ActionCell} from './action-cell';
+import { ActionCell } from './action-cell';
 
-describe('describe action cell', () => {
-  it('action cell snapshot', () => {
+describe('action cell', () => {
+  it('matches snapshot', () => {
     const actionCell = <ActionCell
       onDetail={() => null}
       actions={[]}
     />;
-    const { container, getByText } = render(actionCell);
+    const { container } = render(actionCell);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/action-cell/action-cell.tsx b/web-console/src/components/action-cell/action-cell.tsx
index 5252132..b4448d2 100644
--- a/web-console/src/components/action-cell/action-cell.tsx
+++ b/web-console/src/components/action-cell/action-cell.tsx
@@ -16,11 +16,12 @@
  * limitations under the License.
  */
 
-import { Icon, Popover, Position } from '@blueprintjs/core';
+import { Popover, Position } from '@blueprintjs/core';
 import { IconNames } from '@blueprintjs/icons';
 import * as React from 'react';
 
 import { BasicAction, basicActionsToMenu } from '../../utils/basic-action';
+import { ActionIcon } from '../action-icon/action-icon';
 
 import './action-cell.scss';
 
@@ -45,15 +46,15 @@ export class ActionCell extends React.Component<ActionCellProps, {}> {
     return <div className="action-cell">
       {
         onDetail &&
-        <Icon
+        <ActionIcon
           icon={IconNames.SEARCH_TEMPLATE}
-          onClick={() => onDetail()}
+          onClick={onDetail}
         />
       }
       {
         actionsMenu &&
         <Popover content={actionsMenu} position={Position.BOTTOM_RIGHT}>
-          <Icon icon={IconNames.WRENCH}/>
+          <ActionIcon icon={IconNames.WRENCH}/>
         </Popover>
       }
     </div>;
diff --git a/web-console/src/components/action-icon/__snapshots__/action-icon.spec.tsx.snap b/web-console/src/components/action-icon/__snapshots__/action-icon.spec.tsx.snap
new file mode 100644
index 0000000..97f018b
--- /dev/null
+++ b/web-console/src/components/action-icon/__snapshots__/action-icon.spec.tsx.snap
@@ -0,0 +1,23 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`action icon matches snapshot 1`] = `
+<span
+  class="bp3-icon bp3-icon-clipboard action-icon"
+  icon="clipboard"
+>
+  <svg
+    data-icon="clipboard"
+    height="16"
+    viewBox="0 0 16 16"
+    width="16"
+  >
+    <desc>
+      clipboard
+    </desc>
+    <path
+      d="M11 2c0-.55-.45-1-1-1h.22C9.88.4 9.24 0 8.5 0S7.12.4 6.78 1H7c-.55 0-1 .45-1 1v1h5V2zm2 0h-1v2H5V2H4c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h9c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1z"
+      fill-rule="evenodd"
+    />
+  </svg>
+</span>
+`;
diff --git a/web-console/src/components/null-table-cell/null-table-cell.scss b/web-console/src/components/action-icon/action-icon.scss
similarity index 85%
copy from web-console/src/components/null-table-cell/null-table-cell.scss
copy to web-console/src/components/action-icon/action-icon.scss
index fd5c50c..5d9f1c3 100644
--- a/web-console/src/components/null-table-cell/null-table-cell.scss
+++ b/web-console/src/components/action-icon/action-icon.scss
@@ -16,16 +16,11 @@
  * limitations under the License.
  */
 
-.null-table-cell {
-  &.null {
-    font-style: italic;
-  }
-
-  &.unparseable {
-    color: #9E2B0E;
-  }
+.action-icon {
+  color: #48aff0;
+  cursor: pointer;
 
-  &.timestamp {
-    font-weight: bold;
+  &:hover {
+    color: #4891d2;
   }
 }
diff --git a/web-console/src/components/show-log/show-log.spec.tsx b/web-console/src/components/action-icon/action-icon.spec.tsx
similarity index 78%
copy from web-console/src/components/show-log/show-log.spec.tsx
copy to web-console/src/components/action-icon/action-icon.spec.tsx
index ff418ed..856fe1b 100644
--- a/web-console/src/components/show-log/show-log.spec.tsx
+++ b/web-console/src/components/action-icon/action-icon.spec.tsx
@@ -19,17 +19,13 @@
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {ShowLog} from './show-log';
+import { ActionIcon } from './action-icon';
+import { IconNames } from '@blueprintjs/icons';
 
-
-describe('describe show log', () => {
-  it('describe show log', () => {
-    const showLog =
-      <ShowLog
-        endpoint={'test'}
-        downloadFilename={'test'}
-      />;
-    const { container, getByText } = render(showLog);
+describe('action icon', () => {
+  it('matches snapshot', () => {
+    const actionIcon = <ActionIcon icon={IconNames.CLIPBOARD}/>;
+    const { container } = render(actionIcon);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/action-cell/action-cell.spec.tsx b/web-console/src/components/action-icon/action-icon.tsx
similarity index 62%
copy from web-console/src/components/action-cell/action-cell.spec.tsx
copy to web-console/src/components/action-icon/action-icon.tsx
index 94d6471..9742f44 100644
--- a/web-console/src/components/action-cell/action-cell.spec.tsx
+++ b/web-console/src/components/action-icon/action-icon.tsx
@@ -16,20 +16,26 @@
  * limitations under the License.
  */
 
+import { Icon, IconName } from '@blueprintjs/core';
+import classNames from 'classnames';
+import * as React from 'react';
 
+import './action-icon.scss';
 
-import * as React from 'react';
-import { render } from 'react-testing-library';
+export interface ActionIconProps extends React.Props<any> {
+  className?: string;
+  icon: IconName;
+  onClick?: () => void;
+}
 
-import { ActionCell} from './action-cell';
+export class ActionIcon extends React.Component<ActionIconProps, {}> {
+  render() {
+    const { className, icon, onClick } = this.props;
 
-describe('describe action cell', () => {
-  it('action cell snapshot', () => {
-    const actionCell = <ActionCell
-      onDetail={() => null}
-      actions={[]}
+    return <Icon
+      className={classNames('action-icon', className)}
+      icon={icon}
+      onClick={onClick}
     />;
-    const { container, getByText } = render(actionCell);
-    expect(container.firstChild).toMatchSnapshot();
-  });
-});
+  }
+}
diff --git a/web-console/src/components/array-input/__snapshots__/array-input.spec.tsx.snap b/web-console/src/components/array-input/__snapshots__/array-input.spec.tsx.snap
index 89823bc..c5e34f3 100644
--- a/web-console/src/components/array-input/__snapshots__/array-input.spec.tsx.snap
+++ b/web-console/src/components/array-input/__snapshots__/array-input.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe array input array input snapshot 1`] = `
+exports[`array input matches snapshot 1`] = `
 <textarea
   class="bp3-input bp3-fill test"
   placeholder="test"
diff --git a/web-console/src/components/array-input/array-input.spec.tsx b/web-console/src/components/array-input/array-input.spec.tsx
index 3b858e4..c2a39af 100644
--- a/web-console/src/components/array-input/array-input.spec.tsx
+++ b/web-console/src/components/array-input/array-input.spec.tsx
@@ -16,24 +16,21 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {MenuCheckbox} from '../menu-checkbox/menu-checkbox';
+import { ArrayInput } from './array-input';
 
-import {ArrayInput} from './array-input';
+describe('array input', () => {
+  it('matches snapshot', () => {
+    const arrayInput = <ArrayInput
+      values={['apple', 'banana', 'pear']}
+      className={'test'}
+      placeholder={'test'}
+      onChange={() => null}
+    />;
 
-describe('describe array input', () => {
-  it('array input snapshot', () => {
-    const arrayInput =
-      <ArrayInput
-        values={['apple', 'banana', 'pear']}
-        className={'test'}
-        placeholder={'test'}
-        onChange={() => null}
-      />;
-    const { container, getByText } = render(arrayInput);
+    const { container } = render(arrayInput);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/array-input/array-input.tsx b/web-console/src/components/array-input/array-input.tsx
index 6af8cbf..6b2bab9 100644
--- a/web-console/src/components/array-input/array-input.tsx
+++ b/web-console/src/components/array-input/array-input.tsx
@@ -16,7 +16,6 @@
  * limitations under the License.
  */
 
-
 import { ITagInputProps, TextArea } from '@blueprintjs/core';
 import * as React from 'react';
 
diff --git a/web-console/src/components/auto-form/__snapshots__/auto-form.spec.tsx.snap b/web-console/src/components/auto-form/__snapshots__/auto-form.spec.tsx.snap
index 7e581ec..df8004a 100644
--- a/web-console/src/components/auto-form/__snapshots__/auto-form.spec.tsx.snap
+++ b/web-console/src/components/auto-form/__snapshots__/auto-form.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe auto-form snapshot auto-form snapshot 1`] = `
+exports[`auto-form snapshot matches snapshot 1`] = `
 <div
   class="auto-form"
 >
diff --git a/web-console/src/components/auto-form/auto-form.spec.tsx b/web-console/src/components/auto-form/auto-form.spec.tsx
index 2912924..cee3d8d 100644
--- a/web-console/src/components/auto-form/auto-form.spec.tsx
+++ b/web-console/src/components/auto-form/auto-form.spec.tsx
@@ -19,11 +19,10 @@
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {AutoForm} from './auto-form';
+import { AutoForm } from './auto-form';
 
-
-describe('describe auto-form snapshot', () => {
-  it('auto-form snapshot', () => {
+describe('auto-form snapshot', () => {
+  it('matches snapshot', () => {
     const autoForm =
       <AutoForm
         fields={[{name: 'testOne', type: 'number'},
@@ -36,7 +35,7 @@ describe('describe auto-form snapshot', () => {
         model={String}
         onChange={(newModel: Record<string, any>) => {}}
       />;
-    const { container, getByText } = render(autoForm);
+    const { container } = render(autoForm);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/center-message/__snapshots__/center-message.spec.tsx.snap b/web-console/src/components/center-message/__snapshots__/center-message.spec.tsx.snap
index 2e9140d..a435fff 100644
--- a/web-console/src/components/center-message/__snapshots__/center-message.spec.tsx.snap
+++ b/web-console/src/components/center-message/__snapshots__/center-message.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`decribe center-message center-message snapshot 1`] = `
+exports[`decribe center-message matches snapshot 1`] = `
 <div
   class="center-message bp3-input"
 >
diff --git a/web-console/src/components/center-message/center-message.spec.tsx b/web-console/src/components/center-message/center-message.spec.tsx
index 6787c0a..9cbcebb 100644
--- a/web-console/src/components/center-message/center-message.spec.tsx
+++ b/web-console/src/components/center-message/center-message.spec.tsx
@@ -22,13 +22,13 @@ import { render } from 'react-testing-library';
 import { CenterMessage } from './center-message';
 
 describe('decribe center-message', () => {
-  it('center-message snapshot', () => {
+  it('matches snapshot', () => {
     const centerMessage =
       <CenterMessage>
         <div>Hello World</div>
       </CenterMessage>;
 
-    const { container, getByText } = render(centerMessage);
+    const { container } = render(centerMessage);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/clearable-input/__snapshots__/clearable-input.spec.tsx.snap b/web-console/src/components/clearable-input/__snapshots__/clearable-input.spec.tsx.snap
index c6ef24b..8f6e315 100644
--- a/web-console/src/components/clearable-input/__snapshots__/clearable-input.spec.tsx.snap
+++ b/web-console/src/components/clearable-input/__snapshots__/clearable-input.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`decribe clearable-input clearable-input snapshot 1`] = `
+exports[`decribe clearable-input matches snapshot 1`] = `
 <div
   class="bp3-input-group clearable-input testClassName"
 >
diff --git a/web-console/src/components/clearable-input/clearable-input.spec.tsx b/web-console/src/components/clearable-input/clearable-input.spec.tsx
index 1ce62cc..ca26f6e 100644
--- a/web-console/src/components/clearable-input/clearable-input.spec.tsx
+++ b/web-console/src/components/clearable-input/clearable-input.spec.tsx
@@ -19,11 +19,10 @@
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {ClearableInput} from './clearable-input';
-
+import { ClearableInput } from './clearable-input';
 
 describe('decribe clearable-input', () => {
-  it('clearable-input snapshot', () => {
+  it('matches snapshot', () => {
     const centerMessage =
       <ClearableInput
         className={'testClassName'}
@@ -34,7 +33,7 @@ describe('decribe clearable-input', () => {
         <div>Hello World</div>
       </ClearableInput>;
 
-    const { container, getByText } = render(centerMessage);
+    const { container } = render(centerMessage);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/external-link/__snapshots__/external-link.spec.tsx.snap b/web-console/src/components/external-link/__snapshots__/external-link.spec.tsx.snap
index 3b8a139..fa471eb 100644
--- a/web-console/src/components/external-link/__snapshots__/external-link.spec.tsx.snap
+++ b/web-console/src/components/external-link/__snapshots__/external-link.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe external link external link snapshot 1`] = `
+exports[`external link matches snapshot 1`] = `
 <a
   href="http://test/"
   target="_blank"
diff --git a/web-console/src/components/external-link/external-link.spec.tsx b/web-console/src/components/external-link/external-link.spec.tsx
index a4835ac..235e7ad 100644
--- a/web-console/src/components/external-link/external-link.spec.tsx
+++ b/web-console/src/components/external-link/external-link.spec.tsx
@@ -19,16 +19,16 @@
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {ExternalLink} from './external-link';
+import { ExternalLink } from './external-link';
 
-describe('describe external link', () => {
-  it('external link snapshot', () => {
+describe('external link', () => {
+  it('matches snapshot', () => {
     const externalLink =
       <ExternalLink href={'http://test/'}>
         <div>hello world</div>
       </ExternalLink>;
 
-    const { container, getByText } = render(externalLink);
+    const { container } = render(externalLink);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/header-bar/__snapshots__/header-bar.spec.tsx.snap b/web-console/src/components/header-bar/__snapshots__/header-bar.spec.tsx.snap
index 64769a7..dd220bd 100644
--- a/web-console/src/components/header-bar/__snapshots__/header-bar.spec.tsx.snap
+++ b/web-console/src/components/header-bar/__snapshots__/header-bar.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe header bar header bar snapshot 1`] = `
+exports[`header bar matches snapshot 1`] = `
 <Blueprint3.Navbar
   className="header-bar"
 >
diff --git a/web-console/src/components/header-bar/header-bar.spec.tsx b/web-console/src/components/header-bar/header-bar.spec.tsx
index 54b842b..0cb4ef0 100644
--- a/web-console/src/components/header-bar/header-bar.spec.tsx
+++ b/web-console/src/components/header-bar/header-bar.spec.tsx
@@ -19,10 +19,10 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 
-import {HeaderBar} from './header-bar';
+import { HeaderBar } from './header-bar';
 
-describe('describe header bar', () => {
-  it('header bar snapshot', () => {
+describe('header bar', () => {
+  it('matches snapshot', () => {
     const headerBar = shallow(
       <HeaderBar
         active={'load-data'}
diff --git a/web-console/src/components/index.ts b/web-console/src/components/index.ts
index 0acf625..6a706a7 100644
--- a/web-console/src/components/index.ts
+++ b/web-console/src/components/index.ts
@@ -26,11 +26,11 @@ export * from './json-collapse/json-collapse';
 export * from './json-input/json-input';
 export * from './loader/loader';
 export * from './menu-checkbox/menu-checkbox';
-export * from './null-table-cell/null-table-cell';
+export * from './table-cell/table-cell';
 export * from './rule-editor/rule-editor';
 export * from './show-json/show-json';
 export * from './show-log/show-log';
 export * from './sql-control/sql-control';
-export * from './table-column/table-column-selection';
+export * from './table-column-selector/table-column-selector';
 export * from './view-control-bar/view-control-bar';
 export * from './clearable-input/clearable-input';
diff --git a/web-console/src/components/json-collapse/__snapshots__/json-collapse.spec.tsx.snap b/web-console/src/components/json-collapse/__snapshots__/json-collapse.spec.tsx.snap
index 88bf309..fb28b32 100644
--- a/web-console/src/components/json-collapse/__snapshots__/json-collapse.spec.tsx.snap
+++ b/web-console/src/components/json-collapse/__snapshots__/json-collapse.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe json collapse  json collapse snapshot 1`] = `
+exports[`json collapse matches snapshot 1`] = `
 <div
   class="json-collapse"
 >
diff --git a/web-console/src/components/json-collapse/json-collapse.spec.tsx b/web-console/src/components/json-collapse/json-collapse.spec.tsx
index 7efef6e..680b8a4 100644
--- a/web-console/src/components/json-collapse/json-collapse.spec.tsx
+++ b/web-console/src/components/json-collapse/json-collapse.spec.tsx
@@ -16,21 +16,19 @@
  * limitations under the License.
  */
 
-import {nullableTypeAnnotation} from '@babel/types';
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {JSONCollapse} from './json-collapse';
+import { JSONCollapse } from './json-collapse';
 
-
-describe('describe json collapse ', () => {
-  it('json collapse snapshot', () => {
+describe('json collapse', () => {
+  it('matches snapshot', () => {
     const jsonCollapse =
     <JSONCollapse
       buttonText={'test'}
       stringValue={JSON.stringify({ name : 'test' })}
     />;
-    const { container, getByText } = render(jsonCollapse);
+    const { container } = render(jsonCollapse);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/json-input/__snapshots__/json-collapse.spec.tsx.snap b/web-console/src/components/json-input/__snapshots__/json-input.spec.tsx.snap
similarity index 97%
rename from web-console/src/components/json-input/__snapshots__/json-collapse.spec.tsx.snap
rename to web-console/src/components/json-input/__snapshots__/json-input.spec.tsx.snap
index 9022a26..abcf468 100644
--- a/web-console/src/components/json-input/__snapshots__/json-collapse.spec.tsx.snap
+++ b/web-console/src/components/json-input/__snapshots__/json-input.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe json input json input snapshot 1`] = `
+exports[`json input matches snapshot 1`] = `
 <div
   class=" ace_editor ace-tm"
   id="ace-editor"
diff --git a/web-console/src/components/json-input/json-collapse.spec.tsx b/web-console/src/components/json-input/json-input.spec.tsx
similarity index 85%
rename from web-console/src/components/json-input/json-collapse.spec.tsx
rename to web-console/src/components/json-input/json-input.spec.tsx
index a3fd7e3..134ca83 100644
--- a/web-console/src/components/json-input/json-collapse.spec.tsx
+++ b/web-console/src/components/json-input/json-input.spec.tsx
@@ -19,16 +19,16 @@
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {JSONInput} from './json-input';
+import { JSONInput } from './json-input';
 
-describe('describe json input', () => {
-  it('json input snapshot', () => {
+describe('json input', () => {
+  it('matches snapshot', () => {
     const jsonCollapse =
     <JSONInput
       onChange={(newJSONValue: any) => {}}
       value={'test'}
     />;
-    const { container, getByText } = render(jsonCollapse);
+    const { container } = render(jsonCollapse);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/loader/__snapshots__/loader.spec.tsx.snap b/web-console/src/components/loader/__snapshots__/loader.spec.tsx.snap
index 5568481..5cbceea 100644
--- a/web-console/src/components/loader/__snapshots__/loader.spec.tsx.snap
+++ b/web-console/src/components/loader/__snapshots__/loader.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe loader loader snapshot 1`] = `
+exports[`loader matches snapshot 1`] = `
 <div
   class="loader"
 >
diff --git a/web-console/src/components/loader/loader.spec.tsx b/web-console/src/components/loader/loader.spec.tsx
index 4f1907c..85354aa 100644
--- a/web-console/src/components/loader/loader.spec.tsx
+++ b/web-console/src/components/loader/loader.spec.tsx
@@ -19,16 +19,15 @@
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import { Loader} from './loader';
+import { Loader } from './loader';
 
-describe('describe loader', () => {
-  it('loader snapshot', () => {
-    const loader =
-    <Loader
-      loading={true}
+describe('loader', () => {
+  it('matches snapshot', () => {
+    const loader = <Loader
+      loading
       loadingText={'test'}
     />;
-    const { container, getByText } = render(loader);
+    const { container } = render(loader);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/menu-checkbox/__snapshots__/menu-checkbox.spec.tsx.snap b/web-console/src/components/menu-checkbox/__snapshots__/menu-checkbox.spec.tsx.snap
index fd9dc4e..24b2770 100644
--- a/web-console/src/components/menu-checkbox/__snapshots__/menu-checkbox.spec.tsx.snap
+++ b/web-console/src/components/menu-checkbox/__snapshots__/menu-checkbox.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe menuCheckBox  menuCheckbox snapshot 1`] = `
+exports[`menu checkbox matches snapshot 1`] = `
 <li
   class="menu-checkbox"
 >
diff --git a/web-console/src/components/menu-checkbox/menu-checkbox.spec.tsx b/web-console/src/components/menu-checkbox/menu-checkbox.spec.tsx
index 6ca6efe..f228ee7 100644
--- a/web-console/src/components/menu-checkbox/menu-checkbox.spec.tsx
+++ b/web-console/src/components/menu-checkbox/menu-checkbox.spec.tsx
@@ -19,13 +19,13 @@
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {MenuCheckbox} from './menu-checkbox';
+import { MenuCheckbox } from './menu-checkbox';
 
-describe('describe menuCheckBox ', () => {
-  it('menuCheckbox snapshot', () => {
+describe('menu checkbox', () => {
+  it('matches snapshot', () => {
     const menuCheckbox =
     <MenuCheckbox/>;
-    const { container, getByText } = render(menuCheckbox);
+    const { container } = render(menuCheckbox);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/null-table-cell/__snapshots__/null-table-cell.spec.tsx.snap b/web-console/src/components/null-table-cell/__snapshots__/null-table-cell.spec.tsx.snap
deleted file mode 100644
index 8452cb7..0000000
--- a/web-console/src/components/null-table-cell/__snapshots__/null-table-cell.spec.tsx.snap
+++ /dev/null
@@ -1,3 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`describe nullTable nullTable snapshot 1`] = `test`;
diff --git a/web-console/src/components/null-table-cell/null-table-cell.spec.tsx b/web-console/src/components/null-table-cell/null-table-cell.spec.tsx
deleted file mode 100644
index 83feffa..0000000
--- a/web-console/src/components/null-table-cell/null-table-cell.spec.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 * as React from 'react';
-import { render } from 'react-testing-library';
-
-import {NullTableCell} from './null-table-cell';
-
-describe('describe nullTable', () => {
-  it('nullTable snapshot', () => {
-    const nullTableCell =
-    <NullTableCell
-      value={'test'}
-      unparseable={false}
-      timestamp={false}
-    />
-    const { container, getByText } = render(nullTableCell);
-    expect(container.firstChild).toMatchSnapshot();
-  });
-});
diff --git a/web-console/src/components/null-table-cell/null-table-cell.tsx b/web-console/src/components/null-table-cell/null-table-cell.tsx
deleted file mode 100644
index c3df14b..0000000
--- a/web-console/src/components/null-table-cell/null-table-cell.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 * as React from 'react';
-
-import './null-table-cell.scss';
-
-export interface NullTableCellProps extends React.Props<any> {
-  value?: any;
-  timestamp?: boolean;
-  unparseable?: boolean;
-}
-
-export class NullTableCell extends React.Component<NullTableCellProps, {}> {
-  render() {
-    const { value, timestamp, unparseable } = this.props;
-    if (unparseable) {
-      return <span className="null-table-cell unparseable">{`error`}</span>;
-    } else if (value !== '' && value != null) {
-      if (timestamp) {
-        return <span className="null-table-cell timestamp" title={value}>{new Date(value).toISOString()}</span>;
-      } else if (Array.isArray(value)) {
-        return `[${value.join(', ')}]`;
-      } else {
-        return value;
-      }
-    } else {
-      return <span className="null-table-cell null">null</span>;
-    }
-  }
-}
diff --git a/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap b/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap
index 43ab28c..0088107 100644
--- a/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap
+++ b/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe rule editor rule editor snapshot 1`] = `
+exports[`rule editor matches snapshot 1`] = `
 <div
   class="rule-editor"
 >
diff --git a/web-console/src/components/rule-editor/rule-editor.spec.tsx b/web-console/src/components/rule-editor/rule-editor.spec.tsx
index e15e58e..54330c1 100644
--- a/web-console/src/components/rule-editor/rule-editor.spec.tsx
+++ b/web-console/src/components/rule-editor/rule-editor.spec.tsx
@@ -19,10 +19,10 @@
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {Rule, RuleEditor} from './rule-editor';
+import { Rule, RuleEditor } from './rule-editor';
 
-describe('describe rule editor', () => {
-  it('rule editor snapshot', () => {
+describe('rule editor', () => {
+  it('matches snapshot', () => {
     const ruleEditor =
     <RuleEditor
       rule={{type: 'loadForever' }}
@@ -32,7 +32,7 @@ describe('describe rule editor', () => {
       moveUp={null}
       moveDown={null}
     />;
-    const { container, getByText } = render(ruleEditor);
+    const { container } = render(ruleEditor);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/show-json/__snapshots__/show-json.spec.tsx.snap b/web-console/src/components/show-json/__snapshots__/show-json.spec.tsx.snap
index 7129c21..8f14cce 100644
--- a/web-console/src/components/show-json/__snapshots__/show-json.spec.tsx.snap
+++ b/web-console/src/components/show-json/__snapshots__/show-json.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe rule editor rule editor snapshot 1`] = `
+exports[`rule editor matches snapshot 1`] = `
 <div
   class="show-json"
 >
diff --git a/web-console/src/components/show-json/show-json.spec.tsx b/web-console/src/components/show-json/show-json.spec.tsx
index 3ef08f8..8e002e0 100644
--- a/web-console/src/components/show-json/show-json.spec.tsx
+++ b/web-console/src/components/show-json/show-json.spec.tsx
@@ -19,17 +19,16 @@
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {ShowJson} from './show-json';
+import { ShowJson } from './show-json';
 
-
-describe('describe rule editor', () => {
-  it('rule editor snapshot', () => {
+describe('rule editor', () => {
+  it('matches snapshot', () => {
     const showJson =
       <ShowJson
         endpoint={'test'}
         downloadFilename={'test'}
       />;
-    const { container, getByText } = render(showJson);
+    const { container } = render(showJson);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/show-json/show-json.tsx b/web-console/src/components/show-json/show-json.tsx
index aa255b5..477588a 100644
--- a/web-console/src/components/show-json/show-json.tsx
+++ b/web-console/src/components/show-json/show-json.tsx
@@ -18,8 +18,8 @@
 
 import { Button, ButtonGroup, InputGroup, Intent, TextArea } from '@blueprintjs/core';
 import axios from 'axios';
+import * as copy from 'copy-to-clipboard';
 import * as React from 'react';
-import * as CopyToClipboard from 'react-copy-to-clipboard';
 
 import { AppToaster } from '../../singletons/toaster';
 import { UrlBaser } from '../../singletons/url-baser';
@@ -76,18 +76,17 @@ export class ShowJson extends React.Component<ShowJsonProps, ShowJsonState> {
               onClick={() => downloadFile(jsonValue, 'json', downloadFilename)}
             />
           }
-          <CopyToClipboard text={jsonValue}>
-            <Button
-              text="Copy"
-              minimal
-              onClick={() => {
-                AppToaster.show({
-                  message: 'Copied JSON to clipboard',
-                  intent: Intent.SUCCESS
-                });
-              }}
-            />
-          </CopyToClipboard>
+          <Button
+            text="Copy"
+            minimal
+            onClick={() => {
+              copy(jsonValue, { format: 'text/plain' });
+              AppToaster.show({
+                message: 'JSON copied to clipboard',
+                intent: Intent.SUCCESS
+              });
+            }}
+          />
           <Button
             text="View raw"
             minimal
diff --git a/web-console/src/components/show-log/__snapshots__/show-log.spec.tsx.snap b/web-console/src/components/show-log/__snapshots__/show-log.spec.tsx.snap
index cafc804..c287d66 100644
--- a/web-console/src/components/show-log/__snapshots__/show-log.spec.tsx.snap
+++ b/web-console/src/components/show-log/__snapshots__/show-log.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe show log describe show log 1`] = `
+exports[`show log describe show log 1`] = `
 <div
   class="show-log"
 >
diff --git a/web-console/src/components/show-log/show-log.spec.tsx b/web-console/src/components/show-log/show-log.spec.tsx
index ff418ed..8781252 100644
--- a/web-console/src/components/show-log/show-log.spec.tsx
+++ b/web-console/src/components/show-log/show-log.spec.tsx
@@ -19,17 +19,16 @@
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {ShowLog} from './show-log';
+import { ShowLog } from './show-log';
 
-
-describe('describe show log', () => {
+describe('show log', () => {
   it('describe show log', () => {
     const showLog =
       <ShowLog
         endpoint={'test'}
         downloadFilename={'test'}
       />;
-    const { container, getByText } = render(showLog);
+    const { container } = render(showLog);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/show-log/show-log.tsx b/web-console/src/components/show-log/show-log.tsx
index 43fc49a..3549a2f 100644
--- a/web-console/src/components/show-log/show-log.tsx
+++ b/web-console/src/components/show-log/show-log.tsx
@@ -16,10 +16,10 @@
  * limitations under the License.
  */
 
-import {Button, ButtonGroup, Checkbox, InputGroup, Intent, TextArea} from '@blueprintjs/core';
+import { Button, ButtonGroup, Checkbox, InputGroup, Intent, TextArea } from '@blueprintjs/core';
 import axios from 'axios';
+import * as copy from 'copy-to-clipboard';
 import * as React from 'react';
-import * as CopyToClipboard from 'react-copy-to-clipboard';
 
 import { AppToaster } from '../../singletons/toaster';
 import { UrlBaser } from '../../singletons/url-baser';
@@ -116,18 +116,17 @@ export class ShowLog extends React.Component<ShowLogProps, ShowLogState> {
               onClick={() => downloadFile(logValue, 'plain', downloadFilename)}
             />
           }
-          <CopyToClipboard text={logValue}>
-            <Button
-              text="Copy"
-              minimal
-              onClick={() => {
-                AppToaster.show({
-                  message: 'Copied log to clipboard',
-                  intent: Intent.SUCCESS
-                });
-              }}
-            />
-          </CopyToClipboard>
+          <Button
+            text="Copy"
+            minimal
+            onClick={() => {
+              copy(logValue, { format: 'text/plain' });
+              AppToaster.show({
+                message: 'Log copied to clipboard',
+                intent: Intent.SUCCESS
+              });
+            }}
+          />
           <Button
             text="View full log"
             minimal
diff --git a/web-console/src/components/sql-control/__snapshots__/sql-control.spec.tsx.snap b/web-console/src/components/sql-control/__snapshots__/sql-control.spec.tsx.snap
index 033a1b0..fcb5b8f 100644
--- a/web-console/src/components/sql-control/__snapshots__/sql-control.spec.tsx.snap
+++ b/web-console/src/components/sql-control/__snapshots__/sql-control.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe sql control sql control snapshot 1`] = `
+exports[`sql control matches snapshot 1`] = `
 <div
   class="sql-control"
 >
diff --git a/web-console/src/components/sql-control/sql-control.spec.tsx b/web-console/src/components/sql-control/sql-control.spec.tsx
index 8b6bcb4..8f5482a 100644
--- a/web-console/src/components/sql-control/sql-control.spec.tsx
+++ b/web-console/src/components/sql-control/sql-control.spec.tsx
@@ -19,10 +19,10 @@
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {SqlControl} from './sql-control';
+import { SqlControl } from './sql-control';
 
-describe('describe sql control', () => {
-  it('sql control snapshot', () => {
+describe('sql control', () => {
+  it('matches snapshot', () => {
     const sqlControl = <SqlControl
       initSql={'test'}
       onRun={(query, context, wrapQuery) => {}}
@@ -30,7 +30,7 @@ describe('describe sql control', () => {
       queryElapsed={2}
     />;
 
-    const { container, getByText } = render(sqlControl);
+    const { container } = render(sqlControl);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/table-cell/__snapshots__/table-cell.spec.tsx.snap b/web-console/src/components/table-cell/__snapshots__/table-cell.spec.tsx.snap
new file mode 100644
index 0000000..97e1609
--- /dev/null
+++ b/web-console/src/components/table-cell/__snapshots__/table-cell.spec.tsx.snap
@@ -0,0 +1,67 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`table cell matches snapshot array long 1`] = `
+<span
+  class="table-cell truncated"
+>
+  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ...323 omitted... 7, 98, 99]
+  <span
+    class="bp3-icon bp3-icon-clipboard action-icon"
+    icon="clipboard"
+  >
+    <svg
+      data-icon="clipboard"
+      height="16"
+      viewBox="0 0 16 16"
+      width="16"
+    >
+      <desc>
+        clipboard
+      </desc>
+      <path
+        d="M11 2c0-.55-.45-1-1-1h.22C9.88.4 9.24 0 8.5 0S7.12.4 6.78 1H7c-.55 0-1 .45-1 1v1h5V2zm2 0h-1v2H5V2H4c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h9c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1z"
+        fill-rule="evenodd"
+      />
+    </svg>
+  </span>
+</span>
+`;
+
+exports[`table cell matches snapshot array short 1`] = `[a, b, c]`;
+
+exports[`table cell matches snapshot null 1`] = `
+<span
+  class="table-cell null"
+>
+  null
+</span>
+`;
+
+exports[`table cell matches snapshot simple 1`] = `Hello World`;
+
+exports[`table cell matches snapshot truncate 1`] = `
+<span
+  class="table-cell truncated"
+>
+  testtesttesttesttesttesttesttesttesttesttesttesttesttestt ...329 omitted... sttesttest
+  <span
+    class="bp3-icon bp3-icon-clipboard action-icon"
+    icon="clipboard"
+  >
+    <svg
+      data-icon="clipboard"
+      height="16"
+      viewBox="0 0 16 16"
+      width="16"
+    >
+      <desc>
+        clipboard
+      </desc>
+      <path
+        d="M11 2c0-.55-.45-1-1-1h.22C9.88.4 9.24 0 8.5 0S7.12.4 6.78 1H7c-.55 0-1 .45-1 1v1h5V2zm2 0h-1v2H5V2H4c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h9c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1z"
+        fill-rule="evenodd"
+      />
+    </svg>
+  </span>
+</span>
+`;
diff --git a/web-console/src/components/null-table-cell/null-table-cell.scss b/web-console/src/components/table-cell/table-cell.scss
similarity index 91%
rename from web-console/src/components/null-table-cell/null-table-cell.scss
rename to web-console/src/components/table-cell/table-cell.scss
index fd5c50c..783fad4 100644
--- a/web-console/src/components/null-table-cell/null-table-cell.scss
+++ b/web-console/src/components/table-cell/table-cell.scss
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-.null-table-cell {
+.table-cell {
   &.null {
     font-style: italic;
   }
@@ -28,4 +28,10 @@
   &.timestamp {
     font-weight: bold;
   }
+
+  &.truncated {
+    .action-icon {
+      margin-left: 5px;
+    }
+  }
 }
diff --git a/web-console/src/components/table-cell/table-cell.spec.tsx b/web-console/src/components/table-cell/table-cell.spec.tsx
new file mode 100644
index 0000000..821119a
--- /dev/null
+++ b/web-console/src/components/table-cell/table-cell.spec.tsx
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 * as React from 'react';
+import { render } from 'react-testing-library';
+
+import { TableCell } from './table-cell';
+
+describe('table cell', () => {
+  it('matches snapshot null', () => {
+    const tableCell = <TableCell
+      value={null}
+      unparseable={false}
+      timestamp={false}
+    />;
+
+    const { container } = render(tableCell);
+    expect(container.firstChild).toMatchSnapshot();
+  });
+
+  it('matches snapshot simple', () => {
+    const tableCell = <TableCell
+      value="Hello World"
+      unparseable={false}
+      timestamp={false}
+    />;
+
+    const { container } = render(tableCell);
+    expect(container.firstChild).toMatchSnapshot();
+  });
+
+  it('matches snapshot array short', () => {
+    const tableCell = <TableCell
+      value={['a', 'b', 'c']}
+      unparseable={false}
+      timestamp={false}
+    />;
+
+    const { container } = render(tableCell);
+    expect(container.firstChild).toMatchSnapshot();
+  });
+
+  it('matches snapshot array long', () => {
+    const tableCell = <TableCell
+      value={Array.from(new Array(100)).map((_, i) => i)}
+      unparseable={false}
+      timestamp={false}
+    />;
+
+    const { container } = render(tableCell);
+    expect(container.firstChild).toMatchSnapshot();
+  });
+
+  it('matches snapshot truncate', () => {
+    const longString = new Array(100).join('test');
+    const tableCell = <TableCell
+      value={longString}
+      unparseable={false}
+      timestamp={false}
+    />;
+
+    const { container } = render(tableCell);
+    expect(container.firstChild).toMatchSnapshot();
+  });
+});
diff --git a/web-console/src/components/table-cell/table-cell.tsx b/web-console/src/components/table-cell/table-cell.tsx
new file mode 100644
index 0000000..0c82685
--- /dev/null
+++ b/web-console/src/components/table-cell/table-cell.tsx
@@ -0,0 +1,81 @@
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 { Intent } from '@blueprintjs/core';
+import { IconNames } from '@blueprintjs/icons';
+import * as copy from 'copy-to-clipboard';
+import * as React from 'react';
+
+import { AppToaster } from '../../singletons/toaster';
+import { ActionIcon } from '../action-icon/action-icon';
+
+import './table-cell.scss';
+
+export interface NullTableCellProps extends React.Props<any> {
+  value?: any;
+  timestamp?: boolean;
+  unparseable?: boolean;
+}
+
+export class TableCell extends React.Component<NullTableCellProps, {}> {
+  static MAX_CHARS_TO_SHOW = 50;
+
+  static possiblyTruncate(str: string): React.ReactNode {
+    if (str.length < TableCell.MAX_CHARS_TO_SHOW) return str;
+    return <span className="table-cell truncated">
+      {TableCell.shortenString(str)}
+      <ActionIcon
+        icon={IconNames.CLIPBOARD}
+        onClick={() => {
+          copy(str, { format: 'text/plain' });
+          AppToaster.show({
+            message: 'Value copied to clipboard',
+            intent: Intent.SUCCESS
+          });
+        }}
+      />
+    </span>;
+  }
+
+  static shortenString(str: string): string {
+    // Print something like:
+    // BAAAArAAEiQKpDAEAACwZCBAGSBgiSEAAAAQpAIDwAg...23 omitted...gwiRoQBJIC
+    const omit = str.length - (TableCell.MAX_CHARS_TO_SHOW + 17);
+    const prefix = str.substr(0, str.length - (omit + 10));
+    const suffix = str.substr(str.length - 10);
+    return `${prefix} ...${omit} omitted... ${suffix}`;
+  }
+
+  render() {
+    const { value, timestamp, unparseable } = this.props;
+    if (unparseable) {
+      return <span className="table-cell unparseable">error</span>;
+    } else if (value !== '' && value != null) {
+      if (timestamp) {
+        return <span className="table-cell timestamp" title={value}>{new Date(value).toISOString()}</span>;
+      } else if (Array.isArray(value)) {
+        return TableCell.possiblyTruncate(`[${value.join(', ')}]`);
+      } else {
+        return TableCell.possiblyTruncate(String(value));
+      }
+    } else {
+      return <span className="table-cell null">null</span>;
+    }
+  }
+}
diff --git a/web-console/src/components/table-column/__snapshots__/table-column-selection.spec.tsx.snap b/web-console/src/components/table-column-selector/__snapshots__/table-column-selector.spec.tsx.snap
similarity index 87%
rename from web-console/src/components/table-column/__snapshots__/table-column-selection.spec.tsx.snap
rename to web-console/src/components/table-column-selector/__snapshots__/table-column-selector.spec.tsx.snap
index ebd1a0e..2b0c8ca 100644
--- a/web-console/src/components/table-column/__snapshots__/table-column-selection.spec.tsx.snap
+++ b/web-console/src/components/table-column-selector/__snapshots__/table-column-selector.spec.tsx.snap
@@ -1,8 +1,8 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe table column table column snapshot 1`] = `
+exports[`table column matches snapshot 1`] = `
 <span
-  class="bp3-popover-wrapper table-column-selection"
+  class="bp3-popover-wrapper table-column-selector"
 >
   <span
     class="bp3-popover-target"
diff --git a/web-console/src/components/table-column/table-column-selection.scss b/web-console/src/components/table-column-selector/table-column-selector.scss
similarity index 94%
rename from web-console/src/components/table-column/table-column-selection.scss
rename to web-console/src/components/table-column-selector/table-column-selector.scss
index 9e52a5c..7c06435 100644
--- a/web-console/src/components/table-column/table-column-selection.scss
+++ b/web-console/src/components/table-column-selector/table-column-selector.scss
@@ -16,14 +16,14 @@
  * limitations under the License.
  */
 
-.table-column-selection {
+.table-column-selector {
   position: absolute;
   right: 0;
   margin: 0;
 }
 
 // This will be mounted in a portal
-.table-column-selection-menu {
+.table-column-selector-menu {
   .bp3-form-group {
     margin-bottom: 0;
   }
diff --git a/web-console/src/components/table-column/table-column-selection.spec.tsx b/web-console/src/components/table-column-selector/table-column-selector.spec.tsx
similarity index 82%
rename from web-console/src/components/table-column/table-column-selection.spec.tsx
rename to web-console/src/components/table-column-selector/table-column-selector.spec.tsx
index b321963..e6e7c15 100644
--- a/web-console/src/components/table-column/table-column-selection.spec.tsx
+++ b/web-console/src/components/table-column-selector/table-column-selector.spec.tsx
@@ -19,17 +19,17 @@
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {TableColumnSelection} from './table-column-selection';
+import { TableColumnSelector } from './table-column-selector';
 
-describe('describe table column', () => {
-  it('table column snapshot', () => {
+describe('table column', () => {
+  it('matches snapshot', () => {
     const tableColumn =
-      <TableColumnSelection
+      <TableColumnSelector
         columns={['a', 'b', 'c']}
         onChange={(column: string) => {}}
         tableColumnsHidden={['a', 'b', 'c']}
       />;
-    const { container, getByText } = render(tableColumn);
+    const { container } = render(tableColumn);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/components/table-column/table-column-selection.tsx b/web-console/src/components/table-column-selector/table-column-selector.tsx
similarity index 77%
rename from web-console/src/components/table-column/table-column-selection.tsx
rename to web-console/src/components/table-column-selector/table-column-selector.tsx
index aafa95b..718f90d 100644
--- a/web-console/src/components/table-column/table-column-selection.tsx
+++ b/web-console/src/components/table-column-selector/table-column-selector.tsx
@@ -22,21 +22,21 @@ import * as React from 'react';
 
 import { MenuCheckbox } from '../menu-checkbox/menu-checkbox';
 
-import './table-column-selection.scss';
+import './table-column-selector.scss';
 
-interface TableColumnSelectionProps extends React.Props<any> {
+interface TableColumnSelectorProps extends React.Props<any> {
   columns: string[];
   onChange: (column: string) => void;
   tableColumnsHidden: string[];
 }
 
-interface TableColumnSelectionState {
+interface TableColumnSelectorState {
 
 }
 
-export class TableColumnSelection extends React.Component<TableColumnSelectionProps, TableColumnSelectionState> {
+export class TableColumnSelector extends React.Component<TableColumnSelectorProps, TableColumnSelectorState> {
 
-  constructor(props: TableColumnSelectionProps) {
+  constructor(props: TableColumnSelectorProps) {
     super(props);
     this.state = {
 
@@ -45,7 +45,7 @@ export class TableColumnSelection extends React.Component<TableColumnSelectionPr
 
   render() {
     const { columns, onChange, tableColumnsHidden } = this.props;
-    const checkboxes = <Menu className="table-column-selection-menu">
+    const checkboxes = <Menu className="table-column-selector-menu">
       {columns.map(column => (
         <MenuCheckbox
           label={column}
@@ -57,11 +57,11 @@ export class TableColumnSelection extends React.Component<TableColumnSelectionPr
     </Menu>;
 
     return <Popover
-      className="table-column-selection"
+      className="table-column-selector"
       content={checkboxes}
       position={Position.BOTTOM_RIGHT}
     >
-      <Button rightIcon={IconNames.CARET_DOWN} text="Columns" />
+      <Button rightIcon={IconNames.CARET_DOWN} text="Columns"/>
     </Popover>;
   }
 }
diff --git a/web-console/src/components/view-control-bar/__snapshots__/view-control-bar.spec.tsx.snap b/web-console/src/components/view-control-bar/__snapshots__/view-control-bar.spec.tsx.snap
index 003623a..1ef79e5 100644
--- a/web-console/src/components/view-control-bar/__snapshots__/view-control-bar.spec.tsx.snap
+++ b/web-console/src/components/view-control-bar/__snapshots__/view-control-bar.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe view control bar snapshot view control bar snapshot snapshot 1`] = `
+exports[`view control bar snapshot matches snapshot 1`] = `
 <div
   class="view-control-bar"
 >
diff --git a/web-console/src/components/view-control-bar/view-control-bar.scss b/web-console/src/components/view-control-bar/view-control-bar.scss
index 5b7aa89..4c44f2d 100644
--- a/web-console/src/components/view-control-bar/view-control-bar.scss
+++ b/web-console/src/components/view-control-bar/view-control-bar.scss
@@ -41,7 +41,7 @@
     margin-bottom: 8px;
   }
 
-  .table-column-selection {
+  .table-column-selector {
     margin-right: 0;
   }
 }
diff --git a/web-console/src/components/view-control-bar/view-control-bar.spec.tsx b/web-console/src/components/view-control-bar/view-control-bar.spec.tsx
index e4318d3..4108680 100644
--- a/web-console/src/components/view-control-bar/view-control-bar.spec.tsx
+++ b/web-console/src/components/view-control-bar/view-control-bar.spec.tsx
@@ -21,14 +21,14 @@ import { render } from 'react-testing-library';
 
 import { ViewControlBar } from './view-control-bar';
 
-describe('describe view control bar snapshot', () => {
-  it('view control bar snapshot snapshot', () => {
+describe('view control bar snapshot', () => {
+  it('matches snapshot', () => {
     const viewControlBar =
       <ViewControlBar label="A label">
         <div>Hello world</div>
       </ViewControlBar>;
 
-    const { container, getByText } = render(viewControlBar);
+    const { container } = render(viewControlBar);
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/about-dialog/__snapshots__/about-dialog.spec.tsx.snap b/web-console/src/dialogs/about-dialog/__snapshots__/about-dialog.spec.tsx.snap
index 0a94ec7..b313f85 100644
--- a/web-console/src/dialogs/about-dialog/__snapshots__/about-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/about-dialog/__snapshots__/about-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe about dialog about dialog snapshot 1`] = `
+exports[`about dialog matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/about-dialog/about-dialog.spec.tsx b/web-console/src/dialogs/about-dialog/about-dialog.spec.tsx
index 0380167..0df9793 100644
--- a/web-console/src/dialogs/about-dialog/about-dialog.spec.tsx
+++ b/web-console/src/dialogs/about-dialog/about-dialog.spec.tsx
@@ -16,20 +16,18 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {AboutDialog} from './about-dialog';
-
+import { AboutDialog } from './about-dialog';
 
-describe('describe about dialog', () => {
-  it('about dialog snapshot', () => {
+describe('about dialog', () => {
+  it('matches snapshot', () => {
     const aboutDialog =
      <AboutDialog
        onClose={() => null}
      />;
-    const { container, getByText } = render(aboutDialog, { container: document.body });
+    const { container } = render(aboutDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/about-dialog/about-dialog.tsx b/web-console/src/dialogs/about-dialog/about-dialog.tsx
index a863941..e2b9577 100644
--- a/web-console/src/dialogs/about-dialog/about-dialog.tsx
+++ b/web-console/src/dialogs/about-dialog/about-dialog.tsx
@@ -20,7 +20,7 @@ import { AnchorButton, Button, Classes, Dialog, Intent } from '@blueprintjs/core
 import { IconNames } from '@blueprintjs/icons';
 import * as React from 'react';
 
-import { ExternalLink } from '../../components/external-link/external-link';
+import { ExternalLink } from '../../components';
 import { DRUID_COMMUNITY, DRUID_DEVELOPER_GROUP, DRUID_USER_GROUP, DRUID_WEBSITE } from '../../variables';
 
 export interface AboutDialogProps extends React.Props<any> {
diff --git a/web-console/src/dialogs/async-action-dialog/__snapshots__/async-action-dialog.spec.tsx.snap b/web-console/src/dialogs/async-action-dialog/__snapshots__/async-action-dialog.spec.tsx.snap
index a10ad03..a2df6e9 100644
--- a/web-console/src/dialogs/async-action-dialog/__snapshots__/async-action-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/async-action-dialog/__snapshots__/async-action-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe async action dialog async action dialog snapshot 1`] = `
+exports[`async action dialog matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/async-action-dialog/async-action-dialog.spec.tsx b/web-console/src/dialogs/async-action-dialog/async-action-dialog.spec.tsx
index f73396b..63452cc 100644
--- a/web-console/src/dialogs/async-action-dialog/async-action-dialog.spec.tsx
+++ b/web-console/src/dialogs/async-action-dialog/async-action-dialog.spec.tsx
@@ -16,15 +16,13 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {AsyncActionDialog} from './async-action-dialog';
-
+import { AsyncActionDialog } from './async-action-dialog';
 
-describe('describe async action dialog', () => {
-  it('async action dialog snapshot', () => {
+describe('async action dialog', () => {
+  it('matches snapshot', () => {
     const asyncActionDialog =
       <AsyncActionDialog
         action={() => {return  Promise.resolve(); }}
@@ -32,8 +30,8 @@ describe('describe async action dialog', () => {
         confirmButtonText={'test'}
         successText={'test'}
         failText={'test'}
-      />
-    const { container, getByText } = render(asyncActionDialog, { container: document.body });
+      />;
+    const { container } = render(asyncActionDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap b/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap
index 0559735..2375a63 100644
--- a/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe compaction dialog compaction dialog snapshot 1`] = `
+exports[`compaction dialog matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
@@ -404,7 +404,7 @@ exports[`describe compaction dialog compaction dialog snapshot 1`] = `
               class="bp3-form-content"
             >
               <div
-                class=" ace_editor ace-tm"
+                class=" ace_editor ace-solarized-dark ace_dark"
                 id="ace-editor"
                 style="width: 100%; height: 8vh;"
               >
@@ -595,7 +595,7 @@ exports[`describe compaction dialog compaction dialog snapshot 1`] = `
               class="bp3-form-content"
             >
               <div
-                class=" ace_editor ace-tm"
+                class=" ace_editor ace-solarized-dark ace_dark"
                 id="ace-editor"
                 style="width: 100%; height: 8vh;"
               >
diff --git a/web-console/src/dialogs/compaction-dialog/compaction-dialog.spec.tsx b/web-console/src/dialogs/compaction-dialog/compaction-dialog.spec.tsx
index c5d23f2..b17e2f6 100644
--- a/web-console/src/dialogs/compaction-dialog/compaction-dialog.spec.tsx
+++ b/web-console/src/dialogs/compaction-dialog/compaction-dialog.spec.tsx
@@ -16,15 +16,13 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {CompactionDialog} from './compaction-dialog';
-
+import { CompactionDialog } from './compaction-dialog';
 
-describe('describe compaction dialog', () => {
-  it('compaction dialog snapshot', () => {
+describe('compaction dialog', () => {
+  it('matches snapshot', () => {
     const compactionDialog =
       <CompactionDialog
         onClose={() => null}
@@ -33,7 +31,7 @@ describe('describe compaction dialog', () => {
         datasource={'test'}
         configData={'test'}
       />;
-    const { container, getByText } = render(compactionDialog, { container: document.body });
+    const { container } = render(compactionDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx b/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx
index 6129cc7..268f50f 100644
--- a/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx
+++ b/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx
@@ -19,7 +19,7 @@
 import { Button, Classes, Dialog, Intent } from '@blueprintjs/core';
 import * as React from 'react';
 
-import { AutoForm } from '../../components/auto-form/auto-form';
+import { AutoForm } from '../../components';
 
 import './compaction-dialog.scss';
 
diff --git a/web-console/src/dialogs/coordinator-dynamic-config/__snapshots__/coordinator-dynamic-config.spec.tsx.snap b/web-console/src/dialogs/coordinator-dynamic-config/__snapshots__/coordinator-dynamic-config.spec.tsx.snap
index 2d9d6e5..e625089 100644
--- a/web-console/src/dialogs/coordinator-dynamic-config/__snapshots__/coordinator-dynamic-config.spec.tsx.snap
+++ b/web-console/src/dialogs/coordinator-dynamic-config/__snapshots__/coordinator-dynamic-config.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe coordinator dynamic config coordinator dynamic config snapshot 1`] = `
+exports[`coordinator dynamic config matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.spec.tsx b/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.spec.tsx
index a7e3dbc..273eeb4 100644
--- a/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.spec.tsx
+++ b/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.spec.tsx
@@ -16,20 +16,18 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {CoordinatorDynamicConfigDialog} from './coordinator-dynamic-config';
-
+import { CoordinatorDynamicConfigDialog } from './coordinator-dynamic-config';
 
-describe('describe coordinator dynamic config', () => {
-  it('coordinator dynamic config snapshot', () => {
+describe('coordinator dynamic config', () => {
+  it('matches snapshot', () => {
     const coordinatorDynamicConfig =
       <CoordinatorDynamicConfigDialog
         onClose={() => null}
       />;
-    const { container, getByText } = render(coordinatorDynamicConfig, { container: document.body });
+    const { container } = render(coordinatorDynamicConfig, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.tsx b/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.tsx
index 11c4369..17d3b4f 100644
--- a/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.tsx
+++ b/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.tsx
@@ -21,8 +21,7 @@ import { IconNames } from '@blueprintjs/icons';
 import axios from 'axios';
 import * as React from 'react';
 
-import { AutoForm } from '../../components/auto-form/auto-form';
-import { ExternalLink } from '../../components/external-link/external-link';
+import { AutoForm, ExternalLink } from '../../components';
 import { AppToaster } from '../../singletons/toaster';
 import { getDruidErrorMessage, QueryManager } from '../../utils';
 import { SnitchDialog } from '../snitch-dialog/snitch-dialog';
diff --git a/web-console/src/dialogs/history-dialog/__snapshots__/history-dialog.spec.tsx.snap b/web-console/src/dialogs/history-dialog/__snapshots__/history-dialog.spec.tsx.snap
index 522ff36..a39d629 100644
--- a/web-console/src/dialogs/history-dialog/__snapshots__/history-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/history-dialog/__snapshots__/history-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe history dialog history dialog snapshot 1`] = `
+exports[`history dialog matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/history-dialog/history-dialog.spec.tsx b/web-console/src/dialogs/history-dialog/history-dialog.spec.tsx
index ccfa93c..f9e5d9f 100644
--- a/web-console/src/dialogs/history-dialog/history-dialog.spec.tsx
+++ b/web-console/src/dialogs/history-dialog/history-dialog.spec.tsx
@@ -16,21 +16,19 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {HistoryDialog} from './history-dialog';
-
+import { HistoryDialog } from './history-dialog';
 
-describe('describe history dialog', () => {
-  it('history dialog snapshot', () => {
+describe('history dialog', () => {
+  it('matches snapshot', () => {
     const historyDialog =
       <HistoryDialog
         historyRecords={[{auditTime: 'test', auditInfo: 'test', payload: JSON.stringify({ name : 'test' })}, {auditTime: 'test', auditInfo: 'test',  payload: JSON.stringify({ name : 'test' })}]}
-        isOpen={true}
+        isOpen
       />;
-    const { container, getByText } = render(historyDialog, { container: document.body });
+    const { container } = render(historyDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/history-dialog/history-dialog.tsx b/web-console/src/dialogs/history-dialog/history-dialog.tsx
index 75808f7..c20907e 100644
--- a/web-console/src/dialogs/history-dialog/history-dialog.tsx
+++ b/web-console/src/dialogs/history-dialog/history-dialog.tsx
@@ -16,10 +16,10 @@
  * limitations under the License.
  */
 
-import {Card, Dialog, Divider, IDialogProps} from '@blueprintjs/core';
+import { Card, Dialog, Divider, IDialogProps } from '@blueprintjs/core';
 import * as React from 'react';
 
-import { JSONCollapse } from '../../components/json-collapse/json-collapse';
+import { JSONCollapse } from '../../components';
 
 import './history-dialog.scss';
 
diff --git a/web-console/src/dialogs/lookup-edit-dialog/__snapshots__/lookup-edit-dialog.spec.tsx.snap b/web-console/src/dialogs/lookup-edit-dialog/__snapshots__/lookup-edit-dialog.spec.tsx.snap
index 31deed8..abdff9d 100644
--- a/web-console/src/dialogs/lookup-edit-dialog/__snapshots__/lookup-edit-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/lookup-edit-dialog/__snapshots__/lookup-edit-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe lookup edit dialog lookup edit dialog snapshot 1`] = `
+exports[`lookup edit dialog matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.spec.tsx b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.spec.tsx
index cfec37b..e31b652 100644
--- a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.spec.tsx
+++ b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.spec.tsx
@@ -16,18 +16,16 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {LookupEditDialog} from './lookup-edit-dialog';
-
+import { LookupEditDialog } from './lookup-edit-dialog';
 
-describe('describe lookup edit dialog', () => {
-  it('lookup edit dialog snapshot', () => {
+describe('lookup edit dialog', () => {
+  it('matches snapshot', () => {
     const lookupEditDialog =
       <LookupEditDialog
-        isOpen={true}
+        isOpen
         onClose={() => null}
         onSubmit={() => null}
         onChange={() => null}
@@ -37,8 +35,9 @@ describe('describe lookup edit dialog', () => {
         lookupSpec={'test'}
         isEdit={false}
         allLookupTiers={['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']}
-      />
-    const { container, getByText } = render(lookupEditDialog, { container: document.body });
+      />;
+
+    const { container } = render(lookupEditDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/overlord-dynamic-config/__snapshots__/overload-dynamic-config.spec.tsx.snap b/web-console/src/dialogs/overlord-dynamic-config/__snapshots__/overload-dynamic-config.spec.tsx.snap
index 4cc9739..1111228 100644
--- a/web-console/src/dialogs/overlord-dynamic-config/__snapshots__/overload-dynamic-config.spec.tsx.snap
+++ b/web-console/src/dialogs/overlord-dynamic-config/__snapshots__/overload-dynamic-config.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe overload dynamic config overload dynamic config snapshot 1`] = `
+exports[`overload dynamic config matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/overlord-dynamic-config/overload-dynamic-config.spec.tsx b/web-console/src/dialogs/overlord-dynamic-config/overload-dynamic-config.spec.tsx
index 40cc149..ad61b05 100644
--- a/web-console/src/dialogs/overlord-dynamic-config/overload-dynamic-config.spec.tsx
+++ b/web-console/src/dialogs/overlord-dynamic-config/overload-dynamic-config.spec.tsx
@@ -16,17 +16,16 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {LookupEditDialog} from '..';
+import { LookupEditDialog } from '../lookup-edit-dialog/lookup-edit-dialog';
 
-describe('describe overload dynamic config', () => {
-  it('overload dynamic config snapshot', () => {
+describe('overload dynamic config', () => {
+  it('matches snapshot', () => {
     const lookupEditDialog =
       <LookupEditDialog
-        isOpen={true}
+        isOpen
         onClose={() => null}
         onSubmit={() => null}
         onChange={() => null}
@@ -37,7 +36,7 @@ describe('describe overload dynamic config', () => {
         isEdit={false}
         allLookupTiers={['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']}
       />;
-    const { container, getByText } = render(lookupEditDialog, { container: document.body });
+    const { container } = render(lookupEditDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/overlord-dynamic-config/overlord-dynamic-config.tsx b/web-console/src/dialogs/overlord-dynamic-config/overlord-dynamic-config.tsx
index 1c5a4ac..112b0d4 100644
--- a/web-console/src/dialogs/overlord-dynamic-config/overlord-dynamic-config.tsx
+++ b/web-console/src/dialogs/overlord-dynamic-config/overlord-dynamic-config.tsx
@@ -21,8 +21,7 @@ import { IconNames } from '@blueprintjs/icons';
 import axios from 'axios';
 import * as React from 'react';
 
-import { AutoForm } from '../../components/auto-form/auto-form';
-import { ExternalLink } from '../../components/external-link/external-link';
+import { AutoForm, ExternalLink } from '../../components';
 import { AppToaster } from '../../singletons/toaster';
 import { getDruidErrorMessage, QueryManager } from '../../utils';
 import { SnitchDialog } from '../snitch-dialog/snitch-dialog';
diff --git a/web-console/src/dialogs/query-plan-dialog/__snapshots__/query-plan-dialog.spec.tsx.snap b/web-console/src/dialogs/query-plan-dialog/__snapshots__/query-plan-dialog.spec.tsx.snap
index 977f5a0..bb76ef5 100644
--- a/web-console/src/dialogs/query-plan-dialog/__snapshots__/query-plan-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/query-plan-dialog/__snapshots__/query-plan-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe query plan dialog query plan dialog snapshot 1`] = `
+exports[`query plan dialog matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/query-plan-dialog/query-plan-dialog.spec.tsx b/web-console/src/dialogs/query-plan-dialog/query-plan-dialog.spec.tsx
index 4b6e53c..6fbdd55 100644
--- a/web-console/src/dialogs/query-plan-dialog/query-plan-dialog.spec.tsx
+++ b/web-console/src/dialogs/query-plan-dialog/query-plan-dialog.spec.tsx
@@ -16,22 +16,20 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {QueryPlanDialog} from './query-plan-dialog';
-
+import { QueryPlanDialog } from './query-plan-dialog';
 
-describe('describe query plan dialog', () => {
-  it('query plan dialog snapshot', () => {
+describe('query plan dialog', () => {
+  it('matches snapshot', () => {
     const queryPlanDialog =
       <QueryPlanDialog
         explainResult={'test'}
         explainError={{name: 'test', message: 'test'}}
         onClose={() => null}
       />;
-    const { container, getByText } = render(queryPlanDialog, { container: document.body });
+    const { container } = render(queryPlanDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap b/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap
index e114e98..317666f 100644
--- a/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe retention dialog retention dialog snapshot 1`] = `
+exports[`retention dialog matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.array.spec.ts b/web-console/src/dialogs/retention-dialog/retention-dialog.array.spec.ts
index ee37759..7dd280b 100644
--- a/web-console/src/dialogs/retention-dialog/retention-dialog.array.spec.ts
+++ b/web-console/src/dialogs/retention-dialog/retention-dialog.array.spec.ts
@@ -54,4 +54,4 @@ describe('reorderArray', () => {
     expect(newArray).toEqual(['a', 'b', 'd', 'c', 'e']);
     expect(array).toEqual(['a', 'b', 'c', 'd', 'e']);
   });
-})
+});
diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.spec.tsx b/web-console/src/dialogs/retention-dialog/retention-dialog.spec.tsx
index 9a2b9c6..beb60c4 100644
--- a/web-console/src/dialogs/retention-dialog/retention-dialog.spec.tsx
+++ b/web-console/src/dialogs/retention-dialog/retention-dialog.spec.tsx
@@ -16,15 +16,13 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {RetentionDialog} from './retention-dialog';
-
+import { RetentionDialog } from './retention-dialog';
 
-describe('describe retention dialog', () => {
-  it('retention dialog snapshot', () => {
+describe('retention dialog', () => {
+  it('matches snapshot', () => {
     const retentionDialog =
       <RetentionDialog
         datasource={'test'}
@@ -34,7 +32,7 @@ describe('describe retention dialog', () => {
         onCancel={() => null}
         onSave={() => null}
       />;
-    const { container, getByText } = render(retentionDialog, { container: document.body });
+    const { container } = render(retentionDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.tsx b/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
index 10f4f09..bf78d21 100644
--- a/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
+++ b/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
@@ -21,7 +21,7 @@ import { IconNames } from '@blueprintjs/icons';
 import axios from 'axios';
 import * as React from 'react';
 
-import { Rule, RuleEditor } from '../../components/rule-editor/rule-editor';
+import { Rule, RuleEditor } from '../../components';
 import { QueryManager } from '../../utils';
 import { SnitchDialog } from '../snitch-dialog/snitch-dialog';
 
diff --git a/web-console/src/dialogs/snitch-dialog/__snapshots__/snitch-dialog.spec.tsx.snap b/web-console/src/dialogs/snitch-dialog/__snapshots__/snitch-dialog.spec.tsx.snap
index 0c119c8..457731f 100644
--- a/web-console/src/dialogs/snitch-dialog/__snapshots__/snitch-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/snitch-dialog/__snapshots__/snitch-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe snitch dialog snitch dialog snapshot 1`] = `
+exports[`snitch dialog matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/snitch-dialog/snitch-dialog.spec.tsx b/web-console/src/dialogs/snitch-dialog/snitch-dialog.spec.tsx
index 19fdc34..5ffef87 100644
--- a/web-console/src/dialogs/snitch-dialog/snitch-dialog.spec.tsx
+++ b/web-console/src/dialogs/snitch-dialog/snitch-dialog.spec.tsx
@@ -16,20 +16,19 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {SnitchDialog} from './snitch-dialog';
+import { SnitchDialog } from './snitch-dialog';
 
-describe('describe snitch dialog', () => {
-  it('snitch dialog snapshot', () => {
+describe('snitch dialog', () => {
+  it('matches snapshot', () => {
     const snitchDialog =
       <SnitchDialog
         onSave={() => null}
-        isOpen={true}
+        isOpen
       />;
-    const { container, getByText } = render(snitchDialog, { container: document.body });
+    const { container } = render(snitchDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/spec-dialog/__snapshots__/spec-dialog.spec.tsx.snap b/web-console/src/dialogs/spec-dialog/__snapshots__/spec-dialog.spec.tsx.snap
index b3e7177..66eaef4 100644
--- a/web-console/src/dialogs/spec-dialog/__snapshots__/spec-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/spec-dialog/__snapshots__/spec-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe spec dialog spec dialog snapshot 1`] = `
+exports[`spec dialog matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/spec-dialog/spec-dialog.spec.tsx b/web-console/src/dialogs/spec-dialog/spec-dialog.spec.tsx
index 1ec8383..316b583 100644
--- a/web-console/src/dialogs/spec-dialog/spec-dialog.spec.tsx
+++ b/web-console/src/dialogs/spec-dialog/spec-dialog.spec.tsx
@@ -16,21 +16,20 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {SpecDialog} from './spec-dialog';
+import { SpecDialog } from './spec-dialog';
 
-describe('describe spec dialog', () => {
-  it('spec dialog snapshot', () => {
+describe('spec dialog', () => {
+  it('matches snapshot', () => {
     const specDialog =
       <SpecDialog
         onSubmit={(spec: JSON) => null}
         onClose={() => null}
         title={'test'}
       />;
-    const { container, getByText } = render(specDialog, { container: document.body });
+    const { container } = render(specDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/supervisor-table-action-dialog/__snapshots__/supervisor-table-action-dialog.spec.tsx.snap b/web-console/src/dialogs/supervisor-table-action-dialog/__snapshots__/supervisor-table-action-dialog.spec.tsx.snap
index fef46e4..f216fcf 100644
--- a/web-console/src/dialogs/supervisor-table-action-dialog/__snapshots__/supervisor-table-action-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/supervisor-table-action-dialog/__snapshots__/supervisor-table-action-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe supervisor table action dialog supervisor table action dialog snapshot 1`] = `
+exports[`supervisor table action dialog matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.spec.tsx b/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.spec.tsx
index b3a7882..b82536b 100644
--- a/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.spec.tsx
+++ b/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.spec.tsx
@@ -16,24 +16,22 @@
  * limitations under the License.
  */
 
-
-import {Intent} from '@blueprintjs/core';
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {SupervisorTableActionDialog} from './supervisor-table-action-dialog';
+import { SupervisorTableActionDialog } from './supervisor-table-action-dialog';
 
 const basicAction = {title: 'test', onAction: () => null};
-describe('describe supervisor table action dialog', () => {
-  it('supervisor table action dialog snapshot', () => {
+describe('supervisor table action dialog', () => {
+  it('matches snapshot', () => {
     const supervisorTableActionDialog =
       <SupervisorTableActionDialog
         supervisorId={'test'}
         actions={[basicAction, basicAction, basicAction, basicAction]}
         onClose={() => null}
-        isOpen={true}
+        isOpen
       />;
-    const { container, getByText } = render(supervisorTableActionDialog, { container: document.body });
+    const { container } = render(supervisorTableActionDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.tsx b/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.tsx
index 46b9ff8..3b521f1 100644
--- a/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.tsx
+++ b/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.tsx
@@ -19,7 +19,7 @@
 import { IDialogProps } from '@blueprintjs/core';
 import * as React from 'react';
 
-import { ShowJson } from '../../components/show-json/show-json';
+import { ShowJson } from '../../components';
 import { BasicAction, basicActionsToButtons } from '../../utils/basic-action';
 import { SideButtonMetaData, TableActionDialog } from '../table-action-dialog/table-action-dialog';
 
diff --git a/web-console/src/dialogs/table-action-dialog/__snapshots__/table-action-dialog.spec.tsx.snap b/web-console/src/dialogs/table-action-dialog/__snapshots__/table-action-dialog.spec.tsx.snap
index d36e801..00ff48a 100644
--- a/web-console/src/dialogs/table-action-dialog/__snapshots__/table-action-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/table-action-dialog/__snapshots__/table-action-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe table action dialog table action dialog snapshot 1`] = `
+exports[`table action dialog matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/table-action-dialog/table-action-dialog.spec.tsx b/web-console/src/dialogs/table-action-dialog/table-action-dialog.spec.tsx
index 78b4274..ad7f826 100644
--- a/web-console/src/dialogs/table-action-dialog/table-action-dialog.spec.tsx
+++ b/web-console/src/dialogs/table-action-dialog/table-action-dialog.spec.tsx
@@ -16,22 +16,20 @@
  * limitations under the License.
  */
 
-
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {TableActionDialog} from './table-action-dialog';
-
+import { TableActionDialog } from './table-action-dialog';
 
-describe('describe table action dialog', () => {
-  it('table action dialog snapshot', () => {
+describe('table action dialog', () => {
+  it('matches snapshot', () => {
     const tableActionDialog =
       <TableActionDialog
         sideButtonMetadata={[{icon: 'badge', text: 'test' }]}
         onClose={() => null}
-        isOpen={true}
+        isOpen
       />;
-    const { container, getByText } = render(tableActionDialog, { container: document.body });
+    const { container } = render(tableActionDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/task-table-action-dialog/__snapshots__/task-table-action-dialog.spec.tsx.snap b/web-console/src/dialogs/task-table-action-dialog/__snapshots__/task-table-action-dialog.spec.tsx.snap
index edbc733..babf8c9 100644
--- a/web-console/src/dialogs/task-table-action-dialog/__snapshots__/task-table-action-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/task-table-action-dialog/__snapshots__/task-table-action-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe task table action dialog task table action dialog snapshot 1`] = `
+exports[`task table action dialog matches snapshot 1`] = `
 <div
   class="bp3-portal"
 >
diff --git a/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.spec.tsx b/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.spec.tsx
index 4f1ae3c..64b7faa 100644
--- a/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.spec.tsx
+++ b/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.spec.tsx
@@ -16,24 +16,22 @@
  * limitations under the License.
  */
 
-
-import {Intent} from '@blueprintjs/core';
 import * as React from 'react';
 import { render } from 'react-testing-library';
 
-import {TaskTableActionDialog} from './task-table-action-dialog';
+import { TaskTableActionDialog } from './task-table-action-dialog';
 
 const basicAction = {title: 'test', onAction: () => null};
-describe('describe task table action dialog', () => {
-  it('task table action dialog snapshot', () => {
+describe('task table action dialog', () => {
+  it('matches snapshot', () => {
     const taskTableActionDialog =
       <TaskTableActionDialog
         taskId={'test'}
         actions={[basicAction]}
         onClose={() => null}
-        isOpen={true}
+        isOpen
       />;
-    const { container, getByText } = render(taskTableActionDialog, { container: document.body });
+    const { container } = render(taskTableActionDialog, { container: document.body });
     expect(container.firstChild).toMatchSnapshot();
   });
 });
diff --git a/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.tsx b/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.tsx
index 7ed9453..73412d7 100644
--- a/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.tsx
+++ b/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.tsx
@@ -19,8 +19,7 @@
 import { IDialogProps } from '@blueprintjs/core';
 import * as React from 'react';
 
-import { ShowJson } from '../../components/show-json/show-json';
-import { ShowLog } from '../../components/show-log/show-log';
+import { ShowJson, ShowLog } from '../../components';
 import { BasicAction, basicActionsToButtons } from '../../utils/basic-action';
 import { SideButtonMetaData, TableActionDialog } from '../table-action-dialog/table-action-dialog';
 
diff --git a/web-console/src/utils/druid-expression.ts b/web-console/src/utils/druid-expression.ts
index 6737662..03b6f68 100644
--- a/web-console/src/utils/druid-expression.ts
+++ b/web-console/src/utils/druid-expression.ts
@@ -16,7 +16,6 @@
  * limitations under the License.
  */
 
-
 const UNSAFE_CHAR = /[^a-z0-9 ,._\-;:(){}\[\]<>!@#$%^&*`~?]/ig;
 
 function escape(str: string): string {
diff --git a/web-console/src/utils/table-column-selection-handler.tsx b/web-console/src/utils/table-column-selection-handler.tsx
index a2d0712..4decef5 100644
--- a/web-console/src/utils/table-column-selection-handler.tsx
+++ b/web-console/src/utils/table-column-selection-handler.tsx
@@ -43,7 +43,7 @@ export class TableColumnSelectionHandler {
     }
   }
 
-  changeTableColumnSelection(column: string): void {
+  changeTableColumnSelector(column: string): void {
     let newSelections: string[];
     if (this.hiddenColumns.includes(column)) {
       newSelections = this.hiddenColumns.filter(c => c !== column);
diff --git a/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap b/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap
index 9d10655..e74f634 100644
--- a/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap
+++ b/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe data source view data source view snapshot 1`] = `
+exports[`data source view matches snapshot 1`] = `
 <div
   className="data-sources-view app-view"
 >
@@ -22,7 +22,7 @@ exports[`describe data source view data source view snapshot 1`] = `
       label="Show disabled"
       onChange={[Function]}
     />
-    <TableColumnSelection
+    <TableColumnSelector
       columns={
         Array [
           "Datasource",
diff --git a/web-console/src/views/datasource-view/datasource-view.spec.tsx b/web-console/src/views/datasource-view/datasource-view.spec.tsx
index 8248df5..f966f7e 100644
--- a/web-console/src/views/datasource-view/datasource-view.spec.tsx
+++ b/web-console/src/views/datasource-view/datasource-view.spec.tsx
@@ -19,10 +19,10 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 
-import {DatasourcesView} from './datasource-view';
+import { DatasourcesView } from './datasource-view';
 
-describe('describe data source view', () => {
-  it('data source view snapshot', () => {
+describe('data source view', () => {
+  it('matches snapshot', () => {
     const dataSourceView = shallow(
       <DatasourcesView
         goToSql={(initSql: string) => {}}
diff --git a/web-console/src/views/datasource-view/datasource-view.tsx b/web-console/src/views/datasource-view/datasource-view.tsx
index 964b4e1..fc6f685 100644
--- a/web-console/src/views/datasource-view/datasource-view.tsx
+++ b/web-console/src/views/datasource-view/datasource-view.tsx
@@ -22,7 +22,8 @@ import axios from 'axios';
 import * as React from 'react';
 import ReactTable, { Filter } from 'react-table';
 
-import { ActionCell, RuleEditor, TableColumnSelection, ViewControlBar} from '../../components';
+import { ActionCell, RuleEditor, TableColumnSelector, ViewControlBar } from '../../components';
+import { ActionIcon } from '../../components/action-icon/action-icon';
 import { AsyncActionDialog, CompactionDialog, RetentionDialog } from '../../dialogs';
 import { AppToaster } from '../../singletons/toaster';
 import {
@@ -570,7 +571,7 @@ GROUP BY 1`);
                 className="clickable-cell"
               >
                 {text}&nbsp;
-                <a>&#x270E;</a>
+                <ActionIcon icon={IconNames.EDIT}/>
               </span>;
             },
             show: tableColumnSelectionHandler.showColumn('Retention')
@@ -597,7 +598,7 @@ GROUP BY 1`);
                 onClick={() => this.setState({compactionDialogOpenOn: compactionOpenOn})}
               >
                 {text}&nbsp;
-                <a>&#x270E;</a>
+                <ActionIcon icon={IconNames.EDIT}/>
               </span>;
             },
             show: tableColumnSelectionHandler.showColumn('Compaction')
@@ -669,9 +670,9 @@ GROUP BY 1`);
           label="Show disabled"
           onChange={() => this.toggleDisabled(showDisabled)}
         />
-        <TableColumnSelection
+        <TableColumnSelector
           columns={noSqlMode ? tableColumnsNoSql : tableColumns}
-          onChange={(column) => tableColumnSelectionHandler.changeTableColumnSelection(column)}
+          onChange={(column) => tableColumnSelectionHandler.changeTableColumnSelector(column)}
           tableColumnsHidden={tableColumnSelectionHandler.hiddenColumns}
         />
       </ViewControlBar>
diff --git a/web-console/src/views/home-view/__snapshots__/home-view.spec.tsx.snap b/web-console/src/views/home-view/__snapshots__/home-view.spec.tsx.snap
index b62f0f4..4973859 100644
--- a/web-console/src/views/home-view/__snapshots__/home-view.spec.tsx.snap
+++ b/web-console/src/views/home-view/__snapshots__/home-view.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe home view home viexw snapshot 1`] = `
+exports[`home view matches snapshot 1`] = `
 <div
   className="home-view app-view"
 >
diff --git a/web-console/src/views/home-view/home-view.spec.tsx b/web-console/src/views/home-view/home-view.spec.tsx
index 660cb10..321e900 100644
--- a/web-console/src/views/home-view/home-view.spec.tsx
+++ b/web-console/src/views/home-view/home-view.spec.tsx
@@ -16,18 +16,13 @@
  * limitations under the License.
  */
 
-
-
-import * as Enzyme from 'enzyme';
 import { shallow } from 'enzyme';
-import * as enzymeAdapterReact16 from 'enzyme-adapter-react-16';
 import * as React from 'react';
 
-import {HomeView} from './home-view';
+import { HomeView } from './home-view';
 
-Enzyme.configure({ adapter: new enzymeAdapterReact16() });
-describe('describe home view', () => {
-  it('home viexw snapshot', () => {
+describe('home view', () => {
+  it('matches snapshot', () => {
     const homeView = shallow(
       <HomeView
         noSqlMode={false}
diff --git a/web-console/src/views/load-data-view/__snapshots__/load-data-view.spec.tsx.snap b/web-console/src/views/load-data-view/__snapshots__/load-data-view.spec.tsx.snap
index dc90d04..829003f 100644
--- a/web-console/src/views/load-data-view/__snapshots__/load-data-view.spec.tsx.snap
+++ b/web-console/src/views/load-data-view/__snapshots__/load-data-view.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe load data view load data view snapshot 1`] = `
+exports[`load data view matches snapshot 1`] = `
 <div
   className="load-data-view app-view init"
 >
diff --git a/web-console/src/views/load-data-view/load-data-view.spec.tsx b/web-console/src/views/load-data-view/load-data-view.spec.tsx
index 3a33d6a..edbc1fe 100644
--- a/web-console/src/views/load-data-view/load-data-view.spec.tsx
+++ b/web-console/src/views/load-data-view/load-data-view.spec.tsx
@@ -16,17 +16,13 @@
  * limitations under the License.
  */
 
-import * as Enzyme from 'enzyme';
 import { shallow } from 'enzyme';
-import * as enzymeAdapterReact16 from 'enzyme-adapter-react-16';
 import * as React from 'react';
 
-import {LoadDataView} from './load-data-view';
+import { LoadDataView } from './load-data-view';
 
-Enzyme.configure({ adapter: new enzymeAdapterReact16() });
-
-describe('describe load data view', () => {
-  it('load data view snapshot', () => {
+describe('load data view', () => {
+  it('matches snapshot', () => {
     const loadDataView = shallow(
       <LoadDataView
         goToTask={(taskId: string | null) => {}}
diff --git a/web-console/src/views/load-data-view/load-data-view.tsx b/web-console/src/views/load-data-view/load-data-view.tsx
index 61bddda..8c31b82 100644
--- a/web-console/src/views/load-data-view/load-data-view.tsx
+++ b/web-console/src/views/load-data-view/load-data-view.tsx
@@ -31,7 +31,7 @@ import * as classNames from 'classnames';
 import * as React from 'react';
 import ReactTable from 'react-table';
 
-import { AutoForm, CenterMessage, ClearableInput, ExternalLink, JSONInput, Loader, NullTableCell } from '../../components';
+import { AutoForm, CenterMessage, ClearableInput, ExternalLink, JSONInput, Loader, TableCell } from '../../components';
 import { AsyncActionDialog } from '../../dialogs';
 import { AppToaster } from '../../singletons/toaster';
 import {
@@ -755,9 +755,9 @@ export class LoadDataView extends React.Component<LoadDataViewProps, LoadDataVie
               accessor: (row: SampleEntry) => row.parsed ? row.parsed[columnName] : null,
               Cell: row => {
                 if (row.original.unparseable) {
-                  return <NullTableCell unparseable/>;
+                  return <TableCell unparseable/>;
                 }
-                return <NullTableCell value={row.value}/>;
+                return <TableCell value={row.value}/>;
               },
               headerClassName: classNames({
                 flattened: flattenField
@@ -1047,12 +1047,12 @@ export class LoadDataView extends React.Component<LoadDataViewProps, LoadDataVie
               accessor: (row: SampleEntry) => row.parsed ? row.parsed[columnName] : null,
               Cell: row => {
                 if (columnName === '__error__') {
-                  return <NullTableCell value={row.original.error}/>;
+                  return <TableCell value={row.original.error}/>;
                 }
                 if (row.original.unparseable) {
-                  return <NullTableCell unparseable/>;
+                  return <TableCell unparseable/>;
                 }
-                return <NullTableCell value={row.value} timestamp={timestamp}/>;
+                return <TableCell value={row.value} timestamp={timestamp}/>;
               },
               minWidth: timestamp ? 200 : 100,
               resizable: !timestamp
@@ -1247,7 +1247,7 @@ export class LoadDataView extends React.Component<LoadDataViewProps, LoadDataVie
               className: columnClassName,
               id: String(i),
               accessor: row => row.parsed ? row.parsed[columnName] : null,
-              Cell: row => <NullTableCell value={row.value} timestamp={timestamp}/>
+              Cell: row => <TableCell value={row.value} timestamp={timestamp}/>
             };
           })}
           defaultPageSize={50}
@@ -1484,7 +1484,7 @@ export class LoadDataView extends React.Component<LoadDataViewProps, LoadDataVie
               className: columnClassName,
               id: String(i),
               accessor: row => row.parsed ? row.parsed[columnName] : null,
-              Cell: row => <NullTableCell value={row.value} timestamp={timestamp}/>
+              Cell: row => <TableCell value={row.value} timestamp={timestamp}/>
             };
           })}
           defaultPageSize={50}
@@ -1759,7 +1759,7 @@ export class LoadDataView extends React.Component<LoadDataViewProps, LoadDataVie
                 className: columnClassName,
                 id: String(i),
                 accessor: row => row.parsed ? row.parsed[columnName] : null,
-                Cell: row => <NullTableCell value={row.value}/>
+                Cell: row => <TableCell value={row.value}/>
               };
             } else {
               const timestamp = columnName === '__time';
@@ -1804,7 +1804,7 @@ export class LoadDataView extends React.Component<LoadDataViewProps, LoadDataVie
                 className: columnClassName,
                 id: String(i),
                 accessor: (row: SampleEntry) => row.parsed ? row.parsed[columnName] : null,
-                Cell: row => <NullTableCell value={row.value} timestamp={timestamp}/>
+                Cell: row => <TableCell value={row.value} timestamp={timestamp}/>
               };
             }
           })}
diff --git a/web-console/src/views/lookups-view/__snapshots__/lookups-view.spec.tsx.snap b/web-console/src/views/lookups-view/__snapshots__/lookups-view.spec.tsx.snap
index fe2625a..a4a93cb 100644
--- a/web-console/src/views/lookups-view/__snapshots__/lookups-view.spec.tsx.snap
+++ b/web-console/src/views/lookups-view/__snapshots__/lookups-view.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe lookups view lookups view snapshot 1`] = `
+exports[`lookups view matches snapshot 1`] = `
 <div
   className="lookups-view app-view"
 >
@@ -17,7 +17,7 @@ exports[`describe lookups view lookups view snapshot 1`] = `
       onClick={[Function]}
       text="Add"
     />
-    <TableColumnSelection
+    <TableColumnSelector
       columns={
         Array [
           "Lookup name",
diff --git a/web-console/src/views/lookups-view/lookups-view.spec.tsx b/web-console/src/views/lookups-view/lookups-view.spec.tsx
index 3ebaf9b..a10e8c6 100644
--- a/web-console/src/views/lookups-view/lookups-view.spec.tsx
+++ b/web-console/src/views/lookups-view/lookups-view.spec.tsx
@@ -16,17 +16,13 @@
  * limitations under the License.
  */
 
-
-import * as Enzyme from 'enzyme';
 import { shallow } from 'enzyme';
-import * as enzymeAdapterReact16 from 'enzyme-adapter-react-16';
 import * as React from 'react';
 
-import {LookupsView} from './lookups-view';
+import { LookupsView } from './lookups-view';
 
-Enzyme.configure({ adapter: new enzymeAdapterReact16() });
-describe('describe lookups view', () => {
-  it('lookups view snapshot', () => {
+describe('lookups view', () => {
+  it('matches snapshot', () => {
     const lookupsView = shallow(
       <LookupsView/>);
     expect(lookupsView).toMatchSnapshot();
diff --git a/web-console/src/views/lookups-view/lookups-view.tsx b/web-console/src/views/lookups-view/lookups-view.tsx
index 4c1b0a1..b94c2be 100644
--- a/web-console/src/views/lookups-view/lookups-view.tsx
+++ b/web-console/src/views/lookups-view/lookups-view.tsx
@@ -16,14 +16,14 @@
  * limitations under the License.
  */
 
-import {Button, Icon, Intent, Popover, Position} from '@blueprintjs/core';
+import { Button, Icon, Intent, Popover, Position } from '@blueprintjs/core';
 import { IconNames } from '@blueprintjs/icons';
 import axios from 'axios';
 import * as classNames from 'classnames';
 import * as React from 'react';
 import ReactTable from 'react-table';
 
-import {ActionCell, TableColumnSelection, ViewControlBar} from '../../components';
+import { ActionCell, TableColumnSelector, ViewControlBar } from '../../components';
 import { AsyncActionDialog, LookupEditDialog } from '../../dialogs/';
 import { AppToaster } from '../../singletons/toaster';
 import {
@@ -31,7 +31,7 @@ import {
   QueryManager,
   TableColumnSelectionHandler
 } from '../../utils';
-import {BasicAction, basicActionsToMenu} from '../../utils/basic-action';
+import { BasicAction, basicActionsToMenu } from '../../utils/basic-action';
 
 import './lookups-view.scss';
 
@@ -359,9 +359,9 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
             onClick={() => this.openLookupEditDialog('', '')}
           />
         }
-        <TableColumnSelection
+        <TableColumnSelector
           columns={tableColumns}
-          onChange={(column) => tableColumnSelectionHandler.changeTableColumnSelection(column)}
+          onChange={(column) => tableColumnSelectionHandler.changeTableColumnSelector(column)}
           tableColumnsHidden={tableColumnSelectionHandler.hiddenColumns}
         />
       </ViewControlBar>
diff --git a/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap b/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap
index f56b6a7..fd2d81f 100644
--- a/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap
+++ b/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe segments-view segments view snapshot 1`] = `
+exports[`segments-view matches snapshot 1`] = `
 <div
   className="segments-view app-view"
 >
@@ -18,7 +18,7 @@ exports[`describe segments-view segments view snapshot 1`] = `
       onClick={[Function]}
       text="Go to SQL"
     />
-    <TableColumnSelection
+    <TableColumnSelector
       columns={
         Array [
           "Segment ID",
diff --git a/web-console/src/views/segments-view/segments-view.spec.tsx b/web-console/src/views/segments-view/segments-view.spec.tsx
index 7b4531f..f41e0e1 100644
--- a/web-console/src/views/segments-view/segments-view.spec.tsx
+++ b/web-console/src/views/segments-view/segments-view.spec.tsx
@@ -16,17 +16,13 @@
  * limitations under the License.
  */
 
-
-import * as Enzyme from 'enzyme';
 import { shallow } from 'enzyme';
-import * as enzymeAdapterReact16 from 'enzyme-adapter-react-16';
 import * as React from 'react';
 
-import {SegmentsView} from '../segments-view/segments-view';
+import { SegmentsView } from '../segments-view/segments-view';
 
-Enzyme.configure({ adapter: new enzymeAdapterReact16() });
-describe('describe segments-view', () => {
-  it('segments view snapshot', () => {
+describe('segments-view', () => {
+  it('matches snapshot', () => {
     const segmentsView = shallow(
       <SegmentsView
         datasource={'test'}
diff --git a/web-console/src/views/segments-view/segments-view.tsx b/web-console/src/views/segments-view/segments-view.tsx
index 9c53d7f..c81feed 100644
--- a/web-console/src/views/segments-view/segments-view.tsx
+++ b/web-console/src/views/segments-view/segments-view.tsx
@@ -24,7 +24,7 @@ import * as React from 'react';
 import ReactTable from 'react-table';
 import { Filter } from 'react-table';
 
-import { TableColumnSelection, ViewControlBar } from '../../components';
+import { TableColumnSelector, ViewControlBar } from '../../components';
 import {
   addFilter,
   formatBytes,
@@ -400,9 +400,9 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
             onClick={() => goToSql(this.segmentsSqlQueryManager.getLastQuery().query)}
           />
         }
-        <TableColumnSelection
+        <TableColumnSelector
           columns={noSqlMode ? tableColumnsNoSql : tableColumns}
-          onChange={(column) => tableColumnSelectionHandler.changeTableColumnSelection(column)}
+          onChange={(column) => tableColumnSelectionHandler.changeTableColumnSelector(column)}
           tableColumnsHidden={tableColumnSelectionHandler.hiddenColumns}
         />
       </ViewControlBar>
diff --git a/web-console/src/views/servers-view/__snapshots__/servers-view.spec.tsx.snap b/web-console/src/views/servers-view/__snapshots__/servers-view.spec.tsx.snap
index 6e987ef..197ac91 100644
--- a/web-console/src/views/servers-view/__snapshots__/servers-view.spec.tsx.snap
+++ b/web-console/src/views/servers-view/__snapshots__/servers-view.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe servers view action servers view 1`] = `
+exports[`servers view action servers view 1`] = `
 <div
   className="servers-view app-view"
 >
@@ -40,7 +40,7 @@ exports[`describe servers view action servers view 1`] = `
       onClick={[Function]}
       text="Go to SQL"
     />
-    <TableColumnSelection
+    <TableColumnSelector
       columns={
         Array [
           "Server",
diff --git a/web-console/src/views/servers-view/servers-view.spec.tsx b/web-console/src/views/servers-view/servers-view.spec.tsx
index 58667c7..b9e5ca5 100644
--- a/web-console/src/views/servers-view/servers-view.spec.tsx
+++ b/web-console/src/views/servers-view/servers-view.spec.tsx
@@ -16,16 +16,12 @@
  * limitations under the License.
  */
 
-
-import * as Enzyme from 'enzyme';
 import { shallow } from 'enzyme';
-import * as enzymeAdapterReact16 from 'enzyme-adapter-react-16';
 import * as React from 'react';
 
-import {ServersView} from './servers-view';
+import { ServersView } from './servers-view';
 
-Enzyme.configure({ adapter: new enzymeAdapterReact16() });
-describe('describe servers view', () => {
+describe('servers view', () => {
   it('action servers view', () => {
     const serversView = shallow(
       <ServersView
diff --git a/web-console/src/views/servers-view/servers-view.tsx b/web-console/src/views/servers-view/servers-view.tsx
index 3d9446e..4eca97b 100644
--- a/web-console/src/views/servers-view/servers-view.tsx
+++ b/web-console/src/views/servers-view/servers-view.tsx
@@ -26,7 +26,7 @@ import { Filter } from 'react-table';
 
 import {
   ActionCell,
-  TableColumnSelection,
+  TableColumnSelector,
   ViewControlBar
 } from '../../components';
 import { AsyncActionDialog } from '../../dialogs';
@@ -562,9 +562,9 @@ ORDER BY "rank" DESC, "server" DESC`);
             onClick={() => goToSql(this.serverQueryManager.getLastQuery())}
           />
         }
-        <TableColumnSelection
+        <TableColumnSelector
           columns={serverTableColumns}
-          onChange={(column) => serverTableColumnSelectionHandler.changeTableColumnSelection(column)}
+          onChange={(column) => serverTableColumnSelectionHandler.changeTableColumnSelector(column)}
           tableColumnsHidden={serverTableColumnSelectionHandler.hiddenColumns}
         />
       </ViewControlBar>
diff --git a/web-console/src/views/sql-view/__snapshots__/sql-view.spec.tsx.snap b/web-console/src/views/sql-view/__snapshots__/sql-view.spec.tsx.snap
index 68411a1..a2592b3 100644
--- a/web-console/src/views/sql-view/__snapshots__/sql-view.spec.tsx.snap
+++ b/web-console/src/views/sql-view/__snapshots__/sql-view.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe sql view sql view snapshot 1`] = `
+exports[`sql view matches snapshot 1`] = `
 <t
   customClassName="sql-view app-view"
   onDragEnd={null}
diff --git a/web-console/src/views/sql-view/sql-view.spec.tsx b/web-console/src/views/sql-view/sql-view.spec.tsx
index 4d7d3b1..23edd38 100644
--- a/web-console/src/views/sql-view/sql-view.spec.tsx
+++ b/web-console/src/views/sql-view/sql-view.spec.tsx
@@ -16,17 +16,13 @@
  * limitations under the License.
  */
 
-
-import * as Enzyme from 'enzyme';
 import { shallow } from 'enzyme';
-import * as enzymeAdapterReact16 from 'enzyme-adapter-react-16';
 import * as React from 'react';
 
-import {SqlView} from './sql-view';
+import { SqlView } from './sql-view';
 
-Enzyme.configure({ adapter: new enzymeAdapterReact16() });
-describe('describe sql view', () => {
-  it('sql view snapshot', () => {
+describe('sql view', () => {
+  it('matches snapshot', () => {
     const sqlView = shallow(
       <SqlView
         initSql={'test'}
diff --git a/web-console/src/views/sql-view/sql-view.tsx b/web-console/src/views/sql-view/sql-view.tsx
index dbf0f0d..a7e31f3 100644
--- a/web-console/src/views/sql-view/sql-view.tsx
+++ b/web-console/src/views/sql-view/sql-view.tsx
@@ -21,7 +21,7 @@ import * as React from 'react';
 import SplitterLayout from 'react-splitter-layout';
 import ReactTable from 'react-table';
 
-import { NullTableCell, SqlControl } from '../../components';
+import { SqlControl, TableCell } from '../../components';
 import { QueryPlanDialog } from '../../dialogs';
 import {
   BasicQueryExplanation,
@@ -192,7 +192,7 @@ export class SqlView extends React.Component<SqlViewProps, SqlViewState> {
           return {
             Header: h,
             accessor: String(i),
-            Cell: row => <NullTableCell value={row.value}/>
+            Cell: row => <TableCell value={row.value}/>
           };
         })
       }
diff --git a/web-console/src/views/task-view/__snapshots__/tasks-view.spec.tsx.snap b/web-console/src/views/task-view/__snapshots__/tasks-view.spec.tsx.snap
index d2f1cc6..a14c656 100644
--- a/web-console/src/views/task-view/__snapshots__/tasks-view.spec.tsx.snap
+++ b/web-console/src/views/task-view/__snapshots__/tasks-view.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`describe tasks view tasks view snapshot 1`] = `
+exports[`tasks view matches snapshot 1`] = `
 <Fragment>
   <t
     customClassName="tasks-view app-view"
@@ -30,7 +30,7 @@ exports[`describe tasks view tasks view snapshot 1`] = `
           onClick={[Function]}
           text="Submit supervisor"
         />
-        <TableColumnSelection
+        <TableColumnSelector
           columns={
             Array [
               "Datasource",
@@ -361,7 +361,7 @@ exports[`describe tasks view tasks view snapshot 1`] = `
             text="Submit task"
           />
         </Blueprint3.Popover>
-        <TableColumnSelection
+        <TableColumnSelector
           columns={
             Array [
               "Task ID",
diff --git a/web-console/src/views/task-view/tasks-view.spec.tsx b/web-console/src/views/task-view/tasks-view.spec.tsx
index 4bf5740..3ac8029 100644
--- a/web-console/src/views/task-view/tasks-view.spec.tsx
+++ b/web-console/src/views/task-view/tasks-view.spec.tsx
@@ -16,17 +16,13 @@
  * limitations under the License.
  */
 
-
-import * as Enzyme from 'enzyme';
 import { shallow } from 'enzyme';
-import * as enzymeAdapterReact16 from 'enzyme-adapter-react-16';
 import * as React from 'react';
 
-import {TasksView} from './tasks-view';
+import { TasksView } from './tasks-view';
 
-Enzyme.configure({ adapter: new enzymeAdapterReact16() });
-describe('describe tasks view', () => {
-  it('tasks view snapshot', () => {
+describe('tasks view', () => {
+  it('matches snapshot', () => {
     const taskView = shallow(
       <TasksView
         openDialog={'test'}
diff --git a/web-console/src/views/task-view/tasks-view.tsx b/web-console/src/views/task-view/tasks-view.tsx
index 38a9d65..e58424d 100644
--- a/web-console/src/views/task-view/tasks-view.tsx
+++ b/web-console/src/views/task-view/tasks-view.tsx
@@ -24,7 +24,7 @@ import SplitterLayout from 'react-splitter-layout';
 import ReactTable from 'react-table';
 import { Filter } from 'react-table';
 
-import { ActionCell, TableColumnSelection, ViewControlBar} from '../../components';
+import { ActionCell, TableColumnSelector, ViewControlBar } from '../../components';
 import { AsyncActionDialog, SpecDialog, SupervisorTableActionDialog, TaskTableActionDialog } from '../../dialogs';
 import { AppToaster } from '../../singletons/toaster';
 import {
@@ -729,9 +729,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
               text="Submit supervisor"
               onClick={() => this.setState({ supervisorSpecDialogOpen: true })}
             />
-            <TableColumnSelection
+            <TableColumnSelector
               columns={supervisorTableColumns}
-              onChange={(column) => supervisorTableColumnSelectionHandler.changeTableColumnSelection(column)}
+              onChange={(column) => supervisorTableColumnSelectionHandler.changeTableColumnSelector(column)}
               tableColumnsHidden={supervisorTableColumnSelectionHandler.hiddenColumns}
             />
           </ViewControlBar>
@@ -762,9 +762,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
             <Popover content={submitTaskMenu} position={Position.BOTTOM_LEFT}>
               <Button icon={IconNames.PLUS} text="Submit task"/>
             </Popover>
-            <TableColumnSelection
+            <TableColumnSelector
               columns={taskTableColumns}
-              onChange={(column) => taskTableColumnSelectionHandler.changeTableColumnSelection(column)}
+              onChange={(column) => taskTableColumnSelectionHandler.changeTableColumnSelector(column)}
               tableColumnsHidden={taskTableColumnSelectionHandler.hiddenColumns}
             />
           </ViewControlBar>
diff --git a/web-console/tsconfig.json b/web-console/tsconfig.json
index 2747778..b13473c 100644
--- a/web-console/tsconfig.json
+++ b/web-console/tsconfig.json
@@ -20,14 +20,9 @@
 
     "outDir": "build"
   },
-
   "include": [
     "src/**/*.ts",
     "src/**/*.tsx",
     "lib/sql-function-doc.ts"
-  ],
-  "exclude": [
-    "**/*.spec.ts",
-    "**/*.spec.tsx"
   ]
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org