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:38 UTC

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

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"