Browse Source

PanelLinks: began work on drilldown panel links feature, #1041

Torkel Ödegaard 11 years ago
parent
commit
5da3da5962

+ 39 - 0
src/app/components/panellinkeditor/linkSrv.js

@@ -0,0 +1,39 @@
+define([
+  'angular',
+  'kbn',
+],
+function (angular, kbn) {
+  'use strict';
+
+  angular
+    .module('grafana.services')
+    .service('linkSrv', function(templateSrv, timeSrv) {
+
+      this.getPanelLinkAnchorInfo = function(link) {
+        var info = {};
+        if (link.type === 'absolute') {
+          info.target = '_blank';
+          info.href = templateSrv.replace(link.url);
+          info.title = templateSrv.replace(link.title);
+          info.href += '?';
+
+        }
+        else {
+          info.title = templateSrv.replace(link.title);
+          var slug = kbn.slugifyForUrl(link.dashboard);
+          info.href = '#dashboard/db/' + slug + '?';
+        }
+
+        var range = timeSrv.timeRangeForUrl();
+        info.href += 'from=' + range.from;
+        info.href += '&to=' + range.to;
+
+        if (link.params) {
+          info.href += "&" + link.params;
+        }
+
+        return info;
+      };
+
+    });
+});

+ 46 - 0
src/app/components/panellinkeditor/module.html

@@ -0,0 +1,46 @@
+<div class="editor-row">
+  <div class="section">
+		<h5>Drilldown / detail link<tip>These links appear in the dropdown menu in the panel menu</tip></h5>
+
+		<div class="grafana-target" ng-repeat="link in panel.links"j>
+			<div class="grafana-target-inner">
+				<ul class="grafana-segment-list">
+					<li class="grafana-target-segment">
+						<i class="icon-remove pointer" ng-click="deleteLink(link)"></i>
+					</li>
+
+					<li class="grafana-target-segment">title</li>
+					<li>
+						<input type="text" ng-model="link.title" class="input-medium grafana-target-segment-input">
+					</li>
+
+					<li class="grafana-target-segment">type</li>
+					<li>
+						<select class="input-medium grafana-target-segment-input" style="width: 101px;" ng-model="link.type" ng-options="f for f in ['dashboard','absolute']"></select>
+					</li>
+
+					<li class="grafana-target-segment" ng-show="link.type === 'dashboard'">dashboard</li>
+					<li ng-show="link.type === 'dashboard'">
+						<input type="text" ng-model="link.dashboard" class="input-large grafana-target-segment-input">
+					</li>
+
+					<li class="grafana-target-segment" ng-show="link.type === 'absolute'">url</li>
+					<li ng-show="link.type === 'absolute'">
+						<input type="text" ng-model="link.url" class="input-large grafana-target-segment-input">
+					</li>
+
+					<li class="grafana-target-segment">params</li>
+					<li>
+						<input type="text" ng-model="link.params" class="input-medium grafana-target-segment-input">
+					</li>
+				</ul>
+				<div class="clearfix"></div>
+			</div>
+		</div>
+	</div>
+</div>
+
+<div class="editor-row">
+	<br>
+	<button class="btn btn-success" ng-click="addLink()">Add link</button>
+</div>

+ 38 - 0
src/app/components/panellinkeditor/module.js

@@ -0,0 +1,38 @@
+define([
+  'angular',
+  'lodash',
+  './linkSrv',
+],
+function (angular, _) {
+  'use strict';
+
+  angular
+    .module('grafana.directives')
+    .directive('panelLinkEditor', function() {
+      return {
+        scope: {
+          panel: "="
+        },
+        restrict: 'E',
+        controller: 'PanelLinkEditorCtrl',
+        templateUrl: 'app/components/panellinkeditor/module.html',
+        link: function() {
+        }
+      };
+    }).controller('PanelLinkEditorCtrl', function($scope) {
+
+      $scope.panel.links = $scope.panel.links || [];
+
+      $scope.addLink = function() {
+        $scope.panel.links.push({
+          type: 'dashboard',
+          name: 'Drilldown dashboard'
+        });
+      };
+
+      $scope.deleteLink = function(link) {
+        $scope.panel.links = _.without($scope.panel.links, link);
+      };
+
+    });
+});

+ 1 - 8
src/app/controllers/sharePanelCtrl.js

@@ -27,19 +27,12 @@ function (angular, _) {
       }
       }
 
 
       var panelId = $scope.panel.id;
       var panelId = $scope.panel.id;
-      var range = timeSrv.timeRange(false);
       var params = angular.copy($location.search());
       var params = angular.copy($location.search());
 
 
