changeset 187:a7a83b92b855

Convert customizable-error-message directive into component Rename shared/directives to shared/components Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-August/024757.html Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-September/024914.html
author Andrew Azores <aazores@redhat.com>
date Fri, 08 Sep 2017 11:18:34 -0400
parents 4da0690286ac
children 62fa62ee6512
files src/app/app.module.js src/app/components/jvm-info/jvm-memory/jvm-memory.module.js src/app/components/jvm-list/jvm-list.controller.js src/app/components/multichart/multichart.controller.js src/app/components/system-info/system-info.module.js src/app/shared/components/components.module.js src/app/shared/components/customizable-error-message/customizable-error-message.component.js src/app/shared/components/customizable-error-message/customizable-error-message.html src/app/shared/components/dismissible-error-message/dismissible-error-message.directive.js src/app/shared/components/dismissible-error-message/dismissible-error-message.directive.spec.js src/app/shared/components/dismissible-error-message/dismissible-error-message.html src/app/shared/components/dismissible-error-message/dismissible-error-message.module.js src/app/shared/components/multichart-add/en.locale.yaml src/app/shared/components/multichart-add/multichart-add.controller.js src/app/shared/components/multichart-add/multichart-add.controller.spec.js src/app/shared/components/multichart-add/multichart-add.directive.js src/app/shared/components/multichart-add/multichart-add.directive.spec.js src/app/shared/components/multichart-add/multichart-add.html src/app/shared/components/multichart-add/multichart-add.module.js src/app/shared/directives/customizable-error-message/customizable-error-message.directive.js src/app/shared/directives/customizable-error-message/customizable-error-message.directive.spec.js src/app/shared/directives/customizable-error-message/customizable-error-message.html src/app/shared/directives/customizable-error-message/customizable-error-message.module.js src/app/shared/directives/directives.module.js src/app/shared/directives/dismissible-error-message/dismissible-error-message.directive.js src/app/shared/directives/dismissible-error-message/dismissible-error-message.directive.spec.js src/app/shared/directives/dismissible-error-message/dismissible-error-message.html src/app/shared/directives/dismissible-error-message/dismissible-error-message.module.js src/app/shared/directives/multichart-add/en.locale.yaml src/app/shared/directives/multichart-add/multichart-add.controller.js src/app/shared/directives/multichart-add/multichart-add.controller.spec.js src/app/shared/directives/multichart-add/multichart-add.directive.js src/app/shared/directives/multichart-add/multichart-add.directive.spec.js src/app/shared/directives/multichart-add/multichart-add.html src/app/shared/directives/multichart-add/multichart-add.module.js
diffstat 35 files changed, 669 insertions(+), 803 deletions(-) [+]
line wrap: on
line diff
--- a/src/app/app.module.js	Fri Sep 08 10:56:14 2017 -0400
+++ b/src/app/app.module.js	Fri Sep 08 11:18:34 2017 -0400
@@ -37,7 +37,7 @@
 import {default as authModule, config as authModBootstrap} from 'components/auth/auth.module.js';
 import filters from 'shared/filters/filters.module.js';
 import services from 'shared/services/services.module.js';
-import directives from 'shared/directives/directives.module.js';
+import components from 'shared/components/components.module.js';
 import appRouting from './app.routing.js';
 import authInterceptorFactory from './auth-interceptor.factory.js';
 import AppController from './app.controller.js';
@@ -59,7 +59,7 @@
     // non-core modules
     services,
     filters,
-    directives,
+    components,
     appRouting,
     authInterceptorFactory,
     AppController
--- a/src/app/components/jvm-info/jvm-memory/jvm-memory.module.js	Fri Sep 08 10:56:14 2017 -0400
+++ b/src/app/components/jvm-info/jvm-memory/jvm-memory.module.js	Fri Sep 08 11:18:34 2017 -0400
@@ -27,12 +27,12 @@
 
 import JvmMemoryController from './jvm-memory.controller.js';
 import service from './jvm-memory.service.js';
-import directives from 'shared/directives/directives.module.js';
+import components from 'shared/components/components.module.js';
 
 export default angular
   .module('jvmMemory', [
     JvmMemoryController,
     service,
-    directives
+    components
   ])
   .name;
--- a/src/app/components/jvm-list/jvm-list.controller.js	Fri Sep 08 10:56:14 2017 -0400
+++ b/src/app/components/jvm-list/jvm-list.controller.js	Fri Sep 08 11:18:34 2017 -0400
@@ -26,7 +26,7 @@
  */
 
 import filters from 'shared/filters/filters.module.js';
