From 90cca9395110ff84b48b5b5a6287248c31f256c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 5 Nov 2015 13:13:13 +0100 Subject: [PATCH] feat(tablepanel): lots of work on table panel --- .../app/core/directives/dropdown_typeahead.js | 24 ++- public/app/features/panel/panel_helper.js | 4 +- public/app/panels/table/controller.ts | 43 +---- public/app/panels/table/editor.html | 167 ++++++++++++++++++ public/app/panels/table/editor.ts | 106 +++++++++++ public/app/panels/table/module.ts | 13 +- public/app/panels/table/options.html | 119 +------------ .../panels/table/specs/transformers_specs.ts | 35 ++++ public/app/panels/table/transformers.ts | 36 ++-- 9 files changed, 365 insertions(+), 182 deletions(-) create mode 100644 public/app/panels/table/editor.html create mode 100644 public/app/panels/table/editor.ts diff --git a/public/app/core/directives/dropdown_typeahead.js b/public/app/core/directives/dropdown_typeahead.js index c306b9ff8e5..ad484bb18d7 100644 --- a/public/app/core/directives/dropdown_typeahead.js +++ b/public/app/core/directives/dropdown_typeahead.js @@ -45,16 +45,25 @@ function (_, $, coreModule) { } var typeaheadValues = _.reduce($scope.menuItems, function(memo, value, index) { - _.each(value.submenu, function(item, subIndex) { - item.click = 'menuItemSelected(' + index + ',' + subIndex + ')'; - memo.push(value.text + ' ' + item.text); - }); + 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; } }); diff --git a/public/app/features/panel/panel_helper.js b/public/app/features/panel/panel_helper.js index c3ccbfe7e09..52a6727eb2c 100644 --- a/public/app/features/panel/panel_helper.js +++ b/public/app/features/panel/panel_helper.js @@ -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) { diff --git a/public/app/panels/table/controller.ts b/public/app/panels/table/controller.ts index 4ae5a1807fa..7c22ba2823f 100644 --- a/public/app/panels/table/controller.ts +++ b/public/app/panels/table/controller.ts @@ -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(); diff --git a/public/app/panels/table/editor.html b/public/app/panels/table/editor.html new file mode 100644 index 00000000000..e7c76402974 --- /dev/null +++ b/public/app/panels/table/editor.html @@ -0,0 +1,167 @@ +
+
+
Data
+
+
+
    +
  • + To Table Transform +
  • +
  • + +
  • +
+
+
+
+
    +
  • + Fields +
  • +
  • + + + {{field.name}} + +
  • + +
+
+
+
+
+ +
+
Table Display
+
+
+
    +
  • + Pagination (Page size) +
  • +
  • + +
  • +
+
+
+
+
+
+ +
+
Column Styles
+ +
+
+
+
    +
  • + +
  • +
  • + Name or regex +
  • +
  • + +
  • +
  • + Type +
  • +
  • + +
  • +
+
+
+
+
    +
  • + +
  • +
  • + Format +
  • +
  • + +
  • +
+
+
+
+
    +
  • + +
  • +
  • + Coloring +
  • +
  • + +
  • +
  • + ThresholdsComma seperated values +
  • +
  • + +
  • +
  • + Colors +
  • +
  • + + + +
  • +
  • + invert order +
  • +
+
+
+
+
    +
  • + +
  • +
  • + Unit +
  • + +
  • + Decimals +
  • +
  • + +
  • +
+
+
+ +
+
+ + +
+ diff --git a/public/app/panels/table/editor.ts b/public/app/panels/table/editor.ts new file mode 100644 index 00000000000..4cd4070dd1b --- /dev/null +++ b/public/app/panels/table/editor.ts @@ -0,0 +1,106 @@ + +/// + +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; + }); + }; + + } + }; +} + diff --git a/public/app/panels/table/module.ts b/public/app/panels/table/module.ts index 39cd1056290..84955070be4 100644 --- a/public/app/panels/table/module.ts +++ b/public/app/panels/table/module.ts @@ -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); diff --git a/public/app/panels/table/options.html b/public/app/panels/table/options.html index c1d4ca658ac..d43ff958c5d 100644 --- a/public/app/panels/table/options.html +++ b/public/app/panels/table/options.html @@ -1,117 +1,2 @@ -
-
-
Data
-
-
-
    -
  • - To Table Transform -
  • -
  • - -
  • -
-
-
-
-
- -
-
Table Display
-
-
-
    -
  • - Pagination (Page size) -
  • -
  • - -
  • -
-
-
-
-
-
- -
-
Column Styles
- -
-
-
-
    -
  • - -
  • -
  • - Name or regex -
  • -
  • - -
  • -
  • - Unit -
  • - -
  • - Decimals -
  • -
  • - -
  • -
-
-
-
-
    -
  • - -
  • -
  • - Coloring -
  • -
  • - -
  • -
  • - ThresholdsComma seperated values -
  • -
  • - -
  • -
  • - Colors -
  • -
  • - - - -
  • -
  • - invert order -
  • -
-
-
-
-
- - -
- + + diff --git a/public/app/panels/table/specs/transformers_specs.ts b/public/app/panels/table/specs/transformers_specs.ts index ea3eb609976..af9dd880cf1 100644 --- a/public/app/panels/table/specs/transformers_specs.ts +++ b/public/app/panels/table/specs/transformers_specs.ts @@ -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'); + }); + + }); }); }); diff --git a/public/app/panels/table/transformers.ts b/public/app/panels/table/transformers.ts index 42ccae1982e..096ebb73051 100644 --- a/public/app/panels/table/transformers.ts +++ b/public/app/panels/table/transformers.ts @@ -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) { - model.columns.push({text: 'JSON'}); - debugger; + var i, y, z; + for (i = 0; i < panel.fields.length; i++) { + model.columns.push({text: panel.fields[i].name}); + } - for (var i = 0; i < data.length; i++) { + if (model.columns.length === 0) { + model.columns.push({text: 'JSON'}); + } + + 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); } } }