-      if (_.isString(range.to) && range.to.indexOf('now')) {
-        range = timeSrv.timeRange();
-      }
-
+      var range = timeSrv.timeRangeForUrl();
       params.from = range.from;
       params.from = range.from;
       params.to = range.to;
       params.to = range.to;
 
 
-      if (_.isDate(params.from)) { params.from = params.from.getTime(); }
-      if (_.isDate(params.to)) { params.to = params.to.getTime(); }
-
       if ($scope.includeTemplateVars) {
       if ($scope.includeTemplateVars) {
         _.each(templateSrv.variables, function(variable) {
         _.each(templateSrv.variables, function(variable) {
           params['var-' + variable.name] = variable.current.text;
           params['var-' + variable.name] = variable.current.text;

+ 1 - 0
src/app/directives/all.js

@@ -19,5 +19,6 @@ define([
   './graphiteSegment',
   './graphiteSegment',
   './grafanaVersionCheck',
   './grafanaVersionCheck',
   './dropdown.typeahead',
   './dropdown.typeahead',
+  'components/panellinkeditor/module',
   './influxdbFuncEditor'
   './influxdbFuncEditor'
 ], function () {});
 ], function () {});

+ 14 - 2
src/app/directives/panelMenu.js

@@ -8,7 +8,7 @@ function (angular, $, _) {
 
 
   angular
   angular
     .module('grafana.directives')
     .module('grafana.directives')
-    .directive('panelMenu', function($compile) {
+    .directive('panelMenu', function($compile, linkSrv) {
       var linkTemplate = '<span class="panel-title drag-handle pointer">{{panel.title | interpolateTemplateVars}}</span>';
       var linkTemplate = '<span class="panel-title drag-handle pointer">{{panel.title | interpolateTemplateVars}}</span>';
 
 
       function createMenuTemplate($scope) {
       function createMenuTemplate($scope) {
@@ -22,7 +22,7 @@ function (angular, $, _) {
         template += '</div>';
         template += '</div>';
 
 
         template += '<div class="panel-menu-row">';
         template += '<div class="panel-menu-row">';
-        template += '<a class="panel-menu-link" bs-dropdown="panelMeta.extendedMenu"><i class="icon-th-list"></i></a>';
+        template += '<a class="panel-menu-link" bs-dropdown="extendedMenu"><i class="icon-th-list"></i></a>';
 
 
         _.each($scope.panelMeta.menu, function(item) {
         _.each($scope.panelMeta.menu, function(item) {
           template += '<a class="panel-menu-link" ';
           template += '<a class="panel-menu-link" ';
@@ -38,6 +38,17 @@ function (angular, $, _) {
         return template;
         return template;
       }
       }
 
 
+      function getExtendedMenu($scope) {
+        var menu =angular.copy($scope.panelMeta.extendedMenu);
+        if (!$scope.panel.links) { return; }
+
+        _.each($scope.panel.links, function(link) {
+          var info = linkSrv.getPanelLinkAnchorInfo(link);
+          menu.push({text: info.title, href: info.href, target: info.target });
+        });
+        return menu;
+      }
+
       return {
       return {
         restrict: 'A',
         restrict: 'A',
         link: function($scope, elem) {
         link: function($scope, elem) {
@@ -101,6 +112,7 @@ function (angular, $, _) {
             });
             });
 
 
             menuScope = $scope.$new();
             menuScope = $scope.$new();
+            menuScope.extendedMenu = getExtendedMenu($scope);
 
 
             $('.panel-menu').remove();
             $('.panel-menu').remove();
             elem.append($menu);
             elem.append($menu);

+ 4 - 0
src/app/partials/panelgeneral.html

@@ -12,3 +12,7 @@
     </div>
     </div>
   </div>
   </div>
 </div>
 </div>
+
+<panel-link-editor panel="panel"></panel-link-editor>
+
+

+ 12 - 0
src/app/services/timeSrv.js

@@ -95,6 +95,18 @@ define([
       $timeout(this.refreshDashboard, 0);
       $timeout(this.refreshDashboard, 0);
     };
     };
 
 
+    this.timeRangeForUrl = function() {
+      var range = this.timeRange(false);
+      if (_.isString(range.to) && range.to.indexOf('now')) {
+        range = this.timeRange();
+      }
+
+      if (_.isDate(range.from)) { range.from = range.from.getTime(); }
+      if (_.isDate(range.to)) { range.to = range.to.getTime(); }
+
+      return range;
+    };
+
     this.timeRange = function(parse) {
     this.timeRange = function(parse) {
       var _t = this.time;
       var _t = this.time;
       if(_.isUndefined(_t) || _.isUndefined(_t.from)) {
       if(_.isUndefined(_t) || _.isUndefined(_t.from)) {

+ 12 - 13
src/test/specs/sharePanelCtrl-specs.js

@@ -7,6 +7,12 @@ define([
   describe('SharePanelCtrl', function() {
   describe('SharePanelCtrl', function() {
     var ctx = new helpers.ControllerTestContext();
     var ctx = new helpers.ControllerTestContext();
 
 
+    function setTime(range) {
+      ctx.timeSrv.timeRangeForUrl = sinon.stub().returns(range);
+    }
+
+    setTime({ from: 'now-1h', to: 'now' });
+
     beforeEach(module('grafana.controllers'));
     beforeEach(module('grafana.controllers'));
 
 
     beforeEach(ctx.providePhase());
     beforeEach(ctx.providePhase());
@@ -14,10 +20,12 @@ define([
 
 
     describe('shareUrl with current time range and panel', function() {
     describe('shareUrl with current time range and panel', function() {
 
 
+
       it('should generate share url relative time', function() {
       it('should generate share url relative time', function() {
         ctx.$location.path('/test');
         ctx.$location.path('/test');
         ctx.scope.panel = { id: 22 };
         ctx.scope.panel = { id: 22 };
-        ctx.timeSrv.time = { from: 'now-1h', to: 'now' };
+
+        setTime({ from: 'now-1h', to: 'now' });
 
 
         ctx.scope.buildUrl();
         ctx.scope.buildUrl();
         expect(ctx.scope.shareUrl).to.be('http://server/#/test?from=now-1h&to=now&panelId=22&fullscreen');
         expect(ctx.scope.shareUrl).to.be('http://server/#/test?from=now-1h&to=now&panelId=22&fullscreen');
@@ -26,26 +34,17 @@ define([
       it('should generate share url absolute time', function() {
       it('should generate share url absolute time', function() {
         ctx.$location.path('/test');
         ctx.$location.path('/test');
         ctx.scope.panel = { id: 22 };
         ctx.scope.panel = { id: 22 };
-        ctx.timeSrv.time = { from: new Date(1362178800000), to: new Date(1396648800000) };
+        setTime({ from: 1362178800000, to: 1396648800000 });
 
 
         ctx.scope.buildUrl();
         ctx.scope.buildUrl();
         expect(ctx.scope.shareUrl).to.be('http://server/#/test?from=1362178800000&to=1396648800000&panelId=22&fullscreen');
         expect(ctx.scope.shareUrl).to.be('http://server/#/test?from=1362178800000&to=1396648800000&panelId=22&fullscreen');
       });
       });
 
 
-      it('should generate share url with time as JSON strings', function() {
-        ctx.$location.path('/test');
-        ctx.scope.panel = { id: 22 };
-        ctx.timeSrv.time = { from: "2012-01-31T23:00:00.000Z", to: "2014-04-04T22:00:00.000Z" };
-
-        ctx.scope.buildUrl();
-        expect(ctx.scope.shareUrl).to.be('http://server/#/test?from=1328050800000&to=1396648800000&panelId=22&fullscreen');
-      });
-
       it('should remove panel id when toPanel is false', function() {
       it('should remove panel id when toPanel is false', function() {
         ctx.$location.path('/test');
         ctx.$location.path('/test');
         ctx.scope.panel = { id: 22 };
         ctx.scope.panel = { id: 22 };
         ctx.scope.toPanel = false;
         ctx.scope.toPanel = false;
-        ctx.timeSrv.time = { from: 'now-1h', to: 'now' };
+        setTime({ from: 'now-1h', to: 'now' });
 
 
         ctx.scope.buildUrl();
         ctx.scope.buildUrl();
         expect(ctx.scope.shareUrl).to.be('http://server/#/test?from=now-1h&to=now');
         expect(ctx.scope.shareUrl).to.be('http://server/#/test?from=now-1h&to=now');
@@ -57,7 +56,7 @@ define([
         ctx.scope.includeTemplateVars = true;
         ctx.scope.includeTemplateVars = true;
         ctx.scope.toPanel = false;
         ctx.scope.toPanel = false;
         ctx.templateSrv.variables = [{ name: 'app', current: {text: 'mupp' }}, {name: 'server', current: {text: 'srv-01'}}];
         ctx.templateSrv.variables = [{ name: 'app', current: {text: 'mupp' }}, {name: 'server', current: {text: 'srv-01'}}];
-        ctx.timeSrv.time = { from: 'now-1h', to: 'now' };
+        setTime({ from: 'now-1h', to: 'now' });
 
 
         ctx.scope.buildUrl();
         ctx.scope.buildUrl();
         expect(ctx.scope.shareUrl).to.be('http://server/#/test?from=now-1h&to=now&var-app=mupp&var-server=srv-01');
         expect(ctx.scope.shareUrl).to.be('http://server/#/test?from=now-1h&to=now&var-app=mupp&var-server=srv-01');