Commit 90cca939 by Torkel Ödegaard

feat(tablepanel): lots of work on table panel

parent 4e37290a
......@@ -45,16 +45,25 @@ function (_, $, coreModule) {
}
var typeaheadValues = _.reduce($scope.menuItems, function(memo, value, index) {
if (!value.submenu) {
value.click = 'menuItemSelected(' + index + ')';
memo.push(value.text);
} else {
_.each(value.submenu, function(item, subIndex) {
item.click = 'menuItemSelected(' + index + ',' + subIndex + ')';
memo.push(value.text + ' ' + item.text);
});
}
return memo;
}, []);
$scope.menuItemSelected = function(index, subIndex) {
var item = $scope.menuItems[index];
$scope.dropdownTypeaheadOnSelect({$item: item, $subItem: item.submenu[subIndex]});
var menuItem = $scope.menuItems[index];
var payload = {$item: menuItem};
if (menuItem.submenu && subIndex !== void 0) {
payload.$subItem = menuItem.submenu[subIndex];
}
$scope.dropdownTypeaheadOnSelect(payload);
};
$input.attr('data-provide', 'typeahead');
......@@ -65,9 +74,10 @@ function (_, $, coreModule) {
updater: function (value) {
var result = {};
_.each($scope.menuItems, function(menuItem) {
result.$item = menuItem;
_.each(menuItem.submenu, function(submenuItem) {
if (value === (menuItem.text + ' ' + submenuItem.text)) {
result.$item = menuItem;
result.$subItem = submenuItem;
}
});
......
......@@ -32,9 +32,9 @@ function (angular, _, $, kbn, dateMath, rangeUtil) {
scope.timing.renderEnd = new Date().getTime();
};
this.broadcastRender = function(scope, data) {
this.broadcastRender = function(scope, arg1, arg2) {
this.setTimeRenderStart(scope);
scope.$broadcast('render', data);
scope.$broadcast('render', arg1, arg2);
this.setTimeRenderEnd(scope);
if ($rootScope.profilingEnabled) {
......
......@@ -3,24 +3,15 @@
import angular = require('angular');
import _ = require('lodash');
import moment = require('moment');
import kbn = require('app/core/utils/kbn');
import PanelMeta = require('app/features/panel/panel_meta');
import {TableModel} from './table_model';
import {transformers} from './transformers';
export class TablePanelCtrl {
constructor($scope, $rootScope, $q, panelSrv, panelHelper) {
$scope.ctrl = this;
$scope.transformers = transformers;
$scope.pageIndex = 0;
$scope.unitFormats = kbn.getUnitFormats();
$scope.colorModes = {
'cell': {text: 'Cell'},
'value': {text: 'Value'},
'row': {text: 'Row'},
};
$scope.panelMeta = new PanelMeta({
panelName: 'Table',
......@@ -38,23 +29,18 @@ export class TablePanelCtrl {
pageSize: 50,
showHeader: true,
columns: [],
fields: []
};
$scope.init = function() {
_.defaults($scope.panel, panelDefaults);
if ($scope.panel.columns.length === 0) {
$scope.addColumnStyle();
}
panelSrv.init($scope);
};
$scope.setUnitFormat = function(column, subItem) {
column.unit = subItem.value;
$scope.render();
};
$scope.refreshData = function(datasource) {
panelHelper.updateTimeRange($scope);
......@@ -73,32 +59,7 @@ export class TablePanelCtrl {
$scope.render = function() {
$scope.table = TableModel.transform($scope.dataRaw, $scope.panel);
panelHelper.broadcastRender($scope, $scope.table);
};
$scope.getColumnNames = function() {
if (!$scope.table) {
return [];
}
return _.map($scope.table.columns, function(col: any) {
return col.text;
});
};
$scope.addColumnStyle = function() {
var columnStyleDefaults = {
unit: 'short',
decimals: 2,
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
pattern: '/.*/',
colorMode: 'value',
};
$scope.panel.columns.push(angular.copy(columnStyleDefaults));
};
$scope.removeColumnStyle = function(col) {
$scope.panel.columns = _.without($scope.panel.columns, col);
panelHelper.broadcastRender($scope, $scope.table, $scope.dataRaw);
};
$scope.init();
......
<div class="editor-row">
<div class="section">
<h5>Data</h5>
<div class="tight-form-container">
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 140px">
To Table Transform
</li>
<li>
<select class="input-large tight-form-input"
ng-model="panel.transform"
ng-options="k as v.description for (k, v) in transformers"
ng-change="render()"></select>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form" ng-if="panel.transform === 'json'">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 140px">
Fields
</li>
<li class="tight-form-item" ng-repeat="field in panel.fields">
<i class="pointer fa fa-remove" ng-click="removeJsonField(field)"></i>
<span>
{{field.name}}
</span>
</li>
<li class="dropdown" dropdown-typeahead="jsonFieldsMenu" dropdown-typeahead-on-select="addJsonField($item, $subItem)">
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="section">
<h5>Table Display</h5>
<div class="tight-form-container">
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item">
Pagination (Page size)
</li>
<li>
<input type="text" class="input-small tight-form-input" placeholder="50"
empty-to-null ng-model="panel.pageSize" ng-change="render()" ng-model-onblur>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
<div class="editor-row" style="margin-top: 20px">
<h5>Column Styles</h5>
<div class="tight-form-container">
<div ng-repeat="column in panel.columns">
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item">
<i class="fa fa-remove pointer" ng-click="removeColumnStyle(column)"></i>
</li>
<li class="tight-form-item">
Name or regex
</li>
<li>
<input type="text" ng-model="column.pattern" bs-typeahead="getColumnNames" ng-blur="render()" data-min-length=0 data-items=100 class="input-medium tight-form-input">
</li>
<li class="tight-form-item" style="width: 86px">
Type
</li>
<li>
<select class="input-small tight-form-input"
ng-model="column.type"
ng-options="k as v.text for (k, v) in columnTypes"
ng-change="render()"
style="width: 150px"
></select>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form" ng-if="column.type === 'date'">
<ul class="tight-form-list">
<li class="tight-form-item">
<i class="fa fa-remove pointer invisible"></i>
</li>
<li class="tight-form-item text-right" style="width: 93px">
Format
</li>
<li>
<input type="text" class="input-medium tight-form-input" ng-model="column.dateFormat" ng-change="render()" ng-model-onblur>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form" ng-if="column.type === 'number'">
<ul class="tight-form-list">
<li class="tight-form-item">
<i class="fa fa-remove pointer invisible"></i>
</li>
<li class="tight-form-item text-right" style="width: 93px">
Coloring
</li>
<li>
<select class="input-small tight-form-input"
ng-model="column.colorMode"
ng-options="k as v.text for (k, v) in colorModes"
ng-change="render()"
style="width: 150px"
></select>
</li>
<li class="tight-form-item">
Thresholds<tip>Comma seperated values</tip>
</li>
<li>
<input type="text" class="input-small tight-form-input" style="width: 150px" ng-model="column.thresholds" ng-blur="render()" placeholder="0,50,80"></input>
</li>
<li class="tight-form-item" style="width: 60px">
Colors
</li>
<li class="tight-form-item">
<spectrum-picker ng-model="column.colors[0]" ng-change="render()" ></spectrum-picker>
<spectrum-picker ng-model="column.colors[1]" ng-change="render()" ></spectrum-picker>
<spectrum-picker ng-model="column.colors[2]" ng-change="render()" ></spectrum-picker>
</li>
<li class="tight-form-item last">
<a class="pointer" ng-click="invertColorOrder()">invert order</a>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form" ng-if="column.type === 'number'">
<ul class="tight-form-list">
<li class="tight-form-item">
<i class="fa fa-remove pointer invisible"></i>
</li>
<li class="tight-form-item text-right" style="width: 93px">
Unit
</li>
<li class="dropdown" style="width: 150px"
ng-model="column.unit"
dropdown-typeahead="unitFormats"
dropdown-typeahead-on-select="setUnitFormat(column, $subItem)">
</li>
<li class="tight-form-item" style="width: 86px">
Decimals
</li>
<li style="width: 105px">
<input type="number" class="input-mini tight-form-input" ng-model="column.decimals" ng-change="render()" ng-model-onblur>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>
<button class="btn btn-inverse" style="margin-top: 20px" ng-click="addColumnStyle()">
Add column display rule
</button>
</div>
///<reference path="../../headers/common.d.ts" />
import angular = require('angular');
import $ = require('jquery');
import _ = require('lodash');
import kbn = require('app/core/utils/kbn');
import moment = require('moment');
import {transformers} from './transformers';
export function tablePanelEditor() {
'use strict';
return {
restrict: 'E',
scope: true,
templateUrl: 'app/panels/table/editor.html',
link: function(scope, elem) {
scope.transformers = transformers;
scope.unitFormats = kbn.getUnitFormats();
scope.colorModes = {
'cell': {text: 'Cell'},
'value': {text: 'Value'},
'row': {text: 'Row'},
};
scope.columnTypes = {
'number': {text: 'Number'},
'string': {text: 'String'},
'date': {text: 'Date'},
};
scope.updateJsonFieldsMenu = function(data) {
scope.jsonFieldsMenu = [];
if (!data || data.length === 0) {
return;
}
var names = {};
for (var i = 0; i < data.length; i++) {
var series = data[i];
if (series.type !== 'docs') {
continue;
}
for (var y = 0; y < series.datapoints.length; y++) {
var doc = series.datapoints[y];
for (var propName in doc) {
names[propName] = true;
}
}
}
_.each(names, function(value, key) {
scope.jsonFieldsMenu.push({text: key});
});
};
scope.updateJsonFieldsMenu(scope.dataRaw);
scope.$on('render', function(event, table, rawData) {
scope.updateJsonFieldsMenu(rawData);
});
scope.addJsonField = function(menuItem) {
scope.panel.fields.push({name: menuItem.text});
};
scope.removeJsonField = function(field) {
scope.panel.fields = _.without(scope.panel.fields, field);
};
scope.setUnitFormat = function(column, subItem) {
column.unit = subItem.value;
scope.render();
};
scope.addColumnStyle = function() {
var columnStyleDefaults = {
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: 'value',
pattern: '/.*/',
};
scope.panel.columns.push(angular.copy(columnStyleDefaults));
};
scope.removeColumnStyle = function(col) {
scope.panel.columns = _.without(scope.panel.columns, col);
};
scope.getColumnNames = function() {
if (!scope.table) {
return [];
}
return _.map(scope.table.columns, function(col: any) {
return col.text;
});
};
}
};
}
......@@ -4,10 +4,12 @@ import angular = require('angular');
import $ = require('jquery');
import _ = require('lodash');
import kbn = require('app/core/utils/kbn');
import moment = require('moment');
import {TablePanelCtrl} from './controller';
import {tablePanelEditor} from './editor';
export function tablePanelDirective() {
export function tablePanel() {
'use strict';
return {
restrict: 'E',
......@@ -45,9 +47,13 @@ export function tablePanelDirective() {
if (v === null || v === void 0) {
return '-';
}
if (_.isString(v) || style) {
if (_.isString(v) || !style) {
return v;
}
if (style.type === 'date') {
var date = moment(v);
return date.format(style.dateFormat);
}
let valueFormater = kbn.valueFormats[style.unit];
return valueFormater(v, style.decimals);
};
......@@ -136,4 +142,5 @@ export function tablePanelDirective() {
};
}
angular.module('grafana.directives').directive('grafanaPanelTable', tablePanelDirective);
angular.module('grafana.directives').directive('grafanaPanelTable', tablePanel);
angular.module('grafana.directives').directive('grafanaPanelTableEditor', tablePanelEditor);
<div class="editor-row">
<div class="section">
<h5>Data</h5>
<div class="tight-form-container">
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 170px">
To Table Transform
</li>
<li>
<select class="input-xlarge tight-form-input"
ng-model="panel.transform"
ng-options="k as v.description for (k, v) in transformers"
ng-change="render()"></select>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="section">
<h5>Table Display</h5>
<div class="tight-form-container">
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item">
Pagination (Page size)
</li>
<li>
<input type="text" class="input-small tight-form-input" placeholder="50"
empty-to-null ng-model="panel.pageSize" ng-change="render()" ng-model-onblur>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
<div class="editor-row" style="margin-top: 20px">
<h5>Column Styles</h5>
<div class="tight-form-container">
<div ng-repeat="column in panel.columns">
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item">
<i class="fa fa-remove pointer" ng-click="removeColumnStyle(column)"></i>
</li>
<li class="tight-form-item">
Name or regex
</li>
<li>
<input type="text" ng-model="column.pattern" bs-typeahead="getColumnNames" ng-blur="render()" data-min-length=0 data-items=100 class="input-medium tight-form-input">
</li>
<li class="tight-form-item" style="width: 86px">
Unit
</li>
<li class="dropdown" style="width: 150px"
ng-model="column.unit"
dropdown-typeahead="unitFormats"
dropdown-typeahead-on-select="setUnitFormat(column, $subItem)">
</li>
<li class="tight-form-item" style="width: 60px">
Decimals
</li>
<li style="width: 105px">
<input type="number" class="input-mini tight-form-input" ng-model="column.decimals" ng-change="render()" ng-model-onblur>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item">
<i class="fa fa-remove pointer invisible"></i>
</li>
<li class="tight-form-item text-right" style="width: 93px">
Coloring
</li>
<li>
<select class="input-small tight-form-input"
ng-model="column.colorMode"
ng-options="k as v.text for (k, v) in colorModes"
ng-change="render()"
style="width: 150px"
></select>
</li>
<li class="tight-form-item">
Thresholds<tip>Comma seperated values</tip>
</li>
<li>
<input type="text" class="input-small tight-form-input" style="width: 150px" ng-model="column.thresholds" ng-blur="render()" placeholder="0,50,80"></input>
</li>
<li class="tight-form-item" style="width: 60px">
Colors
</li>
<li class="tight-form-item">
<spectrum-picker ng-model="column.colors[0]" ng-change="render()" ></spectrum-picker>
<spectrum-picker ng-model="column.colors[1]" ng-change="render()" ></spectrum-picker>
<spectrum-picker ng-model="column.colors[2]" ng-change="render()" ></spectrum-picker>
</li>
<li class="tight-form-item last">
<a class="pointer" ng-click="invertColorOrder()">invert order</a>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>
<button class="btn btn-inverse" style="margin-top: 20px" ng-click="addColumnStyle()">
Add column display rule
</button>
</div>
<grafana-panel-table-editor>
</grafana-panel-table-editor>
......@@ -67,6 +67,41 @@ describe('when transforming time series table', () => {
expect(table.rows[1][2]).to.be(undefined);
});
});
describe('JSON Data', () => {
var panel = {
transform: 'json',
fields: [{name: 'timestamp'}, {name: 'message'}]
};
var rawData = [
{
type: 'docs',
datapoints: [
{
timestamp: 'time',
message: 'message'
}
]
}
];
beforeEach(() => {
table = TableModel.transform(rawData, panel);
});
it ('should return 2 columns', () => {
expect(table.columns.length).to.be(2);
expect(table.columns[0].text).to.be('timestamp');
expect(table.columns[1].text).to.be('message');
});
it ('should return 2 rows', () => {
expect(table.rows.length).to.be(2);
expect(table.rows[0][0]).to.be('time');
expect(table.rows[0][1]).to.be('message');
});
});
});
});
......@@ -9,7 +9,7 @@ transformers['timeseries_to_rows'] = {
description: 'Time series to rows',
transform: function(data, panel, model) {
model.columns = [
{text: 'Time'},
{text: 'Time', type: 'date'},
{text: 'Series'},
{text: 'Value'},
];
......@@ -18,9 +18,7 @@ transformers['timeseries_to_rows'] = {
var series = data[i];
for (var y = 0; y < series.datapoints.length; y++) {
var dp = series.datapoints[y];
var time = moment(dp[1]).format('LLL');
var value = dp[0];
model.rows.push([time, series.target, value]);
model.rows.push([dp[1], series.target, dp[0]]);
}
}
},
......@@ -29,7 +27,7 @@ transformers['timeseries_to_rows'] = {
transformers['timeseries_to_columns'] = {
description: 'Time series to columns',
transform: function(data, panel, model) {
model.columns.push({text: 'Time'});
model.columns.push({text: 'Time', type: 'date'});
// group by time
var points = {};
......@@ -54,7 +52,7 @@ transformers['timeseries_to_columns'] = {
for (var time in points) {
var point = points[time];
var values = [moment(point.time).format('LLL')];
var values = [point.time];
for (var i = 0; i < data.length; i++) {
var value = point[i];
......@@ -71,17 +69,31 @@ transformers['annotations'] = {
};
transformers['json'] = {
description: 'JSON',
description: 'JSON Data',
transform: function(data, panel, model) {
var i, y, z;
for (i = 0; i < panel.fields.length; i++) {
model.columns.push({text: panel.fields[i].name});
}
if (model.columns.length === 0) {
model.columns.push({text: 'JSON'});
debugger;
}
for (var i = 0; i < data.length; i++) {
for (i = 0; i < data.length; i++) {
var series = data[i];
for (var y = 0; y < series.datapoints.length; y++) {
for (y = 0; y < series.datapoints.length; y++) {
var dp = series.datapoints[y];
model.rows.push([JSON.stringify(dp)]);
var values = [];
for (z = 0; z < panel.fields.length; z++) {
values.push(dp[panel.fields[z].name]);
}
if (values.length === 0) {
values.push([JSON.stringify(dp)]);
}
model.rows.push(values);
}
}
}
......
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