Commit 2a8b96b6 by Torkel Ödegaard

feat(plugins): last refactoring of how panels are implemented, now the same way as plugin editors

parent 501f21b1
......@@ -3,14 +3,30 @@
import angular from 'angular';
import _ from 'lodash';
import coreModule from '../core_module';
import config from 'app/core/config';
import coreModule from 'app/core/core_module';
function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q) {
/** @ngInject */
function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $templateCache) {
function getTemplate(component) {
if (component.template) {
return $q.when(component.template);
}
var cached = $templateCache.get(component.templateUrl);
if (cached) {
return $q.when(cached);
}
return $http.get(component.templateUrl).then(res => {
return res.data;
});
}
function getPluginComponentDirective(options) {
return function() {
return {
templateUrl: options.Component.templateUrl,
template: options.Component.template,
restrict: 'E',
controller: options.Component,
controllerAs: 'ctrl',
......@@ -20,11 +36,50 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q) {
if (ctrl.link) {
ctrl.link(scope, elem, attrs, ctrl);
}
if (ctrl.init) {
ctrl.init();
}
}
};
};
}
function loadPanelComponentInfo(scope, attrs) {
var panelElemName = 'panel-' + scope.panel.type;
let panelInfo = config.panels[scope.panel.type];
if (!panelInfo) {
// unknown
}
return System.import(panelInfo.module).then(function(panelModule): any {
var PanelCtrl = panelModule.PanelCtrl;
var componentInfo = {
name: 'panel-plugin-' + panelInfo.id,
bindings: {dashboard: "=", panel: "=", row: "="},
attrs: {dashboard: "dashboard", panel: "panel", row: "row"},
Component: PanelCtrl,
};
if (!PanelCtrl || PanelCtrl.registered) {
return componentInfo;
};
if (PanelCtrl.templatePromise) {
return PanelCtrl.templatePromise.then(res => {
return componentInfo;
});
}
PanelCtrl.templatePromise = getTemplate(PanelCtrl).then(template => {
PanelCtrl.templateUrl = null;
PanelCtrl.template = `<grafana-panel ctrl="ctrl">${template}</grafana-panel>`;
return componentInfo;
});
return PanelCtrl.templatePromise;
});
}
function getModule(scope, attrs) {
switch (attrs.type) {
// QueryCtrl
......@@ -82,6 +137,10 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q) {
};
});
}
// Panel
case 'panel': {
return loadPanelComponentInfo(scope, attrs);
}
default: {
return $q.reject({message: "Could not find component type: " + attrs.type });
}
......@@ -127,6 +186,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q) {
registerPluginComponent(scope, elem, attrs, componentInfo);
}).catch(err => {
$rootScope.appEvent('alert-error', ['Plugin Error', err.message || err]);
console.log('Plugin componnet error', err);
});
}
};
......
......@@ -81,8 +81,8 @@
<div ng-repeat="panel in row.panels track by panel.id" class="panel" ui-draggable="!dashboard.meta.fullscreen" drag="panel.id"
ui-on-drop="onDrop($data, row, panel)" drag-handle-class="drag-handle" panel-width>
<panel-loader class="panel-margin" dashboard="dashboard" row="row" panel="panel">
</panel-loader>
<plugin-component type="panel" class="panel-margin">
</plugin-component>
</div>
<div panel-drop-zone class="panel panel-drop-zone" ui-on-drop="onDrop($data, row)" data-drop="true">
......
......@@ -13,6 +13,8 @@ var panelDefaults = {
};
class DashListCtrl extends PanelCtrl {
static templateUrl = 'public/app/plugins/panel/dashlist/module.html';
dashList: any[];
modes: any[];
......@@ -55,10 +57,9 @@ class DashListCtrl extends PanelCtrl {
class DashListPanel extends PanelDirective {
controller = DashListCtrl;
templateUrl = 'public/app/plugins/panel/dashlist/module.html';
}
export {
DashListCtrl,
DashListPanel as Panel
DashListCtrl as DashListCtrl,
DashListCtrl as PanelCtrl,
}
///<reference path="../../../headers/common.d.ts" />
import moment from 'moment';
import kbn from 'app/core/utils/kbn';
import _ from 'lodash';
import TimeSeries from '../../../core/time_series2';
import * as fileExport from '../../../core/utils/file_export';
import {MetricsPanelCtrl} from '../../../features/panel/panel';
var panelDefaults = {
// datasource name, null = default datasource
datasource: null,
// sets client side (flot) or native graphite png renderer (png)
renderer: 'flot',
// Show/hide the x-axis
'x-axis' : true,
// Show/hide y-axis
'y-axis' : true,
// y axis formats, [left axis,right axis]
y_formats : ['short', 'short'],
// grid options
grid : {
leftLogBase: 1,
leftMax: null,
rightMax: null,
leftMin: null,
rightMin: null,
rightLogBase: 1,
threshold1: null,
threshold2: null,
threshold1Color: 'rgba(216, 200, 27, 0.27)',
threshold2Color: 'rgba(234, 112, 112, 0.22)'
},
// show/hide lines
lines : true,
// fill factor
fill : 1,
// line width in pixels
linewidth : 2,
// show hide points
points : false,
// point radius in pixels
pointradius : 5,
// show hide bars
bars : false,
// enable/disable stacking
stack : false,
// stack percentage mode
percentage : false,
// legend options
legend: {
show: true, // disable/enable legend
values: false, // disable/enable legend values
min: false,
max: false,
current: false,
total: false,
avg: false
},
// how null points should be handled
nullPointMode : 'connected',
// staircase line mode
steppedLine: false,
// tooltip options
tooltip : {
value_type: 'cumulative',
shared: true,
},
// time overrides
timeFrom: null,
timeShift: null,
// metric queries
targets: [{}],
// series color overrides
aliasColors: {},
// other style overrides
seriesOverrides: [],
};
class GraphCtrl extends MetricsPanelCtrl {
hiddenSeries: any = {};
seriesList: any = [];
logScales: any;
unitFormats: any;
annotationsPromise: any;
datapointsCount: number;
datapointsOutside: boolean;
datapointsWarning: boolean;
colors: any = [];
/** @ngInject */
constructor($scope, $injector, private annotationsSrv) {
super($scope, $injector);
_.defaults(this.panel, panelDefaults);
_.defaults(this.panel.tooltip, panelDefaults.tooltip);
_.defaults(this.panel.grid, panelDefaults.grid);
_.defaults(this.panel.legend, panelDefaults.legend);
this.colors = $scope.$root.colors;
}
initEditMode() {
super.initEditMode();
this.icon = "fa fa-bar-chart";
this.addEditorTab('Axes & Grid', 'public/app/plugins/panel/graph/axisEditor.html', 2);
this.addEditorTab('Display Styles', 'public/app/plugins/panel/graph/styleEditor.html', 3);
this.logScales = {
'linear': 1,
'log (base 2)': 2,
'log (base 10)': 10,
'log (base 32)': 32,
'log (base 1024)': 1024
};
this.unitFormats = kbn.getUnitFormats();
}
getExtendedMenu() {
var menu = super.getExtendedMenu();
menu.push({text: 'Export CSV', click: 'ctrl.exportCsv()'});
menu.push({text: 'Toggle legend', click: 'ctrl.toggleLegend()'});
return menu;
}
setUnitFormat(axis, subItem) {
this.panel.y_formats[axis] = subItem.value;
this.render();
}
refreshData(datasource) {
this.annotationsPromise = this.annotationsSrv.getAnnotations(this.dashboard);
return this.issueQueries(datasource)
.then(res => this.dataHandler(res))
.catch(err => {
this.seriesList = [];
this.render([]);
throw err;
});
}
zoomOut(evt) {
this.publishAppEvent('zoom-out', evt);
}
loadSnapshot(snapshotData) {
this.updateTimeRange();
this.annotationsPromise = this.annotationsSrv.getAnnotations(this.dashboard);
this.dataHandler(snapshotData);
}
dataHandler(results) {
// png renderer returns just a url
if (_.isString(results)) {
this.render(results);
return;
}
this.datapointsWarning = false;
this.datapointsCount = 0;
this.datapointsOutside = false;
this.seriesList = _.map(results.data, (series, i) => this.seriesHandler(series, i));
this.datapointsWarning = this.datapointsCount === 0 || this.datapointsOutside;
this.annotationsPromise.then(annotations => {
this.loading = false;
this.seriesList.annotations = annotations;
this.render(this.seriesList);
}, () => {
this.loading = false;
this.render(this.seriesList);
});
};
seriesHandler(seriesData, index) {
var datapoints = seriesData.datapoints;
var alias = seriesData.target;
var colorIndex = index % this.colors.length;
var color = this.panel.aliasColors[alias] || this.colors[colorIndex];
var series = new TimeSeries({
datapoints: datapoints,
alias: alias,
color: color,
});
if (datapoints && datapoints.length > 0) {
var last = moment.utc(datapoints[datapoints.length - 1][1]);
var from = moment.utc(this.range.from);
if (last - from < -10000) {
this.datapointsOutside = true;
}
this.datapointsCount += datapoints.length;
}
return series;
}
render(data?: any) {
this.broadcastRender(data);
}
changeSeriesColor(series, color) {
series.color = color;
this.panel.aliasColors[series.alias] = series.color;
this.render();
}
toggleSeries(serie, event) {
if (event.ctrlKey || event.metaKey || event.shiftKey) {
if (this.hiddenSeries[serie.alias]) {
delete this.hiddenSeries[serie.alias];
} else {
this.hiddenSeries[serie.alias] = true;
}
} else {
this.toggleSeriesExclusiveMode(serie);
}
this.render();
}
toggleSeriesExclusiveMode (serie) {
var hidden = this.hiddenSeries;
if (hidden[serie.alias]) {
delete hidden[serie.alias];
}
// check if every other series is hidden
var alreadyExclusive = _.every(this.seriesList, value => {
if (value.alias === serie.alias) {
return true;
}
return hidden[value.alias];
});
if (alreadyExclusive) {
// remove all hidden series
_.each(this.seriesList, value => {
delete this.hiddenSeries[value.alias];
});
} else {
// hide all but this serie
_.each(this.seriesList, value => {
if (value.alias === serie.alias) {
return;
}
this.hiddenSeries[value.alias] = true;
});
}
}
toggleYAxis(info) {
var override = _.findWhere(this.panel.seriesOverrides, { alias: info.alias });
if (!override) {
override = { alias: info.alias };
this.panel.seriesOverrides.push(override);
}
override.yaxis = info.yaxis === 2 ? 1 : 2;
this.render();
};
addSeriesOverride(override) {
this.panel.seriesOverrides.push(override || {});
}
removeSeriesOverride(override) {
this.panel.seriesOverrides = _.without(this.panel.seriesOverrides, override);
this.render();
}
// Called from panel menu
toggleLegend() {
this.panel.legend.show = !this.panel.legend.show;
this.refresh();
}
legendValuesOptionChanged() {
var legend = this.panel.legend;
legend.values = legend.min || legend.max || legend.avg || legend.current || legend.total;
this.render();
}
exportCsv() {
fileExport.exportSeriesListToCsv(this.seriesList);
}
}
export {GraphCtrl}
import {PanelDirective} from '../../../features/panel/panel';
import {GraphCtrl} from './graph_ctrl';
///<reference path="../../../headers/common.d.ts" />
import './graph';
import './legend';
import './seriesOverridesCtrl';
class GraphPanel extends PanelDirective {
controller = GraphCtrl;
templateUrl = 'public/app/plugins/panel/graph/module.html';
}
import moment from 'moment';
import kbn from 'app/core/utils/kbn';
import _ from 'lodash';
import TimeSeries from '../../../core/time_series2';
import * as fileExport from '../../../core/utils/file_export';
import {MetricsPanelCtrl} from '../../../features/panel/panel';
var panelDefaults = {
// datasource name, null = default datasource
datasource: null,
// sets client side (flot) or native graphite png renderer (png)
renderer: 'flot',
// Show/hide the x-axis
'x-axis' : true,
// Show/hide y-axis
'y-axis' : true,
// y axis formats, [left axis,right axis]
y_formats : ['short', 'short'],
// grid options
grid : {
leftLogBase: 1,
leftMax: null,
rightMax: null,
leftMin: null,
rightMin: null,
rightLogBase: 1,
threshold1: null,
threshold2: null,
threshold1Color: 'rgba(216, 200, 27, 0.27)',
threshold2Color: 'rgba(234, 112, 112, 0.22)'
},
// show/hide lines
lines : true,
// fill factor
fill : 1,
// line width in pixels
linewidth : 2,
// show hide points
points : false,
// point radius in pixels
pointradius : 5,
// show hide bars
bars : false,
// enable/disable stacking
stack : false,
// stack percentage mode
percentage : false,
// legend options
legend: {
show: true, // disable/enable legend
values: false, // disable/enable legend values
min: false,
max: false,
current: false,
total: false,
avg: false
},
// how null points should be handled
nullPointMode : 'connected',
// staircase line mode
steppedLine: false,
// tooltip options
tooltip : {
value_type: 'cumulative',
shared: true,
},
// time overrides
timeFrom: null,
timeShift: null,
// metric queries
targets: [{}],
// series color overrides
aliasColors: {},
// other style overrides
seriesOverrides: [],
};
class GraphCtrl extends MetricsPanelCtrl {
static templateUrl = 'public/app/plugins/panel/graph/module.html';
hiddenSeries: any = {};
seriesList: any = [];
logScales: any;
unitFormats: any;
annotationsPromise: any;
datapointsCount: number;
datapointsOutside: boolean;
datapointsWarning: boolean;
colors: any = [];
/** @ngInject */
constructor($scope, $injector, private annotationsSrv) {
super($scope, $injector);
_.defaults(this.panel, panelDefaults);
_.defaults(this.panel.tooltip, panelDefaults.tooltip);
_.defaults(this.panel.grid, panelDefaults.grid);
_.defaults(this.panel.legend, panelDefaults.legend);
this.colors = $scope.$root.colors;
}
initEditMode() {
super.initEditMode();
this.icon = "fa fa-bar-chart";
this.addEditorTab('Axes & Grid', 'public/app/plugins/panel/graph/axisEditor.html', 2);
this.addEditorTab('Display Styles', 'public/app/plugins/panel/graph/styleEditor.html', 3);
this.logScales = {
'linear': 1,
'log (base 2)': 2,
'log (base 10)': 10,
'log (base 32)': 32,
'log (base 1024)': 1024
};
this.unitFormats = kbn.getUnitFormats();
}
getExtendedMenu() {
var menu = super.getExtendedMenu();
menu.push({text: 'Export CSV', click: 'ctrl.exportCsv()'});
menu.push({text: 'Toggle legend', click: 'ctrl.toggleLegend()'});
return menu;
}
setUnitFormat(axis, subItem) {
this.panel.y_formats[axis] = subItem.value;
this.render();
}
refreshData(datasource) {
this.annotationsPromise = this.annotationsSrv.getAnnotations(this.dashboard);
return this.issueQueries(datasource)
.then(res => this.dataHandler(res))
.catch(err => {
this.seriesList = [];
this.render([]);
throw err;
});
}
zoomOut(evt) {
this.publishAppEvent('zoom-out', evt);
}
loadSnapshot(snapshotData) {
this.updateTimeRange();
this.annotationsPromise = this.annotationsSrv.getAnnotations(this.dashboard);
this.dataHandler(snapshotData);
}
dataHandler(results) {
// png renderer returns just a url
if (_.isString(results)) {
this.render(results);
return;
}
this.datapointsWarning = false;
this.datapointsCount = 0;
this.datapointsOutside = false;
this.seriesList = _.map(results.data, (series, i) => this.seriesHandler(series, i));
this.datapointsWarning = this.datapointsCount === 0 || this.datapointsOutside;
this.annotationsPromise.then(annotations => {
this.loading = false;
this.seriesList.annotations = annotations;
this.render(this.seriesList);
}, () => {
this.loading = false;
this.render(this.seriesList);
});
};
seriesHandler(seriesData, index) {
var datapoints = seriesData.datapoints;
var alias = seriesData.target;
var colorIndex = index % this.colors.length;
var color = this.panel.aliasColors[alias] || this.colors[colorIndex];
export {
GraphPanel,
GraphPanel as Panel
var series = new TimeSeries({
datapoints: datapoints,
alias: alias,
color: color,
});
if (datapoints && datapoints.length > 0) {
var last = moment.utc(datapoints[datapoints.length - 1][1]);
var from = moment.utc(this.range.from);
if (last - from < -10000) {
this.datapointsOutside = true;
}
this.datapointsCount += datapoints.length;
}
return series;
}
render(data?: any) {
this.broadcastRender(data);
}
changeSeriesColor(series, color) {
series.color = color;
this.panel.aliasColors[series.alias] = series.color;
this.render();
}
toggleSeries(serie, event) {
if (event.ctrlKey || event.metaKey || event.shiftKey) {
if (this.hiddenSeries[serie.alias]) {
delete this.hiddenSeries[serie.alias];
} else {
this.hiddenSeries[serie.alias] = true;
}
} else {
this.toggleSeriesExclusiveMode(serie);
}
this.render();
}
toggleSeriesExclusiveMode (serie) {
var hidden = this.hiddenSeries;
if (hidden[serie.alias]) {
delete hidden[serie.alias];
}
// check if every other series is hidden
var alreadyExclusive = _.every(this.seriesList, value => {
if (value.alias === serie.alias) {
return true;
}
return hidden[value.alias];
});
if (alreadyExclusive) {
// remove all hidden series
_.each(this.seriesList, value => {
delete this.hiddenSeries[value.alias];
});
} else {
// hide all but this serie
_.each(this.seriesList, value => {
if (value.alias === serie.alias) {
return;
}
this.hiddenSeries[value.alias] = true;
});
}
}
toggleYAxis(info) {
var override = _.findWhere(this.panel.seriesOverrides, { alias: info.alias });
if (!override) {
override = { alias: info.alias };
this.panel.seriesOverrides.push(override);
}
override.yaxis = info.yaxis === 2 ? 1 : 2;
this.render();
};
addSeriesOverride(override) {
this.panel.seriesOverrides.push(override || {});
}
removeSeriesOverride(override) {
this.panel.seriesOverrides = _.without(this.panel.seriesOverrides, override);
this.render();
}
// Called from panel menu
toggleLegend() {
this.panel.legend.show = !this.panel.legend.show;
this.refresh();
}
legendValuesOptionChanged() {
var legend = this.panel.legend;
legend.values = legend.min || legend.max || legend.avg || legend.current || legend.total;
this.render();
}
exportCsv() {
fileExport.exportSeriesListToCsv(this.seriesList);
}
}
export {GraphCtrl, GraphCtrl as PanelCtrl}
......@@ -3,7 +3,7 @@
import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
import angular from 'angular';
import {GraphCtrl} from '../graph_ctrl';
import {GraphCtrl} from '../module';
import helpers from '../../../../../test/specs/helpers';
describe('GraphCtrl', function() {
......
......@@ -10,6 +10,8 @@ var panelDefaults = {
};
export class TextPanelCtrl extends PanelCtrl {
static templateUrl = `public/app/plugins/panel/text/module.html`;
converter: any;
content: string;
......@@ -79,9 +81,4 @@ export class TextPanelCtrl extends PanelCtrl {
}
}
class TextPanel extends PanelDirective {
templateUrl = `public/app/plugins/panel/text/module.html`;
controller = TextPanelCtrl;
}
export {TextPanel as Panel}
export {TextPanelCtrl as PanelCtrl}
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