diff options
Diffstat (limited to 'ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget')
4 files changed, 578 insertions, 0 deletions
diff --git a/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.js b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.js new file mode 100644 index 00000000..9ac57b19 --- /dev/null +++ b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.js @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2014 DataTorrent, Inc. ALL Rights Reserved. + * + * Licensed 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. + */ + +'use strict'; + +angular.module('ui.dashboard') + .controller('DashboardWidgetCtrl', ['$scope', '$element', '$compile', '$window', '$timeout', + function($scope, $element, $compile, $window, $timeout) { + + $scope.status = { + isopen: false + }; + + // Fills "container" with compiled view + $scope.makeTemplateString = function() { + + var widget = $scope.widget; + + // First, build template string + var templateString = ''; + + if (widget.templateUrl) { + + // Use ng-include for templateUrl + templateString = '<div ng-include="\'' + widget.templateUrl + '\'"></div>'; + + } else if (widget.template) { + + // Direct string template + templateString = widget.template; + + } else { + + // Assume attribute directive + templateString = '<div ' + widget.directive; + + // Check if data attribute was specified + if (widget.dataAttrName) { + widget.attrs = widget.attrs || {}; + widget.attrs[widget.dataAttrName] = 'widgetData'; + } + + // Check for specified attributes + if (widget.attrs) { + + // First check directive name attr + if (widget.attrs[widget.directive]) { + templateString += '="' + widget.attrs[widget.directive] + '"'; + } + + // Add attributes + _.each(widget.attrs, function(value, attr) { + + // make sure we aren't reusing directive attr + if (attr !== widget.directive) { + templateString += ' ' + attr + '="' + value + '"'; + } + + }); + } + templateString += '></div>'; + } + return templateString; + }; + + $scope.grabResizer = function(e) { + + var widget = $scope.widget; + var widgetElm = $element.find('.widget'); + + // ignore middle- and right-click + if (e.which !== 1) { + return; + } + + e.stopPropagation(); + e.originalEvent.preventDefault(); + + // get the starting horizontal position + var initX = e.clientX; + // console.log('initX', initX); + + // Get the current width of the widget and dashboard + var pixelWidth = widgetElm.width(); + var pixelHeight = widgetElm.height(); + var widgetStyleWidth = widget.containerStyle.width; + var widthUnits = widget.widthUnits; + var unitWidth = parseFloat(widgetStyleWidth); + + // create marquee element for resize action + var $marquee = angular.element('<div class="widget-resizer-marquee" style="height: ' + pixelHeight + 'px; width: ' + pixelWidth + 'px; z-index:'+ 200 +';"></div>'); + widgetElm.append($marquee); + // create an overlaying div to block other widgets in order to stop their iframe events from being triggered + var $marquee2 = angular.element('<div style=" position: absolute; top: 0; left: 0; height: ' + pixelHeight + 'px; width: ' + (pixelWidth+200) + 'px; z-index:'+ 100 +';"></div>'); + widgetElm.append($marquee2); + + // determine the unit/pixel ratio + var transformMultiplier = unitWidth / pixelWidth; + + // updates marquee with preview of new width + var mousemove = function(e) { + var curX = e.clientX; +// console.log(curX); +// console.log(e); + var pixelChange = curX - initX; + var newWidth = pixelWidth + pixelChange; + $marquee.css('width', newWidth + 'px'); + $marquee2.css('width', (newWidth + 200) + 'px'); + + }; + + // sets new widget width on mouseup + var mouseup = function(e) { + // remove listener and marquee + jQuery($window).off('mousemove', mousemove); + $marquee.remove(); + $marquee2.remove(); + + // calculate change in units + var curX = e.clientX; + var pixelChange = curX - initX; + var unitChange = Math.round(pixelChange * transformMultiplier * 100) / 100; + + // add to initial unit width + var newWidth = unitWidth * 1 + unitChange; + widget.setWidth(newWidth, widthUnits); + $scope.$emit('widgetChanged', widget); + $scope.$apply(); + $scope.$broadcast('widgetResized', { + width: newWidth + }); + }; + +// jQuery($window).on('mousemove', mousemove).one('mouseup', mouseup); + jQuery($window).on('mousemove', mousemove).one('mouseup', mouseup); + }; + + //TODO refactor + $scope.grabSouthResizer = function(e) { + var widgetElm = $element.find('.widget'); + + // ignore middle- and right-click + if (e.which !== 1) { + return; + } + + e.stopPropagation(); + e.originalEvent.preventDefault(); + + // get the starting horizontal position + var initY = e.clientY; + // console.log('initX', initX); + + // Get the current width of the widget and dashboard + var pixelWidth = widgetElm.width(); + var pixelHeight = widgetElm.height(); + + // create marquee element for resize action + var $marquee = angular.element('<div class="widget-resizer-marquee" style="height: ' + pixelHeight + 'px; width: ' + pixelWidth + 'px;"></div>'); + widgetElm.append($marquee); + + // updates marquee with preview of new height + var mousemove = function(e) { + var curY = e.clientY; + var pixelChange = curY - initY; + var newHeight = pixelHeight + pixelChange; + $marquee.css('height', newHeight + 'px'); + }; + + // sets new widget width on mouseup + var mouseup = function(e) { + // remove listener and marquee + jQuery($window).off('mousemove', mousemove); + $marquee.remove(); + + // calculate height change + var curY = e.clientY; + var pixelChange = curY - initY; + + //var widgetContainer = widgetElm.parent(); // widget container responsible for holding widget width and height + var widgetContainer = widgetElm.find('.widget-content'); + + var diff = pixelChange; + var height = parseInt(widgetContainer.css('height'), 10); + var newHeight = (height + diff); + + //$scope.widget.style.height = newHeight + 'px'; + + $scope.widget.setHeight(newHeight + 'px'); + + $scope.$emit('widgetChanged', $scope.widget); + $scope.$apply(); // make AngularJS to apply style changes + + $scope.$broadcast('widgetResized', { + height: newHeight + }); + }; + + jQuery($window).on('mousemove', mousemove).one('mouseup', mouseup); + }; + + // replaces widget title with input + $scope.editTitle = function(widget) { + var widgetElm = $element.find('.widget'); + widget.editingTitle = true; + // HACK: get the input to focus after being displayed. + $timeout(function() { + widgetElm.find('form.widget-title input:eq(0)').focus()[0].setSelectionRange(0, 9999); + }); + }; + + // saves whatever is in the title input as the new title + $scope.saveTitleEdit = function(widget) { + widget.editingTitle = false; + $scope.$emit('widgetChanged', widget); + }; + + $scope.compileTemplate = function() { + var container = $scope.findWidgetContainer($element); + var templateString = $scope.makeTemplateString(); + var widgetElement = angular.element(templateString); + + container.empty(); + container.append(widgetElement); + $compile(widgetElement)($scope); + }; + + $scope.findWidgetContainer = function(element) { + // widget placeholder is the first (and only) child of .widget-content + return element.find('.widget-content'); + }; + } + ]);
\ No newline at end of file diff --git a/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.spec.js b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.spec.js new file mode 100644 index 00000000..55604646 --- /dev/null +++ b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/DashboardWidgetCtrl.spec.js @@ -0,0 +1,164 @@ +'use strict'; + +describe('Controller: DashboardWidgetCtrl', function() { + + var $scope, $element, $timeout, injections; + + beforeEach(module('ui.dashboard')); + + beforeEach(inject(function($rootScope, $controller){ + $scope = $rootScope.$new(); + $element = angular.element('<div><div class="widget"></div></div>'); + $timeout = function timeout(fn) { + fn(); + }; + injections = { + $scope: $scope, + $element: $element, + $timeout: $timeout + }; + spyOn(injections, '$timeout'); + $controller('DashboardWidgetCtrl', injections); + })); + + describe('the makeTemplateString method', function() { + + it('should return a string', function() { + $scope.widget = { + templateUrl: 'some/template.html' + }; + expect(typeof $scope.makeTemplateString()).toEqual('string'); + }); + + it('should use ng-include if templateUrl is specified on widget, despite any other options', function() { + $scope.widget = { + templateUrl: 'some/template.html', + template: 'not this one', + directive: 'or-this', + attrs: { + something: 'awesome', + other: 'thing' + } + }; + expect($scope.makeTemplateString()).toMatch(/ng-include="'some\/template\.html'"/); + }); + + it('should return widget.template if specified, regardless of presence of directive or attrs', function() { + $scope.widget = { + template: '<div class="testing"></div>', + directive: 'no-good' + }; + expect($scope.makeTemplateString()).toEqual($scope.widget.template); + }); + + it('should use widget.directive as attribute directive', function() { + $scope.widget = { + directive: 'ng-awesome' + }; + expect($scope.makeTemplateString()).toEqual('<div ng-awesome></div>'); + }); + + it('should attach attributes if provided', function() { + $scope.widget = { + directive: 'ng-awesome', + attrs: { + 'ng-awesome': 'test1', + other: 'attr', + more: 'stuff' + } + }; + expect($scope.makeTemplateString()).toEqual('<div ng-awesome="test1" other="attr" more="stuff"></div>'); + }); + + it('should place widgetData into dataAttrName attribute if specified', function() { + $scope.widget = { + directive: 'ng-awesome', + attrs: { + 'ng-awesome': 'test1', + other: 'attr', + more: 'stuff' + }, + dataAttrName: 'data' + }; + expect($scope.makeTemplateString()).toEqual('<div ng-awesome="test1" other="attr" more="stuff" data="widgetData"></div>'); + }); + + it('should add attrs to the widget object if it does not exist and dataAttrName is specified', function() { + $scope.widget = { + directive: 'ng-awesome', + dataAttrName: 'data' + }; + expect($scope.makeTemplateString()).toEqual('<div ng-awesome data="widgetData"></div>'); + }); + + }); + + describe('the grabResizer method', function() { + + var evt, widget, WidgetModel; + + beforeEach(inject(function (_WidgetModel_) { + WidgetModel = _WidgetModel_; + })); + + beforeEach(function() { + evt = { + stopPropagation: jasmine.createSpy('stopPropagation'), + originalEvent: { + preventDefault: jasmine.createSpy('preventDefault') + }, + clientX: 100, + which: 1 + }; + $scope.widget = widget = new WidgetModel({ + style: { + width: '30%' + } + }); + }); + + it('should do nothing if event.which is not 1 (left click)', function() { + evt.which = 2; + $scope.grabResizer(evt); + expect(evt.stopPropagation).not.toHaveBeenCalled(); + }); + + it('should call stopPropagation and preventDefault', function() { + $scope.grabResizer(evt); + expect(evt.stopPropagation).toHaveBeenCalled(); + expect(evt.originalEvent.preventDefault).toHaveBeenCalled(); + }); + + it('should add a .widget-resizer-marquee element to the .widget element', function() { + $scope.grabResizer(evt); + expect($element.find('.widget-resizer-marquee').length).toBeGreaterThan(0); + }); + + }); + + describe('the editTitle method', function() { + + it('should set editingTitle=true on the widget object', function() { + var widget = {}; + $scope.editTitle(widget); + expect(widget.editingTitle).toEqual(true); + }); + + it('should call $timeout', function() { + var widget = {}; + $scope.editTitle(widget); + expect(injections.$timeout).toHaveBeenCalled(); + }); + + }); + + describe('the saveTitleEdit method', function() { + + it('should set editingTitle=false', function() { + var widget = { editingTitle: true }; + $scope.saveTitleEdit(widget); + expect(widget.editingTitle).toEqual(false); + }); + }); + +});
\ No newline at end of file diff --git a/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.js b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.js new file mode 100644 index 00000000..f5a6ebef --- /dev/null +++ b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.js @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 DataTorrent, Inc. ALL Rights Reserved. + * + * Licensed 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. + */ + +'use strict'; + +angular.module('ui.dashboard') + .directive('widget', ['$injector', function ($injector) { + + return { + + controller: 'DashboardWidgetCtrl', + + link: function (scope) { + + var widget = scope.widget; + var dataModelType = widget.dataModelType; + + // set up data source + if (dataModelType) { + var DataModelConstructor; // data model constructor function + + if (angular.isFunction(dataModelType)) { + DataModelConstructor = dataModelType; + } else if (angular.isString(dataModelType)) { + $injector.invoke([dataModelType, function (DataModelType) { + DataModelConstructor = DataModelType; + }]); + } else { + throw new Error('widget dataModelType should be function or string'); + } + + var ds; + if (widget.dataModelArgs) { + ds = new DataModelConstructor(widget.dataModelArgs); + } else { + ds = new DataModelConstructor(); + } + widget.dataModel = ds; + ds.setup(widget, scope); + ds.init(); + scope.$on('$destroy', _.bind(ds.destroy,ds)); + } + + // Compile the widget template, emit add event + scope.compileTemplate(); + scope.$emit('widgetAdded', widget); + + } + + }; + }]); diff --git a/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.spec.js b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.spec.js new file mode 100644 index 00000000..0997e071 --- /dev/null +++ b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/scripts/view-models/reportdashboard-page/src/components/directives/widget/widget.spec.js @@ -0,0 +1,104 @@ +// 'use strict'; + +describe('Directive: widget', function () { + + var element, scope, rootScope, isoScope, compile, provide; + + function Type() { + } + + Type.prototype = { + setup: function () { + }, + init: function () { + }, + destroy: function () { + } + }; + + beforeEach(function () { + spyOn(Type.prototype, 'setup'); + spyOn(Type.prototype, 'init'); + spyOn(Type.prototype, 'destroy'); + // define mock objects here + }); + + // load the directive's module + beforeEach(module('ui.dashboard', function ($provide, $controllerProvider) { + provide = $provide; + // Inject dependencies like this: + $controllerProvider.register('DashboardWidgetCtrl', function ($scope) { + + }); + + })); + + beforeEach(inject(function ($compile, $rootScope) { + // Cache these for reuse + rootScope = $rootScope; + compile = $compile; + + // Other setup, e.g. helper functions, etc. + + // Set up the outer scope + scope = $rootScope.$new(); + scope.widget = { + dataModelType: Type + }; + + compileTemplate = jasmine.createSpy('compileTemplate'); + scope.compileTemplate = compileTemplate; + })); + + function compileWidget() { + // Define and compile the element + element = angular.element('<div widget><div class="widget-content"></div></div>'); + element = compile(element)(scope); + scope.$digest(); + isoScope = element.isolateScope(); + } + + it('should create a new instance of dataModelType if provided in scope.widget', function () { + compileWidget(); + expect(scope.widget.dataModel instanceof Type).toBe(true); + }); + + it('should call setup and init on the new dataModel', function () { + compileWidget(); + expect(Type.prototype.setup).toHaveBeenCalled(); + expect(Type.prototype.init).toHaveBeenCalled(); + }); + + it('should call compile template', function () { + compileWidget(); + expect(scope.compileTemplate).toHaveBeenCalled(); + }); + + it('should create a new instance of dataModelType from string name', function () { + // register data model with $injector + provide.factory('StringNameDataModel', function () { + return Type; + }); + + scope.widget = { + dataModelType: 'StringNameDataModel' + }; + + compileWidget(); + + expect(scope.widget.dataModel instanceof Type).toBe(true); + expect(Type.prototype.setup).toHaveBeenCalled(); + expect(Type.prototype.init).toHaveBeenCalled(); + }); + + it('should validate data model type', function () { + scope.widget = { + dataModelType: {} + }; + + expect(function () { + compileWidget() + }).toThrowError(); + }); + +});
\ No newline at end of file |