You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tez.apache.org by je...@apache.org on 2021/08/24 23:32:37 UTC

[tez] branch TEZ-4329 created (now e6a5e0a)

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

jeagles pushed a change to branch TEZ-4329
in repository https://gitbox.apache.org/repos/asf/tez.git.


      at e6a5e0a  TEZ-4329. Import external tez component em-table

This branch includes the following new commits:

     new e6a5e0a  TEZ-4329. Import external tez component em-table

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[tez] 01/01: TEZ-4329. Import external tez component em-table

Posted by je...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jeagles pushed a commit to branch TEZ-4329
in repository https://gitbox.apache.org/repos/asf/tez.git

commit e6a5e0aa0f2a075d38c1c18eedfee9a3995c181a
Author: Jonathan Eagles <je...@verizonmedia.com>
AuthorDate: Tue Aug 24 11:54:14 2021 -0500

    TEZ-4329. Import external tez component em-table
---
 tez-ui/src/main/resources/META-INF/LICENSE.txt     |   1 -
 .../main/webapp/app/components/em-table-cell.js    | 124 ++++++++
 .../main/webapp/app/components/em-table-column.js  | 109 +++++++
 .../app/components/em-table-facet-panel-values.js  | 199 ++++++++++++
 .../webapp/app/components/em-table-facet-panel.js  |  86 +++++
 .../webapp/app/components/em-table-header-cell.js  |  64 ++++
 ...able-status-cell.js => em-table-linked-cell.js} |  44 ++-
 .../app/components/em-table-pagination-ui.js       |  98 ++++++
 ...le-status-cell.js => em-table-progress-cell.js} |  26 +-
 .../webapp/app/components/em-table-search-ui.js    |  95 ++++++
 .../webapp/app/components/em-table-status-cell.js  |   2 +
 tez-ui/src/main/webapp/app/components/em-table.js  | 281 +++++++++++++++++
 .../src/main/webapp/app/controllers/app/configs.js |   2 +-
 tez-ui/src/main/webapp/app/controllers/app/dags.js |   2 +-
 .../main/webapp/app/controllers/counters-table.js  |   2 +-
 .../main/webapp/app/controllers/dag/attempts.js    |   2 +-
 .../main/webapp/app/controllers/dag/graphical.js   |   2 +-
 .../main/webapp/app/controllers/dag/index/index.js |   2 +-
 .../main/webapp/app/controllers/dag/swimlane.js    |   2 +-
 .../src/main/webapp/app/controllers/dag/tasks.js   |   2 +-
 .../main/webapp/app/controllers/dag/vertices.js    |   2 +-
 .../src/main/webapp/app/controllers/home/index.js  |   4 +-
 .../main/webapp/app/controllers/home/queries.js    |   4 +-
 .../main/webapp/app/controllers/query/configs.js   |   2 +-
 .../main/webapp/app/controllers/query/timeline.js  |   2 +-
 tez-ui/src/main/webapp/app/controllers/table.js    |   2 +-
 .../main/webapp/app/controllers/task/attempts.js   |   2 +-
 .../main/webapp/app/controllers/vertex/attempts.js |   2 +-
 .../main/webapp/app/controllers/vertex/configs.js  |   2 +-
 .../main/webapp/app/controllers/vertex/tasks.js    |   2 +-
 tez-ui/src/main/webapp/app/styles/app.less         |   7 +
 .../webapp/app/styles/em-table-facet-panel.less    | 218 +++++++++++++
 tez-ui/src/main/webapp/app/styles/em-table.less    | 346 +++++++++++++++++++++
 .../src/main/webapp/app/styles/pagination-ui.less  |  88 ++++++
 .../progress-cell.less}                            |  22 +-
 .../search-ui.less}                                |  29 +-
 tez-ui/src/main/webapp/app/styles/shared.less      | 115 +++++++
 tez-ui/src/main/webapp/app/styles/status-cell.less |  86 +++++
 .../variables.less}                                |  22 +-
 .../app/templates/components/em-table-cell.hbs     |  41 +++
 .../components/em-table-column.hbs}                |  26 +-
 .../components/em-table-facet-panel-values.hbs     |  50 +++
 .../components/em-table-facet-panel.hbs}           |  35 +--
 .../components/em-table-header-cell.hbs}           |  34 +-
 .../components/em-table-linked-cell.hbs}           |  45 +--
 .../components/em-table-pagination-ui.hbs          |  45 +++
 .../components/em-table-progress-cell.hbs}         |  33 +-
 .../templates/components/em-table-search-ui.hbs    |  52 ++++
 .../webapp/app/templates/components/em-table.hbs   | 106 +++++++
 .../src/main/webapp/app/utils/column-definition.js | 125 ++++++++
 .../webapp/app/utils/counter-column-definition.js  |   2 +-
 tez-ui/src/main/webapp/app/utils/data-processor.js | 275 ++++++++++++++++
 tez-ui/src/main/webapp/app/utils/facet-types.js    |  85 +++++
 tez-ui/src/main/webapp/app/utils/sql.js            |  94 ++++++
 .../table-definition.js}                           |  45 ++-
 tez-ui/src/main/webapp/bower.json                  |   1 +
 tez-ui/src/main/webapp/ember-cli-build.js          |   1 +
 tez-ui/src/main/webapp/package.json                |   1 -
 .../integration/components/em-table-cell-test.js}  |  31 +-
 .../components/em-table-column-test.js}            |  22 +-
 .../components/em-table-facet-panel-test.js}       |  31 +-
 .../components/em-table-facet-panel-values-test.js |  44 +++
 .../components/em-table-header-cell-test.js}       |  22 +-
 .../components/em-table-linked-cell-test.js}       |  22 +-
 .../components/em-table-pagination-ui-test.js      | 204 ++++++++++++
 .../components/em-table-progress-cell-test.js}     |  31 +-
 .../components/em-table-search-ui-test.js}         |  22 +-
 .../tests/integration/components/em-table-test.js  |  48 +++
 .../integration/em-table-status-cell-test.js}      |  28 +-
 .../tests/unit/utils/column-definition-test.js     | 104 +++++++
 .../webapp/tests/unit/utils/data-processor-test.js | 137 ++++++++
 .../unit/utils/facet-types-test.js}                |  20 +-
 .../src/main/webapp/tests/unit/utils/sql-test.js   |  90 ++++++
 .../tests/unit/utils/table-definition-test.js      |  52 ++++
 tez-ui/src/main/webapp/yarn.lock                   |  10 -
 75 files changed, 3796 insertions(+), 320 deletions(-)

