Commit dbafc8c9 by Torkel Ödegaard

feat(plugins): work on plugin directive loading

parent 6c6c3a50
......@@ -4,4 +4,5 @@ define([
'./panel_srv',
'./panel_helper',
'./solo_panel_ctrl',
'./panel_loader',
], function () {});
define([
'angular',
'jquery',
'app/core/config',
],
function (angular, $, config) {
function (angular, $) {
'use strict';
var module = angular.module('grafana.directives');
module.directive('panelLoader', function($compile, $parse) {
return {
restrict: 'E',
link: function(scope, elem, attr) {
var getter = $parse(attr.type), panelType = getter(scope);
var module = config.panels[panelType].module;
System.import(module).then(function() {
var panelEl = angular.element(document.createElement('grafana-panel-' + panelType));
elem.append(panelEl);
$compile(panelEl)(scope);
}).catch(function(err) {
console.log('Failed to load panel:', err);
scope.appEvent('alert-error', ['Panel Load Error', 'Failed to load panel ' + panelType + ', ' + err]);
});
}
};
});
module.directive('grafanaPanel', function() {
return {
restrict: 'E',
......
///<reference path="../../headers/common.d.ts" />
import angular from 'angular';
import config from 'app/core/config';
/** @ngInject */
function panelLoader($parse, dynamicDirectiveSrv) {
return dynamicDirectiveSrv.create({
directive: scope => {
let modulePath = config.panels[scope.panel.type].module;
return System.import(modulePath).then(function(panelModule) {
return {
name: 'panel-directive-' + scope.panel.type,
fn: panelModule.panel,
};
});
},
});
}
angular.module('grafana.directives').directive('panelLoader', panelLoader);
<div class="editor-row" style="margin-bottom: 20px;">
<span style="float: right; font-size: 12px;"><i>Last updated by Grafana October 4, 2015 12:15:04 by $username</i></span>
<div class="section">
<h5>General Alerting Options</h5>
<div class="tight-form last">
<ul class="tight-form-list">
<li class="tight-form-item">
Alert Title
</li>
<li>
<input type="text" class="input-xlarge tight-form-input"></input>
</li>
<li class="tight-form-item">
Alerting Backend
</li>
<li>
<select class="input-medium tight-form-input">
<option>Grafana Alerting</option>
</select>
</li>
<li class="tight-form-item last">
<label class="checkbox-label" for="alerting-enabled">Enabled</label>
<input class="cr1" id="alerting-enabled" type="checkbox">
<label for="alerting-enabled" class="cr1"></label>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="editor-row" style="margin-bottom: 20px;">
<h5>Choose your query:</h5>
<p>Select an exising query to alert on:</p>
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" /></li>
<li class="tight-form-item">None</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" /></li>
<li class="tight-form-item" style="min-width: 15px; text-align: center">A</li>
<li class="tight-form-item">apps</li>
<li class="tight-form-item"><i class="fa fa-asterisk"><i></i></i></li>
<li class="tight-form-item">fakesite</li>
<li class="tight-form-item">counters</li>
<li class="tight-form-item">requests</li>
<li class="tight-form-item">count</li>
<li class="tight-form-item">scaleToSeconds(1)</li>
<li class="tight-form-item last">aliasByNode(2)</li>
<li><div class="copy-query" bs-tooltip="'Copy to custom query'" data-placement="top"></div></li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" /></li>
<li class="tight-form-item" style="min-width: 15px; text-align: center">B</li>
<li class="tight-form-item last"><span class="query-keyword">Metric:</span> us-west-2 AWS/EC2 CPUUtilization <span class="query-keyword">Stats:</span> Minimum Maximum <span class="query-keyword">Dimensions</span> InstanceIS <span class="query-segment-operator">=</span> i-b0e8a447 <span class="query-keyword">Alias</span> {{stat}} <span class="query-keyword">Period</span> 60</li>
<li><div class="copy-query" bs-tooltip="'Copy to custom query'" data-placement="top"></div></li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" /></li>
<li class="tight-form-item" style="min-width: 15px; text-align: center">C</li>
<li class="tight-form-item last"><span class="query-keyword">Query:</span> avg(counters_logins) by(server) <span class="query-keyword">Legend Format:</span> {{app}} - {{server}} <span class="query-keyword">Step:</span> 1s <span class="query-keyword">Resolution:</span> 1/2</li>
<li><div class="copy-query" bs-tooltip="'Copy to custom query'" data-placement="top"></div></li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" /></li>
<li class="tight-form-item" style="min-width: 15px; text-align: center">D</li>
<li class="tight-form-item last"><span class="query-keyword">SELECT</span> mean(value) <span class="query-keyword">FROM</span> logins.count <span class="query-keyword">WHERE</span> hostname <span class="query-segment-operator">=</span> /$Hostname$/ <span class="query-keyword">GROUP BY</span> time($internal) hostname</li>
<li><div class="copy-query" bs-tooltip="'Copy to custom query'" data-placement="top"></div></li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form last">
<ul class="tight-form-list">
<li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" checked /></li>
<li class="tight-form-item" style="min-width: 15px; text-align: center">E</li>
<li class="tight-form-item last"><span class="query-keyword">Metric:</span> apps.backend.backend_01.counters.requests.count <span class="query-keyword">Alias:</span> Bristow <span class="query-keyword">Aggregator:</span> Sum <span class="query-keyword">Downsample:</span> 1m <span class="query-keyword">Aggregator</span> Sum <span class="query-keyword">Tags</span> host = test</li>
<li><div class="copy-query" bs-tooltip="'Copy to custom query'" data-placement="top"></div></li>
</ul>
<div class="clearfix"></div>
</div>
</div>
<div class="editor-row" style="margin-bottom: 20px;">
<p>Or write a new custom alerting query:</p>
<div class="section">
<div class="tight-form last">
<ul class="tight-form-list">
<li class="tight-form-item"><input type="radio" class="radio input-small" name="query" style="margin: 0 4px 4px;" /></li>
<li class="tight-form-item">
<a class="pointer">
<i class="fa fa-pencil"></i>
</a>
</li>
<li class="tight-form-item">
select metric
</li>
<li>
<a class="tight-form-item tight-form-func last dropdown-toggle"><i class="fa fa-plus"></i></a>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="editor-row" style="margin-bottom: 10px;">
<div class="section">
<h5>Define Your States</h5>
<div class="tight-form last">
<ul class="tight-form-list">
<li class="tight-form-item">
by
</li>
<li>
<select class="input-medium tight-form-input">
<option>Averaging</option>
</select>
</li>
<li class="tight-form-item">
the values in the query over the last
</li>
<li>
<input type="text" class="input-mini tight-form-input last"></input>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="editor-row" style="margin-bottom: 20px;">
<div class="section">
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 100px;">
<span class="alert-state alert-state-warning">Warn</span>
</li>
<li>
<input type="text" class="input-mini tight-form-input" value=">" style="text-align: center;"></input>
</li>
<li>
<input type="text" class="input-mini tight-form-input" value="#B" style="text-align: center;"></input>
</li>
<li class="tight-form-item">
.notify
</li>
<li class="alert-notify-emails">
<bootstrap-tagsinput tagclass="label label-tag label-tag-email"></bootstrap-tagsinput>
</li>
<li class="tight-form-item last">
<label class="checkbox-label" for="state-enabled">Enabled</label>
<input class="cr1" id="state-enabled" type="checkbox">
<label for="state-enabled" class="cr1"></label>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form last">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 100px;">
<span class="alert-state alert-state-critical">Critical</span>
</li>
<li>
<input type="text" class="input-mini tight-form-input"></input>
</li>
<li>
<input type="text" class="input-mini tight-form-input"></input>
</li>
<li class="tight-form-item">
.notify
</li>
<li class="alert-notify-emails">
<bootstrap-tagsinput tagclass="label label-tag label-tag-email"></bootstrap-tagsinput>
</li>
<li class="tight-form-item last">
<label class="checkbox-label" for="state-enabled2">Enabled</label>
<input class="cr1" id="state-enabled2" type="checkbox">
<label for="state-enabled2" class="cr1"></label>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="editor-row">
<div class="section">
<h5>What to Say <span style="float: right; font-size: 12px; font-weight: normal;"><a href="#">Variables</a> | <a href="#">Preview</a></span></h5>
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 100px;">
Summary
</li>
<li>
<input type="text" class="input-xxlarge tight-form-input last"></input>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form last">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 100px;">
Description
</li>
<li>
<textarea class="tight-form-textarea input-xxlarge last"></textarea>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>
......@@ -83,7 +83,7 @@
<div ng-repeat="(name, panel) in row.panels track by panel.id" class="panel" ui-draggable="!dashboardViewState.fullscreen" drag="panel.id"
ui-on-Drop="onDrop($data, row, panel)" drag-handle-class="drag-handle" panel-width>
<panel-loader type="panel.type" class="panel-margin"></panel-loader>
<panel-loader class="panel-margin"></panel-loader>
</div>
<div panel-drop-zone class="panel panel-drop-zone" ui-on-drop="onDrop($data, row)" data-drop="true">
......
......@@ -11,14 +11,8 @@ function (angular, app, _, config, PanelMeta) {
var module = angular.module('grafana.panels.dashlist', []);
app.useModule(module);
module.directive('grafanaPanelDashlist', function() {
return {
controller: 'DashListPanelCtrl',
templateUrl: 'app/plugins/panel/dashlist/module.html',
};
});
module.controller('DashListPanelCtrl', function($scope, panelSrv, backendSrv) {
/** @ngInject */
function DashListPanelCtrl($scope, panelSrv, backendSrv) {
$scope.panelMeta = new PanelMeta({
panelName: 'Dashboard list',
......@@ -73,5 +67,16 @@ function (angular, app, _, config, PanelMeta) {
};
$scope.init();
});
}
function dashListPanelDirective() {
return {
controller: DashListPanelCtrl,
templateUrl: 'app/plugins/panel/dashlist/module.html',
};
}
return {
panel: dashListPanelDirective
};
});
......@@ -4,7 +4,7 @@ define([
'moment',
'lodash',
'app/core/utils/kbn',
'./graph.tooltip',
'./graph_tooltip',
'jquery.flot',
'jquery.flot.events',
'jquery.flot.selection',
......
declare var GraphTooltip: any;
export default GraphTooltip;
declare var panel: any;
declare var GraphCtrl: any;
export {panel, GraphCtrl};
......@@ -12,16 +12,8 @@ define([
function (angular, _, moment, kbn, TimeSeries, PanelMeta) {
'use strict';
var module = angular.module('grafana.panels.graph');
module.directive('grafanaPanelGraph', function() {
return {
controller: 'GraphCtrl',
templateUrl: 'app/plugins/panel/graph/module.html',
};
});
module.controller('GraphCtrl', function($scope, $rootScope, panelSrv, annotationsSrv, panelHelper) {
/** @ngInject */
function GraphCtrl($scope, $rootScope, panelSrv, annotationsSrv, panelHelper) {
$scope.panelMeta = new PanelMeta({
panelName: 'Graph',
......@@ -294,7 +286,17 @@ function (angular, _, moment, kbn, TimeSeries, PanelMeta) {
};
panelSrv.init($scope);
}
});
function graphPanelDirective() {
return {
controller: GraphCtrl,
templateUrl: 'app/plugins/panel/graph/module.html',
};
}
return {
GraphCtrl: GraphCtrl,
panel: graphPanelDirective,
};
});
define([
'./helpers',
'app/features/panel/panel_srv',
'app/features/panel/panel_helper',
'app/plugins/panel/graph/module'
], function(helpers) {
'use strict';
///<reference path="../../../../headers/common.d.ts" />
describe('GraphCtrl', function() {
var ctx = new helpers.ControllerTestContext();
import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
beforeEach(module('grafana.services'));
beforeEach(module('grafana.panels.graph'));
import 'app/features/panel/panel_srv';
import 'app/features/panel/panel_helper';
beforeEach(ctx.providePhase());
beforeEach(ctx.createControllerPhase('GraphCtrl'));
import angular from 'angular';
import {GraphCtrl} from '../module';
import helpers from '../../../../../test/specs/helpers';
describe('get_data with 2 series', function() {
beforeEach(function() {
ctx.annotationsSrv.getAnnotations = sinon.stub().returns(ctx.$q.when([]));
ctx.datasource.query = sinon.stub().returns(ctx.$q.when({
data: [
{ target: 'test.cpu1', datapoints: [[1, 10]]},
{ target: 'test.cpu2', datapoints: [[1, 10]]}
]
}));
ctx.scope.render = sinon.spy();
ctx.scope.refreshData(ctx.datasource);
ctx.scope.$digest();
});
angular.module('grafana.controllers').controller('GraphCtrl', GraphCtrl);
it('should send time series to render', function() {
var data = ctx.scope.render.getCall(0).args[0];
expect(data.length).to.be(2);
});
describe('GraphCtrl', function() {
var ctx = new helpers.ControllerTestContext();
beforeEach(angularMocks.module('grafana.services'));
beforeEach(angularMocks.module('grafana.controllers'));
beforeEach(ctx.providePhase());
beforeEach(ctx.createControllerPhase('GraphCtrl'));
describe('get_data failure following success', function() {
beforeEach(function() {
ctx.datasource.query = sinon.stub().returns(ctx.$q.reject('Datasource Error'));
ctx.scope.refreshData(ctx.datasource);
ctx.scope.$digest();
});
describe('get_data with 2 series', function() {
beforeEach(function() {
ctx.annotationsSrv.getAnnotations = sinon.stub().returns(ctx.$q.when([]));
ctx.datasource.query = sinon.stub().returns(ctx.$q.when({
data: [
{ target: 'test.cpu1', datapoints: [[1, 10]]},
{ target: 'test.cpu2', datapoints: [[1, 10]]}
]
}));
ctx.scope.render = sinon.spy();
ctx.scope.refreshData(ctx.datasource);
ctx.scope.$digest();
});
it('should send time series to render', function() {
var data = ctx.scope.render.getCall(0).args[0];
expect(data.length).to.be(2);
});
describe('get_data failure following success', function() {
beforeEach(function() {
ctx.datasource.query = sinon.stub().returns(ctx.$q.reject('Datasource Error'));
ctx.scope.refreshData(ctx.datasource);
ctx.scope.$digest();
});
});
......@@ -48,4 +51,3 @@ define([
});
});
define([
'jquery',
'app/plugins/panel/graph/graph.tooltip'
], function($, GraphTooltip) {
'use strict';
var scope = {
appEvent: sinon.spy(),
onAppEvent: sinon.spy(),
///<reference path="../../../../headers/common.d.ts" />
import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
import $ from 'jquery';
import GraphTooltip from '../graph_tooltip';
var scope = {
appEvent: sinon.spy(),
onAppEvent: sinon.spy(),
};
var elem = $('<div></div>');
var dashboard = { };
function describeSharedTooltip(desc, fn) {
var ctx: any = {};
ctx.scope = scope;
ctx.scope.panel = {
tooltip: {
shared: true
},
legend: { },
stack: false
};
var elem = $('<div></div>');
var dashboard = { };
ctx.setup = function(setupFn) {
ctx.setupFn = setupFn;
};
function describeSharedTooltip(desc, fn) {
var ctx = {};
ctx.scope = scope;
ctx.scope.panel = {
tooltip: {
shared: true
},
legend: { },
stack: false
};
ctx.setup = function(setupFn) {
ctx.setupFn = setupFn;
};
describe(desc, function() {
beforeEach(function() {
ctx.setupFn();
var tooltip = new GraphTooltip(elem, dashboard, scope);
ctx.results = tooltip.getMultiSeriesPlotHoverInfo(ctx.data, ctx.pos);
});
fn(ctx);
});
}
describeSharedTooltip("steppedLine false, stack false", function(ctx) {
ctx.setup(function() {
ctx.data = [
{ data: [[10, 15], [12, 20]], lines: {} },
{ data: [[10, 2], [12, 3]], lines: {} }
];
ctx.pos = { x: 11 };
describe(desc, function() {
beforeEach(function() {
ctx.setupFn();
var tooltip = new GraphTooltip(elem, dashboard, scope);
ctx.results = tooltip.getMultiSeriesPlotHoverInfo(ctx.data, ctx.pos);
});
it('should return 2 series', function() {
expect(ctx.results.length).to.be(2);
});
it('should add time to results array', function() {
expect(ctx.results.time).to.be(10);
});
it('should set value and hoverIndex', function() {
expect(ctx.results[0].value).to.be(15);
expect(ctx.results[1].value).to.be(2);
expect(ctx.results[0].hoverIndex).to.be(0);
});
fn(ctx);
});
}
describeSharedTooltip("steppedLine false, stack false", function(ctx) {
ctx.setup(function() {
ctx.data = [
{ data: [[10, 15], [12, 20]], lines: {} },
{ data: [[10, 2], [12, 3]], lines: {} }
];
ctx.pos = { x: 11 };
});
describeSharedTooltip("one series is hidden", function(ctx) {
ctx.setup(function() {
ctx.data = [
{ data: [[10, 15], [12, 20]], },
{ data: [] }
];
ctx.pos = { x: 11 };
});
it('should return 2 series', function() {
expect(ctx.results.length).to.be(2);
});
it('should add time to results array', function() {
expect(ctx.results.time).to.be(10);
});
it('should set value and hoverIndex', function() {
expect(ctx.results[0].value).to.be(15);
expect(ctx.results[1].value).to.be(2);
expect(ctx.results[0].hoverIndex).to.be(0);
});
});
describeSharedTooltip("one series is hidden", function(ctx) {
ctx.setup(function() {
ctx.data = [
{ data: [[10, 15], [12, 20]], },
{ data: [] }
];
ctx.pos = { x: 11 };
});
});
describeSharedTooltip("steppedLine false, stack true, individual false", function(ctx) {
ctx.setup(function() {
ctx.data = [
{
data: [[10, 15], [12, 20]],
lines: {},
datapoints: {
pointsize: 2,
points: [[10,15], [12,20]],
},
stack: true,
describeSharedTooltip("steppedLine false, stack true, individual false", function(ctx) {
ctx.setup(function() {
ctx.data = [
{
data: [[10, 15], [12, 20]],
lines: {},
datapoints: {
pointsize: 2,
points: [[10,15], [12,20]],
},
{
data: [[10, 2], [12, 3]],
lines: {},
datapoints: {
pointsize: 2,
points: [[10, 2], [12, 3]],
},
stack: true
}
];
ctx.scope.panel.stack = true;
ctx.pos = { x: 11 };
});
stack: true,
},
{
data: [[10, 2], [12, 3]],
lines: {},
datapoints: {
pointsize: 2,
points: [[10, 2], [12, 3]],
},
stack: true
}
];
ctx.scope.panel.stack = true;
ctx.pos = { x: 11 };
});
it('should show stacked value', function() {
expect(ctx.results[1].value).to.be(17);
});
it('should show stacked value', function() {
expect(ctx.results[1].value).to.be(17);
});
});
describeSharedTooltip("steppedLine false, stack true, individual false, series stack false", function(ctx) {
ctx.setup(function() {
ctx.data = [
{
data: [[10, 15], [12, 20]],
lines: {},
datapoints: {
pointsize: 2,
points: [[10, 15], [12, 20]],
},
stack: true
describeSharedTooltip("steppedLine false, stack true, individual false, series stack false", function(ctx) {
ctx.setup(function() {
ctx.data = [
{
data: [[10, 15], [12, 20]],
lines: {},
datapoints: {
pointsize: 2,
points: [[10, 15], [12, 20]],
},
{
data: [[10, 2], [12, 3]],
lines: {},
datapoints: {
pointsize: 2,
points: [[10, 2], [12, 3]],
},
stack: false
}
];
ctx.scope.panel.stack = true;
ctx.pos = { x: 11 };
});
it('should not show stacked value', function() {
expect(ctx.results[1].value).to.be(2);
});
stack: true
},
{
data: [[10, 2], [12, 3]],
lines: {},
datapoints: {
pointsize: 2,
points: [[10, 2], [12, 3]],
},
stack: false
}
];
ctx.scope.panel.stack = true;
ctx.pos = { x: 11 };
});
it('should not show stacked value', function() {
expect(ctx.results[1].value).to.be(2);
});
describeSharedTooltip("steppedLine false, stack true, individual true", function(ctx) {
ctx.setup(function() {
ctx.data = [
{
data: [[10, 15], [12, 20]],
lines: {},
datapoints: {
pointsize: 2,
points: [[10, 15], [12, 20]],
},
stack: true
},
{
data: [[10, 2], [12, 3]],
lines: {},
datapoints: {
pointsize: 2,
points: [[10, 2], [12, 3]],
},
stack: false
}
];
ctx.scope.panel.stack = true;
ctx.scope.panel.tooltip.value_type = 'individual';
ctx.pos = { x: 11 };
});
});
it('should not show stacked value', function() {
expect(ctx.results[1].value).to.be(2);
});
describeSharedTooltip("steppedLine false, stack true, individual true", function(ctx) {
ctx.setup(function() {
ctx.data = [
{
data: [[10, 15], [12, 20]],
lines: {},
datapoints: {
pointsize: 2,
points: [[10, 15], [12, 20]],
},
stack: true
},
{
data: [[10, 2], [12, 3]],
lines: {},
datapoints: {
pointsize: 2,
points: [[10, 2], [12, 3]],
},
stack: false
}
];
ctx.scope.panel.stack = true;
ctx.scope.panel.tooltip.value_type = 'individual';
ctx.pos = { x: 11 };
});
it('should not show stacked value', function() {
expect(ctx.results[1].value).to.be(2);
});
});
declare module "test/specs/helpers" {
let helpers: any;
export default helpers;
}
declare let helpers: any;
export default helpers;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment