Commit 14cc771c by Torkel Ödegaard

feat(plugins): made panels loaded via plugin-componet directive

parent 2a8b96b6
...@@ -5,6 +5,7 @@ import _ from 'lodash'; ...@@ -5,6 +5,7 @@ import _ from 'lodash';
import config from 'app/core/config'; import config from 'app/core/config';
import coreModule from 'app/core/core_module'; import coreModule from 'app/core/core_module';
import {UnknownPanelCtrl} from 'app/plugins/panel/unknown/module';
/** @ngInject */ /** @ngInject */
function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $templateCache) { function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $templateCache) {
...@@ -45,20 +46,23 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ ...@@ -45,20 +46,23 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
} }
function loadPanelComponentInfo(scope, attrs) { function loadPanelComponentInfo(scope, attrs) {
var componentInfo: any = {
name: 'panel-plugin-' + scope.panel.type,
bindings: {dashboard: "=", panel: "=", row: "="},
attrs: {dashboard: "dashboard", panel: "panel", row: "row"},
};
var panelElemName = 'panel-' + scope.panel.type; var panelElemName = 'panel-' + scope.panel.type;
let panelInfo = config.panels[scope.panel.type]; let panelInfo = config.panels[scope.panel.type];
if (!panelInfo) { var panelCtrlPromise = Promise.resolve(UnknownPanelCtrl);
// unknown if (panelInfo) {
panelCtrlPromise = System.import(panelInfo.module).then(function(panelModule) {
return panelModule.PanelCtrl;
});
} }
return System.import(panelInfo.module).then(function(panelModule): any { return panelCtrlPromise.then(function(PanelCtrl: any) {
var PanelCtrl = panelModule.PanelCtrl; componentInfo.Component = 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) { if (!PanelCtrl || PanelCtrl.registered) {
return componentInfo; return componentInfo;
......
...@@ -2,7 +2,6 @@ define([ ...@@ -2,7 +2,6 @@ define([
'./panel_menu', './panel_menu',
'./panel_directive', './panel_directive',
'./solo_panel_ctrl', './solo_panel_ctrl',
'./panel_loader',
'./query_ctrl', './query_ctrl',
'./panel_editor_tab', './panel_editor_tab',
'./query_editor_row', './query_editor_row',
......
...@@ -4,12 +4,18 @@ import config from 'app/core/config'; ...@@ -4,12 +4,18 @@ import config from 'app/core/config';
import {PanelCtrl} from './panel_ctrl'; import {PanelCtrl} from './panel_ctrl';
import {MetricsPanelCtrl} from './metrics_panel_ctrl'; import {MetricsPanelCtrl} from './metrics_panel_ctrl';
import {PanelDirective} from './panel_directive';
import {QueryCtrl} from './query_ctrl'; import {QueryCtrl} from './query_ctrl';
class DefaultPanelCtrl extends PanelCtrl {
/** @ngInject */
constructor($scope, $injector) {
super($scope, $injector);
}
}
export { export {
PanelCtrl, PanelCtrl,
DefaultPanelCtrl,
MetricsPanelCtrl, MetricsPanelCtrl,
PanelDirective,
QueryCtrl, QueryCtrl,
} }
...@@ -3,48 +3,6 @@ ...@@ -3,48 +3,6 @@
import angular from 'angular'; import angular from 'angular';
import $ from 'jquery'; import $ from 'jquery';
import {PanelCtrl} from './panel_ctrl';
export class DefaultPanelCtrl extends PanelCtrl {
/** @ngInject */
constructor($scope, $injector) {
super($scope, $injector);
}
}
export class PanelDirective {
template: string;
templateUrl: string;
bindToController: boolean;
scope: any;
controller: any;
controllerAs: string;
getDirective() {
if (!this.controller) {
this.controller = DefaultPanelCtrl;
}
return {
template: this.template,
templateUrl: this.templateUrl,
controller: this.controller,
controllerAs: 'ctrl',
bindToController: true,
scope: {dashboard: "=", panel: "=", row: "="},
link: (scope, elem, attrs, ctrl) => {
ctrl.init();
this.link(scope, elem, attrs, ctrl);
}
};
}
link(scope, elem, attrs, ctrl) {
return null;
}
}
var module = angular.module('grafana.directives'); var module = angular.module('grafana.directives');
module.directive('grafanaPanel', function() { module.directive('grafanaPanel', function() {
......
///<reference path="../../headers/common.d.ts" />
import angular from 'angular';
import config from 'app/core/config';
import {UnknownPanel} from '../../plugins/panel/unknown/module';
var directiveModule = angular.module('grafana.directives');
/** @ngInject */
function panelLoader($compile, $http, $q, $injector, $templateCache) {
return {
restrict: 'E',
scope: {
dashboard: "=",
row: "=",
panel: "="
},
link: function(scope, elem, attrs) {
function getTemplate(directive) {
if (directive.template) {
return $q.when(directive.template);
}
var cached = $templateCache.get(directive.templateUrl);
if (cached) {
return $q.when(cached);
}
return $http.get(directive.templateUrl).then(res => {
return res.data;
});
}
function addPanelAndCompile(name) {
var child = angular.element(document.createElement(name));
child.attr('dashboard', 'dashboard');
child.attr('panel', 'panel');
child.attr('row', 'row');
$compile(child)(scope);
elem.empty();
elem.append(child);
}
function addPanel(name, Panel) {
if (Panel.registered) {
addPanelAndCompile(name);
return;
}
if (Panel.promise) {
Panel.promise.then(() => {
addPanelAndCompile(name);
});
return;
}
var panelInstance = $injector.instantiate(Panel);
var directive = panelInstance.getDirective();
Panel.promise = getTemplate(directive).then(template => {
directive.templateUrl = null;
directive.template = `<grafana-panel ctrl="ctrl">${template}</grafana-panel>`;
directiveModule.directive(attrs.$normalize(name), function() {
return directive;
});
Panel.registered = true;
addPanelAndCompile(name);
});
}
var panelElemName = 'panel-directive-' + scope.panel.type;
let panelInfo = config.panels[scope.panel.type];
if (!panelInfo) {
addPanel(panelElemName, UnknownPanel);
return;
}
System.import(panelInfo.module).then(function(panelModule) {
addPanel(panelElemName, panelModule.Panel);
}).catch(err => {
console.log('Panel err: ', err);
});
}
};
}
directiveModule.directive('panelLoader', panelLoader);
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import _ from 'lodash'; import _ from 'lodash';
import config from 'app/core/config'; import config from 'app/core/config';
import {PanelDirective, PanelCtrl} from '../../../features/panel/panel'; import {PanelCtrl} from '../../../features/panel/panel';
// Set and populate defaults // Set and populate defaults
var panelDefaults = { var panelDefaults = {
...@@ -55,11 +55,4 @@ class DashListCtrl extends PanelCtrl { ...@@ -55,11 +55,4 @@ class DashListCtrl extends PanelCtrl {
} }
} }
class DashListPanel extends PanelDirective { export {DashListCtrl, DashListCtrl as PanelCtrl}
controller = DashListCtrl;
}
export {
DashListCtrl as DashListCtrl,
DashListCtrl as PanelCtrl,
}
///<reference path="../../../headers/common.d.ts" />
import angular from 'angular';
import _ from 'lodash';
import kbn from 'app/core/utils/kbn';
import TimeSeries from '../../../core/time_series2';
import {MetricsPanelCtrl} from '../../../features/panel/panel';
// Set and populate defaults
var panelDefaults = {
links: [],
datasource: null,
maxDataPoints: 100,
interval: null,
targets: [{}],
cacheTimeout: null,
format: 'none',
prefix: '',
postfix: '',
nullText: null,
valueMaps: [
{ value: 'null', op: '=', text: 'N/A' }
],
nullPointMode: 'connected',
valueName: 'avg',
prefixFontSize: '50%',
valueFontSize: '80%',
postfixFontSize: '50%',
thresholds: '',
colorBackground: false,
colorValue: false,
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
sparkline: {
show: false,
full: false,
lineColor: 'rgb(31, 120, 193)',
fillColor: 'rgba(31, 118, 189, 0.18)',
}
};
export class SingleStatCtrl extends MetricsPanelCtrl {
series: any[];
data: any[];
fontSizes: any[];
unitFormats: any[];
/** @ngInject */
constructor($scope, $injector) {
super($scope, $injector);
_.defaults(this.panel, panelDefaults);
}
initEditMode() {
super.initEditMode();
this.icon = "fa fa-dashboard";
this.fontSizes = ['20%', '30%','50%','70%','80%','100%', '110%', '120%', '150%', '170%', '200%'];
this.addEditorTab('Options', 'app/plugins/panel/singlestat/editor.html', 2);
this.unitFormats = kbn.getUnitFormats();
}
setUnitFormat(subItem) {
this.panel.format = subItem.value;
this.render();
}
refreshData(datasource) {
return this.issueQueries(datasource)
.then(this.dataHandler.bind(this))
.catch(err => {
this.series = [];
this.render();
throw err;
});
}
loadSnapshot(snapshotData) {
this.updateTimeRange();
this.dataHandler(snapshotData);
}
dataHandler(results) {
this.series = _.map(results.data, this.seriesHandler.bind(this));
this.render();
}
seriesHandler(seriesData) {
var series = new TimeSeries({
datapoints: seriesData.datapoints,
alias: seriesData.target,
});
series.flotpairs = series.getFlotPairs(this.panel.nullPointMode);
return series;
}
setColoring(options) {
if (options.background) {
this.panel.colorValue = false;
this.panel.colors = ['rgba(71, 212, 59, 0.4)', 'rgba(245, 150, 40, 0.73)', 'rgba(225, 40, 40, 0.59)'];
} else {
this.panel.colorBackground = false;
this.panel.colors = ['rgba(50, 172, 45, 0.97)', 'rgba(237, 129, 40, 0.89)', 'rgba(245, 54, 54, 0.9)'];
}
this.render();
}
invertColorOrder() {
var tmp = this.panel.colors[0];
this.panel.colors[0] = this.panel.colors[2];
this.panel.colors[2] = tmp;
this.render();
}
getDecimalsForValue(value) {
if (_.isNumber(this.panel.decimals)) {
return {decimals: this.panel.decimals, scaledDecimals: null};
}
var delta = value / 2;
var dec = -Math.floor(Math.log(delta) / Math.LN10);
var magn = Math.pow(10, -dec),
norm = delta / magn, // norm is between 1.0 and 10.0
size;
if (norm < 1.5) {
size = 1;
} else if (norm < 3) {
size = 2;
// special case for 2.5, requires an extra decimal
if (norm > 2.25) {
size = 2.5;
++dec;
}
} else if (norm < 7.5) {
size = 5;
} else {
size = 10;
}
size *= magn;
// reduce starting decimals if not needed
if (Math.floor(value) === value) { dec = 0; }
var result: any = {};
result.decimals = Math.max(0, dec);
result.scaledDecimals = result.decimals - Math.floor(Math.log(size) / Math.LN10) + 2;
return result;
}
render() {
var data: any = {};
this.setValues(data);
data.thresholds = this.panel.thresholds.split(',').map(function(strVale) {
return Number(strVale.trim());
});
data.colorMap = this.panel.colors;
this.data = data;
this.broadcastRender();
}
setValues(data) {
data.flotpairs = [];
if (this.series.length > 1) {
this.inspector.error = new Error();
this.inspector.error.message = 'Multiple Series Error';
this.inspector.error.data = 'Metric query returns ' + this.series.length +
' series. Single Stat Panel expects a single series.\n\nResponse:\n'+JSON.stringify(this.series);
throw this.inspector.error;
}
if (this.series && this.series.length > 0) {
var lastPoint = _.last(this.series[0].datapoints);
var lastValue = _.isArray(lastPoint) ? lastPoint[0] : null;
if (_.isString(lastValue)) {
data.value = 0;
data.valueFormated = lastValue;
data.valueRounded = 0;
} else {
data.value = this.series[0].stats[this.panel.valueName];
data.flotpairs = this.series[0].flotpairs;
var decimalInfo = this.getDecimalsForValue(data.value);
var formatFunc = kbn.valueFormats[this.panel.format];
data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
}
}
// check value to text mappings
for (var i = 0; i < this.panel.valueMaps.length; i++) {
var map = this.panel.valueMaps[i];
// special null case
if (map.value === 'null') {
if (data.value === null || data.value === void 0) {
data.valueFormated = map.text;
return;
}
continue;
}
// value/number to text mapping
var value = parseFloat(map.value);
if (value === data.value) {
data.valueFormated = map.text;
return;
}
}
if (data.value === null || data.value === void 0) {
data.valueFormated = "no value";
}
};
removeValueMap(map) {
var index = _.indexOf(this.panel.valueMaps, map);
this.panel.valueMaps.splice(index, 1);
this.render();
};
addValueMap() {
this.panel.valueMaps.push({value: '', op: '=', text: '' });
}
}
///<reference path="../../../headers/common.d.ts" /> ///<reference path="../../../headers/common.d.ts" />
import angular from 'angular';
import _ from 'lodash'; import _ from 'lodash';
import $ from 'jquery'; import $ from 'jquery';
import 'jquery.flot'; import 'jquery.flot';
import {SingleStatCtrl} from './controller';
import {PanelDirective} from '../../../features/panel/panel';
class SingleStatPanel extends PanelDirective { import kbn from 'app/core/utils/kbn';
templateUrl = 'public/app/plugins/panel/singlestat/module.html'; import TimeSeries from '../../../core/time_series2';
controller = SingleStatCtrl; import {MetricsPanelCtrl} from '../../../features/panel/panel';
// Set and populate defaults
var panelDefaults = {
links: [],
datasource: null,
maxDataPoints: 100,
interval: null,
targets: [{}],
cacheTimeout: null,
format: 'none',
prefix: '',
postfix: '',
nullText: null,
valueMaps: [
{ value: 'null', op: '=', text: 'N/A' }
],
nullPointMode: 'connected',
valueName: 'avg',
prefixFontSize: '50%',
valueFontSize: '80%',
postfixFontSize: '50%',
thresholds: '',
colorBackground: false,
colorValue: false,
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
sparkline: {
show: false,
full: false,
lineColor: 'rgb(31, 120, 193)',
fillColor: 'rgba(31, 118, 189, 0.18)',
}
};
class SingleStatCtrl extends MetricsPanelCtrl {
static templateUrl = 'public/app/plugins/panel/singlestat/module.html';
series: any[];
data: any[];
fontSizes: any[];
unitFormats: any[];
/** @ngInject */ /** @ngInject */
constructor(private $location, private linkSrv, private $timeout, private templateSrv) { constructor($scope, $injector, private $location, private linkSrv, private templateSrv) {
super(); super($scope, $injector);
_.defaults(this.panel, panelDefaults);
}
initEditMode() {
super.initEditMode();
this.icon = "fa fa-dashboard";
this.fontSizes = ['20%', '30%','50%','70%','80%','100%', '110%', '120%', '150%', '170%', '200%'];
this.addEditorTab('Options', 'app/plugins/panel/singlestat/editor.html', 2);
this.unitFormats = kbn.getUnitFormats();
}
setUnitFormat(subItem) {
this.panel.format = subItem.value;
this.render();
}
refreshData(datasource) {
return this.issueQueries(datasource)
.then(this.dataHandler.bind(this))
.catch(err => {
this.series = [];
this.render();
throw err;
});
}
loadSnapshot(snapshotData) {
this.updateTimeRange();
this.dataHandler(snapshotData);
}
dataHandler(results) {
this.series = _.map(results.data, this.seriesHandler.bind(this));
this.render();
}
seriesHandler(seriesData) {
var series = new TimeSeries({
datapoints: seriesData.datapoints,
alias: seriesData.target,
});
series.flotpairs = series.getFlotPairs(this.panel.nullPointMode);
return series;
}
setColoring(options) {
if (options.background) {
this.panel.colorValue = false;
this.panel.colors = ['rgba(71, 212, 59, 0.4)', 'rgba(245, 150, 40, 0.73)', 'rgba(225, 40, 40, 0.59)'];
} else {
this.panel.colorBackground = false;
this.panel.colors = ['rgba(50, 172, 45, 0.97)', 'rgba(237, 129, 40, 0.89)', 'rgba(245, 54, 54, 0.9)'];
}
this.render();
}
invertColorOrder() {
var tmp = this.panel.colors[0];
this.panel.colors[0] = this.panel.colors[2];
this.panel.colors[2] = tmp;
this.render();
}
getDecimalsForValue(value) {
if (_.isNumber(this.panel.decimals)) {
return {decimals: this.panel.decimals, scaledDecimals: null};
}
var delta = value / 2;
var dec = -Math.floor(Math.log(delta) / Math.LN10);
var magn = Math.pow(10, -dec),
norm = delta / magn, // norm is between 1.0 and 10.0
size;
if (norm < 1.5) {
size = 1;
} else if (norm < 3) {
size = 2;
// special case for 2.5, requires an extra decimal
if (norm > 2.25) {
size = 2.5;
++dec;
}
} else if (norm < 7.5) {
size = 5;
} else {
size = 10;
}
size *= magn;
// reduce starting decimals if not needed
if (Math.floor(value) === value) { dec = 0; }
var result: any = {};
result.decimals = Math.max(0, dec);
result.scaledDecimals = result.decimals - Math.floor(Math.log(size) / Math.LN10) + 2;
return result;
}
render() {
var data: any = {};
this.setValues(data);
data.thresholds = this.panel.thresholds.split(',').map(function(strVale) {
return Number(strVale.trim());
});
data.colorMap = this.panel.colors;
this.data = data;
this.broadcastRender();
}
setValues(data) {
data.flotpairs = [];
if (this.series.length > 1) {
this.inspector.error = new Error();
this.inspector.error.message = 'Multiple Series Error';
this.inspector.error.data = 'Metric query returns ' + this.series.length +
' series. Single Stat Panel expects a single series.\n\nResponse:\n'+JSON.stringify(this.series);
throw this.inspector.error;
}
if (this.series && this.series.length > 0) {
var lastPoint = _.last(this.series[0].datapoints);
var lastValue = _.isArray(lastPoint) ? lastPoint[0] : null;
if (_.isString(lastValue)) {
data.value = 0;
data.valueFormated = lastValue;
data.valueRounded = 0;
} else {
data.value = this.series[0].stats[this.panel.valueName];
data.flotpairs = this.series[0].flotpairs;
var decimalInfo = this.getDecimalsForValue(data.value);
var formatFunc = kbn.valueFormats[this.panel.format];
data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
}
}
// check value to text mappings
for (var i = 0; i < this.panel.valueMaps.length; i++) {
var map = this.panel.valueMaps[i];
// special null case
if (map.value === 'null') {
if (data.value === null || data.value === void 0) {
data.valueFormated = map.text;
return;
}
continue;
}
// value/number to text mapping
var value = parseFloat(map.value);
if (value === data.value) {
data.valueFormated = map.text;
return;
}
}
if (data.value === null || data.value === void 0) {
data.valueFormated = "no value";
}
};
removeValueMap(map) {
var index = _.indexOf(this.panel.valueMaps, map);
this.panel.valueMaps.splice(index, 1);
this.render();
};
addValueMap() {
this.panel.valueMaps.push({value: '', op: '=', text: '' });
} }
link(scope, elem, attrs, ctrl) { link(scope, elem, attrs, ctrl) {
...@@ -235,7 +454,7 @@ function getColorForValue(data, value) { ...@@ -235,7 +454,7 @@ function getColorForValue(data, value) {
} }
export { export {
SingleStatPanel, SingleStatCtrl,
SingleStatPanel as Panel, SingleStatCtrl as PanelCtrl,
getColorForValue getColorForValue
}; };
...@@ -4,7 +4,7 @@ import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../.. ...@@ -4,7 +4,7 @@ import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../..
import angular from 'angular'; import angular from 'angular';
import helpers from '../../../../../test/specs/helpers'; import helpers from '../../../../../test/specs/helpers';
import {SingleStatCtrl} from '../controller'; import {SingleStatCtrl} from '../module';
describe('SingleStatCtrl', function() { describe('SingleStatCtrl', function() {
var ctx = new helpers.ControllerTestContext(); var ctx = new helpers.ControllerTestContext();
......
///<reference path="../../../headers/common.d.ts" />
import angular from 'angular';
import _ from 'lodash';
import moment from 'moment';
import * as FileExport from 'app/core/utils/file_export';
import {MetricsPanelCtrl} from '../../../features/panel/panel';
import {transformDataToTable} from './transformers';
import {tablePanelEditor} from './editor';
var panelDefaults = {
targets: [{}],
transform: 'timeseries_to_columns',
pageSize: null,
showHeader: true,
styles: [
{
type: 'date',
pattern: 'Time',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
},
{
unit: 'short',
type: 'number',
decimals: 2,
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
colorMode: null,
pattern: '/.*/',
thresholds: [],
}
],
columns: [],
scroll: true,
fontSize: '100%',
sort: {col: 0, desc: true},
};
export class TablePanelCtrl extends MetricsPanelCtrl {
pageIndex: number;
dataRaw: any;
table: any;
/** @ngInject */
constructor($scope, $injector, private annotationsSrv) {
super($scope, $injector);
this.pageIndex = 0;
if (this.panel.styles === void 0) {
this.panel.styles = this.panel.columns;
this.panel.columns = this.panel.fields;
delete this.panel.columns;
delete this.panel.fields;
}
_.defaults(this.panel, panelDefaults);
}
initEditMode() {
super.initEditMode();
this.addEditorTab('Options', tablePanelEditor, 2);
}
getExtendedMenu() {
var menu = super.getExtendedMenu();
menu.push({text: 'Export CSV', click: 'ctrl.exportCsv()'});
return menu;
}
refreshData(datasource) {
this.pageIndex = 0;
if (this.panel.transform === 'annotations') {
return this.annotationsSrv.getAnnotations(this.dashboard).then(annotations => {
this.dataRaw = annotations;
this.render();
});
}
return this.issueQueries(datasource)
.then(this.dataHandler.bind(this))
.catch(err => {
this.render();
throw err;
});
}
toggleColumnSort(col, colIndex) {
if (this.panel.sort.col === colIndex) {
if (this.panel.sort.desc) {
this.panel.sort.desc = false;
} else {
this.panel.sort.col = null;
}
} else {
this.panel.sort.col = colIndex;
this.panel.sort.desc = true;
}
this.render();
}
dataHandler(results) {
this.dataRaw = results.data;
this.pageIndex = 0;
this.render();
}
render() {
// automatically correct transform mode
// based on data
if (this.dataRaw && this.dataRaw.length) {
if (this.dataRaw[0].type === 'table') {
this.panel.transform = 'table';
} else {
if (this.dataRaw[0].type === 'docs') {
this.panel.transform = 'json';
} else {
if (this.panel.transform === 'table' || this.panel.transform === 'json') {
this.panel.transform = 'timeseries_to_rows';
}
}
}
}
this.table = transformDataToTable(this.dataRaw, this.panel);
this.table.sort(this.panel.sort);
this.broadcastRender(this.table);
}
exportCsv() {
FileExport.exportTableDataToCsv(this.table);
}
}
///<reference path="../../../headers/common.d.ts" /> ///<reference path="../../../headers/common.d.ts" />
import kbn = require('app/core/utils/kbn'); import angular from 'angular';
import _ from 'lodash'; import _ from 'lodash';
import $ from 'jquery'; import $ from 'jquery';
import moment from 'moment'; import moment from 'moment';
import {PanelDirective} from '../../../features/panel/panel'; import * as FileExport from 'app/core/utils/file_export';
import {TablePanelCtrl} from './controller'; import {MetricsPanelCtrl} from '../../../features/panel/panel';
import {transformDataToTable} from './transformers';
import {tablePanelEditor} from './editor';
import {TableRenderer} from './renderer'; import {TableRenderer} from './renderer';
class TablePanel extends PanelDirective { var panelDefaults = {
templateUrl = 'public/app/plugins/panel/table/module.html'; targets: [{}],
controller = TablePanelCtrl; transform: 'timeseries_to_columns',
pageSize: null,
showHeader: true,
styles: [
{
type: 'date',
pattern: 'Time',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
},
{
unit: 'short',
type: 'number',
decimals: 2,
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
colorMode: null,
pattern: '/.*/',
thresholds: [],
}
],
columns: [],
scroll: true,
fontSize: '100%',
sort: {col: 0, desc: true},
};
class TablePanelCtrl extends MetricsPanelCtrl {
static templateUrl = 'public/app/plugins/panel/table/module.html';
pageIndex: number;
dataRaw: any;
table: any;
/** @ngInject */
constructor($scope, $injector, private annotationsSrv) {
super($scope, $injector);
this.pageIndex = 0;
if (this.panel.styles === void 0) {
this.panel.styles = this.panel.columns;
this.panel.columns = this.panel.fields;
delete this.panel.columns;
delete this.panel.fields;
}
_.defaults(this.panel, panelDefaults);
}
initEditMode() {
super.initEditMode();
this.addEditorTab('Options', tablePanelEditor, 2);
}
getExtendedMenu() {
var menu = super.getExtendedMenu();
menu.push({text: 'Export CSV', click: 'ctrl.exportCsv()'});
return menu;
}
refreshData(datasource) {
this.pageIndex = 0;
if (this.panel.transform === 'annotations') {
return this.annotationsSrv.getAnnotations(this.dashboard).then(annotations => {
this.dataRaw = annotations;
this.render();
});
}
return this.issueQueries(datasource)
.then(this.dataHandler.bind(this))
.catch(err => {
this.render();
throw err;
});
}
toggleColumnSort(col, colIndex) {
if (this.panel.sort.col === colIndex) {
if (this.panel.sort.desc) {
this.panel.sort.desc = false;
} else {
this.panel.sort.col = null;
}
} else {
this.panel.sort.col = colIndex;
this.panel.sort.desc = true;
}
this.render();
}
dataHandler(results) {
this.dataRaw = results.data;
this.pageIndex = 0;
this.render();
}
render() {
// automatically correct transform mode
// based on data
if (this.dataRaw && this.dataRaw.length) {
if (this.dataRaw[0].type === 'table') {
this.panel.transform = 'table';
} else {
if (this.dataRaw[0].type === 'docs') {
this.panel.transform = 'json';
} else {
if (this.panel.transform === 'table' || this.panel.transform === 'json') {
this.panel.transform = 'timeseries_to_rows';
}
}
}
}
this.table = transformDataToTable(this.dataRaw, this.panel);
this.table.sort(this.panel.sort);
this.broadcastRender(this.table);
}
exportCsv() {
FileExport.exportTableDataToCsv(this.table);
}
link(scope, elem, attrs, ctrl) { link(scope, elem, attrs, ctrl) {
var data; var data;
...@@ -97,6 +219,6 @@ class TablePanel extends PanelDirective { ...@@ -97,6 +219,6 @@ class TablePanel extends PanelDirective {
} }
export { export {
TablePanel, TablePanelCtrl,
TablePanel as Panel TablePanelCtrl as PanelCtrl
}; };
///<reference path="../../../headers/common.d.ts" /> ///<reference path="../../../headers/common.d.ts" />
import _ from 'lodash'; import _ from 'lodash';
import {PanelDirective, PanelCtrl} from '../../../features/panel/panel'; import {PanelCtrl} from '../../../features/panel/panel';
// Set and populate defaults // Set and populate defaults
var panelDefaults = { var panelDefaults = {
......
///<reference path="../../../headers/common.d.ts" /> ///<reference path="../../../headers/common.d.ts" />
import {PanelDirective} from '../../../features/panel/panel'; import {PanelCtrl} from '../../../features/panel/panel';
class UnknownPanel extends PanelDirective { export class UnknownPanelCtrl extends PanelCtrl {
templateUrl = 'public/app/plugins/panel/unknown/module.html'; static templateUrl = 'public/app/plugins/panel/unknown/module.html';
constructor($scope, $injector) {
super($scope, $injector);
}
} }
export {
UnknownPanel,
UnknownPanel as Panel
}
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