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 15:21:06 UTC

[tez] branch branch-0.9 updated: TEZ-4328. Import external tez component em-helpers (#145)

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

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


The following commit(s) were added to refs/heads/branch-0.9 by this push:
     new 219221f  TEZ-4328. Import external tez component em-helpers (#145)
219221f is described below

commit 219221f31268a572229cb6a995bcda9b99d92f4e
Author: jteagles <je...@gmail.com>
AuthorDate: Tue Aug 24 10:13:15 2021 -0500

    TEZ-4328. Import external tez component em-helpers (#145)
    
    Co-authored-by: Sreenath Somarajapuram <sr...@apache.org>
    (cherry picked from commit e2c4ee0842e43e67e4d1dadc8308b427b24aadee)
---
 tez-ui/src/main/resources/META-INF/LICENSE.txt     |   1 -
 .../main/webapp/app/components/em-breadcrumbs.js   |  69 ++++++++++
 .../src/main/webapp/app/components/em-progress.js  | 102 ++++++++++++++
 tez-ui/src/main/webapp/app/helpers/txt.js          |  62 +++++++++
 tez-ui/src/main/webapp/app/styles/app.less         |   5 +
 .../src/main/webapp/app/styles/em-breadcrumbs.less |  29 ++++
 tez-ui/src/main/webapp/app/styles/em-progress.less |  52 ++++++++
 tez-ui/src/main/webapp/app/styles/shared.less      |   4 +
 tez-ui/src/main/webapp/app/styles/txt.less         |  24 ++++
 .../app/templates/components/em-breadcrumbs.hbs    |  45 +++++++
 .../app/templates/components/em-progress.hbs       |  25 ++++
 tez-ui/src/main/webapp/app/utils/formatters.js     | 146 +++++++++++++++++++++
 tez-ui/src/main/webapp/config/environment.js       |   4 +
 tez-ui/src/main/webapp/package.json                |   1 -
 .../integration/components/em-breadcrumbs-test.js  | 107 +++++++++++++++
 .../integration/components/em-progress-test.js     |  73 +++++++++++
 .../src/main/webapp/tests/unit/helpers/txt-test.js |  59 +++++++++
 .../webapp/tests/unit/utils/formatters-test.js     |  99 ++++++++++++++
 tez-ui/src/main/webapp/yarn.lock                   |  10 --
 19 files changed, 905 insertions(+), 12 deletions(-)

diff --git a/tez-ui/src/main/resources/META-INF/LICENSE.txt b/tez-ui/src/main/resources/META-INF/LICENSE.txt
index ba6f29b..354d745 100644
--- a/tez-ui/src/main/resources/META-INF/LICENSE.txt
+++ b/tez-ui/src/main/resources/META-INF/LICENSE.txt
@@ -233,7 +233,6 @@ The Apache TEZ tez-ui bundles the following files under the MIT License:
  - 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)
- - em-helpers v0.5.8 (https://github.com/sreenaths/em-helpers)
  - 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-breadcrumbs.js b/tez-ui/src/main/webapp/app/components/em-breadcrumbs.js
new file mode 100644
index 0000000..fcf90cf
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-breadcrumbs.js
@@ -0,0 +1,69 @@
+/**
+ * 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-breadcrumbs';
+
+export default Ember.Component.extend({
+  layout: layout,
+
+  itemStyle: Ember.computed("items", function () {
+    var itemCount = this.get("items.length");
+
+    if(itemCount) {
+      let widthPercent = 100 / itemCount;
+      return new Ember.Handlebars.SafeString(`max-width: ${widthPercent}%`);
+    }
+  }),
+
+  normalizedItems: Ember.computed("items", function () {
+    var items = this.get("items");
+
+    if(items) {
+      let lastIndex = items.length - 1;
+      items = items.map(function (item, index) {
+        var itemDef = {
+          text: item.text || "",
+          classNames: item.classNames || [],
+        };
+
+        Ember.assert("classNames must be an array", Array.isArray(itemDef.classNames));
+
+        if(index === lastIndex) {
+          itemDef.classNames.push("active");
+        }
+        else {
+          itemDef.routeName = item.routeName;
+          itemDef.model = item.model;
+          itemDef.href = item.href;
+          if(item.queryParams) {
+            itemDef.queryParams = {
+              isQueryParams: true,
+              values: item.queryParams
+            };
+          }
+        }
+
+        itemDef.classNames = itemDef.classNames.join(" ");
+        return itemDef;
+      });
+    }
+
+    return items;
+  })
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-progress.js b/tez-ui/src/main/webapp/app/components/em-progress.js
new file mode 100644
index 0000000..926764b
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-progress.js
@@ -0,0 +1,102 @@
+/**
+ * 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-progress';
+
+export default Ember.Component.extend({
+  layout: layout,
+
+  value: 0,
+  valueMin: 0,
+  valueMax: 1,
+
+  classNames: ["em-progress-container"],
+  classNameBindings: ["animated", "striped"],
+
+  striped: false,
+  style: null,
+
+  progressBar: null,
+
+  widthPercent: Ember.computed("value", "valueMin", "valueMax", function () {
+    var value = this.get("value"),
+        valueMin = this.get("valueMin"),
+        valueMax = this.get("valueMax");
+
+    if(value < valueMin) {
+      value = valueMin;
+    }
+    else if(value > valueMax) {
+      value = valueMax;
+    }
+
+    value -= valueMin;
+    valueMax -= valueMin;
+
+    return (value / valueMax) * 100;
+  }),
+
+  progressText: Ember.computed("widthPercent", function () {
+    var percent = parseInt(this.get("widthPercent"));
+    if(isNaN(percent)) {
+      percent = 0;
+    }
+    return percent + "%";
+  }),
+
+  animated: Ember.computed("widthPercent", "striped", function () {
+    return this.get('striped') && this.get('widthPercent') > 0 && this.get('widthPercent') < 100;
+  }),
+
+  progressBarClasses: Ember.computed("style", "striped", "animated", function () {
+    var classes = [],
+        style = this.get("style");
+
+    if(style) {
+      classes.push(`progress-bar-${style}`);
+    }
+    if(this.get("striped")) {
+      classes.push("progress-bar-striped");
+    }
+    if(this.get("animated")) {
+      classes.push("active");
+    }
+
+    return classes.join(" ");
+  }),
+
+  renderProgress: Ember.observer("progressBar", "widthPercent", function () {
+    var widthPercent = this.get('widthPercent');
+    this.get("progressBar").width(widthPercent + "%");
+  }),
+
+  didInsertElement: function () {
+    Ember.run.scheduleOnce('afterRender', this, function() {
+      this.setProperties({
+        progressBar: this.$(".progress-bar")
+      });
+    });
+  },
+
+  willDestroy: function () {
+    this.setProperties({
+      progressBar: null,
+    });
+  }
+});
diff --git a/tez-ui/src/main/webapp/app/helpers/txt.js b/tez-ui/src/main/webapp/app/helpers/txt.js
new file mode 100644
index 0000000..81fdbda
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/helpers/txt.js
@@ -0,0 +1,62 @@
+/**
+ * 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 formatters from '../utils/formatters';
+
+export function txt(value, hash) {
+  var message,
+      dataType = hash.type,
+      formatter = hash.formatter,
+      titleAttr = "";
+
+  if(value) {
+    value = value[0];
+  }
+
+  if(value instanceof Error) {
+    message = value.message;
+    titleAttr = `title="${value.message}" `;
+  }
+  else {
+    try {
+      if(value !== undefined && !formatter && dataType) {
+        formatter = formatters[dataType];
+      }
+
+      if(formatter && value !== undefined && value !== null) {
+        value = formatter(value, hash);
+      }
+
+      if(value === undefined || value === null) {
+        message = 'Not Available!';
+      }
+      else {
+        return Ember.String.htmlSafe(Ember.Handlebars.Utils.escapeExpression(value.toString()));
+      }
+    }
+    catch(error) {
+      message = "Invalid Data!";
+      Ember.Logger.error(error);
+    }
+  }
+
+  return Ember.String.htmlSafe(`<span ${titleAttr}class="txt-message"> ${message} </span>`);
+}
+
+export default Ember.Helper.helper(txt);
diff --git a/tez-ui/src/main/webapp/app/styles/app.less b/tez-ui/src/main/webapp/app/styles/app.less
index 7c66229..f8a66e3 100644
--- a/tez-ui/src/main/webapp/app/styles/app.less
+++ b/tez-ui/src/main/webapp/app/styles/app.less
@@ -44,6 +44,8 @@
 @import "em-table-status-cell";
 @import "query-timeline";
 @import "home-table-controls";
+@import "em-progress";
+@import "em-breadcrumbs";
 
 // Modals
 @import "column-selector";
@@ -54,3 +56,6 @@
 @import "details-page";
 @import "swimlane-page";
 @import "vertex-configs-page";
+
+// Helpers
+@import "txt";
diff --git a/tez-ui/src/main/webapp/app/styles/em-breadcrumbs.less b/tez-ui/src/main/webapp/app/styles/em-breadcrumbs.less
new file mode 100644
index 0000000..05ed77e
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/em-breadcrumbs.less
@@ -0,0 +1,29 @@
+/**
+ * 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 "bower_components/snippet-ss/less/no";
+
+.breadcrumb {
+  .no-wrap;
+  li {
+    .no-wrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+}
diff --git a/tez-ui/src/main/webapp/app/styles/em-progress.less b/tez-ui/src/main/webapp/app/styles/em-progress.less
new file mode 100644
index 0000000..416b696
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/em-progress.less
@@ -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 "bower_components/bootstrap/less/variables";
+@import "bower_components/snippet-ss/less/effects";
+
+.progress {
+  .progress-bar {
+    .progress-text {
+      padding-left: 10px;
+      .text-outer-glow(@progress-bar-bg);
+    }
+  }
+
+  .progress-bar-success {
+    .progress-text {
+      .text-outer-glow(@progress-bar-success-bg);
+    }
+  }
+  .progress-bar-info {
+    .progress-text {
+      .text-outer-glow(@progress-bar-info-bg);
+    }
+  }
+  .progress-bar-warning {
+    .progress-text {
+      .text-outer-glow(@progress-bar-warning-bg);
+    }
+  }
+  .progress-bar-danger {
+    .progress-text {
+      .text-outer-glow(@progress-bar-danger-bg);
+    }
+  }
+}
+
diff --git a/tez-ui/src/main/webapp/app/styles/shared.less b/tez-ui/src/main/webapp/app/styles/shared.less
index dedac8c..b34cfa6 100644
--- a/tez-ui/src/main/webapp/app/styles/shared.less
+++ b/tez-ui/src/main/webapp/app/styles/shared.less
@@ -86,3 +86,7 @@ b {
     }
   }
 }
+
+.em-message {
+  opacity: .5;
+}
diff --git a/tez-ui/src/main/webapp/app/styles/txt.less b/tez-ui/src/main/webapp/app/styles/txt.less
new file mode 100644
index 0000000..171c250
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/txt.less
@@ -0,0 +1,24 @@
+/**
+ * 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 "./shared";
+
+.txt-message {
+  .em-message;
+}
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-breadcrumbs.hbs b/tez-ui/src/main/webapp/app/templates/components/em-breadcrumbs.hbs
new file mode 100644
index 0000000..f50972a
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-breadcrumbs.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.
+}}
+
+<ol class="breadcrumb">
+  {{#each normalizedItems key="text" as |item|}}
+    <li class={{item.classNames}} title="{{item.text}}" style={{itemStyle}}>
+      {{#if item.routeName}}
+        {{#if item.queryParams}}
+          {{#link-to item.routeName item.queryParams}}
+            {{item.text}}
+          {{/link-to}}
+        {{else if item.model}}
+          {{#link-to item.routeName item.model}}
+            {{item.text}}
+          {{/link-to}}
+        {{else}}
+          {{#link-to item.routeName}}
+            {{item.text}}
+          {{/link-to}}
+        {{/if}}
+      {{else if item.href}}
+        <a href={{item.href}}>
+          {{item.text}}
+        </a>
+      {{else}}
+        {{item.text}}
+      {{/if}}
+    </li>
+  {{/each}}
+</ol>
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-progress.hbs b/tez-ui/src/main/webapp/app/templates/components/em-progress.hbs
new file mode 100644
index 0000000..bd7fafc
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-progress.hbs
@@ -0,0 +1,25 @@
+{{!
+ * 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="progress">
+  <div class="progress-bar {{progressBarClasses}}" role="progressbar" aria-valuenow="{{value}}" aria-valuemin="{{valueMin}}" aria-valuemax="{{valueMax}}">
+    <span class="progress-text">
+      {{progressText}}
+    </span>
+  </div>
+</div>
diff --git a/tez-ui/src/main/webapp/app/utils/formatters.js b/tez-ui/src/main/webapp/app/utils/formatters.js
new file mode 100644
index 0000000..d724eb8
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/utils/formatters.js
@@ -0,0 +1,146 @@
+/**
+ * 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 moment from 'moment';
+import numeral from 'numeral';
+
+const DEFAULT_DATE_TIMEZONE = "UTC",
+      DEFAULT_DATE_FORMAT = "DD MMM YYYY HH:mm:ss",
+      DEFAULT_NUM_FORMAT = '0,0',
+      DEFAULT_MEM_FORMAT = '0 b';
+
+function durationFormatter(arr, value, unit) {
+  if(value > 0) {
+    if(value > 1) {
+      unit += 's';
+    }
+    arr.push(value + unit);
+  }
+}
+
+const DURATION_FORMATS = {
+  long: {
+    collateFunction: durationFormatter,
+
+    year: " year",
+    month: " month",
+    day: " day",
+    hour: " hour",
+    minute: " minute",
+    second: " second",
+    millisecond: " millisecond"
+  },
+  short: {
+    collateFunction: durationFormatter,
+
+    year: " yr",
+    month: " mo",
+    day: " day",
+    hour: " hr",
+    minute: " min",
+    second: " sec",
+    millisecond: " msec"
+  },
+  xshort: {
+    collateFunction: function (arr, value, unit) {
+      if(value > 0) {
+        arr.push(value + unit);
+      }
+    },
+
+    year: "Y",
+    month: "M",
+    day: "D",
+    hour: "h",
+    minute: "m",
+    second: "s",
+    millisecond: "ms"
+  }
+};
+
+function validateNumber(value, message) {
+  value = parseFloat(value);
+
+  if(isNaN(value)) {
+    throw new Error(message || "Invalid number!");
+  }
+
+  return value;
+}
+
+export default Ember.Controller.create({
+  date: function (value, options) {
+    var date = moment.tz(value, options.valueFormat, options.valueTimeZone || DEFAULT_DATE_TIMEZONE);
+
+    date = options.timeZone ? date.tz(options.timeZone) : date.local();
+    date = date.format(options.format || DEFAULT_DATE_FORMAT);
+
+    if(date === "Invalid date") {
+      throw new Error(date);
+    }
+
+    return date;
+  },
+  duration: function (value, options) {
+    var format = DURATION_FORMATS[options.format || "xshort"],
+        duration,
+        ret = [];
+
+    value = validateNumber(value, "Invalid duration");
+
+    if(value === 0) {
+      return `0${format.millisecond}`;
+    }
+
+    duration = moment.duration(value, options.valueUnit);
+
+    format.collateFunction(ret, duration.years(), format.year);
+    format.collateFunction(ret, duration.months(), format.month);
+    format.collateFunction(ret, duration.days(), format.day);
+    format.collateFunction(ret, duration.hours(), format.hour);
+    format.collateFunction(ret, duration.minutes(), format.minute);
+    format.collateFunction(ret, duration.seconds(), format.second);
+    format.collateFunction(ret, Math.round(duration.milliseconds()), format.millisecond);
+
+    return ret.join(" ");
+  },
+  number: function (value, options) {
+    value = validateNumber(value);
+    return numeral(value).format(options.format || DEFAULT_NUM_FORMAT);
+  },
+  memory: function (value) {
+    value = validateNumber(value, "Invalid memory");
+    if(value === 0) {
+      return "0 B";
+    }
+    return numeral(value).format(DEFAULT_MEM_FORMAT);
+  },
+  json: function (value, options) {
+    if(value && typeof value === "object" && value.constructor === Object) {
+      try {
+        value = JSON.stringify(value, options.replacer, options.space || 4);
+      }
+      catch(err){
+        Ember.Logger.error(err);
+      }
+    }
+    return value;
+  }
+});
diff --git a/tez-ui/src/main/webapp/config/environment.js b/tez-ui/src/main/webapp/config/environment.js
index 0c755ac..68a16cf 100644
--- a/tez-ui/src/main/webapp/config/environment.js
+++ b/tez-ui/src/main/webapp/config/environment.js
@@ -39,6 +39,10 @@ module.exports = function(environment) {
       'child-src': "'self' 'unsafe-inline'",
       'style-src': "'self' 'unsafe-inline'",
       'script-src': "'self' 'unsafe-inline'"
+    },
+
+    moment: {
+      includeTimezone: '2010-2020'
     }
   };
 
diff --git a/tez-ui/src/main/webapp/package.json b/tez-ui/src/main/webapp/package.json
index 92f54d2..fa80389 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-helpers": "0.8.0",
     "em-table": "0.11.3",
     "em-tgraph": "0.0.14"
   }
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-breadcrumbs-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-breadcrumbs-test.js
new file mode 100644
index 0000000..63edbc4
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-breadcrumbs-test.js
@@ -0,0 +1,107 @@
+/**
+ * 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 { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-breadcrumbs', 'Integration | Component | em breadcrumbs', {
+  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 +
+
+  this.render(hbs`{{em-breadcrumbs}}`);
+
+  assert.equal(this.$().text().trim(), '');
+
+  // Template block usage:" + EOL +
+  this.render(hbs`
+    {{#em-breadcrumbs}}
+      template block text
+    {{/em-breadcrumbs}}
+  `);
+
+  assert.equal(this.$().text().trim(), '');
+});
+
+test('Test with one link-to item', function(assert) {
+  var testItems = [{
+    routeName: "foo",
+    text: "fooText"
+  }],
+  elements;
+
+  this.set("items", testItems);
+  this.render(hbs`{{em-breadcrumbs items=items}}`);
+
+  elements = this.$("li");
+
+  assert.equal(elements.length, 1);
+  assert.equal(Ember.$(elements[0]).text().trim(), testItems[0].text);
+  assert.equal(elements[0].title, testItems[0].text);
+  assert.equal(elements[0].style.maxWidth, "100%");
+});
+
+test('Test with two link-to item', function(assert) {
+  var testItems = [{
+    routeName: "foo",
+    text: "fooText"
+  },{
+    routeName: "bar",
+    text: "barText"
+  }],
+  elements;
+
+  this.set("items", testItems);
+  this.render(hbs`{{em-breadcrumbs items=items}}`);
+
+  elements = this.$("li");
+
+  assert.equal(elements.length, 2);
+
+  assert.equal(Ember.$(elements[0]).text().trim(), testItems[0].text);
+  assert.equal(elements[0].title, testItems[0].text);
+  assert.equal(elements[0].style.maxWidth, "50%");
+
+  assert.equal(Ember.$(elements[1]).text().trim(), testItems[1].text);
+  assert.equal(elements[1].title, testItems[1].text);
+  assert.equal(elements[1].style.maxWidth, "50%");
+});
+
+test('Test with one anchor tag item', function(assert) {
+  var testItems = [{
+    href: "foo.bar",
+    text: "fooText"
+  }],
+  elements;
+
+  this.set("items", testItems);
+  this.render(hbs`{{em-breadcrumbs items=items}}`);
+
+  elements = this.$("li");
+
+  assert.equal(elements.length, 1);
+  assert.equal(Ember.$(elements[0]).text().trim(), testItems[0].text);
+  assert.equal(elements[0].title, testItems[0].text);
+  assert.equal(elements[0].style.maxWidth, "100%");
+});
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-progress-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-progress-test.js
new file mode 100644
index 0000000..8fcdfaf
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-progress-test.js
@@ -0,0 +1,73 @@
+/**
+ * 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-progress', 'Integration | Component | em progress', {
+  integration: true
+});
+
+test('It renders', function(assert) {
+
+  // Set any properties with this.set('myProperty', 'value');
+  // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL +
+
+  this.render(hbs`{{em-progress}}`);
+
+  assert.equal(this.$().text().trim(), '0%');
+
+  this.render(hbs`{{#em-progress}}{{/em-progress}}`);
+  assert.equal(this.$().text().trim(), '0%');
+});
+
+test('With a specific value', function(assert) {
+  this.render(hbs`{{em-progress value=0.5}}`);
+  assert.equal(this.$().text().trim(), '50%');
+});
+
+test('Custom valueMin & valueMax', function(assert) {
+  this.render(hbs`{{em-progress value=15 valueMin=10 valueMax=20}}`);
+  assert.equal(this.$().text().trim(), '50%');
+
+  assert.notOk(this.$('.striped')[0], "Striped class added");
+});
+
+test('Check for stripes & animation while in progress', function(assert) {
+  this.render(hbs`{{em-progress value=0.5 striped=true}}`);
+
+  assert.equal(this.$().text().trim(), '50%');
+  assert.ok(this.$('.striped')[0], "Striped class added");
+  assert.ok(this.$('.animated')[0], "Animated class should be added!");
+});
+
+test('Check for stripes & animation while starting', function(assert) {
+  this.render(hbs`{{em-progress value=0 striped=true}}`);
+
+  assert.equal(this.$().text().trim(), '0%');
+  assert.ok(this.$('.striped')[0], "Striped class added");
+  assert.ok(!this.$('.animated')[0], "Animated class shouldn't be added!");
+});
+
+test('Check for stripes & animation on completion', function(assert) {
+  this.render(hbs`{{em-progress value=1 striped=true}}`);
+
+  assert.equal(this.$().text().trim(), '100%');
+  assert.ok(this.$('.striped')[0], "Striped class added");
+  assert.ok(!this.$('.animated')[0], "Animated class shouldn't be added!");
+});
diff --git a/tez-ui/src/main/webapp/tests/unit/helpers/txt-test.js b/tez-ui/src/main/webapp/tests/unit/helpers/txt-test.js
new file mode 100644
index 0000000..18d3de7
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/helpers/txt-test.js
@@ -0,0 +1,59 @@
+/**
+ * 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 { txt } from '../../../helpers/txt';
+import { module, test } from 'qunit';
+
+module('Unit | Helper | txt');
+
+test('txt: created', function(assert) {
+  assert.ok(txt);
+});
+
+test('txt: String', function(assert) {
+  assert.equal(txt(["Abc"], {}), "Abc");
+  assert.equal(txt(null, {}).string, '<span class="txt-message"> Not Available! </span>');
+});
+
+test('txt: String - success', function(assert) {
+  assert.equal(txt(["Abc"], {}), "Abc");
+  assert.equal(txt(null, {}).string, '<span class="txt-message"> Not Available! </span>');
+  assert.equal(txt([null], {}).string, '<span class="txt-message"> Not Available! </span>');
+});
+
+test('txt: String - error', function(assert) {
+  var obj = {};
+
+  obj.toString = null;
+  assert.equal(txt([obj], {}).string, '<span class="txt-message"> Invalid Data! </span>');
+});
+
+test('txt: json', function(assert) {
+  var obj = {
+    x: 1,
+    y: 2
+  };
+  assert.equal(txt([obj], {
+    type: "json",
+  }).string, '{\n    &quot;x&quot;: 1,\n    &quot;y&quot;: 2\n}');
+});
+
+test('txt: error', function(assert) {
+  var err = new Error("testError");
+  assert.equal(txt([err], {}).string, '<span title="testError" class="txt-message"> testError </span>');
+});
diff --git a/tez-ui/src/main/webapp/tests/unit/utils/formatters-test.js b/tez-ui/src/main/webapp/tests/unit/utils/formatters-test.js
new file mode 100644
index 0000000..4ecc143
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/utils/formatters-test.js
@@ -0,0 +1,99 @@
+/**
+ * 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 fmts from '../../../utils/formatters';
+import { module, test } from 'qunit';
+
+import Ember from 'ember';
+
+module('Unit | Utility | formatters');
+
+test('Formatter functions created', function(assert) {
+  assert.ok(fmts);
+
+  assert.ok(fmts.date);
+  assert.ok(fmts.duration);
+  assert.ok(fmts.number);
+  assert.ok(fmts.memory);
+});
+
+test('duration', function(assert) {
+  var options = {
+    format: "long"
+  };
+  assert.equal(fmts.duration(0, options), "0 millisecond");
+  assert.equal(fmts.duration(1, options), "1 millisecond");
+  assert.equal(fmts.duration(60, options), "60 milliseconds");
+  assert.equal(fmts.duration(6000, options), "6 seconds");
+  assert.equal(fmts.duration(66000, options), "1 minute 6 seconds");
+  assert.equal(fmts.duration(666000, options), "11 minutes 6 seconds");
+  assert.equal(fmts.duration(6666000, options), "1 hour 51 minutes 6 seconds");
+  assert.equal(fmts.duration(66666000, options), "18 hours 31 minutes 6 seconds");
+
+  options = {
+    format: "short"
+  }; // By default format = short
+  assert.equal(fmts.duration(0, options), "0 msec");
+  assert.equal(fmts.duration(60, options), "60 msecs");
+  assert.equal(fmts.duration(6000, options), "6 secs");
+  assert.equal(fmts.duration(66000, options), "1 min 6 secs");
+  assert.equal(fmts.duration(666000, options), "11 mins 6 secs");
+  assert.equal(fmts.duration(6666000, options), "1 hr 51 mins 6 secs");
+  assert.equal(fmts.duration(66666000, options), "18 hrs 31 mins 6 secs");
+
+  assert.equal(fmts.duration(60.4, options), "60 msecs");
+  assert.equal(fmts.duration(60.6, options), "61 msecs");
+
+  options = {}; // By default format = xshort
+  assert.equal(fmts.duration(0, options), "0ms");
+  assert.equal(fmts.duration(60, options), "60ms");
+  assert.equal(fmts.duration(6000, options), "6s");
+  assert.equal(fmts.duration(66000, options), "1m 6s");
+  assert.equal(fmts.duration(666000, options), "11m 6s");
+  assert.equal(fmts.duration(6666000, options), "1h 51m 6s");
+  assert.equal(fmts.duration(66666000, options), "18h 31m 6s");
+});
+
+test('number', function(assert) {
+  assert.equal(fmts.number(6000, {}), "6,000");
+  assert.equal(fmts.number(6000000, {}), "6,000,000");
+});
+
+test('memory', function(assert) {
+  assert.equal(fmts.memory(0, {}), "0 B");
+  assert.equal(fmts.memory(600, {}), "600 B");
+  assert.equal(fmts.memory(1024, {}), "1 KB");
+  assert.equal(fmts.memory(1024 * 1024, {}), "1 MB");
+  assert.equal(fmts.memory(1024 * 1024 * 1024, {}), "1 GB");
+  assert.equal(fmts.memory(1024 * 1024 * 1024 * 1024, {}), "1 TB");
+});
+
+test('json', function(assert) {
+  var str = "testString",
+      complexObj = Ember.Object.create();
+
+  assert.equal(fmts.json(str, {}), str);
+  assert.equal(fmts.json(complexObj, {}), complexObj);
+
+  assert.equal(fmts.json(null, {}), null);
+  assert.equal(fmts.json(undefined, {}), undefined);
+
+  assert.equal(fmts.json({x: 1}, {}), '{\n    "x": 1\n}');
+  assert.equal(fmts.json({x: 1, y: 2}, {space: 1}), '{\n "x": 1,\n "y": 2\n}');
+  assert.equal(fmts.json({x: 1, y: {z: 3}}, {space: 1}), '{\n "x": 1,\n "y": {\n  "z": 3\n }\n}');
+});
diff --git a/tez-ui/src/main/webapp/yarn.lock b/tez-ui/src/main/webapp/yarn.lock
index fbcdd21..00250e8 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-helpers@0.8.0:
-  version "0.8.0"
-  resolved "https://registry.yarnpkg.com/em-helpers/-/em-helpers-0.8.0.tgz#01678f3692a61d563cce68e49459e206d14db095"
-  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-table@0.11.3:
   version "0.11.3"
   resolved "https://registry.yarnpkg.com/em-table/-/em-table-0.11.3.tgz#20e605cc3814214e644199399a2383cee8d23eeb"