diff --git a/tez-ui/src/main/resources/META-INF/LICENSE.txt b/tez-ui/src/main/resources/META-INF/LICENSE.txt
index 354d745..608dc61 100644
--- a/tez-ui/src/main/resources/META-INF/LICENSE.txt
+++ b/tez-ui/src/main/resources/META-INF/LICENSE.txt
@@ -232,7 +232,6 @@ The Apache TEZ tez-ui bundles the following files under the MIT License:
  - more-js v0.8.2 (https://github.com/sreenaths/snippet-ss)
  - snippet-ss v1.11.0 (https://github.com/sreenaths/snippet-ss)
  - em-tgraph v0.0.4 (https://github.com/sreenaths/em-tgraph)
- - em-table v0.3.12 (https://github.com/sreenaths/em-table)
  - ember-cli-app-version v1.0.0 (https://github.com/EmberSherpa/ember-cli-app-version) - Authored by Taras Mankovski <ta...@gmail.com>
  - ember-cli-auto-register v1.1.0 (https://github.com/williamsbdev/ember-cli-auto-register) - Copyright © 2015 Brandon Williams http://williamsbdev.com
  - ember-cli-content-security-policy v0.4.0 (https://github.com/rwjblue/ember-cli-content-security-policy)
diff --git a/tez-ui/src/main/webapp/app/components/em-table-cell.js b/tez-ui/src/main/webapp/app/components/em-table-cell.js
new file mode 100644
index 0000000..d4e6a54
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-cell.js
@@ -0,0 +1,124 @@
+/**
+ * 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 Ember from 'ember';
+import layout from '../templates/components/em-table-cell';
+
+export default Ember.Component.extend({
+  layout: layout,
+
+  classNames: ['table-cell'],
+  classNameBindings: ['innerCell', 'isWaiting'],
+
+  innerCell: Ember.computed('index', function () {
+    if(this.get('index')) {
+      return 'inner';
+    }
+  }),
+
+  row: null,
+  columnDefinition: null,
+
+  isWaiting: false,
+
+  _value: null,
+  _observedPath: null,
+  _comment: null,
+  _cellContent: Ember.computed({
+    set: function (key, value, prevValue) {
+      if(value !== prevValue) {
+        this.highlightCell();
+      }
+      return value;
+    }
+  }),
+
+  _addObserver: function (path) {
+    this._removeObserver();
+    this.get('row').addObserver(path, this, this._onValueChange);
+    this.set('_observedPath', path);
+  },
+
+  _removeObserver: function () {
+    var path = this.get('_observedPath');
+    if(path) {
+      this.get('row').removeObserver(path, this, this._onValueChange);
+      this.set('_observedPath', null);
+    }
+  },
+
+  _pathObserver: Ember.on('init', Ember.observer('row', 'columnDefinition.contentPath', 'columnDefinition.observePath', function () {
+    var path = this.get('columnDefinition.contentPath');
+    if(path && this.get('columnDefinition.observePath')) {
+      this._addObserver(path);
+    }
+  })),
+
+  _onValueChange: function (row, path) {
+    this.set('_value', row.get(path));
+  },
+
+  setContent: function (content) {
+    var comment;
+
+    if(content && content.hasOwnProperty("content")) {
+      comment = content.comment;
+      content = content.content;
+    }
+
+    this.setProperties({
+      _comment: comment,
+      _cellContent: content,
+      isWaiting: false
+    });
+  },
+
+  _cellContentObserver: Ember.on('init', Ember.observer('row', 'columnDefinition', '_value', function () {
+    var cellContent = this.get('columnDefinition').getCellContent(this.get('row'), this.get("_value")),
+        that = this;
+
+    if(cellContent && cellContent.then) {
+      cellContent.then(function (content) {
+        that.setContent(content);
+      });
+      this.set('isWaiting', true);
+    }
+    else if(cellContent === undefined && this.get('columnDefinition.observePath')) {
+      this.set('isWaiting', true);
+    }
+    else {
+      this.setContent(cellContent);
+    }
+  })),
+
+  highlightCell: function () {
+    var element = this.$();
+    if(element) {
+      element.removeClass("bg-transition");
+      element.addClass("highlight");
+      Ember.run.later(function () {
+        element.addClass("bg-transition");
+        element.removeClass("highlight");
+      }, 100);
+    }
+  },
+
+  willDestroy: function () {
+    this._removeObserver();
+  }
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-column.js b/tez-ui/src/main/webapp/app/components/em-table-column.js
new file mode 100644
index 0000000..a3cb5a9
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-column.js
@@ -0,0 +1,109 @@
+/**
+ * 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 Ember from 'ember';
+
+import layout from '../templates/components/em-table-column';
+
+export default Ember.Component.extend({
+  layout: layout,
+
+  definition: null,
+  rows: null,
+  index: 0,
+
+  tableDefinition: null,
+  dataProcessor: null,
+  adjustedWidth: null,
+  defaultWidth: "",
+
+  classNames: ['table-column'],
+  classNameBindings: ['inner', 'extraClassNames'],
+
+  inner: Ember.computed('index', function () {
+    return !!this.get('index');
+  }),
+
+  extraClassNames: Ember.computed("definition.classNames", function () {
+    var classNames = this.get("definition.classNames");
+    if(classNames) {
+      return classNames.join(" ");
+    }
+  }),
+
+  didInsertElement: function () {
+    Ember.run.scheduleOnce('afterRender', this, function() {
+      this.setWidth();
+      this.setMinWidth();
+    });
+  },
+
+  setMinWidth: Ember.observer("definition.minWidth", function () {
+    this.$().css("minWidth", this.get('definition.minWidth'));
+  }),
+
+  setWidth: Ember.observer("adjustedWidth", "defaultWidth", function () {
+    var thisElement = this.$();
+    thisElement.css("width", this.get('adjustedWidth') || this.get('defaultWidth'));
+    Ember.run.scheduleOnce('afterRender', this, function() {
+      this.get('parentView').send('columnWidthChanged', thisElement.width(), this.get("definition"), this.get("index"));
+    });
+  }),
+
+  _onColResize: function (event) {
+    var data = event.data,
+        width;
+
+    if(!data.startEvent) {
+      data.startEvent = event;
+    }
+
+    width = data.startWidth + event.clientX - data.startEvent.clientX;
+    data.thisObj.set('adjustedWidth', width);
+  },
+
+  _endColResize: function (event) {
+    var thisObj = event.data.thisObj;
+    Ember.$(document).off('mousemove', thisObj._onColResize);
+    Ember.$(document).off('mouseup', thisObj._endColResize);
+  },
+
+  actions: {
+    sort: function () {
+      var definition = this.get('definition'),
+          beforeSort = definition.get('beforeSort');
+
+      if(!beforeSort || beforeSort.call(definition, definition)) {
+        let columnId = this.get('definition.id'),
+            sortOrder = this.get('tableDefinition.sortOrder') === 'desc' ? 'asc' : 'desc';
+
+        this.get('parentView').send('sort', columnId, sortOrder);
+      }
+    },
+    startColResize: function () {
+      var mouseTracker = {
+        thisObj: this,
+        startWidth: this.$().width(),
+        startEvent: null
+      };
+
+      Ember.$(document).on('mousemove', mouseTracker, this._onColResize);
+      Ember.$(document).on('mouseup', mouseTracker, this._endColResize);
+    }
+  }
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-facet-panel-values.js b/tez-ui/src/main/webapp/app/components/em-table-facet-panel-values.js
new file mode 100644
index 0000000..ec88181
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-facet-panel-values.js
@@ -0,0 +1,199 @@
+/**
+ * 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 Ember from 'ember';
+import layout from '../templates/components/em-table-facet-panel-values';
+
+const LIST_LIMIT = 7;
+
+export default Ember.Component.extend({
+  layout: layout,
+
+  data: null,
+  checkedCount: null,
+
+  tableDefinition: null,
+  dataProcessor: null,
+
+  tmpFacetConditions: null,
+
+  hideValues: true,
+
+  currentPage: 1,
+
+  classNames: ['em-table-facet-panel-values'],
+  classNameBindings: ['hideValues', 'hideFilter', 'hideSelectAll'],
+
+  filterText: null,
+  allButtonTitle: Ember.computed("filterText", function () {
+    let filterText = this.get("filterText");
+    return filterText ? `Select all with substring '${filterText}'` : "Select all";
+  }),
+  isVisible: Ember.computed("data.facets.length", "tableDefinition.minValuesToDisplay", function () {
+    return this.get("data.facets.length") >= this.get("tableDefinition.minValuesToDisplay");
+  }),
+  hideFilter: Ember.computed("allFacets.length", function () {
+    return this.get("allFacets.length") < LIST_LIMIT;
+  }),
+  hideSelectAll: Ember.computed("fieldFacetConditions", "checkedCount", "data.facets", function () {
+    return this.get("fieldFacetConditions.in.length") === this.get("data.facets.length");
+  }),
+
+  fieldFacetConditions: Ember.computed("tmpFacetConditions", "data.column.id", function () {
+    var columnID = this.get("data.column.id"),
+        conditions = this.get(`tmpFacetConditions.${columnID}`),
+        facets = this.get("data.facets") || [];
+
+    if(!conditions) {
+      conditions = {
+        in: facets.map(facet => facet.value)
+      };
+      this.set(`tmpFacetConditions.${columnID}`, conditions);
+    }
+
+    return conditions;
+  }),
+
+  allFacets: Ember.computed("data.facets", "fieldFacetConditions", function () {
+    var facets = this.get("data.facets") || [],
+
+        checkedValues = this.get("fieldFacetConditions.in"),
+        selectionHash = {};
+
+    if(checkedValues) {
+      checkedValues.forEach(function (valueText) {
+        selectionHash[valueText] = 1;
+      });
+    }
+
+    return Ember.A(facets.map(function (facet) {
+      facet = Ember.Object.create(facet);
+      facet.set("checked", selectionHash[facet.value]);
+
+      if(!facet.get("displayText")) {
+        facet.set("displayText", facet.get("value"));
+      }
+
+      return facet;
+    }));
+  }),
+
+  filteredFacets: Ember.computed("allFacets", "filterText", function () {
+    var allFacets = this.get("allFacets"),
+        filterText = this.get("filterText"),
+        filteredFacets;
+
+    if(filterText) {
+      filteredFacets = allFacets.filter(function (facet) {
+        return facet.get("value").match(filterText);
+      });
+    }
+    else {
+      filteredFacets = allFacets;
+    }
+
+    return filteredFacets;
+  }),
+
+  _filterObserver: Ember.observer("filterText", function () {
+    this.set("currentPage", 1);
+  }),
+
+  totalPages: Ember.computed("filteredFacets.length", "tableDefinition.facetValuesPageSize", function () {
+    return Math.ceil(this.get("filteredFacets.length") / this.get("tableDefinition.facetValuesPageSize"));
+  }),
+  showPagination: Ember.computed("totalPages", function () {
+    return this.get("totalPages") > 1;
+  }),
+  showPrevious: Ember.computed("currentPage", function () {
+    return this.get("currentPage") > 1;
+  }),
+  showNext: Ember.computed("currentPage", "totalPages", function () {
+    return this.get("currentPage") < this.get("totalPages");
+  }),
+
+  paginatedFacets: Ember.computed("filteredFacets", "currentPage", "tableDefinition.facetValuesPageSize", function () {
+    let currentPage = this.get("currentPage"),
+        pageSize = this.get("tableDefinition.facetValuesPageSize");
+    return this.get("filteredFacets").slice(
+      (currentPage - 1) * pageSize,
+      currentPage * pageSize);
+  }),
+
+  actions: {
+    changePage: function (factor) {
+      var newPage = this.get("currentPage") + factor;
+      if(newPage > 0 && newPage <= this.get("totalPages")) {
+        this.set("currentPage", newPage);
+      }
+    },
+    toggleValueDisplay: function () {
+      this.toggleProperty("hideValues");
+      this.get("parentView").sendAction("toggleValuesDisplayAction", !this.get("hideValues"), this.get("data"));
+    },
+    clickedCheckbox: function (facet) {
+      var checkedValues = this.get("fieldFacetConditions.in"),
+          value = facet.get("value"),
+          valueIndex = checkedValues.indexOf(value);
+
+      facet.toggleProperty("checked");
+
+      if(facet.get("checked")) {
+        if(valueIndex === -1) {
+          checkedValues.push(value);
+        }
+      }
+      else if(valueIndex !== -1) {
+        checkedValues.splice(valueIndex, 1);
+      }
+
+      this.set("checkedCount", checkedValues.length);
+    },
+
+    selectAll: function () {
+      var filteredFacets = this.get("filteredFacets"),
+          checkedValues = this.get("fieldFacetConditions.in");
+
+      filteredFacets.forEach(function (facet) {
+        if(!facet.get("checked")) {
+          checkedValues.push(facet.get("value"));
+        }
+
+        facet.set("checked", true);
+      });
+
+      this.set("fieldFacetConditions.in", checkedValues);
+      this.set("checkedCount", checkedValues.length);
+    },
+    clickedOnly: function (facet) {
+      var allFacets = this.get("allFacets"),
+          checkedValues = [];
+
+      allFacets.forEach(function (facet) {
+        facet.set("checked", false);
+      });
+
+      facet.set("checked", true);
+      checkedValues.push(facet.get("value"));
+
+      this.set("fieldFacetConditions.in", checkedValues);
+      this.set("checkedCount", checkedValues.length);
+    }
+  }
+
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-facet-panel.js b/tez-ui/src/main/webapp/app/components/em-table-facet-panel.js
new file mode 100644
index 0000000..fdbd8f5
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-facet-panel.js
@@ -0,0 +1,86 @@
+/**
+ * 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 Ember from 'ember';
+import layout from '../templates/components/em-table-facet-panel';
+
+export default Ember.Component.extend({
+  layout: layout,
+
+  classNames: ["em-table-facet-panel"],
+  classNameBindings: ['isEmpty', 'hideFilter'],
+
+  isVisible: Ember.computed.alias('tableDefinition.enableFaceting'),
+
+  tableDefinition: null,
+  dataProcessor: null,
+  tmpFacetConditions: {},
+
+  filterText: null,
+  isEmpty: Ember.computed("dataProcessor.facetedFields.length", function () {
+    return this.get("dataProcessor.facetedFields.length") === 0;
+  }),
+  hideFilter: Ember.computed("dataProcessor.facetedFields.length", "tableDefinition.minFieldsForFilter", function () {
+    return this.get("dataProcessor.facetedFields.length") < this.get("tableDefinition.minFieldsForFilter");
+  }),
+
+  didInsertElement: Ember.observer("filterText", "dataProcessor.facetedFields", function () {
+    var fields = this.get("dataProcessor.facetedFields"),
+        filterText = this.get("filterText"),
+        filterRegex = new RegExp(filterText, "i"),
+        elements = Ember.$(this.get("element")).find(".field-list>li");
+
+    elements.each(function (index, element) {
+      var foundMatch = !filterText || Ember.get(fields, `${index}.column.headerTitle`).match(filterRegex);
+      Ember.$(element)[foundMatch ? "show" : "hide"]();
+    });
+  }),
+
+  _facetConditionsObserver: Ember.observer("tableDefinition.facetConditions", "dataProcessor.processedRows.[]", function () {
+    var facetConditions = Ember.$.extend({}, this.get("tableDefinition.facetConditions"));
+    this.set("tmpFacetConditions", facetConditions);
+  }),
+
+  actions: {
+    applyFilters: function () {
+      var tmpFacetConditions = this.get("tmpFacetConditions"),
+          facetedFields = this.get("dataProcessor.facetedFields"),
+          normalizedTmpFacetConditions = {};
+
+      facetedFields.forEach(function (field) {
+        var column = field.column,
+            columnId = column.get("id"),
+            facetType = column.get("facetType"),
+            normalizedConditions;
+
+        if(facetType) {
+          normalizedConditions = facetType.normaliseConditions(tmpFacetConditions[columnId], field.facets);
+          if(normalizedConditions) {
+            normalizedTmpFacetConditions[columnId] = normalizedConditions;
+          }
+        }
+      });
+
+      this.set("tableDefinition.facetConditions", normalizedTmpFacetConditions);
+    },
+    clearFilters: function () {
+      this.set("tmpFacetConditions", {});
+      this.set("tableDefinition.facetConditions", {});
+    },
+  }
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-header-cell.js b/tez-ui/src/main/webapp/app/components/em-table-header-cell.js
new file mode 100644
index 0000000..c0a8e12
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-header-cell.js
@@ -0,0 +1,64 @@
+/**
+ * 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 Ember from 'ember';
+import layout from '../templates/components/em-table-header-cell';
+
+export default Ember.Component.extend({
+  layout: layout,
+
+  title: null, // Header cell Name
+  attributeBindings: ['title'],
+
+  definition: null,
+  tableDefinition: null,
+  dataProcessor: null,
+
+  classNames: ['table-header-cell'],
+  classNameBindings: ['isSorting'],
+
+  isSorting: Ember.computed("dataProcessor.isSorting", function () {
+    return this.get("dataProcessor.isSorting") && this.get('tableDefinition.sortColumnId') === this.get('definition.id');
+  }),
+
+  sortIconCSS: Ember.computed('tableDefinition.sortOrder', 'tableDefinition.sortColumnId', function () {
+    if(this.get('tableDefinition.sortColumnId') === this.get('definition.id')) {
+      return this.get('tableDefinition.sortOrder');
+    }
+  }),
+
+  sortToggledTitle: Ember.computed('tableDefinition.sortOrder', 'tableDefinition.sortColumnId', function () {
+    if(this.get('tableDefinition.sortColumnId') === this.get('definition.id')) {
+      switch(this.get('tableDefinition.sortOrder')) {
+        case "asc":
+          return "descending";
+        case "desc":
+          return "ascending";
+      }
+    }
+  }),
+
+  actions: {
+    sort: function () {
+      this.get('parentView').send('sort');
+    },
+    startColResize: function () {
+      this.get('parentView').send('startColResize');
+    }
+  }
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/components/em-table-linked-cell.js
similarity index 50%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/app/components/em-table-linked-cell.js
index 7751719..c42c566 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/components/em-table-linked-cell.js
@@ -17,20 +17,48 @@
  */
 
 import Ember from 'ember';
+import layout from '../templates/components/em-table-linked-cell';
 
 export default Ember.Component.extend({
+  layout: layout,
 
+  definition: null,
   content: null,
 
-  classNames: ["em-table-status-cell"],
+  normalizedLinks: Ember.computed("content", function () {
+    var content = this.get("content"),
+        links;
 
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
+    if(content) {
+      if(!Array.isArray(content)) {
+        content = [content];
+      }
 
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
+      links = content.map(function (link) {
+        var model,
+            text = Ember.get(link, "text") || Ember.get(link, "displayText");
+
+        if(text) {
+          link = Ember.Object.create(link, {
+            text: text
+          });
+
+          if(link.get("model") === undefined) {
+            link.set("model", link.get("id"));
+          }
+
+          model = link.get("model");
+          link.set("withModel", model !== undefined);
+
+          return link;
+        }
+      });
+
+      links = links.filter(function (link) {
+        return link;
+      });
     }
-    return status;
-  }),
+
+    return links;
+  })
 });
diff --git a/tez-ui/src/main/webapp/app/components/em-table-pagination-ui.js b/tez-ui/src/main/webapp/app/components/em-table-pagination-ui.js
new file mode 100644
index 0000000..858928b
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-pagination-ui.js
@@ -0,0 +1,98 @@
+/**
+ * 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 Ember from 'ember';
+import layout from '../templates/components/em-table-pagination-ui';
+
+export default Ember.Component.extend({
+  layout: layout,
+
+  tableDefinition: null,
+  dataProcessor: null,
+
+  classNames: ['pagination-ui'],
+  isVisible: Ember.computed.alias('tableDefinition.enablePagination'),
+
+  showFirst: Ember.computed('_possiblePages', function () {
+    return this.get("dataProcessor.totalPages") && this.get('_possiblePages.0.pageNum') !== 1;
+  }),
+
+  showLast: Ember.computed('_possiblePages', 'dataProcessor.totalPages', function () {
+    var possiblePages = this.get("_possiblePages");
+    if(possiblePages.length) {
+      return possiblePages[possiblePages.length - 1].pageNum !== this.get("dataProcessor.totalPages");
+    }
+  }),
+
+  rowCountOptions: Ember.computed('tableDefinition.rowCountOptions', 'tableDefinition.rowCount', function () {
+    var options = this.get('tableDefinition.rowCountOptions'),
+        rowCount = this.get('tableDefinition.rowCount');
+
+    return options.map(function (option) {
+      return {
+        value: option,
+        selected: option === rowCount
+      };
+    });
+  }),
+
+  _possiblePages: Ember.computed('tableDefinition.pageNum', 'dataProcessor.totalPages', function () {
+    var pageNum = this.get('tableDefinition.pageNum'),
+        totalPages = this.get('dataProcessor.totalPages'),
+        possiblePages = [],
+        startPage = 1,
+        endPage = totalPages,
+        delta = 0;
+
+    if(totalPages > 5) {
+      startPage = pageNum - 2;
+      endPage = pageNum + 2;
+
+      if(startPage < 1) {
+        delta = 1 - startPage;
+      }
+      else if(endPage > totalPages) {
+        delta = totalPages - endPage;
+      }
+
+      startPage += delta;
+      endPage += delta;
+    }
+
+    while(startPage <= endPage) {
+      possiblePages.push({
+        isCurrent: startPage === pageNum,
+        pageNum: startPage++
+      });
+    }
+
+    return possiblePages;
+  }),
+
+  actions: {
+    rowSelected: function (value) {
+      value = parseInt(value);
+      if(this.get('tableDefinition.rowCount') !== value) {
+        this.get('parentView').send('rowChanged', value);
+      }
+    },
+    changePage: function (value) {
+      this.get('parentView').send('pageChanged', value);
+    }
+  }
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/components/em-table-progress-cell.js
similarity index 62%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/app/components/em-table-progress-cell.js
index 7751719..32f75c4 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/components/em-table-progress-cell.js
@@ -17,20 +17,30 @@
  */
 
 import Ember from 'ember';
+import layout from '../templates/components/em-table-progress-cell';
 
 export default Ember.Component.extend({
+  layout: layout,
 
   content: null,
 
-  classNames: ["em-table-status-cell"],
+  message: Ember.computed("content", function () {
+    var content = this.get("content");
 
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
-
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
+    if(content === undefined || content === null) {
+      return "Not Available!";
+    }
+    else if(isNaN(parseFloat(content))){
+      return "Invalid Data!";
     }
-    return status;
   }),
+
+  _definition: Ember.computed("definition", function () {
+    return Ember.Object.extend({
+      valueMin: 0,
+      valueMax: 1,
+      striped: true,
+      style: null
+    }).create(this.get("definition"));
+  })
 });
diff --git a/tez-ui/src/main/webapp/app/components/em-table-search-ui.js b/tez-ui/src/main/webapp/app/components/em-table-search-ui.js
new file mode 100644
index 0000000..58c4f75
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-search-ui.js
@@ -0,0 +1,95 @@
+/**
+ * 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 Ember from 'ember';
+import layout from '../templates/components/em-table-search-ui';
+
+export default Ember.Component.extend({
+  layout: layout,
+
+  tableDefinition: null,
+  dataProcessor: null,
+
+  classNames: ['search-ui'],
+  classNameBindings: ['hasError'],
+  isVisible: Ember.computed.alias('tableDefinition.enableSearch'),
+
+  searchTypes: ["Regex", "SQL"],
+  actualSearchType: null,
+
+  text: Ember.computed.oneWay('tableDefinition.searchText'),
+
+  _actualSearchTypeDecider: Ember.observer("tableDefinition.searchType", "text", function () {
+    var searchType = this.get("tableDefinition.searchType"),
+        actualSearchType = this.get("actualSearchType");
+
+    switch(searchType) {
+      case "SQL":
+      case "Regex":
+        actualSearchType = searchType;
+        break;
+
+      case "manual":
+        if(!actualSearchType) {
+          actualSearchType = "Regex";
+        }
+        // Will be set from the template
+        break;
+
+      case "auto":
+        var text = this.get("text"),
+            columns = this.get('tableDefinition.columns');
+
+        if(text) {
+          actualSearchType = this.get("dataProcessor.sql").validateClause(text, columns) ? "SQL" : "Regex";
+        }
+        else {
+          actualSearchType = null;
+        }
+        break;
+    }
+
+    this.set("actualSearchType", actualSearchType);
+  }),
+
+  hasError: Ember.computed("text", "actualSearchType", "tableDefinition.searchType", function () {
+    var text = this.get("text"),
+        columns = this.get('tableDefinition.columns'),
+        actualSearchType = this.get("actualSearchType");
+
+    if(text) {
+      switch(actualSearchType) {
+        case "SQL":
+            return !this.get("dataProcessor.sql").validateClause(text, columns);
+        case "Regex":
+          try {
+            new RegExp(text);
+          }
+          catch(e) {
+            return true;
+          }
+      }
+    }
+  }),
+
+  actions: {
+    search: function () {
+      this.get('parentView').send('search', this.get('text'), this.get("actualSearchType"));
+    }
+  }
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
index 7751719..7f1fee8 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
@@ -17,8 +17,10 @@
  */
 
 import Ember from 'ember';
+import layout from '../templates/components/em-table-status-cell';
 
 export default Ember.Component.extend({
+  layout: layout,
 
   content: null,
 
diff --git a/tez-ui/src/main/webapp/app/components/em-table.js b/tez-ui/src/main/webapp/app/components/em-table.js
new file mode 100644
index 0000000..79aae3d
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table.js
@@ -0,0 +1,281 @@
+/**
+ * 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 Ember from 'ember';
+import Definition from '../utils/table-definition';
+import ColumnDefinition from '../utils/column-definition';
+import DataProcessor from '../utils/data-processor';
+
+import layout from '../templates/components/em-table';
+
+const DEFAULT_ROW_HIGHLIGHT_COLOR = "#EEE";
+
+function createAssigner(targetPath, targetKey, sourcePath) {
+  return Ember.on("init", Ember.observer(targetPath, sourcePath, function () {
+    var target = this.get(targetPath),
+        source = this.get(sourcePath);
+    if(target && source !== undefined) {
+      target.set(targetKey, source);
+    }
+  }));
+}
+
+const HANDLERS = {
+  // Mouse handlers
+  mouseOver: function(event) {
+    var index = Ember.$(this).index() + 1;
+    event.data.highlightRow(index);
+  },
+  mouseLeave: function(event) {
+    event.data.highlightRow(-1);
+  },
+
+  // Scroll handler
+  onScroll: function(event) {
+    var tableBody = event.currentTarget,
+        scrollValues = event.data.get("scrollValues");
+
+    scrollValues.set("left", tableBody.scrollLeft);
+    scrollValues.set("width", tableBody.scrollWidth);
+  }
+};
+
+export default Ember.Component.extend({
+  layout: layout,
+
+  classNames: ["em-table"],
+  classNameBindings: ["showScrollShadow", "showLeftScrollShadow", "showRightScrollShadow"],
+
+  definition: null,
+  dataProcessor: null,
+
+  highlightRowOnMouse: false, // Could be true or {color: "#XYZ"}
+
+  headerComponentNames: ['em-table-search-ui', 'em-table-pagination-ui'],
+  footerComponentNames: ['em-table-pagination-ui'],
+
+  leftPanelComponentName: "em-table-facet-panel",
+  rightPanelComponentName: "",
+
+  columnWidthChangeAction: null,
+
+  scrollChangeAction: null,
+  scrollValues: null,
+  _widthTrackerTimer: null,
+
+  init: function() {
+    this._super();
+    this.set("scrollValues", Ember.Object.create({
+      left: 0,
+      width: 0,
+      viewPortWidth: 0
+    }));
+  },
+
+  showScrollShadow: false,
+  showLeftScrollShadow: false,
+  showRightScrollShadow: false,
+
+  assignDefinitionInProcessor: createAssigner('_dataProcessor', 'tableDefinition', '_definition'),
+  assignRowsInProcessor: createAssigner('_dataProcessor', 'rows', 'rows'),
+  assignColumnsInDefinition: createAssigner('_definition', 'columns', 'columns'),
+
+  assignEnableSortInDefinition: createAssigner('_definition', 'enableSort', 'enableSort'),
+  assignEnableSearchInDefinition: createAssigner('_definition', 'enableSearch', 'enableSearch'),
+  assignEnablePaginationInDefinition: createAssigner('_definition', 'enablePagination', 'enablePagination'),
+  assignRowCountInDefinition: createAssigner('_definition', 'rowCount', 'rowCount'),
+
+  _definition: Ember.computed('definition', 'definitionClass', function () {
+    return this.get('definition') || (this.get('definitionClass') || Definition).create();
+  }),
+  _dataProcessor: Ember.computed('dataProcessor', 'dataProcessorClass', function () {
+    return this.get('dataProcessor') || (this.get('dataProcessorClass') || DataProcessor).create();
+  }),
+
+  displayFooter: Ember.computed("_definition.minRowsForFooter", "_dataProcessor.processedRows.length", function () {
+    return this.get("_definition.minRowsForFooter") <= this.get("_dataProcessor.processedRows.length");
+  }),
+
+  _processedRowsObserver: Ember.observer('_dataProcessor.processedRows', function () {
+    this.sendAction('rowsChanged', this.get('_dataProcessor.processedRows'));
+  }),
+
+  _setColumnWidth: function (columns) {
+    var widthText = (100 / columns.length) + "%";
+    columns.forEach(function (column) {
+      if(!column.width) {
+        column.width = widthText;
+      }
+    });
+  },
+
+  _columns: Ember.computed('_definition.columns', function () {
+    var rawColumns = this.get('_definition.columns'),
+        normalisedColumns = {
+          left: [],
+          center: [],
+          right: [],
+          length: rawColumns.length
+        };
+
+    rawColumns.forEach(function (column) {
+      normalisedColumns[column.get("pin")].push({
+        definition: column,
+        width: column.width
+      });
+    });
+
+    if(normalisedColumns.center.length === 0) {
+      normalisedColumns.center = [{
+        definition: ColumnDefinition.fillerColumn,
+      }];
+    }
+
+    this._setColumnWidth(normalisedColumns.center);
+
+    return normalisedColumns;
+  }),
+
+  message: Ember.computed('_dataProcessor.message', '_columns.length', '_dataProcessor.processedRows.length', function () {
+    var message = this.get("_dataProcessor.message");
+    if(message) {
+      return message;
+    }
+    else if(!this.get('_columns.length')) {
+      return "No columns available!";
+    }
+    else if(!this.get("_dataProcessor.processedRows.length")) {
+      let identifiers = Ember.String.pluralize(this.get('_definition.recordType') || "record");
+      return `No ${identifiers} available!`;
+    }
+  }),
+
+  highlightRow: function (index) {
+    var element = Ember.$(this.get("element")),
+        sheet = element.find("style")[0].sheet,
+        elementID = element.attr("id"),
+        color = this.get("highlightRowOnMouse.color") || DEFAULT_ROW_HIGHLIGHT_COLOR;
+
+    try {
+      sheet.deleteRule(0);
+    }catch(e){}
+
+    if(index >= 0) {
+      sheet.insertRule(`#${elementID} .table-cell:nth-child(${index}){ background-color: ${color}; }`, 0);
+    }
+  },
+
+  didInsertElement: function () {
+    Ember.run.scheduleOnce('afterRender', this, function() {
+      this.highlightRowOnMouseObserver();
+      this.scrollChangeActionObserver();
+    });
+  },
+
+  highlightRowOnMouseObserver: Ember.observer("highlightRowOnMouse", function () {
+    var highlightRowOnMouse = this.get("highlightRowOnMouse"),
+        element = this.get("element");
+
+    if(element) {
+      element = Ember.$(element).find(".table-mid");
+
+      if(highlightRowOnMouse) {
+        element.on('mouseover', '.table-cell', this, HANDLERS.mouseOver);
+        element.on('mouseleave', this, HANDLERS.mouseLeave);
+      }
+      else {
+        element.off('mouseover', '.table-cell', HANDLERS.mouseOver);
+        element.off('mouseleave', HANDLERS.mouseLeave);
+      }
+    }
+  }),
+
+  scrollValuesObserver: Ember.observer("scrollValues.left", "scrollValues.width", "scrollValues.viewPortWidth", function () {
+    var scrollValues = this.get("scrollValues");
+
+    this.sendAction("scrollChangeAction", scrollValues);
+
+
+    this.set("showLeftScrollShadow", scrollValues.left > 1);
+    this.set("showRightScrollShadow", scrollValues.left < (scrollValues.width - scrollValues.viewPortWidth));
+  }),
+
+  scrollChangeActionObserver: Ember.observer("scrollChangeAction", "message", "showScrollShadow", function () {
+    Ember.run.scheduleOnce('afterRender', this, function() {
+      var addScrollListener = this.get("scrollChangeAction") || this.get("showScrollShadow"),
+          element = this.$().find(".table-body"),
+          scrollValues = this.get("scrollValues");
+
+      if(addScrollListener && element) {
+        element = element.get(0);
+
+        clearInterval(this.get("_widthTrackerTimer"));
+
+        if(element) {
+          if(addScrollListener) {
+            Ember.$(element).on('scroll', this, HANDLERS.onScroll);
+
+            this.set("_widthTrackerTimer", setInterval(function () {
+              scrollValues.setProperties({
+                width: element.scrollWidth,
+                viewPortWidth: element.offsetWidth
+              });
+            }, 1000));
+          }
+          else {
+            element.off('scroll', HANDLERS.onScroll);
+          }
+        }
+      }
+    });
+  }),
+
+  willDestroyElement: function () {
+    this._super();
+    clearInterval(this.get("_widthTrackerTimer"));
+    Ember.$(this.$().find(".table-body")).off();
+    Ember.$(this.$().find(".table-mid")).off();
+    Ember.$(this.$()).off();
+  },
+
+  actions: {
+    search: function (searchText, actualSearchType) {
+      this.set('_definition.searchText', searchText);
+      this.set('_definition._actualSearchType', actualSearchType);
+      this.sendAction("searchAction", searchText);
+    },
+    sort: function (sortColumnId, sortOrder) {
+      this.get("_definition").setProperties({
+        sortColumnId,
+        sortOrder
+      });
+      this.sendAction("sortAction", sortColumnId, sortOrder);
+    },
+    rowChanged: function (rowCount) {
+      this.set('_definition.rowCount', rowCount);
+      this.sendAction("rowAction", rowCount);
+    },
+    pageChanged: function (pageNum) {
+      this.set('_definition.pageNum', pageNum);
+      this.sendAction("pageAction", pageNum);
+    },
+    columnWidthChanged: function (width, columnDefinition, index) {
+      this.sendAction("columnWidthChangeAction", width, columnDefinition, index);
+    }
+  }
+});
diff --git a/tez-ui/src/main/webapp/app/controllers/app/configs.js b/tez-ui/src/main/webapp/app/controllers/app/configs.js
index 838abc1..e8f13fc 100644
--- a/tez-ui/src/main/webapp/app/controllers/app/configs.js
+++ b/tez-ui/src/main/webapp/app/controllers/app/configs.js
@@ -20,7 +20,7 @@
 import Ember from 'ember';
 
 import TableController from '../table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 
 var MoreObject = more.Object;
 
diff --git a/tez-ui/src/main/webapp/app/controllers/app/dags.js b/tez-ui/src/main/webapp/app/controllers/app/dags.js
index bb4502a..1febc66 100644
--- a/tez-ui/src/main/webapp/app/controllers/app/dags.js
+++ b/tez-ui/src/main/webapp/app/controllers/app/dags.js
@@ -17,7 +17,7 @@
  */
 
 import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 
 export default MultiTableController.extend({
   breadcrumbs: [{
diff --git a/tez-ui/src/main/webapp/app/controllers/counters-table.js b/tez-ui/src/main/webapp/app/controllers/counters-table.js
index 42361b4..37bae66 100644
--- a/tez-ui/src/main/webapp/app/controllers/counters-table.js
+++ b/tez-ui/src/main/webapp/app/controllers/counters-table.js
@@ -20,7 +20,7 @@
 import Ember from 'ember';
 
 import TableController from './table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../utils/column-definition';
 
 var MoreObject = more.Object;
 
diff --git a/tez-ui/src/main/webapp/app/controllers/dag/attempts.js b/tez-ui/src/main/webapp/app/controllers/dag/attempts.js
index 47e95d9..4616638 100644
--- a/tez-ui/src/main/webapp/app/controllers/dag/attempts.js
+++ b/tez-ui/src/main/webapp/app/controllers/dag/attempts.js
@@ -17,7 +17,7 @@
  */
 
 import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 
 export default MultiTableController.extend({
   breadcrumbs: [{
diff --git a/tez-ui/src/main/webapp/app/controllers/dag/graphical.js b/tez-ui/src/main/webapp/app/controllers/dag/graphical.js
index c55ab8b..cc4130f 100644
--- a/tez-ui/src/main/webapp/app/controllers/dag/graphical.js
+++ b/tez-ui/src/main/webapp/app/controllers/dag/graphical.js
@@ -19,7 +19,7 @@
 import Ember from 'ember';
 
 import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 
 export default MultiTableController.extend({
 
diff --git a/tez-ui/src/main/webapp/app/controllers/dag/index/index.js b/tez-ui/src/main/webapp/app/controllers/dag/index/index.js
index c9adde4..6196b9a 100644
--- a/tez-ui/src/main/webapp/app/controllers/dag/index/index.js
+++ b/tez-ui/src/main/webapp/app/controllers/dag/index/index.js
@@ -19,7 +19,7 @@
 import Ember from 'ember';
 
 import MultiTableController from '../../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../../utils/column-definition';
 
 export default MultiTableController.extend({
   columns: ColumnDefinition.make([{
diff --git a/tez-ui/src/main/webapp/app/controllers/dag/swimlane.js b/tez-ui/src/main/webapp/app/controllers/dag/swimlane.js
index bbac40b..1fe2988 100644
--- a/tez-ui/src/main/webapp/app/controllers/dag/swimlane.js
+++ b/tez-ui/src/main/webapp/app/controllers/dag/swimlane.js
@@ -19,7 +19,7 @@
 import Ember from 'ember';
 
 import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 import VertexProcess from '../../utils/vertex-process';
 
 import fullscreen from 'em-tgraph/utils/fullscreen';
diff --git a/tez-ui/src/main/webapp/app/controllers/dag/tasks.js b/tez-ui/src/main/webapp/app/controllers/dag/tasks.js
index 92f674a..834abf9 100644
--- a/tez-ui/src/main/webapp/app/controllers/dag/tasks.js
+++ b/tez-ui/src/main/webapp/app/controllers/dag/tasks.js
@@ -17,7 +17,7 @@
  */
 
 import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 
 export default MultiTableController.extend({
   breadcrumbs: [{
diff --git a/tez-ui/src/main/webapp/app/controllers/dag/vertices.js b/tez-ui/src/main/webapp/app/controllers/dag/vertices.js
index 313a5a9..34295e5 100644
--- a/tez-ui/src/main/webapp/app/controllers/dag/vertices.js
+++ b/tez-ui/src/main/webapp/app/controllers/dag/vertices.js
@@ -17,7 +17,7 @@
  */
 
 import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 
 export default MultiTableController.extend({
   breadcrumbs: [{
diff --git a/tez-ui/src/main/webapp/app/controllers/home/index.js b/tez-ui/src/main/webapp/app/controllers/home/index.js
index 754e5e6..53c4e6d 100644
--- a/tez-ui/src/main/webapp/app/controllers/home/index.js
+++ b/tez-ui/src/main/webapp/app/controllers/home/index.js
@@ -19,8 +19,8 @@
 import Ember from 'ember';
 
 import TableController from '../table';
-import ColumnDefinition from 'em-table/utils/column-definition';
-import TableDefinition from 'em-table/utils/table-definition';
+import ColumnDefinition from '../../utils/column-definition';
+import TableDefinition from '../../utils/table-definition';
 
 export default TableController.extend({
 
diff --git a/tez-ui/src/main/webapp/app/controllers/home/queries.js b/tez-ui/src/main/webapp/app/controllers/home/queries.js
index ba7e6e3..b5e483d 100644
--- a/tez-ui/src/main/webapp/app/controllers/home/queries.js
+++ b/tez-ui/src/main/webapp/app/controllers/home/queries.js
@@ -19,8 +19,8 @@
 import Ember from 'ember';
 
 import TableController from '../table';
-import ColumnDefinition from 'em-table/utils/column-definition';
-import TableDefinition from 'em-table/utils/table-definition';
+import ColumnDefinition from '../../utils/column-definition';
+import TableDefinition from '../../utils/table-definition';
 
 export default TableController.extend({
 
diff --git a/tez-ui/src/main/webapp/app/controllers/query/configs.js b/tez-ui/src/main/webapp/app/controllers/query/configs.js
index 8dcc91c..d828088 100644
--- a/tez-ui/src/main/webapp/app/controllers/query/configs.js
+++ b/tez-ui/src/main/webapp/app/controllers/query/configs.js
@@ -20,7 +20,7 @@
 import Ember from 'ember';
 
 import TableController from '../table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 
 var MoreObject = more.Object;
 
diff --git a/tez-ui/src/main/webapp/app/controllers/query/timeline.js b/tez-ui/src/main/webapp/app/controllers/query/timeline.js
index b52fc26..a7cd85e 100644
--- a/tez-ui/src/main/webapp/app/controllers/query/timeline.js
+++ b/tez-ui/src/main/webapp/app/controllers/query/timeline.js
@@ -19,7 +19,7 @@
 
 import Ember from 'ember';
 import TableController from '../table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 
 var MoreObject = more.Object;
 
diff --git a/tez-ui/src/main/webapp/app/controllers/table.js b/tez-ui/src/main/webapp/app/controllers/table.js
index 57adf00..01aec40 100644
--- a/tez-ui/src/main/webapp/app/controllers/table.js
+++ b/tez-ui/src/main/webapp/app/controllers/table.js
@@ -20,7 +20,7 @@
 import Ember from 'ember';
 
 import AbstractController from './abstract';
-import TableDefinition from 'em-table/utils/table-definition';
+import TableDefinition from '../utils/table-definition';
 import isIOCounter from '../utils/misc';
 
 import CounterColumnDefinition from '../utils/counter-column-definition';
diff --git a/tez-ui/src/main/webapp/app/controllers/task/attempts.js b/tez-ui/src/main/webapp/app/controllers/task/attempts.js
index a6acaec..04eb22a 100644
--- a/tez-ui/src/main/webapp/app/controllers/task/attempts.js
+++ b/tez-ui/src/main/webapp/app/controllers/task/attempts.js
@@ -17,7 +17,7 @@
  */
 
 import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 
 import AutoCounterColumn from '../../mixins/auto-counter-column';
 
diff --git a/tez-ui/src/main/webapp/app/controllers/vertex/attempts.js b/tez-ui/src/main/webapp/app/controllers/vertex/attempts.js
index b07be92..107ecdb 100644
--- a/tez-ui/src/main/webapp/app/controllers/vertex/attempts.js
+++ b/tez-ui/src/main/webapp/app/controllers/vertex/attempts.js
@@ -17,7 +17,7 @@
  */
 
 import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 
 import AutoCounterColumn from '../../mixins/auto-counter-column';
 
diff --git a/tez-ui/src/main/webapp/app/controllers/vertex/configs.js b/tez-ui/src/main/webapp/app/controllers/vertex/configs.js
index 1cf4a3d..2e1d94e 100644
--- a/tez-ui/src/main/webapp/app/controllers/vertex/configs.js
+++ b/tez-ui/src/main/webapp/app/controllers/vertex/configs.js
@@ -20,7 +20,7 @@
 import Ember from 'ember';
 
 import TableController from '../table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 
 var MoreObject = more.Object;
 
diff --git a/tez-ui/src/main/webapp/app/controllers/vertex/tasks.js b/tez-ui/src/main/webapp/app/controllers/vertex/tasks.js
index 560c8ba..dac000e 100644
--- a/tez-ui/src/main/webapp/app/controllers/vertex/tasks.js
+++ b/tez-ui/src/main/webapp/app/controllers/vertex/tasks.js
@@ -17,7 +17,7 @@
  */
 
 import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
 
 import AutoCounterColumn from '../../mixins/auto-counter-column';
 
diff --git a/tez-ui/src/main/webapp/app/styles/app.less b/tez-ui/src/main/webapp/app/styles/app.less
index f8a66e3..5caf44a 100644
--- a/tez-ui/src/main/webapp/app/styles/app.less
+++ b/tez-ui/src/main/webapp/app/styles/app.less
@@ -41,11 +41,18 @@
 @import "em-swimlane";
 @import "em-tooltip";
 @import "em-swimlane-vertex-name";
+@import "em-table.less";
+@import "em-table-facet-panel.less";
 @import "em-table-status-cell";
 @import "query-timeline";
 @import "home-table-controls";
 @import "em-progress";
 @import "em-breadcrumbs";
+@import "pagination-ui.less";
+@import "progress-cell.less";
+@import "search-ui.less";
+@import "status-cell.less";
+@import "variables.less";
 
 // Modals
 @import "column-selector";
diff --git a/tez-ui/src/main/webapp/app/styles/em-table-facet-panel.less b/tez-ui/src/main/webapp/app/styles/em-table-facet-panel.less
new file mode 100644
index 0000000..28be8f0
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/em-table-facet-panel.less
@@ -0,0 +1,218 @@
+/**
+ * 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.
+ */
+
+.em-table-facet-panel {
+  width: 160px;
+
+  margin: 5px 5px 0 0;
+
+  border: 1px solid @border-color;
+  border-radius: @border-radius;
+
+  padding: 5px 10px 10px 10px;
+
+  background-color: @table-bg;
+
+  overflow: hidden;
+
+  .field-filter-box {
+    width: 100%;
+  }
+
+  &.hide-filter {
+    .field-filter-box {
+      display: none;
+    }
+  }
+
+  .filter-message {
+    color: #999;
+  }
+
+  h4 {
+    text-align: center;
+    margin-top: 5px;
+    margin-bottom: 0px;
+  }
+
+  ul {
+    list-style-type: none;
+  }
+
+  li {
+    margin: 2px 0;
+  }
+
+  ul.field-list {
+    padding-top: 5px;
+    padding-left: 0px;
+
+    .em-table-facet-panel-values {
+
+      position: relative;
+
+      .field-name {
+        .no-select;
+
+        padding-right: 20px;
+
+        cursor: pointer;
+        display: flex;
+
+        &::before {
+          content: "\25bc";
+          font-size: .7em;
+          color: @text-light;
+          margin-top: 5px;
+        }
+
+        .field-title {
+          overflow-x: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          margin: 0 3px;
+        }
+
+        .field-count {
+          color: @text-light;
+          white-space: nowrap;
+        }
+
+        .all-button {
+          margin-left: 5px;
+          position: absolute;
+          right: 0px;
+        }
+      }
+
+      &.hide-select-all {
+        .field-name {
+          padding-right: 0px;
+
+          .all-button {
+            display: none;
+          }
+        }
+      }
+
+      .value-list {
+        overflow: hidden;
+
+        padding-left: 10px;
+
+        .filter-box {
+          width: 100%;
+        }
+
+        li {
+          display: flex;
+
+          .checkbox-container {
+            order: 0;
+            flex: 0 1 auto;
+            align-self: auto;
+
+            padding-right: 5px;
+          }
+
+          .facet-value {
+            order: 0;
+            flex: 1 1 auto;
+            align-self: auto;
+            padding-right: 2px;
+
+            overflow-x: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+          }
+
+          .only-button {
+            order: 0;
+            flex: 0 1 auto;
+            align-self: auto;
+
+            cursor: pointer;
+
+            display: none;
+            padding: 0 5px;
+          }
+
+          .facet-count {
+            order: 0;
+            flex: 0 1 auto;
+            align-self: auto;
+
+            &:hover{
+              text-decoration: none;
+            }
+          }
+
+          &:hover {
+            .only-button {
+              display: inline;
+            }
+          }
+        }
+
+        .pagination-controls {
+          padding-top: 5px;
+
+          position: relative;
+
+          .arrows {
+            position: absolute;
+            top: 5px;
+            right: 0px;
+          }
+
+          span {
+            user-select: none;
+            color: lightgrey;
+
+            &.active {
+              cursor: pointer;
+              color: #3B99FC;
+            }
+          }
+        }
+
+      }
+
+      &.hide-values {
+        .value-list {
+          display: none;
+        }
+
+        .field-name::before {
+          transform: rotate(-90deg) translate(2px, 2px);
+        }
+
+        .field-name .all-button {
+          display: none;
+        }
+      }
+
+      &.hide-filter {
+        .filter-box {
+          display: none;
+        }
+      }
+
+    }
+  }
+}
diff --git a/tez-ui/src/main/webapp/app/styles/em-table.less b/tez-ui/src/main/webapp/app/styles/em-table.less
new file mode 100644
index 0000000..dbea8a5
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/em-table.less
@@ -0,0 +1,346 @@
+/**
+ * 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.
+ */
+
+
+// Imports
+@import (once) "bower_components/bootstrap/less/bootstrap";
+
+@import (once) "bower_components/snippet-ss/less/use";
+@import (once) "bower_components/snippet-ss/less/background";
+@import (once) "bower_components/snippet-ss/less/effects";
+
+@import "./variables";
+@import "./shared";
+
+@import "./search-ui";
+@import "./pagination-ui";
+
+@import "./progress-cell";
+@import "./status-cell";
+
+@import "./em-table-facet-panel";
+
+.em-table {
+  font-size: @font-size;
+  color: @text-color;
+
+  margin: 10px 0px;
+  overflow: hidden;
+
+  .table-header {
+    .clear-fix;
+  }
+
+  .table-mid {
+    display: flex;
+    flex-direction: row;
+    flex-wrap: nowrap;
+    justify-content: flex-start;
+    align-content: stretch;
+    align-items: flex-start;
+
+    .table-panel-left, table-panel-right, .table-body-left, .table-body-right {
+      order: 0;
+      flex: 0 1 auto;
+      align-self: auto;
+    }
+
+    .table-body, .table-message {
+      order: 0;
+      flex: 1 1 auto;
+      align-self: auto;
+      border: 1px solid @border-color;
+
+      margin-top: 5px;
+    }
+
+    &>div:nth-child(2) {
+      border-top-left-radius: @border-radius;
+      border-bottom-left-radius: @border-radius;
+      border-left: 1px solid @border-color;
+    }
+
+    &>div:nth-last-child(2) {
+      border-top-right-radius: @border-radius;
+      border-bottom-right-radius: @border-radius;
+      border-right: 1px solid @border-color;
+    }
+  }
+
+  .table-footer {
+    .clear-fix;
+  }
+
+  .table-body-left, .table-body-right, .table-body {
+    border-top: 1px solid @border-color;
+    border-bottom: 1px solid @border-color;
+    background-color: @table-bg;
+  }
+
+  .table-message {
+    border-radius: @border-radius;
+    background-color: @table-bg;
+
+    text-align: center;
+    padding: 10px;
+  }
+
+  .table-body-left, .table-body-right, .table-body {
+    margin: 5px 0px;
+  }
+
+  .table-body-left, .table-body-right {
+    white-space: nowrap;
+    font-size: 0; // If not set, each column will have a space in between
+  }
+
+  .table-body{
+    .force-scrollbar;
+
+    .table-scroll-body {
+      //Adding this here will keep the column, and table background same white
+      //making the UI look better when scroll bar is shown
+      .dotted-bg;
+
+      white-space: nowrap;
+      font-size: 0; // If not set, each column will have a space in between
+    }
+  }
+
+  &.show-scroll-shadow {
+    .left-scroll-shadow, .right-scroll-shadow {
+      order: 0;
+      flex: 0 1 auto;
+      align-self: stretch;
+      position: relative;
+
+      opacity: 0;
+      transition: opacity 0.3s;
+
+      width: 0px;
+      z-index: 99;
+
+      pointer-events: none;
+
+      .shadow-container {
+        position: absolute;
+        overflow: hidden;
+
+        top: 0px;
+        bottom: 0px;
+        width: 50px;
+
+        &:before {
+          content: "";
+          position: absolute;
+
+          top: 10px;
+          bottom: 15px;
+          width: 50px;
+        }
+      }
+    }
+    .left-scroll-shadow {
+      .shadow-container {
+        &:before {
+          left: -50px;
+          box-shadow: 12px 0 40px -4px rgba(0, 0, 0, 0.2);
+        }
+      }
+    }
+    .right-scroll-shadow {
+      .shadow-container {
+        right: 0px;
+        &:before {
+          left: 50px;
+          box-shadow: -12px 0 40px -4px rgba(0, 0, 0, 0.2);
+        }
+      }
+    }
+
+    &.show-left-scroll-shadow {
+      .left-scroll-shadow {
+        opacity: 1;
+      }
+    }
+    &.show-right-scroll-shadow {
+      .right-scroll-shadow {
+        opacity: 1;
+      }
+    }
+  }
+
+  .table-column {
+    .use-border-padding-in-width-height;
+
+    background-color: @table-bg;
+
+    vertical-align: top;
+    overflow: hidden;
+    display: inline-block;
+    min-width: 150px;
+
+    &.inner {
+      border-left: 1px solid @border-color;
+    }
+
+    // Just the shaded header
+    .table-header-cell {
+      background-color: @bg-grey;
+      border-bottom: 1px solid @border-color;
+
+      &.is-sorting {
+        .animated-stripes;
+      }
+    }
+
+    .header-body, .table-cell {
+      font-size: @font-size;
+      white-space: nowrap;
+
+      text-overflow: ellipsis;
+      overflow: hidden;
+
+      height: 2.1em;
+      padding: 5px;
+
+      .ember-view {
+        text-overflow: ellipsis;
+        overflow: hidden;
+      }
+    }
+
+    .header-body {
+      font-weight: bold;
+
+      padding-right: 1.1em; // To compensate space occupied by sort/resize buttons
+      position: relative; // So that buttons can be positioned
+
+      .sort-bar {
+        cursor: pointer;
+        position: absolute;
+
+        left: 0;
+        right: .5em;
+        top: 0;
+        bottom: 0;
+      }
+
+      .sort-icon {
+        cursor: pointer;
+        position: absolute;
+        right: .5em;
+        top: .2em;
+
+        &:before, &:after {
+          font-size: .7em;
+          opacity: .5;
+          position: absolute;
+        }
+
+        &:before {
+          content: "\25B2";
+
+          top: 0em;
+          right: 0px;
+        }
+
+        &:after {
+          content: "\25BC";
+
+          top: 1em;
+          right: 0px;
+        }
+
+        &.asc{
+          &:before {
+            opacity: 1;
+          }
+          &:after {
+            opacity: .5;
+          }
+        }
+
+        &.desc {
+          &:before {
+            opacity: .5;
+          }
+          &:after {
+            opacity: 1;
+          }
+        }
+      }
+
+      .resize-column:after {
+        content: "\22EE";
+        cursor: col-resize;
+        opacity: .3;
+
+        position: absolute;
+        right: 2px;
+        top: 6px;
+      }
+    }
+
+    .table-cell {
+      position: relative;
+
+      .comment-indicator {
+        position: absolute;
+
+        color: white;
+        font-size: 10px;
+        padding-left: 4px;
+
+        top: -4px;
+        right: -4px;
+
+        width: 10px;
+        height: 10px;
+        background-color: orange;
+        border-radius: 10px;
+
+        opacity: 0.6;
+
+        &:hover {
+          top: -2px;
+          right: -2px;
+        }
+      }
+
+      &.bg-transition {
+        -webkit-transition: box-shadow 500ms ease-out 500ms;
+        -moz-transition: box-shadow 500ms ease-out 500ms;
+        -o-transition: box-shadow 500ms ease-out 500ms;
+        transition: box-shadow 500ms ease-out 500ms;
+      }
+
+      &.highlight {
+        box-shadow: 0 0 60px lighten(@brand-primary, 10%) inset;
+      }
+      &.is-waiting {
+        .animated-stripes;
+      }
+      &.inner {
+        border-top: 1px dotted @border-color;
+        margin-top: -1px;
+      }
+    }
+  }
+
+}
diff --git a/tez-ui/src/main/webapp/app/styles/pagination-ui.less b/tez-ui/src/main/webapp/app/styles/pagination-ui.less
new file mode 100644
index 0000000..df3d7c0
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/pagination-ui.less
@@ -0,0 +1,88 @@
+/**
+ * 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.
+ */
+
+.pagination-ui {
+  .inline-block;
+  .align-top;
+
+  float: right;
+
+  .page-list {
+    .inline-block;
+    .align-top;
+
+    overflow: hidden;
+
+    border: 1px solid @border-color;
+    border-radius: 5px;
+    background-color: @table-bg;
+
+    padding: 0px;
+
+    font-size: 0px;
+
+    li {
+      .inline-block;
+
+      padding: 6px 12px;
+      height: 32px;
+
+      font-size: @font-size;
+      color: @text-light;
+
+      border-left: 1px solid @border-color;
+
+      pointer-events: none;
+
+      &.clickable {
+        pointer-events: auto;
+        color: @text-color;
+
+        &:hover {
+          background-color: @bg-grey;
+          cursor: pointer;
+        }
+      }
+    }
+
+    .total-page-count {
+      font-size: .8em;
+    }
+
+    :first-child {
+      border-left: none;
+    }
+  }
+
+  .row-select {
+    margin-left: 5px;
+
+    display: inline-block;
+    text-align: center;
+
+    select {
+      cursor: pointer;
+    }
+  }
+}
+
+.table-footer {
+  .pagination-ui {
+    position: static;
+  }
+}
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/styles/progress-cell.less
similarity index 70%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/app/styles/progress-cell.less
index 7751719..9030216 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/styles/progress-cell.less
@@ -16,21 +16,11 @@
  * limitations under the License.
  */
 
-import Ember from 'ember';
 
-export default Ember.Component.extend({
-
-  content: null,
-
-  classNames: ["em-table-status-cell"],
-
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
-
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
+.table-cell {
+  .em-progress-container {
+    .progress {
+      margin: -1px 0 0 0;
     }
-    return status;
-  }),
-});
+  }
+}
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/styles/search-ui.less
similarity index 69%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/app/styles/search-ui.less
index 7751719..b850031 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/styles/search-ui.less
@@ -16,21 +16,22 @@
  * limitations under the License.
  */
 
-import Ember from 'ember';
+.search-ui {
+  .inline-block;
+  .align-top;
 
-export default Ember.Component.extend({
+  max-width: 500px;
 
-  content: null,
+  margin-bottom: 2px;
 
-  classNames: ["em-table-status-cell"],
+  .type-select {
+    width: 5em;
+    margin-right: -3px !important;
+  }
 
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
-
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
-});
+  .search-syntax {
+    .label;
+    .label-default;
+    .no-select;
+  }
+}
diff --git a/tez-ui/src/main/webapp/app/styles/shared.less b/tez-ui/src/main/webapp/app/styles/shared.less
index b34cfa6..b448c30 100644
--- a/tez-ui/src/main/webapp/app/styles/shared.less
+++ b/tez-ui/src/main/webapp/app/styles/shared.less
@@ -33,6 +33,99 @@ b {
   padding-left: 10px;
 }
 
+.no-select {
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+
+  cursor: default;
+}
+
+.no-display {
+  display: none !important;
+}
+
+.no-visible {
+  visibility: hidden !important;
+}
+
+.no-margin {
+  margin: 0px !important;
+}
+
+.no-pointer {
+  pointer-events: none;
+}
+
+.inactive {
+  .no-pointer;
+  opacity: 0.4;
+}
+
+.no-wrap {
+  white-space: nowrap;
+}
+
+.no-border {
+  border: none !important;
+}
+
+.align-top {
+  vertical-align: top;
+}
+
+.align-super {
+  vertical-align: super;
+}
+
+.inline-block {
+  display: inline-block;
+}
+
+.dotted-background {
+  background:
+  radial-gradient(#EEE 15%, transparent 17%) 0 0,
+  radial-gradient(#EEE 15%, transparent 17%) 5px -5px,
+  radial-gradient(#EEE 15%, transparent 17%) 5px 5px;
+  background-color: #DDD;
+  background-size: 10px 10px;
+}
+
+.absolute {
+  position: absolute;
+}
+
+.use-gpu {
+  -webkit-transform: translateZ(0);
+     -moz-transform: translateZ(0);
+      -ms-transform: translateZ(0);
+       -o-transform: translateZ(0);
+          transform: translateZ(0);
+}
+
+.force-scrollbar {
+  overflow: auto;
+
+  &::-webkit-scrollbar {
+    -webkit-appearance: none;
+  }
+  &::-webkit-scrollbar:vertical {
+    width: 11px;
+  }
+  &::-webkit-scrollbar:horizontal {
+    height: 11px;
+  }
+  &::-webkit-scrollbar-thumb {
+    border-radius: 8px;
+    border: 2px solid #EEE;
+    background-color: #BBB;
+  }
+  &::-webkit-scrollbar-track {
+    background-color: #EEE;
+    border-radius: 8px;
+  }
+}
+
 .align-checknradio {
   input[type=checkbox], input[type=radio] {
     vertical-align: middle;
@@ -41,6 +134,28 @@ b {
   }
 }
 
+.left-divider{
+  padding-left: 5px;
+  border-left: 1px solid lightgrey;
+  margin-left: 5px;
+}
+
+.clear-fix {
+  &:after {
+    content: " ";
+    visibility: hidden;
+    display: block;
+    height: 0;
+    clear: both;
+  }
+}
+
+.animated-stripes {
+  .diagonal-stripes-background(#FFF, #EEE);
+  .animate;
+  .white-inner-glow;
+}
+
 .diagnostics {
   padding: 10px;
   white-space: pre-line;
diff --git a/tez-ui/src/main/webapp/app/styles/status-cell.less b/tez-ui/src/main/webapp/app/styles/status-cell.less
new file mode 100644
index 0000000..57f39cf
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/status-cell.less
@@ -0,0 +1,86 @@
+/**
+ * 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.
+ */
+
+.em-table-status-cell {
+  overflow: visible !important;
+
+  .status {
+    .label;
+    .label-default;
+  }
+  .status-icon {
+    // Must be defined by the top-level project
+  }
+
+  .status-new, .status-inited, .status-started {
+    .status-icon {
+      // Must be defined by the top-level project
+    }
+  }
+
+  .status-initializing, .status-scheduled {
+    .label-primary;
+    .status-icon {
+      // Must be defined by the top-level project
+    }
+  }
+
+  .status-running, .status-in-progress {
+    .diagonal-stripes-bg;
+    .animate;
+    .label-info;
+    .status-icon {
+      // Must be defined by the top-level project
+    }
+  }
+
+  .status-committing {
+    .label-info;
+    .status-icon {
+      // Must be defined by the top-level project
+    }
+  }
+
+  .status-finished, .status-succeeded {
+    .label-success;
+    .status-icon {
+      // Must be defined by the top-level project
+    }
+  }
+
+  .status-terminating {
+    .label-warning;
+    .status-icon {
+      // Must be defined by the top-level project
+    }
+  }
+
+  .status-failed {
+    .label-warning;
+    .status-icon {
+      // Must be defined by the top-level project
+    }
+  }
+
+  .status-killed, .status-error {
+    .label-danger;
+    .status-icon {
+      // Must be defined by the top-level project
+    }
+  }
+}
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/styles/variables.less
similarity index 69%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/app/styles/variables.less
index 7751719..e6dbeca 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/styles/variables.less
@@ -16,21 +16,13 @@
  * limitations under the License.
  */
 
-import Ember from 'ember';
+@text-light: #BBBBBB;
+@text-color: #222222;
 
-export default Ember.Component.extend({
+@bg-grey: #f0f0f0;
+@table-bg: white;
 
-  content: null,
+@border-color: #dcdcdc;
+@border-radius: 5px;
 
-  classNames: ["em-table-status-cell"],
-
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
-
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
-});
+@font-size: 14px;
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-cell.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-cell.hbs
new file mode 100644
index 0000000..777be93
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-cell.hbs
@@ -0,0 +1,41 @@
+{{!
+ * 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.
+}}
+
+{{#if isWaiting}}
+  Waiting...
+{{else}}
+  {{#if columnDefinition.cellComponentName}}
+    {{component columnDefinition.cellComponentName content=_cellContent definition=columnDefinition.cellDefinition}}
+  {{else}}
+    {{#unless columnDefinition.cellDefinition}}
+      {{txt _cellContent}}
+    {{else}}
+      {{txt _cellContent
+        type=columnDefinition.cellDefinition.type
+        format=columnDefinition.cellDefinition.format
+        timeZone=columnDefinition.cellDefinition.timeZone
+        valueFormat=columnDefinition.cellDefinition.valueFormat
+        valueTimeZone=columnDefinition.cellDefinition.valueTimeZone
+        valueUnit=columnDefinition.cellDefinition.valueUnit
+      }}
+    {{/unless}}
+  {{/if}}
+  {{#if _comment}}
+    <div title="{{_comment}}" class="comment-indicator"></div>
+  {{/if}}
+{{/if}}
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/templates/components/em-table-column.hbs
similarity index 68%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/app/templates/components/em-table-column.hbs
index 7751719..a3908db 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-column.hbs
@@ -1,4 +1,4 @@
-/**
+{{!
  * 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
@@ -14,23 +14,9 @@
  * 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 Ember from 'ember';
-
-export default Ember.Component.extend({
-
-  content: null,
-
-  classNames: ["em-table-status-cell"],
-
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
-
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
-});
+{{em-table-header-cell title=definition.headerTitle definition=definition tableDefinition=tableDefinition dataProcessor=dataProcessor}}
+{{#each rows as |row rowIndex|}}
+  {{em-table-cell columnDefinition=definition row=row index=rowIndex}}
+{{/each}}
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-facet-panel-values.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-facet-panel-values.hbs
new file mode 100644
index 0000000..579e9eb
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-facet-panel-values.hbs
@@ -0,0 +1,50 @@
+{{!
+ * 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.
+}}
+
+<div class="field-name" {{action "toggleValueDisplay"}} title={{data.column.headerTitle}}>
+  <div class="field-title">{{data.column.headerTitle}}</div>
+  <div class="field-count">({{data.facets.length}})</div>
+  <a class="all-button" title={{allButtonTitle}} {{action "selectAll" bubbles=false}}>All</a>
+</div>
+
+<ul class="value-list">
+  {{input type="text" class="filter-box" value=filterText placeholder="Filter"}}
+
+  {{#if showPagination}}
+    <div class="pagination-controls">
+      {{currentPage}}/{{totalPages}}
+      <div class="arrows">
+        <span {{action "changePage" -1}} class="{{if showPrevious 'active'}}">&#9668;</span>
+        <span {{action "changePage" 1}} class="{{if showNext 'active'}}">&#9658;</span>
+      </div>
+    </div>
+  {{/if}}
+
+  {{#each paginatedFacets key="value" as |facet index|}}
+    <li title={{facet.displayText}}>
+      <div class="checkbox-container">
+        <input type="checkbox" checked={{facet.checked}} onclick={{action "clickedCheckbox" facet}} />
+      </div>
+      <div class="facet-value">{{facet.displayText}}</div>
+      <a class="only-button" {{action "clickedOnly" facet}}>only</a>
+      <a class="facet-count">{{facet.count}}</a>
+    </li>
+  {{else}}
+    <span class="filter-message">No fields!</span>
+  {{/each}}
+</ul>
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/templates/components/em-table-facet-panel.hbs
similarity index 53%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/app/templates/components/em-table-facet-panel.hbs
index 7751719..d513786 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-facet-panel.hbs
@@ -1,4 +1,4 @@
-/**
+{{!
  * 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
@@ -14,23 +14,20 @@
  * 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 Ember from 'ember';
+{{#if dataProcessor.facetedFields.length}}
+  <ul class="field-list">
+    {{input type="text" class="field-filter-box" value=filterText placeholder="Filter"}}
+    {{#each dataProcessor.facetedFields key="column.id" as |field fieldIndex|}}
+      <li>{{component field.column.facetType.componentName data=field tableDefinition=tableDefinition dataProcessor=dataProcessor tmpFacetConditions=tmpFacetConditions}}</li>
+    {{/each}}
+  </ul>
 
-export default Ember.Component.extend({
-
-  content: null,
-
-  classNames: ["em-table-status-cell"],
-
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
-
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
-});
+  <div class="buttons">
+    <button type="button" class="btn btn-primary" {{action "applyFilters"}}>Apply</button>
+    <button type="button" class="btn btn-default" {{action "clearFilters"}}>Clear</button>
+  </div>
+{{else}}
+  <h4>Not Available!</h4>
+{{/if}}
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/templates/components/em-table-header-cell.hbs
similarity index 58%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/app/templates/components/em-table-header-cell.hbs
index 7751719..c48eb7a 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-header-cell.hbs
@@ -1,4 +1,4 @@
-/**
+{{!
  * 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
@@ -14,23 +14,17 @@
  * 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 Ember from 'ember';
-
-export default Ember.Component.extend({
-
-  content: null,
-
-  classNames: ["em-table-status-cell"],
-
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
-
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
-});
+<div class="header-body">
+  {{title}}
+  {{#if tableDefinition.enableSort}}{{#if definition.enableSort}}
+    <span title="Sort {{sortToggledTitle}}" class="sort-icon {{sortIconCSS}}" {{action 'sort'}}></span>
+    {{#if tableDefinition.headerAsSortButton}}
+      <span class="sort-bar" {{action 'sort'}}></span>
+    {{/if}}
+  {{/if}}{{/if}}
+  {{#if tableDefinition.enableColumnResize}}{{#if definition.enableColumnResize}}
+    <span title="Resize column" class="resize-column" {{action 'startColResize' on="mouseDown"}}></span>
+  {{/if}}{{/if}}
+</div>
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/templates/components/em-table-linked-cell.hbs
similarity index 55%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/app/templates/components/em-table-linked-cell.hbs
index 7751719..8a82631 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-linked-cell.hbs
@@ -1,4 +1,4 @@
-/**
+{{!
  * 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
@@ -14,23 +14,28 @@
  * 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 Ember from 'ember';
-
-export default Ember.Component.extend({
-
-  content: null,
-
-  classNames: ["em-table-status-cell"],
-
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
-
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
-});
+{{#if normalizedLinks.length}}
+  {{#each normalizedLinks as |link|}}
+    {{#if link.routeName}}
+      {{#if link.withModel}}
+        {{#link-to link.routeName link.model target=definition.target}}
+          {{link.text}}
+        {{/link-to}}
+      {{else}}
+        {{#link-to link.routeName target=definition.target}}
+          {{link.text}}
+        {{/link-to}}
+      {{/if}}
+    {{else if link.href}}
+      <a href={{link.href}} target={{definition.target}} download={{link.download}}>
+        {{link.text}}
+      </a>
+    {{else}}
+      {{link.text}}
+    {{/if}}
+  {{/each}}
+{{else}}
+  <span class="txt-message">Not Available!</span>
+{{/if}}
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-pagination-ui.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-pagination-ui.hbs
new file mode 100644
index 0000000..78edcae
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-pagination-ui.hbs
@@ -0,0 +1,45 @@
+{{!
+ * 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.
+}}
+
+<ul class="page-list">
+  {{#if showFirst}}
+    <li title="Go to first page" class="clickable" {{action 'changePage' 1}}>
+      First
+    </li>
+  {{/if}}
+  {{#each _possiblePages as |page|}}
+    <li class="{{if page.isCurrent 'is-current' 'clickable'}}" {{action 'changePage' page.pageNum}}>
+      {{page.pageNum}}
+    </li>
+  {{/each}}
+  {{#if showLast}}
+    <li title="Go to last page, {{dataProcessor.totalPages}} page(s) available in total"  class="clickable" {{action 'changePage' dataProcessor.totalPages}}>
+      Last - {{dataProcessor.totalPages}}
+    </li>
+  {{/if}}
+</ul>
+
+<div class='row-select'>
+  <select title="Select rows to display" class="form-control" onchange={{action "rowSelected" value="target.value"}}>
+    {{#each rowCountOptions as |option|}}
+      <option value={{option.value}} selected={{option.selected}}>
+        {{option.value}} Rows
+      </option>
+    {{/each}}
+  </select>
+</div>
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/templates/components/em-table-progress-cell.hbs
similarity index 68%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/app/templates/components/em-table-progress-cell.hbs
index 7751719..9df8be0 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-progress-cell.hbs
@@ -1,4 +1,4 @@
-/**
+{{!
  * 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
@@ -14,23 +14,16 @@
  * 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 Ember from 'ember';
-
-export default Ember.Component.extend({
-
-  content: null,
-
-  classNames: ["em-table-status-cell"],
-
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
-
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
-});
+{{#if message}}
+  <span class="em-message">{{message}}</span>
+{{else}}
+  {{em-progress
+    value=content
+    valueMin=_definition.valueMin
+    valueMax=_definition.valueMax
+    striped=_definition.striped
+    style=_definition.style
+  }}
+{{/if}}
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-search-ui.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-search-ui.hbs
new file mode 100644
index 0000000..13cb1dd
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-search-ui.hbs
@@ -0,0 +1,52 @@
+{{!
+ * 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.
+}}
+
+<div class="input-group">
+  {{#if (eq tableDefinition.searchType "manual")}}
+    <span class="input-group-btn">
+      <select class="type-select form-control btn btn-default" onchange={{action (mut actualSearchType) value="target.value"}}>
+        {{#each searchTypes as |searchType|}}
+          <option value={{searchType}} selected={{eq searchType actualSearchType}}>
+            {{searchType}}
+          </option>
+        {{/each}}
+      </select>
+    </span>
+  {{/if}}
+
+  {{input
+    type="text"
+    class="form-control"
+    placeholder="Search..."
+    enter="search"
+    value=text
+  }}
+
+  <span class="input-group-btn">
+    <button class="btn btn-default {{if dataProcessor.isSearching 'animated-stripes'}}" type="button" {{action "search"}}>
+      {{#if dataProcessor.isSearching}}
+        Searching...
+      {{else}}
+        {{#if (eq tableDefinition.searchType "auto")}}
+          {{actualSearchType}}
+        {{/if}}
+        Search
+      {{/if}}
+    </button>
+  </span>
+</div>
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table.hbs
new file mode 100644
index 0000000..f7be9d4
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table.hbs
@@ -0,0 +1,106 @@
+{{!
+ * 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.
+}}
+
+{{!--To add CSS rules at runtime!--}}
+<style></style>
+
+{{!--Header--}}
+<div class='table-header'>
+  {{#each headerComponentNames as |componentName|}}
+    {{component componentName tableDefinition=_definition dataProcessor=_dataProcessor}}
+  {{/each}}
+</div>
+
+<div class="table-mid">
+  <div class='table-panel-left'>
+    {{#if leftPanelComponentName}}
+      {{component leftPanelComponentName tableDefinition=_definition dataProcessor=_dataProcessor}}
+    {{/if}}
+  </div>
+
+  {{#if message}}
+    <h4 class="table-message">{{message}}</h4>
+  {{else}}
+    {{!--Body--}}
+    {{#if _columns.left.length}}
+      <div class='table-body-left'>
+        {{#each _columns.left as |column colIndex|}}
+          {{em-table-column
+          rows=_dataProcessor.processedRows
+          definition=column.definition
+          defaultWidth=column.width
+          tableDefinition=_definition
+          dataProcessor=_dataProcessor
+          index=colIndex
+          }}
+        {{/each}}
+      </div>
+    {{/if}}
+
+    <span class='left-scroll-shadow'>
+      <span class='shadow-container'></span>
+    </span>
+    <div class='table-body'>
+      <div class='table-scroll-body'>
+        {{#each _columns.center as |column colIndex|}}
+          {{em-table-column
+          rows=_dataProcessor.processedRows
+          definition=column.definition
+          defaultWidth=column.width
+          tableDefinition=_definition
+          dataProcessor=_dataProcessor
+          index=colIndex
+          }}
+        {{/each}}
+      </div>
+    </div>
+    <span class='right-scroll-shadow'>
+      <span class='shadow-container'></span>
+    </span>
+
+    {{#if _columns.right.length}}
+      <div class='table-body-right'>
+        {{#each _columns.right as |column colIndex|}}
+          {{em-table-column
+          rows=_dataProcessor.processedRows
+          definition=column.definition
+          defaultWidth=column.width
+          tableDefinition=_definition
+          dataProcessor=_dataProcessor
+          index=colIndex
+          }}
+        {{/each}}
+      </div>
+    {{/if}}
+  {{/if}}
+
+  <div class='table-panel-right'>
+    {{#if rightPanelComponentName}}
+      {{component rightPanelComponentName tableDefinition=_definition dataProcessor=_dataProcessor}}
+    {{/if}}
+  </div>
+</div>
+
+{{!--Footer--}}
+{{#if displayFooter}}
+  <div class='table-footer'>
+    {{#each footerComponentNames as |componentName|}}
+      {{component componentName tableDefinition=_definition dataProcessor=_dataProcessor}}
+    {{/each}}
+  </div>
+{{/if}}
diff --git a/tez-ui/src/main/webapp/app/utils/column-definition.js b/tez-ui/src/main/webapp/app/utils/column-definition.js
new file mode 100644
index 0000000..1316866
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/utils/column-definition.js
@@ -0,0 +1,125 @@
+/**
+ * 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 Ember from 'ember';
+
+import facetTypes from './facet-types';
+
+function getContentAtPath(row) {
+  var contentPath = this.get('contentPath');
+
+  if(contentPath) {
+    return Ember.get(row, contentPath);
+  }
+  else {
+    throw new Error("contentPath not set!");
+  }
+}
+
+function returnEmptyString() {
+  return "";
+}
+
+var ColumnDefinition = Ember.Object.extend({
+  id: "",
+  headerTitle: "Not Available!",
+
+  classNames: [],
+
+  cellComponentName: null,
+
+  enableSearch: true,
+  enableSort: true,
+  enableColumnResize: true,
+
+  width: null,
+  minWidth: "150px",
+
+  contentPath: null,
+  observePath: false,
+
+  cellDefinition: null,
+
+  pin: "center",
+
+  facetType: facetTypes.VALUES,
+
+  beforeSort: null,
+  getCellContent: getContentAtPath,
+  getSearchValue: getContentAtPath,
+  getSortValue: getContentAtPath,
+
+  init: function () {
+    if(!this.get("id")) {
+      throw new Error("ID is not set.");
+    }
+  },
+});
+
+ColumnDefinition.make = function (rawDefinition) {
+  if(Array.isArray(rawDefinition)) {
+    return rawDefinition.map(function (def) {
+      return ColumnDefinition.create(def);
+    });
+  }
+  else if(typeof rawDefinition === 'object') {
+    return ColumnDefinition.create(rawDefinition);
+  }
+  else {
+    throw new Error("rawDefinition must be an Array or an Object.");
+  }
+};
+
+ColumnDefinition.makeFromModel = function (ModelClass, columnOptions) {
+  var attributes = Ember.get(ModelClass, 'attributes'),
+      columns = [];
+  if(attributes) {
+    attributes.forEach(function (meta, name) {
+      var column = Ember.Object.create({
+        id: name,
+        headerTitle: name.capitalize(),
+        contentPath: name,
+      });
+
+      if(columnOptions) {
+        column.setProperties(columnOptions);
+      }
+
+      columns.push(column);
+    });
+
+    return ColumnDefinition.make(columns);
+  }
+  else {
+    throw new Error("Value passed is not a model class");
+  }
+};
+
+ColumnDefinition.fillerColumn = ColumnDefinition.create({
+  id: "fillerColumn",
+  headerTitle: "",
+  getCellContent: returnEmptyString,
+  getSearchValue: returnEmptyString,
+  getSortValue: returnEmptyString,
+
+  enableSearch: false,
+  enableSort: false,
+  enableColumnResize: false,
+});
+
+export default ColumnDefinition;
diff --git a/tez-ui/src/main/webapp/app/utils/counter-column-definition.js b/tez-ui/src/main/webapp/app/utils/counter-column-definition.js
index d66e551..5590e10 100644
--- a/tez-ui/src/main/webapp/app/utils/counter-column-definition.js
+++ b/tez-ui/src/main/webapp/app/utils/counter-column-definition.js
@@ -19,7 +19,7 @@
 import Ember from 'ember';
 
 import isIOCounter from '../utils/misc';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from './column-definition';
 
 /*
  * Returns a counter value from for a row
diff --git a/tez-ui/src/main/webapp/app/utils/data-processor.js b/tez-ui/src/main/webapp/app/utils/data-processor.js
new file mode 100644
index 0000000..07d31c0
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/utils/data-processor.js
@@ -0,0 +1,275 @@
+/**
+ * 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 Ember from 'ember';
+
+import SQL from './sql';
+
+/**
+ * Handles Sorting, Searching & Pagination
+ */
+export default Ember.Object.extend({
+  isSorting: false,
+  isSearching: false,
+
+  tableDefinition: null,
+
+  sql: SQL.create(),
+
+  rows: [],
+  _sortedRows: [],
+  _searchedRows: [],
+  _facetFilteredRows: [],
+
+  _searchObserver: Ember.on("init", Ember.observer('tableDefinition.searchText', 'tableDefinition._actualSearchType', '_sortedRows.[]', function () {
+    Ember.run.once(this, "startSearch");
+  })),
+
+  _sortObserver: Ember.on("init", Ember.observer(
+    'tableDefinition.sortColumnId',
+    'tableDefinition.sortOrder',
+    'rows.[]', function () {
+      Ember.run.once(this, "startSort");
+  })),
+
+  _facetedFilterObserver: Ember.on("init", Ember.observer('tableDefinition.facetConditions', '_searchedRows.[]', function () {
+    Ember.run.once(this, "startFacetedFilter");
+  })),
+
+  regexSearch: function (clause, rows, columns) {
+    var regex;
+
+    try {
+      regex = new RegExp(clause, "i");
+    }
+    catch(e) {
+      regex = new RegExp("", "i");
+    }
+
+    function checkRow(column) {
+      var value;
+      if(!column.get('enableSearch')) {
+        return false;
+      }
+      value = column.getSearchValue(this);
+
+      if(typeof value === 'string') {
+        value = value.toLowerCase();
+        return value.match(regex);
+      }
+
+      return false;
+    }
+
+    return rows.filter(function (row) {
+      return columns.some(checkRow, row);
+    });
+  },
+
+  startSearch: function () {
+    var searchText = String(this.get('tableDefinition.searchText')),
+        rows = this.get('_sortedRows') || [],
+        columns = this.get('tableDefinition.columns'),
+        actualSearchType = this.get('tableDefinition._actualSearchType'),
+        that = this;
+
+    if(searchText) {
+      this.set("isSearching", true);
+
+      Ember.run.later(function () {
+        var result;
+
+        switch(actualSearchType) {
+          case "SQL":
+            result = that.get("sql").search(searchText, rows, columns);
+            break;
+
+          //case "Regex": Commenting as default will be called anyways
+          default:
+            result = that.regexSearch(searchText, rows, columns);
+            break;
+        }
+
+        that.setProperties({
+          _searchedRows: result,
+          isSearching: false
+        });
+      });
+    }
+    else {
+      this.set("_searchedRows", rows);
+    }
+  },
+
+  compareFunction: function (a, b){
+    // Checking for undefined and null to handle some special cases in JavaScript comparison
+    // Eg: 1 > undefined = false & 1 < undefined = false
+    // "a1" > null = false & "a1" < null = false
+    if(a === undefined || a === null) {
+      return -1;
+    }
+    else if(b === undefined || b === null) {
+      return 1;
+    }
+    else if(a < b) {
+      return -1;
+    }
+    else if(a > b) {
+      return 1;
+    }
+    else {
+      return 0;
+    }
+  },
+
+  startSort: function () {
+    var rows = this.get('rows'),
+        tableDefinition = this.get('tableDefinition'),
+        sortColumnId = this.get('tableDefinition.sortColumnId'),
+        descending = this.get('tableDefinition.sortOrder') === 'desc',
+        that = this,
+        column;
+
+    if(tableDefinition) {
+      column = tableDefinition.get('columns').find(function (element) {
+        return element.get('id') === sortColumnId;
+      });
+    }
+
+    if(rows && Array.isArray(rows.content)) {
+      rows = rows.toArray();
+    }
+
+    if(rows && rows.get('length') > 0 && column) {
+      this.set('isSorting', true);
+
+      Ember.run.later(function () {
+        /*
+         * Creating sortArray as calling getSortValue form inside the
+         * sort function every time would be more costly.
+         */
+        var sortArray = rows.map(function (row) {
+          return {
+            value: column.getSortValue(row),
+            row: row
+          };
+        }),
+        compareFunction = that.get("compareFunction");
+
+        sortArray.sort(function (a, b) {
+          var result = compareFunction(a.value, b.value);
+          if(descending && result) {
+            result = -result;
+          }
+          return result;
+        });
+
+        that.setProperties({
+          _sortedRows: sortArray.map(function (record) {
+            return record.row;
+          }),
+          isSorting: false
+        });
+      });
+    }
+    else {
+      this.set('_sortedRows', rows);
+    }
+  },
+
+  startFacetedFilter: function () {
+    var clause = this.get("sql").createFacetClause(this.get('tableDefinition.facetConditions'), this.get("tableDefinition.columns")),
+        rows = this.get('_searchedRows') || [],
+        columns = this.get('tableDefinition.columns'),
+        that = this;
+
+    if(clause && columns) {
+      this.set("isSearching", true);
+
+      Ember.run.later(function () {
+        var result = that.get("sql").search(clause, rows, columns);
+
+        that.setProperties({
+          _facetFilteredRows: result,
+          isSearching: false
+        });
+      });
+    }
+    else {
+      this.set("_facetFilteredRows", rows);
+    }
+  },
+
+  facetedFields: Ember.computed('_searchedRows.[]', 'tableDefinition.columns', function () {
+    var searchedRows = this.get("_searchedRows"),
+        columns = this.get('tableDefinition.columns'),
+        fields = [];
+
+    if(columns) {
+      columns.forEach(function (column) {
+        var facetedData;
+        if(column.facetType) {
+          facetedData = column.facetType.facetRows(column, searchedRows);
+          if(facetedData) {
+            fields.push({
+              column: column,
+              facets: facetedData
+            });
+          }
+        }
+      });
+    }
+
+    return fields;
+  }),
+
+  pageDetails: Ember.computed("tableDefinition.rowCount", "tableDefinition.pageNum", "_facetFilteredRows.length", function () {
+    var tableDefinition = this.get("tableDefinition"),
+
+        pageNum = tableDefinition.get('pageNum'),
+        rowCount =  tableDefinition.get('rowCount'),
+
+        startIndex = (pageNum - 1) * rowCount,
+
+        totalRecords = this.get('_facetFilteredRows.length');
+
+    if(startIndex < 0) {
+      startIndex = 0;
+    }
+
+    return {
+      pageNum: pageNum,
+      totalPages: Math.ceil(totalRecords / rowCount),
+      rowCount: rowCount,
+
+      startIndex: startIndex,
+
+      fromRecord: totalRecords ? startIndex + 1 : 0,
+      toRecord: Math.min(startIndex + rowCount, totalRecords),
+      totalRecords: totalRecords
+    };
+  }),
+  totalPages: Ember.computed.alias("pageDetails.totalPages"), // Adding an alias for backward compatibility
+
+  // Paginate
+  processedRows: Ember.computed('_facetFilteredRows.[]', 'tableDefinition.rowCount', 'tableDefinition.pageNum', function () {
+    var rowCount =  this.get('tableDefinition.rowCount'),
+        startIndex = (this.get('tableDefinition.pageNum') - 1) * rowCount;
+    return this.get('_facetFilteredRows').slice(startIndex, startIndex + rowCount);
+  }),
+});
diff --git a/tez-ui/src/main/webapp/app/utils/facet-types.js b/tez-ui/src/main/webapp/app/utils/facet-types.js
new file mode 100644
index 0000000..0a340bb
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/utils/facet-types.js
@@ -0,0 +1,85 @@
+/**
+ * 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 Ember from 'ember';
+
+var facetTypes = {
+  VALUES: {
+    componentName: "em-table-facet-panel-values",
+
+    toClause: function (column, facetConditions) {
+      var values, clauses = [];
+
+      if(facetConditions) {
+        if(Ember.get(facetConditions, "in.length")) {
+          values = facetConditions.in.map(function (value) {
+            value = value.replace(/'/g, "''");
+            return `'${value}'`;
+          });
+          clauses.push(`${column.id} IN (${values})`);
+        }
+
+        if(Ember.get(facetConditions, "notIn.length")) {
+          values = facetConditions.notIn.map(function (value) {
+            value = value.replace(/'/g, "''");
+            return `'${value}'`;
+          });
+          clauses.push(`${column.id} NOT IN (${values})`);
+        }
+
+        return clauses.join(" AND ");
+      }
+    },
+
+    facetRows: function (column, rows) {
+      var facetedDataHash = {},
+          facetedDataArr = [];
+
+      rows.forEach(function (row) {
+        var value = column.getSearchValue(row);
+
+        if(typeof value === "string") {
+          if(!facetedDataHash[value]) {
+            facetedDataHash[value] = {
+              count: 0,
+              value: value
+            };
+            facetedDataArr.push(facetedDataHash[value]);
+          }
+          facetedDataHash[value].count++;
+        }
+
+      });
+
+      if(facetedDataArr.length) {
+        facetedDataArr = facetedDataArr.sort(function (a, b) {
+          return -(a.count - b.count); // Sort in reverse order
+        });
+        return facetedDataArr;
+      }
+    },
+
+    normaliseConditions: function (conditions, data) {
+      if(Ember.get(conditions, "in.length") < data.length) {
+        return conditions;
+      }
+    }
+  },
+};
+
+export default facetTypes;
diff --git a/tez-ui/src/main/webapp/app/utils/sql.js b/tez-ui/src/main/webapp/app/utils/sql.js
new file mode 100644
index 0000000..81db3a0
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/utils/sql.js
@@ -0,0 +1,94 @@
+/*global alasql*/
+/**
+ * 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 Ember from 'ember';
+
+/*
+ * A wrapper around AlaSQL
+ */
+export default Ember.Object.extend({
+
+  constructQuery: function(clause) {
+    return `SELECT * FROM ? WHERE ${clause}`;
+  },
+
+  validateClause: function (clause, columns) {
+    clause = clause.toString();
+
+    var query = this.constructQuery(this.normaliseClause(clause, columns || [])),
+        valid = false;
+
+    if(clause.match(/\W/g)) { // If it contain special characters including space
+      try {
+        alasql(query, [[{}]]);
+        valid = true;
+      }
+      catch(e) {}
+    }
+
+    return valid;
+  },
+
+  createFacetClause: function (conditions, columns) {
+    if(conditions && columns) {
+      return columns.map(function (column) {
+        if(column.get("facetType")) {
+          return column.get("facetType.toClause")(column, conditions[Ember.get(column, "id")]);
+        }
+      }).filter(clause => clause).join(" AND ");
+    }
+  },
+
+  normaliseClause: function (clause, columns) {
+    clause = clause.toString();
+    columns.forEach(function (column) {
+      var headerTitle = column.get("headerTitle");
+      clause = clause.replace(new RegExp(`"${headerTitle}"`, "gi"), column.get("id"));
+    });
+    return clause;
+  },
+
+  search: function (clause, rows, columns) {
+    clause = this.normaliseClause(clause, columns);
+
+    // Convert into a form that alasql can digest easily
+    var dataSet = rows.map(function (row, index) {
+      var rowObj = {
+        _index_: index
+      };
+
+      columns.forEach(function (column) {
+        if(column.get("enableSearch") && row) {
+          rowObj[column.get("id")] = column.getSearchValue(row);
+        }
+      });
+
+      return rowObj;
+    });
+
+    // Search
+    dataSet = alasql(this.constructQuery(clause), [dataSet]);
+
+    return dataSet.map(function (data) {
+      return rows[data._index_];
+    });
+  }
+
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/utils/table-definition.js
similarity index 51%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/app/utils/table-definition.js
index 7751719..c304ec4 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/utils/table-definition.js
@@ -18,19 +18,44 @@
 
 import Ember from 'ember';
 
-export default Ember.Component.extend({
+export default Ember.Object.extend({
 
-  content: null,
+  recordType: "",
 
-  classNames: ["em-table-status-cell"],
+  // Search
+  enableSearch: true,
+  searchText: '',
+  searchType: 'auto', // Can be either of auto, manual, regex OR sql
+  _actualSearchType: "Regex", // Set from em-table-search-ui
 
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
+  // Faceting
+  enableFaceting: false,
+  facetConditions: null,
+  minFieldsForFilter: 15,
+  minValuesToDisplay: 2,
+  facetValuesPageSize: 10,
 
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
+  // Sort
+  enableSort: true,
+  sortColumnId: '',
+  sortOrder: '',
+  headerAsSortButton: false,
+
+  // Pagination
+  enablePagination: true,
+  pageNum: 1,
+  rowCount: 10,
+  rowCountOptions: [5, 10, 25, 50, 100],
+
+  enableColumnResize: true,
+  showScrollShadow: false,
+
+  minRowsForFooter: 25,
+
+  columns: [],
+
+  _pageNumResetObserver: Ember.observer('searchText', 'facetConditions', 'rowCount', function () {
+    this.set('pageNum', 1);
   }),
+
 });
diff --git a/tez-ui/src/main/webapp/bower.json b/tez-ui/src/main/webapp/bower.json
index 56a69f3..cca56d8 100644
--- a/tez-ui/src/main/webapp/bower.json
+++ b/tez-ui/src/main/webapp/bower.json
@@ -1,6 +1,7 @@
 {
   "name": "tez-ui",
   "dependencies": {
+    "alasql": "^0.4.0",
     "ember": "2.2.0",
     "ember-cli-shims": "0.0.6",
     "ember-cli-test-loader": "0.2.1",
diff --git a/tez-ui/src/main/webapp/ember-cli-build.js b/tez-ui/src/main/webapp/ember-cli-build.js
index 7bbc77d..e4217e9 100644
--- a/tez-ui/src/main/webapp/ember-cli-build.js
+++ b/tez-ui/src/main/webapp/ember-cli-build.js
@@ -71,6 +71,7 @@ module.exports = function(defaults) {
   app.import('bower_components/codemirror/mode/sql/sql.js');
   app.import('bower_components/codemirror/mode/pig/pig.js');
   app.import('bower_components/codemirror/lib/codemirror.css');
+  app.import('bower_components/alasql/dist/alasql.js');
 
   return app.toTree(new MergeTrees([configEnv, zipWorker, copyFonts]));
 };
diff --git a/tez-ui/src/main/webapp/package.json b/tez-ui/src/main/webapp/package.json
index fa80389..ad3aa74 100644
--- a/tez-ui/src/main/webapp/package.json
+++ b/tez-ui/src/main/webapp/package.json
@@ -61,7 +61,6 @@
     "phantomjs-prebuilt": "2.1.13"
   },
   "dependencies": {
-    "em-table": "0.11.3",
     "em-tgraph": "0.0.14"
   }
 }
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-cell-test.js
similarity index 53%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/tests/integration/components/em-table-cell-test.js
index 7751719..ccf5358 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-cell-test.js
@@ -18,19 +18,28 @@
 
 import Ember from 'ember';
 
-export default Ember.Component.extend({
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
 
-  content: null,
+import ColumnDefinition from '../../../utils/column-definition';
 
-  classNames: ["em-table-status-cell"],
+moduleForComponent('em-table-cell', 'Integration | Component | em table cell', {
+  integration: true
+});
+
+test('Basic rendering test', function(assert) {
+  var columnDefinition = ColumnDefinition.create({
+        id: 'id',
+        contentPath: 'keyA'
+      }),
+      row = Ember.Object.create({
+        keyA: 'valueA',
+        keyB: 'valueB'
+      });
 
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
+  this.set('columnDefinition', columnDefinition);
+  this.set('row', row);
+  this.render(hbs`{{em-table-cell columnDefinition=columnDefinition row=row}}`);
 
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
+  assert.equal(this.$().text().trim(), 'valueA');
 });
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-column-test.js
similarity index 70%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/tests/integration/components/em-table-column-test.js
index 7751719..96eff7a 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-column-test.js
@@ -16,21 +16,15 @@
  * limitations under the License.
  */
 
-import Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
 
-export default Ember.Component.extend({
-
-  content: null,
-
-  classNames: ["em-table-status-cell"],
+moduleForComponent('em-table-column', 'Integration | Component | em table column', {
+  integration: true
+});
 
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
+test('Basic rendering test', function(assert) {
+  this.render(hbs`{{em-table-column}}`);
 
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
+  assert.equal(this.$().text().trim(), '');
 });
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-facet-panel-test.js
similarity index 51%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/tests/integration/components/em-table-facet-panel-test.js
index 7751719..cc0f1f0 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-facet-panel-test.js
@@ -16,21 +16,28 @@
  * limitations under the License.
  */
 
-import Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
 
-export default Ember.Component.extend({
+moduleForComponent('em-table-facet-panel', 'Integration | Component | em table facet panel', {
+  integration: true
+});
+
+test('Basic renders', function(assert) {
+
+  // Set any properties with this.set('myProperty', 'value');
+  // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL +
 
-  content: null,
+  this.render(hbs`{{em-table-facet-panel}}`);
 
-  classNames: ["em-table-status-cell"],
+  assert.equal(this.$().text().replace(/\n|\r\n|\r| /g, '').trim(), 'NotAvailable!');
 
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
+  // Template block usage:" + EOL +
+  this.render(hbs`
+    {{#em-table-facet-panel}}
+      template block text
+    {{/em-table-facet-panel}}
+  `);
 
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
+  assert.equal(this.$().text().replace(/\n|\r\n|\r| /g, '').trim(), 'NotAvailable!');
 });
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-facet-panel-values-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-facet-panel-values-test.js
new file mode 100644
index 0000000..f401a7d
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-facet-panel-values-test.js
@@ -0,0 +1,44 @@
+/**
+ * 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 { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-table-facet-panel-values', 'Integration | Component | em table facet panel values', {
+  integration: true
+});
+
+test('Basic render test', function(assert) {
+
+  // Set any properties with this.set('myProperty', 'value');
+  // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL +
+
+  this.set("tmpFacetConditions", {});
+  this.render(hbs`{{em-table-facet-panel-values tmpFacetConditions=tmpFacetConditions}}`);
+
+  assert.ok(this.$().text().trim());
+
+  // Template block usage:" + EOL +
+  this.render(hbs`
+    {{#em-table-facet-panel-values tmpFacetConditions=tmpFacetConditions}}
+      template block text
+    {{/em-table-facet-panel-values}}
+  `);
+
+  assert.ok(this.$().text().trim());
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-header-cell-test.js
similarity index 69%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/tests/integration/components/em-table-header-cell-test.js
index 7751719..0c502ce 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-header-cell-test.js
@@ -16,21 +16,15 @@
  * limitations under the License.
  */
 
-import Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
 
-export default Ember.Component.extend({
-
-  content: null,
-
-  classNames: ["em-table-status-cell"],
+moduleForComponent('em-table-header-cell', 'Integration | Component | em table header cell', {
+  integration: true
+});
 
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
+test('Basic rendering test', function(assert) {
+  this.render(hbs`{{em-table-header-cell}}`);
 
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
+  assert.equal(this.$().text().trim(), '');
 });
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-linked-cell-test.js
similarity index 68%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/tests/integration/components/em-table-linked-cell-test.js
index 7751719..7553c41 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-linked-cell-test.js
@@ -16,21 +16,15 @@
  * limitations under the License.
  */
 
-import Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
 
-export default Ember.Component.extend({
-
-  content: null,
-
-  classNames: ["em-table-status-cell"],
+moduleForComponent('em-table-linked-cell', 'Integration | Component | em table linked cell', {
+  integration: true
+});
 
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
+test('Basic rendering test', function(assert) {
+  this.render(hbs`{{em-table-linked-cell}}`);
 
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
+  assert.equal(this.$().text().trim(), 'Not Available!');
 });
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-pagination-ui-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-pagination-ui-test.js
new file mode 100644
index 0000000..0333d0c
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-pagination-ui-test.js
@@ -0,0 +1,204 @@
+/**
+ * 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 Ember from 'ember';
+
+import DataProcessor from '../../../utils/data-processor';
+import TableDefinition from '../../../utils/table-definition';
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-table-pagination-ui', 'Integration | Component | em table pagination ui', {
+  integration: true
+});
+
+test('Basic rendering test', function(assert) {
+  var customRowCount = 25,
+      definition = TableDefinition.create({
+        rowCount: customRowCount
+      }),
+      processor;
+
+  Ember.run(function () {
+    processor = DataProcessor.create({
+      tableDefinition: definition,
+      rows: Ember.A([Ember.Object.create()])
+    });
+  });
+
+  this.set('definition', definition);
+  this.set('processor', processor);
+  this.render(hbs`{{em-table-pagination-ui tableDefinition=definition dataProcessor=processor}}`);
+
+  var paginationItems = this.$('li');
+  assert.equal(paginationItems.length, 1);
+  assert.equal($(paginationItems[0]).text().trim(), "1");
+
+  var rowSelection = this.$('select')[0];
+  assert.ok(rowSelection);
+  assert.equal($(rowSelection).val(), customRowCount);
+});
+
+test('No data test', function(assert) {
+  var customRowCount = 2,
+      definition = TableDefinition.create({
+        rowCount: customRowCount
+      }),
+      processor;
+
+  Ember.run(function () {
+    processor = DataProcessor.create({
+      tableDefinition: definition,
+      rows: Ember.A()
+    });
+  });
+
+  this.set('definition', definition);
+  this.set('processor', processor);
+  this.render(hbs`{{em-table-pagination-ui tableDefinition=definition dataProcessor=processor}}`);
+
+  var paginationItems = this.$('li');
+  assert.equal(paginationItems.length, 0);
+});
+
+test('Multiple page test; without first & last', function(assert) {
+  var customRowCount = 2,
+      definition = TableDefinition.create({
+        rowCount: customRowCount
+      }),
+      processor;
+
+  Ember.run(function () {
+    processor = DataProcessor.create({
+      tableDefinition: definition,
+      rows: Ember.A([Ember.Object.create(), Ember.Object.create(), Ember.Object.create()])
+    });
+  });
+
+  this.set('definition', definition);
+  this.set('processor', processor);
+  this.render(hbs`{{em-table-pagination-ui tableDefinition=definition dataProcessor=processor}}`);
+
+  var paginationItems = this.$('li');
+  assert.equal(paginationItems.length, 2);
+  assert.equal($(paginationItems[0]).text().trim(), "1");
+  assert.equal($(paginationItems[1]).text().trim(), "2");
+});
+
+test('Display last test', function(assert) {
+  var customRowCount = 5,
+      definition = TableDefinition.create({
+        rowCount: customRowCount
+      }),
+      processor,
+      rows = [];
+
+  for(var i = 0; i < 100; i++) {
+    rows.push(Ember.Object.create());
+  }
+
+  Ember.run(function () {
+    processor = DataProcessor.create({
+      tableDefinition: definition,
+      rows: Ember.A(rows)
+    });
+  });
+
+  this.set('definition', definition);
+  this.set('processor', processor);
+  this.render(hbs`{{em-table-pagination-ui tableDefinition=definition dataProcessor=processor}}`);
+
+  var paginationItems = this.$('li');
+  assert.equal(paginationItems.length, 6);
+  assert.equal($(paginationItems[0]).text().trim(), "1");
+  assert.equal($(paginationItems[1]).text().trim(), "2");
+  assert.equal($(paginationItems[2]).text().trim(), "3");
+  assert.equal($(paginationItems[3]).text().trim(), "4");
+  assert.equal($(paginationItems[4]).text().trim(), "5");
+  assert.equal($(paginationItems[5]).text().trim(), "Last - 20");
+});
+
+test('Display first test', function(assert) {
+  var customRowCount = 5,
+      definition = TableDefinition.create({
+        pageNum: 20,
+        rowCount: customRowCount
+      }),
+      processor,
+      rows = [];
+
+  for(var i = 0; i < 100; i++) {
+    rows.push(Ember.Object.create());
+  }
+
+  Ember.run(function () {
+    processor = DataProcessor.create({
+      tableDefinition: definition,
+      rows: Ember.A(rows)
+    });
+  });
+
+  this.set('definition', definition);
+  this.set('processor', processor);
+  this.render(hbs`{{em-table-pagination-ui tableDefinition=definition dataProcessor=processor}}`);
+
+  var paginationItems = this.$('li');
+  assert.equal(paginationItems.length, 6);
+  assert.equal($(paginationItems[0]).text().trim(), "First");
+  assert.equal($(paginationItems[1]).text().trim(), "16");
+  assert.equal($(paginationItems[2]).text().trim(), "17");
+  assert.equal($(paginationItems[3]).text().trim(), "18");
+  assert.equal($(paginationItems[4]).text().trim(), "19");
+  assert.equal($(paginationItems[5]).text().trim(), "20");
+});
+
+test('Display first & last test', function(assert) {
+  var customRowCount = 5,
+      definition = TableDefinition.create({
+        pageNum: 10,
+        rowCount: customRowCount
+      }),
+      processor,
+      rows = [];
+
+  for(var i = 0; i < 100; i++) {
+    rows.push(Ember.Object.create());
+  }
+
+  Ember.run(function () {
+    processor = DataProcessor.create({
+      tableDefinition: definition,
+      rows: Ember.A(rows)
+    });
+  });
+
+  this.set('definition', definition);
+  this.set('processor', processor);
+  this.render(hbs`{{em-table-pagination-ui tableDefinition=definition dataProcessor=processor}}`);
+
+  var paginationItems = this.$('li');
+  assert.equal(paginationItems.length, 7);
+  assert.equal($(paginationItems[0]).text().trim(), "First");
+  assert.equal($(paginationItems[1]).text().trim(), "8");
+  assert.equal($(paginationItems[2]).text().trim(), "9");
+  assert.equal($(paginationItems[3]).text().trim(), "10");
+  assert.equal($(paginationItems[4]).text().trim(), "11");
+  assert.equal($(paginationItems[5]).text().trim(), "12");
+  assert.equal($(paginationItems[6]).text().trim(), "Last - 20");
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-progress-cell-test.js
similarity index 52%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/tests/integration/components/em-table-progress-cell-test.js
index 7751719..b7eced3 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-progress-cell-test.js
@@ -16,21 +16,28 @@
  * limitations under the License.
  */
 
-import Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
 
-export default Ember.Component.extend({
+moduleForComponent('em-table-progress-cell', 'Integration | Component | em table progress cell', {
+  integration: true
+});
+
+test('Basic creation test', function(assert) {
+
+  // Set any properties with this.set('myProperty', 'value');
+  // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL +
 
-  content: null,
+  this.render(hbs`{{em-table-progress-cell content=0.5}}`);
 
-  classNames: ["em-table-status-cell"],
+  assert.equal(this.$().text().trim(), '50%');
 
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
+  // Template block usage:" + EOL +
+  this.render(hbs`
+    {{#em-table-progress-cell content=0.5}}
+      template block text
+    {{/em-table-progress-cell}}
+  `);
 
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
+  assert.equal(this.$().text().trim(), '50%');
 });
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-search-ui-test.js
similarity index 69%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/tests/integration/components/em-table-search-ui-test.js
index 7751719..0cd2bbc 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-search-ui-test.js
@@ -16,21 +16,15 @@
  * limitations under the License.
  */
 
-import Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
 
-export default Ember.Component.extend({
-
-  content: null,
-
-  classNames: ["em-table-status-cell"],
+moduleForComponent('em-table-search-ui', 'Integration | Component | em table search ui', {
+  integration: true
+});
 
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
+test('Basic rendering test', function(assert) {
+  this.render(hbs`{{em-table-search-ui}}`);
 
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
+  assert.equal(this.$().text().trim(), 'Search');
 });
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-test.js
new file mode 100644
index 0000000..96baf79
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-test.js
@@ -0,0 +1,48 @@
+/**
+ * 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 { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+import TableDefinition from '../../../utils/table-definition';
+import ColumnDefinition from '../../../utils/column-definition';
+
+moduleForComponent('em-table', 'Integration | Component | em table', {
+  integration: true
+});
+
+test('Basic rendering test', function(assert) {
+  this.render(hbs`{{em-table}}`);
+
+  assert.equal(this.$('.table-message').text().trim(), 'No columns available!');
+});
+
+test('Records missing test', function(assert) {
+  var definition = TableDefinition.create({
+    recordType: "vertex"
+  });
+
+  this.set("columns", [ColumnDefinition.fillerColumn]);
+
+  this.render(hbs`{{em-table columns=columns}}`);
+  assert.equal(this.$('.table-message').text().trim(), 'No records available!');
+
+  this.set("definition", definition);
+  this.render(hbs`{{em-table columns=columns definition=definition}}`);
+  assert.equal(this.$('.table-message').text().trim(), 'No vertices available!');
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/tests/integration/em-table-status-cell-test.js
similarity index 58%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/tests/integration/em-table-status-cell-test.js
index 7751719..3148339 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/tests/integration/em-table-status-cell-test.js
@@ -16,21 +16,25 @@
  * limitations under the License.
  */
 
-import Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
 
-export default Ember.Component.extend({
+moduleForComponent('em-table-status-cell', 'Integration | Component | em table status cell', {
+  integration: true
+});
+
+test('Basic creation test', function(assert) {
 
-  content: null,
+  this.render(hbs`{{em-table-status-cell}}`);
 
-  classNames: ["em-table-status-cell"],
+  assert.equal(this.$().text().trim(), 'Not Available!');
 
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
+  // Template block usage:" + EOL +
+  this.render(hbs`
+    {{#em-table-status-cell}}
+      template block text
+    {{/em-table-status-cell}}
+  `);
 
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
+  assert.equal(this.$().text().trim(), 'Not Available!');
 });
diff --git a/tez-ui/src/main/webapp/tests/unit/utils/column-definition-test.js b/tez-ui/src/main/webapp/tests/unit/utils/column-definition-test.js
new file mode 100644
index 0000000..5ee9a49
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/utils/column-definition-test.js
@@ -0,0 +1,104 @@
+/**
+ * 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 Ember from 'ember';
+import ColumnDefinition from '../../../utils/column-definition';
+import { module, test } from 'qunit';
+
+module('Unit | Utility | column definition');
+
+test('Class creation test', function(assert) {
+  assert.ok(ColumnDefinition);
+
+  assert.ok(ColumnDefinition.make);
+  assert.ok(ColumnDefinition.makeFromModel);
+});
+
+test('make - Instance creation test', function(assert) {
+
+  var definition = ColumnDefinition.make({
+    id: "testId"
+  });
+  var definitions = ColumnDefinition.make([{
+    id: "testId 1"
+  },{
+    id: "testId 2"
+  }]);
+
+  // Single
+  assert.ok(definition);
+
+  // Multiple
+  assert.ok(definitions);
+  assert.ok(Array.isArray(definitions));
+  assert.equal(definitions.length, 2);
+});
+
+test('make - Instance creation failure test', function(assert) {
+  assert.throws(function () {
+    ColumnDefinition.make({});
+  });
+});
+
+test('makeFromModel test', function(assert) {
+  var attributes = Ember.Map.create(),
+      DummyModel = Ember.Object.create({
+        attributes: attributes
+      }),
+      getCellContent = function () {},
+      columns;
+
+  attributes.set("attr1", "path1");
+  attributes.set("attr2", "path2");
+  attributes.set("attr3", "path3");
+
+  columns = ColumnDefinition.makeFromModel(DummyModel, {
+    getCellContent: getCellContent
+  });
+
+  assert.equal(columns.length, 3);
+  assert.equal(columns[0].id, "attr1");
+  assert.equal(columns[0].headerTitle, "Attr1");
+  assert.equal(columns[0].contentPath, "attr1");
+  assert.equal(columns[0].getCellContent, getCellContent);
+});
+
+test('Instance test', function(assert) {
+  var definition = ColumnDefinition.make({
+    id: "testId",
+    contentPath: "a.b"
+  });
+  var data = Ember.Object.create({
+    a: {
+      b: 42
+    }
+  });
+
+  assert.ok(definition.getCellContent);
+  assert.ok(definition.getSearchValue);
+  assert.ok(definition.getSortValue);
+
+  assert.equal(definition.id, "testId");
+  assert.equal(definition.headerTitle, "Not Available!");
+  assert.equal(definition.minWidth, "150px");
+  assert.equal(definition.contentPath, "a.b");
+
+  assert.equal(definition.getCellContent(data), 42);
+  assert.equal(definition.getSearchValue(data), 42);
+  assert.equal(definition.getSortValue(data), 42);
+});
diff --git a/tez-ui/src/main/webapp/tests/unit/utils/data-processor-test.js b/tez-ui/src/main/webapp/tests/unit/utils/data-processor-test.js
new file mode 100644
index 0000000..58f52dd
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/utils/data-processor-test.js
@@ -0,0 +1,137 @@
+/**
+ * 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 Ember from 'ember';
+
+import DataProcessor from '../../../utils/data-processor';
+import ColumnDefinition from '../../../utils/column-definition';
+import { module, test } from 'qunit';
+
+module('Unit | Utility | data processor');
+
+test('Class creation test', function(assert) {
+  assert.ok(DataProcessor);
+});
+
+test('Instance default test', function(assert) {
+  var processor;
+
+  Ember.run(function () {
+    processor = DataProcessor.create({
+      tableDefinition: Ember.Object.create(),
+      startSearch: function () {
+        // Test Search
+      },
+      startSort: function () {
+        // Test Sort
+      }
+    });
+  });
+
+  assert.ok(processor);
+  assert.equal(processor.get('isSorting'), false);
+  assert.equal(processor.get('isSearching'), false);
+
+  assert.ok(processor._searchObserver);
+  assert.ok(processor._sortObserver);
+  assert.ok(processor.startSearch);
+  assert.ok(processor.startSort);
+  assert.ok(processor.compareFunction);
+  assert.ok(processor.totalPages);
+  assert.ok(processor.processedRows);
+});
+
+test('compareFunction test', function(assert) {
+  var processor;
+
+  Ember.run(function () {
+    processor = DataProcessor.create({
+      tableDefinition: Ember.Object.create(),
+      startSearch: function () {},
+      startSort: function () {}
+    });
+  });
+
+  assert.equal(processor.compareFunction(1, 1), 0);
+  assert.equal(processor.compareFunction(1, 2), -1);
+  assert.equal(processor.compareFunction(2, 1), 1);
+
+  assert.equal(processor.compareFunction("a", "a"), 0);
+  assert.equal(processor.compareFunction("a", "b"), -1);
+  assert.equal(processor.compareFunction("b", "a"), 1);
+
+  assert.equal(processor.compareFunction(null, null), -1);
+  assert.equal(processor.compareFunction(1, null), 1);
+  assert.equal(processor.compareFunction(null, 2), -1);
+  assert.equal(processor.compareFunction("a", null), 1);
+  assert.equal(processor.compareFunction(null, "b"), -1);
+
+  assert.equal(processor.compareFunction(undefined, undefined), -1);
+  assert.equal(processor.compareFunction(1, undefined), 1);
+  assert.equal(processor.compareFunction(undefined, 2), -1);
+  assert.equal(processor.compareFunction("a", undefined), 1);
+  assert.equal(processor.compareFunction(undefined, "b"), -1);
+});
+
+test('startSearch test', function(assert) {
+  var processor,
+      runLater = Ember.run.later;
+
+  assert.expect(3);
+
+  Ember.run.later = function (callback) {
+    callback();
+    assert.equal(processor.get("_searchedRows.length"), 2);
+    assert.equal(processor.get("_searchedRows.0.foo"), "Foo1");
+    assert.equal(processor.get("_searchedRows.1.foo"), "Foo12");
+
+    Ember.run.later = runLater; // Reset
+  };
+
+  Ember.run(function () {
+    processor = DataProcessor.create({
+      tableDefinition: Ember.Object.create({
+        searchText: "foo1",
+        columns: [ColumnDefinition.create({
+          id: "foo",
+          contentPath: 'foo'
+        }), ColumnDefinition.create({
+          id: "bar",
+          contentPath: 'bar'
+        })]
+      }),
+      startSort: function () {
+        // Test Sort
+      },
+      _sortedRows: [Ember.Object.create({
+        foo: "Foo1",
+        bar: "Bar1"
+      }), Ember.Object.create({
+        foo: "Foo12",
+        bar: "Bar2"
+      }), Ember.Object.create({
+        foo: "Foo3",
+        bar: "Bar3"
+      }), Ember.Object.create({
+        foo: "Foo4",
+        bar: "Bar4"
+      })],
+    });
+  });
+
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/tests/unit/utils/facet-types-test.js
similarity index 70%
copy from tez-ui/src/main/webapp/app/components/em-table-status-cell.js
copy to tez-ui/src/main/webapp/tests/unit/utils/facet-types-test.js
index 7751719..f3af952 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/tests/unit/utils/facet-types-test.js
@@ -16,21 +16,13 @@
  * limitations under the License.
  */
 
-import Ember from 'ember';
+import facetTypes from '../../../utils/facet-types';
+import { module, test } from 'qunit';
 
-export default Ember.Component.extend({
+module('Unit | Utility | facet types');
 
-  content: null,
+test('Basic creation test', function(assert) {
+  assert.ok(facetTypes);
 
-  classNames: ["em-table-status-cell"],
-
-  statusName: Ember.computed("content", function () {
-    var status = this.get("content");
-
-    if(status) {
-      status = status.toString().dasherize();
-      status = "status-" + status;
-    }
-    return status;
-  }),
+  assert.ok(facetTypes.VALUES);
 });
diff --git a/tez-ui/src/main/webapp/tests/unit/utils/sql-test.js b/tez-ui/src/main/webapp/tests/unit/utils/sql-test.js
new file mode 100644
index 0000000..7aed218
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/utils/sql-test.js
@@ -0,0 +1,90 @@
+/**
+ * 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 SQL from '../../../utils/sql';
+import ColumnDefinition from '../../../utils/column-definition';
+import { module, test } from 'qunit';
+
+module('Unit | Utility | sql');
+
+test('Class creation test', function(assert) {
+  var sql = SQL.create();
+
+  assert.ok(sql.constructQuery);
+  assert.ok(sql.validateClause);
+  assert.ok(sql.normaliseClause);
+  assert.ok(sql.search);
+});
+
+test('constructQuery test', function(assert) {
+  var sql = SQL.create();
+
+  assert.equal(sql.constructQuery("x = y"), "SELECT * FROM ? WHERE x = y");
+});
+
+test('validateClause test', function(assert) {
+  var sql = SQL.create();
+
+  assert.ok(sql.validateClause("x = y"));
+  assert.ok(sql.validateClause("x = y AND a = b"));
+  assert.ok(sql.validateClause("(x = y OR y = z) AND a = b"));
+  assert.ok(sql.validateClause("x BETWEEN 1 AND 2"));
+
+  assert.notOk(sql.validateClause("foo"));
+  assert.notOk(sql.validateClause("foo bar"));
+  assert.notOk(sql.validateClause("^[a-z0-9_-]{3,16}$"));
+  assert.notOk(sql.validateClause("^[a-z0-9_-]{6,18}$"));
+  assert.notOk(sql.validateClause("^[a-z0-9-]+$"));
+});
+
+test('normaliseClause test', function(assert) {
+  var sql = SQL.create(),
+      column = ColumnDefinition.create({
+        headerTitle: "Column Header",
+        id: "columnID",
+        contentPath: "col"
+      });
+
+  assert.equal(sql.normaliseClause('"Column Header" = value', [column]), "columnID = value");
+  assert.equal(sql.normaliseClause('"Another Column Header" = value', [column]), '"Another Column Header" = value');
+});
+
+test('search test', function(assert) {
+  var sql = SQL.create(),
+      data = [{
+        colA: "x1",
+        colB: "y1"
+      }, {
+        colA: "x2",
+        colB: "y2"
+      }, {
+        colA: "x1",
+        colB: "y3"
+      }],
+      columns = [ColumnDefinition.create({
+        headerTitle: "Column A",
+        id: "colA",
+        contentPath: "colA"
+      })];
+
+  var result = sql.search('"Column A" = "x1"', data, columns);
+
+  assert.equal(result.length, 2);
+  assert.equal(result[0].colB, "y1");
+  assert.equal(result[1].colB, "y3");
+});
diff --git a/tez-ui/src/main/webapp/tests/unit/utils/table-definition-test.js b/tez-ui/src/main/webapp/tests/unit/utils/table-definition-test.js
new file mode 100644
index 0000000..234994b
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/utils/table-definition-test.js
@@ -0,0 +1,52 @@
+/**
+ * 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 TableDefinition from '../../../utils/table-definition';
+import { module, test } from 'qunit';
+
+module('Unit | Utility | table definition');
+
+test('Class creation test', function(assert) {
+  assert.ok(TableDefinition);
+});
+
+test('Default instance test', function(assert) {
+  var definition = TableDefinition.create();
+
+  assert.ok(definition);
+
+  assert.equal(definition.pageNum, 1);
+  assert.equal(definition.rowCount, 10);
+  assert.equal(definition.minRowsForFooter, 25);
+});
+
+test('Page-num reset test', function(assert) {
+  var definition = TableDefinition.create();
+
+  assert.equal(definition.pageNum, 1);
+
+  definition.set("pageNum", 5);
+  assert.equal(definition.pageNum, 5);
+
+  definition.set("searchText", "x");
+  assert.equal(definition.pageNum, 1);
+
+  definition.set("pageNum", 5);
+  definition.set("rowCount", 5);
+  assert.equal(definition.pageNum, 1);
+});
diff --git a/tez-ui/src/main/webapp/yarn.lock b/tez-ui/src/main/webapp/yarn.lock
index 00250e8..660ac80 100644
--- a/tez-ui/src/main/webapp/yarn.lock
+++ b/tez-ui/src/main/webapp/yarn.lock
@@ -1391,16 +1391,6 @@ ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
 
-em-table@0.11.3:
-  version "0.11.3"
-  resolved "https://registry.yarnpkg.com/em-table/-/em-table-0.11.3.tgz#20e605cc3814214e644199399a2383cee8d23eeb"
-  dependencies:
-    ember-cli-htmlbars "^1.0.1"
-    ember-cli-less "^1.4.0"
-    source-map "^0.5.6"
-  optionalDependencies:
-    phantomjs-prebuilt "2.1.13"
-
 em-tgraph@0.0.14:
   version "0.0.14"
   resolved "https://registry.yarnpkg.com/em-tgraph/-/em-tgraph-0.0.14.tgz#4d48b911760f85dec41904e4056ec52542391cc1"