-import directives from 'shared/directives/directives.module.js';
+import components from 'shared/components/components.module.js';
 import jvmListService from './jvm-list.service.js';
 import systemInfoService from 'components/system-info/system-info.service.js';
 
@@ -273,7 +273,7 @@
     'patternfly',
     'patternfly.toolbars',
     filters,
-    directives,
+    components,
     jvmListService,
     systemInfoService
   ])
--- a/src/app/components/multichart/multichart.controller.js	Fri Sep 08 10:56:14 2017 -0400
+++ b/src/app/components/multichart/multichart.controller.js	Fri Sep 08 11:18:34 2017 -0400
@@ -26,7 +26,7 @@
  */
 
 import services from 'shared/services/services.module.js';
-import directives from 'shared/directives/directives.module.js';
+import components from 'shared/components/components.module.js';
 
 class MultiChartController {
   constructor ($scope, multichartService, $translate) {
@@ -71,7 +71,7 @@
   .module('multichartController', [
     'patternfly',
     services,
-    directives
+    components
   ])
   .controller('MultichartController', MultiChartController)
   .name;
--- a/src/app/components/system-info/system-info.module.js	Fri Sep 08 10:56:14 2017 -0400
+++ b/src/app/components/system-info/system-info.module.js	Fri Sep 08 11:18:34 2017 -0400
@@ -29,7 +29,7 @@
 import SystemCpuController from './system-cpu.controller.js';
 import SystemMemoryController from './system-memory.controller.js';
 import service from './system-info.service.js';
-import directives from 'shared/directives/directives.module.js';
+import components from 'shared/components/components.module.js';
 
 export default angular
   .module('systemInfo', [
@@ -37,6 +37,6 @@
     SystemCpuController,
     SystemMemoryController,
     service,
-    directives
+    components
   ])
   .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/components.module.js	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+let components = [];
+let req = require.context('./', true, /\.component\.js/);
+req.keys().map(v => {
+  let name = req(v).default;
+  components.push(name);
+});
+
+export default angular
+  .module('app.component', components)
+  .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/customizable-error-message/customizable-error-message.component.js	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+export default angular
+  .module('customizableErrorMessage.component', [])
+  .component('customizableErrorMessage', {
+    bindings: {
+      errTitle: '<',
+      errMessage: '<',
+      dismissible: '<'
+    },
+    template: require('./customizable-error-message.html')
+  })
+  .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/customizable-error-message/customizable-error-message.html	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,8 @@
+<div ng-hide="showElement" class="alert alert-warning alert-dismissable" >
+  <button ng-show="{{$ctrl.dismissible}}" type="button" class="close" data-dismiss="alert" aria-hidden="true" ng-click="showElement=true">
+    <span class="pficon pficon-close"></span>
+  </button>
+
+  <span class="pficon pficon-warning-triangle-o"></span>
+  <strong>{{$ctrl.errTitle}}</strong> {{$ctrl.errMessage}}
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/dismissible-error-message/dismissible-error-message.directive.js	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import dismissibleErrorMessageTemplate from './dismissible-error-message.html';
+
+export let dismissibleErrorMessageFunc = () => {
+  return {
+    restrict: 'E',
+    scope: {
+      errTitle: '<',
+      errMessage: '<'
+    },
+    template: dismissibleErrorMessageTemplate
+  };
+};
+
+export default angular
+    .module('dismissibleErrorMessage.directive', [])
+    .directive('dismissibleErrorMessage', dismissibleErrorMessageFunc)
+    .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/dismissible-error-message/dismissible-error-message.directive.spec.js	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,76 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import directiveModule from './dismissible-error-message.directive.js';
+import {dismissibleErrorMessageFunc} from './dismissible-error-message.directive.js';
+
+describe('dismissibleErrorMessage Directive', () => {
+  let compiledDirectiveElement;
+  beforeEach(angular.mock.module(directiveModule));
+
+  let initDummyModule = () => {
+    let compile, rootScope;
+    angular.mock.inject(($compile, $rootScope) => {
+      'ngInject';
+      compile = $compile;
+      rootScope = $rootScope;
+    });
+
+    rootScope.errTitle = 'foo';
+    rootScope.errMessage = 'bar';
+    let element = '<dismissible-error-message err-title=errTitle ' +
+      'err-message=errMessage> </dismissible-error-message>';
+
+    compiledDirectiveElement = compile(element)(rootScope);
+    rootScope.$digest();
+  };
+
+  describe('dismissibleErrorMessage Directive.function', () => {
+    it('should return a valid object', () => {
+      let fnResult = dismissibleErrorMessageFunc();
+      fnResult.should.have.ownProperty('template');
+      fnResult.should.have.ownProperty('restrict');
+      fnResult.should.have.ownProperty('scope');
+    });
+  });
+
+  describe('dismissibleErrorMessage Directive.content', () => {
+    beforeEach(initDummyModule);
+
+    it('should be a valid object', () => {
+      should.exist(compiledDirectiveElement);
+    });
+
+    it('should insert the correct error title', () => {
+      compiledDirectiveElement.html().should.containEql('foo');
+    });
+
+    it('should insert the correct error message', () => {
+      compiledDirectiveElement.html().should.containEql('bar');
+    });
+  });
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/dismissible-error-message/dismissible-error-message.html	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,8 @@
+<div ng-hide="showElement" class="toast-pf alert alert-warning alert-dismissable" >
+  <button type="button" class="close" data-dismiss="alert" aria-hidden="true" ng-click="showElement=true">
+    <span class="pficon pficon-close"></span>
+  </button>
+
+  <span class="pficon pficon-warning-triangle-o"></span>
+  <strong>{{errTitle}}</strong> {{errMessage}}
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/dismissible-error-message/dismissible-error-message.module.js	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import directive from './dismissible-error-message.directive.js';
+
+export default angular
+  .module('dismissibleErrorMessage', [directive])
+  .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/multichart-add/en.locale.yaml	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,2 @@
+mcAdd:
+  TITLE: Multicharts
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/multichart-add/multichart-add.controller.js	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import servicesModule from 'shared/services/services.module.js';
+
+class MultichartAddController {
+  constructor (multichartService, $scope, $timeout) {
+    'ngInject';
+    this.svc = multichartService;
+    this.scope = $scope;
+    this.scope.multicharts = multichartService.chartNames;
+
+    this.scope.$watch('multicharts', cur => {
+      $timeout(() => {
+        cur.forEach(chart => {
+          let el = angular.element('#' + chart + '-' + this.scope.svcName);
+          el.bootstrapSwitch();
+          el.on('switchChange.bootstrapSwitch', event => {
+            this.toggleChart(event.currentTarget.getAttribute('data-chart'));
+          });
+        });
+      });
+    });
+  }
+
+  toggleChart (chartName) {
+    if (this.isInChart(chartName)) {
+      this.removeFromChart(chartName);
+    } else {
+      this.addToChart(chartName);
+    }
+  }
+
+  removeFromChart (chartName) {
+    this.svc.removeService(chartName, this.scope.svcName);
+  }
+
+  addToChart (chartName) {
+    this.svc.addService(chartName, this.scope.svcName, this.scope.getFn);
+  }
+
+  isInChart (chartName) {
+    return this.svc.hasServiceForChart(chartName, this.scope.svcName);
+  }
+}
+
+export default angular
+  .module('multichartAddControllerModule', [servicesModule])
+  .controller('MultichartAddController', MultichartAddController)
+  .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/multichart-add/multichart-add.controller.spec.js	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,172 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import controllerModule from './multichart-add.controller.js';
+
+describe('MultichartAddController', () => {
+
+  let svc, scope, timeout, ctrl;
+
+  beforeEach(() => {
+    angular.mock.module(controllerModule);
+    angular.mock.inject($controller => {
+      'ngInject';
+
+      svc = {
+        chartNames: ['foo', 'bar'],
+        addService: sinon.spy(),
+        removeService: sinon.spy(),
+        hasServiceForChart: sinon.stub().returns(true)
+      };
+
+      scope = {
+        svcName: 'foo-svc',
+        getFn: sinon.spy(),
+        $watch: sinon.spy()
+      };
+
+      timeout = sinon.spy();
+
+      ctrl = $controller('MultichartAddController', {
+        multichartService: svc,
+        $scope: scope,
+        $timeout: timeout
+      });
+    });
+  });
+
+  it('should exist', () => {
+    should.exist(ctrl);
+  });
+
+  it('should assign multichart names from service', () => {
+    scope.multicharts.should.deepEqual(svc.chartNames);
+  });
+
+  describe('bootstrapSwitch', () => {
+    let mockElem;
+    beforeEach(() => {
+      mockElem = {
+        bootstrapSwitch: sinon.spy(),
+        on: sinon.spy()
+      };
+      sinon.stub(angular, 'element').returns(mockElem);
+    });
+
+    afterEach(() => {
+      angular.element.restore();
+    });
+
+    it('should watch on multicharts', () => {
+      scope.$watch.should.be.calledWith('multicharts', sinon.match.func);
+    });
+
+    it('should use $timeout to wait for $scope $digest to complete', () => {
+      timeout.should.not.be.called();
+      let fn = scope.$watch.firstCall.args[1];
+      fn();
+      timeout.should.be.calledOnce();
+      timeout.should.be.calledWith(sinon.match.func);
+    });
+
+    it('should set up bootstrapSwitch functionality for each switch', () => {
+      svc.removeService.should.not.be.called();
+      let fn = scope.$watch.firstCall.args[1];
+      fn(['foo']);
+      timeout.yield();
+
+      angular.element.should.be.calledOnce();
+      angular.element.should.be.calledWith('#foo-foo-svc');
+      mockElem.bootstrapSwitch.should.be.calledOnce();
+      mockElem.on.should.be.calledOnce();
+      mockElem.on.should.be.calledWith('switchChange.bootstrapSwitch', sinon.match.func);
+      let evtHandler = mockElem.on.firstCall.args[1];
+      evtHandler({
+        currentTarget: {
+          getAttribute: () => 'foo-chart'
+        }
+      });
+      svc.removeService.should.be.calledOnce();
+      svc.removeService.should.be.calledWith('foo-chart', 'foo-svc');
+    });
+  });
+
+  describe('isInChart', () => {
+    it('should delegate to service', () => {
+      svc.hasServiceForChart.should.not.be.called();
+      ctrl.isInChart('foo').should.be.true();
+      svc.hasServiceForChart.should.be.calledOnce();
+      svc.hasServiceForChart.should.be.calledWith('foo', scope.svcName);
+    });
+  });
+
+  describe('addToChart', () => {
+    it('should delegate to service', () => {
+      svc.addService.should.not.be.called();
+      ctrl.addToChart('foo');
+      svc.addService.should.be.calledOnce();
+      svc.addService.should.be.calledWith('foo', scope.svcName, scope.getFn);
+    });
+  });
+
+  describe('removeFromChart', () => {
+    it('should delegate to service', () => {
+      svc.removeService.should.not.be.called();
+      ctrl.removeFromChart('foo');
+      svc.removeService.should.be.calledOnce();
+      svc.removeService.should.be.calledWith('foo', scope.svcName);
+    });
+  });
+
+  describe('toggleChart', () => {
+    it('should remove if already added', () => {
+      svc.hasServiceForChart.should.not.be.called();
+      svc.addService.should.not.be.called();
+      svc.removeService.should.not.be.called();
+      ctrl.toggleChart('foo');
+      svc.hasServiceForChart.should.be.calledOnce();
+      svc.hasServiceForChart.should.be.calledWith('foo', scope.svcName);
+      svc.addService.should.not.be.called();
+      svc.removeService.should.be.calledOnce();
+      svc.removeService.should.be.calledWith('foo', scope.svcName);
+    });
+
+    it('should add if not present', () => {
+      svc.hasServiceForChart.returns(false);
+      svc.hasServiceForChart.should.not.be.called();
+      svc.addService.should.not.be.called();
+      svc.removeService.should.not.be.called();
+      ctrl.toggleChart('foo');
+      svc.hasServiceForChart.should.be.calledOnce();
+      svc.hasServiceForChart.should.be.calledWith('foo', scope.svcName);
+      svc.addService.should.be.calledOnce();
+      svc.addService.should.be.calledWith('foo', scope.svcName);
+      svc.removeService.should.not.be.called();
+    });
+  });
+
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/multichart-add/multichart-add.directive.js	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,48 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import controller from './multichart-add.controller.js';
+
+function configFactory () {
+  return {
+    restrict: 'EA',
+    controller: 'MultichartAddController',
+    controllerAs: 'ctrl',
+    template: require('./multichart-add.html'),
+    scope: {
+      svcName: '@',
+      getFn: '&'
+    }
+  };
+}
+
+export { configFactory };
+
+export default angular
+  .module('multichartAddDirective', [controller])
+  .directive('mcAdd', configFactory)
+  .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/multichart-add/multichart-add.directive.spec.js	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,62 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import { configFactory } from './multichart-add.directive.js';
+
+describe('MultichartAddDirective', () => {
+  let cfg;
+  beforeEach(() => {
+    cfg = configFactory();
+  });
+
+  it('should be restricted to an element or attribute', () => {
+    cfg.restrict.should.have.length(2);
+    cfg.restrict.should.containEql('E');
+    cfg.restrict.should.containEql('A');
+  });
+
+  it('should expect a svcName string in scope', () => {
+    cfg.scope.should.have.ownProperty('svcName');
+    cfg.scope.svcName.should.equal('@');
+  });
+
+  it('should expect a getFn expression in scope', () => {
+    cfg.scope.should.have.ownProperty('getFn');
+    cfg.scope.getFn.should.equal('&');
+  });
+
+  it('should use correct template', () => {
+    cfg.template.should.equal(require('./multichart-add.html'));
+  });
+
+  it('should attach multichartAddController', () => {
+    cfg.should.have.ownProperty('controller');
+    cfg.controller.should.equal('MultichartAddController');
+    cfg.should.have.ownProperty('controllerAs');
+    cfg.controllerAs.should.equal('ctrl');
+  });
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/multichart-add/multichart-add.html	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,22 @@
+<div class="dropdown dropdown-kebab-pf">
+  <button class="btn btn-link dropdown-toggle" type="button" id="kebab" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
+    <span class="fa fa-ellipsis-v"></span>
+  </button>
+  <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="kebab">
+    <li><a ui-sref="multichart" translate>mcAdd.TITLE</a></li>
+    <li role="separator" class="divider"></li>
+    <li ng-repeat="chart in multicharts">
+      <div class="text-right">
+        <div class="form-group">
+          <label for="switch-{{chart}}-{{svcName}}" class="label label-info pull-left">{{chart}}</label>
+          <input class="bootstrap-switch pull-right" id="{{chart}}-{{svcName}}" name="switch-{{chart}}-{{svcName}}" data-chart="{{chart}}"
+                                                                                                                    data-size="mini"
+                                                                                                                    type="checkbox"
+                                                                                                                    ng-checked="ctrl.isInChart(chart)"
+                                                                                                                    />
+        </div>
+      </div>
+    </li>
+    <li>
+  </ul>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/shared/components/multichart-add/multichart-add.module.js	Fri Sep 08 11:18:34 2017 -0400
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import directive from './multichart-add.directive.js';
+import controller from './multichart-add.controller.js';
+
+export default angular
+  .module('multichartAddModule', [
+    directive,
+    controller
+  ])
+  .name;
--- a/src/app/shared/directives/customizable-error-message/customizable-error-message.directive.js	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import customizableErrorMessageTemplate from './customizable-error-message.html';
-
-export let customizableErrorMessageFunc = () => {
-  return {
-    restrict: 'E',
-    scope: {
-      errTitle: '<',
-      errMessage: '<',
-      dismissible: '<'
-    },
-    template: customizableErrorMessageTemplate
-  };
-};
-
-export default angular
-    .module('customizableErrorMessage.directive', [])
-    .directive('customizableErrorMessage', customizableErrorMessageFunc)
-    .name;
--- a/src/app/shared/directives/customizable-error-message/customizable-error-message.directive.spec.js	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import directiveModule from './customizable-error-message.directive.js';
-import {customizableErrorMessageFunc} from './customizable-error-message.directive.js';
-
-describe('customizableErrorMessage Directive', () => {
-  let compiledDirectiveElement;
-  let element = '<customizable-error-message err-title=errTitle err-message=errMessage> </customizable-error-message>';
-
-  beforeEach(angular.mock.module(directiveModule));
-
-  let initDummyModule = () => {
-    let compile, rootScope;
-    angular.mock.inject(($compile, $rootScope) => {
-      'ngInject';
-      compile = $compile;
-      rootScope = $rootScope;
-    });
-
-    rootScope.errTitle = 'foo';
-    rootScope.errMessage = 'bar';
-
-    compiledDirectiveElement = compile(element)(rootScope);
-    rootScope.$digest();
-  };
-
-  describe('customizableErrorMessage Directive.function', () => {
-    it('should return a valid object', () => {
-      let fnResult = customizableErrorMessageFunc();
-      fnResult.should.have.ownProperty('template');
-      fnResult.should.have.ownProperty('restrict');
-      fnResult.should.have.ownProperty('scope');
-    });
-  });
-
-  describe('customizableErrorMessage Directive.content', () => {
-    beforeEach(initDummyModule);
-
-    it('should be a valid object', () => {
-      should.exist(compiledDirectiveElement);
-    });
-
-    it('should insert the correct error title', () => {
-      compiledDirectiveElement.html().should.containEql('foo');
-    });
-
-    it('should insert the correct error message', () => {
-      compiledDirectiveElement.html().should.containEql('bar');
-    });
-  });
-
-  describe('customizableErrorMessage Directive.content', () => {
-
-    beforeEach(initDummyModule);
-
-    it('should be a valid object', () => {
-      should.exist(compiledDirectiveElement);
-    });
-
-    it('should insert the correct error title', () => {
-      compiledDirectiveElement.html().should.containEql('foo');
-    });
-
-    it('should insert the correct error message', () => {
-      compiledDirectiveElement.html().should.containEql('bar');
-    });
-  });
-});
--- a/src/app/shared/directives/customizable-error-message/customizable-error-message.html	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-<div ng-hide="showElement" class="alert alert-warning alert-dismissable" >
-  <button ng-show="{{dismissible}}" type="button" class="close" data-dismiss="alert" aria-hidden="true" ng-click="showElement=true">
-    <span class="pficon pficon-close"></span>
-  </button>
-
-  <span class="pficon pficon-warning-triangle-o"></span>
-  <strong>{{errTitle}}</strong> {{errMessage}}
-</div>
--- a/src/app/shared/directives/customizable-error-message/customizable-error-message.module.js	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import directive from './customizable-error-message.directive.js';
-
-export default angular
-  .module('customizableErrorMessageDirective', [directive])
-  .name;
--- a/src/app/shared/directives/directives.module.js	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-let mods = [];
-let req = require.context('./', true, /\.module\.js/);
-req.keys().map(v => {
-  let name = req(v).default;
-  if (name) {
-    mods.push(name);
-  }
-});
-
-export default angular
-  .module('app.directives', mods)
-  .name;
--- a/src/app/shared/directives/dismissible-error-message/dismissible-error-message.directive.js	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import dismissibleErrorMessageTemplate from './dismissible-error-message.html';
-
-export let dismissibleErrorMessageFunc = () => {
-  return {
-    restrict: 'E',
-    scope: {
-      errTitle: '<',
-      errMessage: '<'
-    },
-    template: dismissibleErrorMessageTemplate
-  };
-};
-
-export default angular
-    .module('dismissibleErrorMessage.directive', [])
-    .directive('dismissibleErrorMessage', dismissibleErrorMessageFunc)
-    .name;
--- a/src/app/shared/directives/dismissible-error-message/dismissible-error-message.directive.spec.js	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import directiveModule from './dismissible-error-message.directive.js';
-import {dismissibleErrorMessageFunc} from './dismissible-error-message.directive.js';
-
-describe('dismissibleErrorMessage Directive', () => {
-  let compiledDirectiveElement;
-  beforeEach(angular.mock.module(directiveModule));
-
-  let initDummyModule = () => {
-    let compile, rootScope;
-    angular.mock.inject(($compile, $rootScope) => {
-      'ngInject';
-      compile = $compile;
-      rootScope = $rootScope;
-    });
-
-    rootScope.errTitle = 'foo';
-    rootScope.errMessage = 'bar';
-    let element = '<dismissible-error-message err-title=errTitle ' +
-      'err-message=errMessage> </dismissible-error-message>';
-
-    compiledDirectiveElement = compile(element)(rootScope);
-    rootScope.$digest();
-  };
-
-  describe('dismissibleErrorMessage Directive.function', () => {
-    it('should return a valid object', () => {
-      let fnResult = dismissibleErrorMessageFunc();
-      fnResult.should.have.ownProperty('template');
-      fnResult.should.have.ownProperty('restrict');
-      fnResult.should.have.ownProperty('scope');
-    });
-  });
-
-  describe('dismissibleErrorMessage Directive.content', () => {
-    beforeEach(initDummyModule);
-
-    it('should be a valid object', () => {
-      should.exist(compiledDirectiveElement);
-    });
-
-    it('should insert the correct error title', () => {
-      compiledDirectiveElement.html().should.containEql('foo');
-    });
-
-    it('should insert the correct error message', () => {
-      compiledDirectiveElement.html().should.containEql('bar');
-    });
-  });
-});
--- a/src/app/shared/directives/dismissible-error-message/dismissible-error-message.html	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-<div ng-hide="showElement" class="toast-pf alert alert-warning alert-dismissable" >
-  <button type="button" class="close" data-dismiss="alert" aria-hidden="true" ng-click="showElement=true">
-    <span class="pficon pficon-close"></span>
-  </button>
-
-  <span class="pficon pficon-warning-triangle-o"></span>
-  <strong>{{errTitle}}</strong> {{errMessage}}
-</div>
--- a/src/app/shared/directives/dismissible-error-message/dismissible-error-message.module.js	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import directive from './dismissible-error-message.directive.js';
-
-export default angular
-  .module('dismissibleErrorMessage', [directive])
-  .name;
--- a/src/app/shared/directives/multichart-add/en.locale.yaml	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-mcAdd:
-  TITLE: Multicharts
--- a/src/app/shared/directives/multichart-add/multichart-add.controller.js	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import servicesModule from 'shared/services/services.module.js';
-
-class MultichartAddController {
-  constructor (multichartService, $scope, $timeout) {
-    'ngInject';
-    this.svc = multichartService;
-    this.scope = $scope;
-    this.scope.multicharts = multichartService.chartNames;
-
-    this.scope.$watch('multicharts', cur => {
-      $timeout(() => {
-        cur.forEach(chart => {
-          let el = angular.element('#' + chart + '-' + this.scope.svcName);
-          el.bootstrapSwitch();
-          el.on('switchChange.bootstrapSwitch', event => {
-            this.toggleChart(event.currentTarget.getAttribute('data-chart'));
-          });
-        });
-      });
-    });
-  }
-
-  toggleChart (chartName) {
-    if (this.isInChart(chartName)) {
-      this.removeFromChart(chartName);
-    } else {
-      this.addToChart(chartName);
-    }
-  }
-
-  removeFromChart (chartName) {
-    this.svc.removeService(chartName, this.scope.svcName);
-  }
-
-  addToChart (chartName) {
-    this.svc.addService(chartName, this.scope.svcName, this.scope.getFn);
-  }
-
-  isInChart (chartName) {
-    return this.svc.hasServiceForChart(chartName, this.scope.svcName);
-  }
-}
-
-export default angular
-  .module('multichartAddControllerModule', [servicesModule])
-  .controller('MultichartAddController', MultichartAddController)
-  .name;
--- a/src/app/shared/directives/multichart-add/multichart-add.controller.spec.js	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,172 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import controllerModule from './multichart-add.controller.js';
-
-describe('MultichartAddController', () => {
-
-  let svc, scope, timeout, ctrl;
-
-  beforeEach(() => {
-    angular.mock.module(controllerModule);
-    angular.mock.inject($controller => {
-      'ngInject';
-
-      svc = {
-        chartNames: ['foo', 'bar'],
-        addService: sinon.spy(),
-        removeService: sinon.spy(),
-        hasServiceForChart: sinon.stub().returns(true)
-      };
-
-      scope = {
-        svcName: 'foo-svc',
-        getFn: sinon.spy(),
-        $watch: sinon.spy()
-      };
-
-      timeout = sinon.spy();
-
-      ctrl = $controller('MultichartAddController', {
-        multichartService: svc,
-        $scope: scope,
-        $timeout: timeout
-      });
-    });
-  });
-
-  it('should exist', () => {
-    should.exist(ctrl);
-  });
-
-  it('should assign multichart names from service', () => {
-    scope.multicharts.should.deepEqual(svc.chartNames);
-  });
-
-  describe('bootstrapSwitch', () => {
-    let mockElem;
-    beforeEach(() => {
-      mockElem = {
-        bootstrapSwitch: sinon.spy(),
-        on: sinon.spy()
-      };
-      sinon.stub(angular, 'element').returns(mockElem);
-    });
-
-    afterEach(() => {
-      angular.element.restore();
-    });
-
-    it('should watch on multicharts', () => {
-      scope.$watch.should.be.calledWith('multicharts', sinon.match.func);
-    });
-
-    it('should use $timeout to wait for $scope $digest to complete', () => {
-      timeout.should.not.be.called();
-      let fn = scope.$watch.firstCall.args[1];
-      fn();
-      timeout.should.be.calledOnce();
-      timeout.should.be.calledWith(sinon.match.func);
-    });
-
-    it('should set up bootstrapSwitch functionality for each switch', () => {
-      svc.removeService.should.not.be.called();
-      let fn = scope.$watch.firstCall.args[1];
-      fn(['foo']);
-      timeout.yield();
-
-      angular.element.should.be.calledOnce();
-      angular.element.should.be.calledWith('#foo-foo-svc');
-      mockElem.bootstrapSwitch.should.be.calledOnce();
-      mockElem.on.should.be.calledOnce();
-      mockElem.on.should.be.calledWith('switchChange.bootstrapSwitch', sinon.match.func);
-      let evtHandler = mockElem.on.firstCall.args[1];
-      evtHandler({
-        currentTarget: {
-          getAttribute: () => 'foo-chart'
-        }
-      });
-      svc.removeService.should.be.calledOnce();
-      svc.removeService.should.be.calledWith('foo-chart', 'foo-svc');
-    });
-  });
-
-  describe('isInChart', () => {
-    it('should delegate to service', () => {
-      svc.hasServiceForChart.should.not.be.called();
-      ctrl.isInChart('foo').should.be.true();
-      svc.hasServiceForChart.should.be.calledOnce();
-      svc.hasServiceForChart.should.be.calledWith('foo', scope.svcName);
-    });
-  });
-
-  describe('addToChart', () => {
-    it('should delegate to service', () => {
-      svc.addService.should.not.be.called();
-      ctrl.addToChart('foo');
-      svc.addService.should.be.calledOnce();
-      svc.addService.should.be.calledWith('foo', scope.svcName, scope.getFn);
-    });
-  });
-
-  describe('removeFromChart', () => {
-    it('should delegate to service', () => {
-      svc.removeService.should.not.be.called();
-      ctrl.removeFromChart('foo');
-      svc.removeService.should.be.calledOnce();
-      svc.removeService.should.be.calledWith('foo', scope.svcName);
-    });
-  });
-
-  describe('toggleChart', () => {
-    it('should remove if already added', () => {
-      svc.hasServiceForChart.should.not.be.called();
-      svc.addService.should.not.be.called();
-      svc.removeService.should.not.be.called();
-      ctrl.toggleChart('foo');
-      svc.hasServiceForChart.should.be.calledOnce();
-      svc.hasServiceForChart.should.be.calledWith('foo', scope.svcName);
-      svc.addService.should.not.be.called();
-      svc.removeService.should.be.calledOnce();
-      svc.removeService.should.be.calledWith('foo', scope.svcName);
-    });
-
-    it('should add if not present', () => {
-      svc.hasServiceForChart.returns(false);
-      svc.hasServiceForChart.should.not.be.called();
-      svc.addService.should.not.be.called();
-      svc.removeService.should.not.be.called();
-      ctrl.toggleChart('foo');
-      svc.hasServiceForChart.should.be.calledOnce();
-      svc.hasServiceForChart.should.be.calledWith('foo', scope.svcName);
-      svc.addService.should.be.calledOnce();
-      svc.addService.should.be.calledWith('foo', scope.svcName);
-      svc.removeService.should.not.be.called();
-    });
-  });
-
-});
--- a/src/app/shared/directives/multichart-add/multichart-add.directive.js	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import controller from './multichart-add.controller.js';
-
-function configFactory () {
-  return {
-    restrict: 'EA',
-    controller: 'MultichartAddController',
-    controllerAs: 'ctrl',
-    template: require('./multichart-add.html'),
-    scope: {
-      svcName: '@',
-      getFn: '&'
-    }
-  };
-}
-
-export { configFactory };
-
-export default angular
-  .module('multichartAddDirective', [controller])
-  .directive('mcAdd', configFactory)
-  .name;
--- a/src/app/shared/directives/multichart-add/multichart-add.directive.spec.js	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import { configFactory } from './multichart-add.directive.js';
-
-describe('MultichartAddDirective', () => {
-  let cfg;
-  beforeEach(() => {
-    cfg = configFactory();
-  });
-
-  it('should be restricted to an element or attribute', () => {
-    cfg.restrict.should.have.length(2);
-    cfg.restrict.should.containEql('E');
-    cfg.restrict.should.containEql('A');
-  });
-
-  it('should expect a svcName string in scope', () => {
-    cfg.scope.should.have.ownProperty('svcName');
-    cfg.scope.svcName.should.equal('@');
-  });
-
-  it('should expect a getFn expression in scope', () => {
-    cfg.scope.should.have.ownProperty('getFn');
-    cfg.scope.getFn.should.equal('&');
-  });
-
-  it('should use correct template', () => {
-    cfg.template.should.equal(require('./multichart-add.html'));
-  });
-
-  it('should attach multichartAddController', () => {
-    cfg.should.have.ownProperty('controller');
-    cfg.controller.should.equal('MultichartAddController');
-    cfg.should.have.ownProperty('controllerAs');
-    cfg.controllerAs.should.equal('ctrl');
-  });
-});
--- a/src/app/shared/directives/multichart-add/multichart-add.html	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-<div class="dropdown dropdown-kebab-pf">
-  <button class="btn btn-link dropdown-toggle" type="button" id="kebab" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
-    <span class="fa fa-ellipsis-v"></span>
-  </button>
-  <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="kebab">
-    <li><a ui-sref="multichart" translate>mcAdd.TITLE</a></li>
-    <li role="separator" class="divider"></li>
-    <li ng-repeat="chart in multicharts">
-      <div class="text-right">
-        <div class="form-group">
-          <label for="switch-{{chart}}-{{svcName}}" class="label label-info pull-left">{{chart}}</label>
-          <input class="bootstrap-switch pull-right" id="{{chart}}-{{svcName}}" name="switch-{{chart}}-{{svcName}}" data-chart="{{chart}}"
-                                                                                                                    data-size="mini"
-                                                                                                                    type="checkbox"
-                                                                                                                    ng-checked="ctrl.isInChart(chart)"
-                                                                                                                    />
-        </div>
-      </div>
-    </li>
-    <li>
-  </ul>
-</div>
--- a/src/app/shared/directives/multichart-add/multichart-add.module.js	Fri Sep 08 10:56:14 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import directive from './multichart-add.directive.js';
-import controller from './multichart-add.controller.js';
-
-export default angular
-  .module('multichartAddModule', [
-    directive,
-    controller
-  ])
-  .name;