mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
PanelLinks: began work on drilldown panel links feature, #1041
This commit is contained in:
parent
8bb51d47f8
commit
5da3da5962
39
src/app/components/panellinkeditor/linkSrv.js
Normal file
39
src/app/components/panellinkeditor/linkSrv.js
Normal file
@ -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
src/app/components/panellinkeditor/module.html
Normal file
46
src/app/components/panellinkeditor/module.html
Normal file
@ -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
src/app/components/panellinkeditor/module.js
Normal file
38
src/app/components/panellinkeditor/module.js
Normal file
@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
@ -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')) {
|
var range = timeSrv.timeRangeForUrl();
|
||||||
range = timeSrv.timeRange();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -19,5 +19,6 @@ define([
|
|||||||
'./graphiteSegment',
|
'./graphiteSegment',
|
||||||
'./grafanaVersionCheck',
|
'./grafanaVersionCheck',
|
||||||
'./dropdown.typeahead',
|
'./dropdown.typeahead',
|
||||||
|
'components/panellinkeditor/module',
|
||||||
'./influxdbFuncEditor'
|
'./influxdbFuncEditor'
|
||||||
], function () {});
|
], function () {});
|
||||||
|
@ -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);
|
||||||
|
@ -12,3 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<panel-link-editor panel="panel"></panel-link-editor>
|
||||||
|
|
||||||
|
|
||||||
|
@ -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)) {
|
||||||
|
@ -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');
|
||||||
|
Loading…
Reference in New Issue
Block a user