diff --git a/docs/sources/features/panels/table_panel.md b/docs/sources/features/panels/table_panel.md index 9ddc2cddaee..69cd02fdbcc 100644 --- a/docs/sources/features/panels/table_panel.md +++ b/docs/sources/features/panels/table_panel.md @@ -85,8 +85,9 @@ The column styles allow you control how dates and numbers are formatted. 1. `Name or regex`: The Name or Regex field controls what columns the rule should be applied to. The regex or name filter will be matched against the column name not against column values. 2. `Type`: The three supported types of types are `Number`, `String` and `Date`. -3. `Format`: Specify date format. Only available when `Type` is set to `Date`. -4. `Coloring` and `Thresholds`: Specify color mode and thresholds limits. -5. `Unit` and `Decimals`: Specify unit and decimal precision for numbers. -6. `Add column style rule`: Add new column rule. +3. `Title`: Title for the column, when using a Regex the title can include replacement strings like `$1`. +4. `Format`: Specify date format. Only available when `Type` is set to `Date`. +5. `Coloring` and `Thresholds`: Specify color mode and thresholds limits. +6. `Unit` and `Decimals`: Specify unit and decimal precision for numbers. +7. `Add column style rule`: Add new column rule. diff --git a/public/app/core/directives/metric_segment.js b/public/app/core/directives/metric_segment.js index c3e51dc7a0c..2e9442c15a0 100644 --- a/public/app/core/directives/metric_segment.js +++ b/public/app/core/directives/metric_segment.js @@ -77,7 +77,7 @@ function (_, $, coreModule) { $scope.source = function(query, callback) { $scope.$apply(function() { - $scope.getOptions({ measurementFilter: query }).then(function(altSegments) { + $scope.getOptions({ $query: query }).then(function(altSegments) { $scope.altSegments = altSegments; options = _.map($scope.altSegments, function(alt) { return alt.value; }); diff --git a/public/app/core/utils/file_export.ts b/public/app/core/utils/file_export.ts index f2f0192e034..24b501eee28 100644 --- a/public/app/core/utils/file_export.ts +++ b/public/app/core/utils/file_export.ts @@ -53,7 +53,7 @@ export function exportTableDataToCsv(table) { var text = 'sep=;\n'; // add header _.each(table.columns, function(column) { - text += column.text + ';'; + text += (column.title || column.text) + ';'; }); text += '\n'; // process data diff --git a/public/app/plugins/datasource/elasticsearch/query_builder.js b/public/app/plugins/datasource/elasticsearch/query_builder.js index fbbd4f7e929..dd296c00a34 100644 --- a/public/app/plugins/datasource/elasticsearch/query_builder.js +++ b/public/app/plugins/datasource/elasticsearch/query_builder.js @@ -119,7 +119,7 @@ function (queryDef) { } query.script_fields = {}, - query.fielddata_fields = [this.timeField]; + query.docvalue_fields = [this.timeField]; return query; }; diff --git a/public/app/plugins/datasource/influxdb/partials/query.editor.html b/public/app/plugins/datasource/influxdb/partials/query.editor.html index bff03fbb7d5..31bc7e6d8ad 100644 --- a/public/app/plugins/datasource/influxdb/partials/query.editor.html +++ b/public/app/plugins/datasource/influxdb/partials/query.editor.html @@ -11,7 +11,7 @@ - +
diff --git a/public/app/plugins/panel/heatmap/specs/renderer_specs.ts b/public/app/plugins/panel/heatmap/specs/renderer_specs.ts index 3467c5b7542..3bb3c04d122 100644 --- a/public/app/plugins/panel/heatmap/specs/renderer_specs.ts +++ b/public/app/plugins/panel/heatmap/specs/renderer_specs.ts @@ -81,8 +81,8 @@ describe('grafanaHeatmap', function () { getTimezone: sinon.stub().returns('utc') }, range: { - from: moment.utc("01 Mar 2017 10:00:00"), - to: moment.utc("01 Mar 2017 11:00:00"), + from: moment.utc("01 Mar 2017 10:00:00", 'DD MMM YYYY HH:mm:ss'), + to: moment.utc("01 Mar 2017 11:00:00", 'DD MMM YYYY HH:mm:ss'), }, }; @@ -263,5 +263,5 @@ function getTicks(element, axisSelector) { function formatLocalTime(timeStr) { let format = "HH:mm"; - return moment.utc(timeStr).local().format(format); + return moment.utc(timeStr, 'DD MMM YYYY HH:mm:ss').local().format(format); } diff --git a/public/app/plugins/panel/table/editor.html b/public/app/plugins/panel/table/editor.html index 448669ca6a9..7eae11d7b0a 100644 --- a/public/app/plugins/panel/table/editor.html +++ b/public/app/plugins/panel/table/editor.html @@ -29,112 +29,122 @@
Table Display
-
-
- - -
- -
- -
- -
+
+ + +
+ +
+ +
+ +
+
+
+
+ +
+ Column Style Rules + +
+ + + +
+ +
+
Options
+
+
+ + +
+
+
+ + +
+
+ +
+
Type
+ +
+ +
+ +
+
+
+ + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+
+
+ +
+
Thresholds
+
+ + +
+
+ +
+ +
+
+
+ + + + + + + + + + +
+ Invert
-
- -
-
-
Column Styles
-
-
-
- - -
-
- -
- -
-
-
- - -
- -
-
-
-
- -
-
- -
-
- -
-
-
-
-
- - -
-
- -
- -
-
-
-
-
-
- -
-
- - -
-
- - - - - - - - - - -
-
-
- Invert -
-
-
- -
-
-
- -
+ +
+ + +
+
diff --git a/public/app/plugins/panel/table/editor.ts b/public/app/plugins/panel/table/editor.ts index 6b803cc0444..9379702e7db 100644 --- a/public/app/plugins/panel/table/editor.ts +++ b/public/app/plugins/panel/table/editor.ts @@ -21,10 +21,12 @@ export class TablePanelEditorCtrl { addColumnSegment: any; unitFormats: any; getColumnNames: any; + activeStyleIndex: number; /** @ngInject */ constructor($scope, private $q, private uiSegmentSrv) { $scope.editor = this; + this.activeStyleIndex = 0; this.panelCtrl = $scope.ctrl; this.panel = this.panelCtrl.panel; this.transformers = transformers; @@ -104,18 +106,32 @@ export class TablePanelEditorCtrl { } addColumnStyle() { - var columnStyleDefaults = { + var newStyleRule = { unit: 'short', type: 'number', + alias: '', decimals: 2, colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], colorMode: null, - pattern: '/.*/', + pattern: '', dateFormat: 'YYYY-MM-DD HH:mm:ss', thresholds: [], }; - this.panel.styles.push(angular.copy(columnStyleDefaults)); + var styles = this.panel.styles; + var stylesCount = styles.length; + var indexToInsert = stylesCount; + + // check if last is a catch all rule, then add it before that one + if (stylesCount > 0) { + var last = styles[stylesCount-1]; + if (last.pattern === '/.*/') { + indexToInsert = stylesCount-1; + } + } + + styles.splice(indexToInsert, 0, newStyleRule); + this.activeStyleIndex = indexToInsert; } removeColumnStyle(style) { diff --git a/public/app/plugins/panel/table/module.html b/public/app/plugins/panel/table/module.html index e405c6c42e3..5c6fcbfdb1e 100644 --- a/public/app/plugins/panel/table/module.html +++ b/public/app/plugins/panel/table/module.html @@ -7,7 +7,7 @@
- {{col.text}} + {{col.title}} diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index bf872b47d27..9b563ca5b18 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -16,6 +16,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { pageIndex: number; dataRaw: any; table: any; + renderer: any; panelDefaults = { targets: [{}], @@ -26,11 +27,13 @@ class TablePanelCtrl extends MetricsPanelCtrl { { type: 'date', pattern: 'Time', + alias: 'Time', dateFormat: 'YYYY-MM-DD HH:mm:ss', }, { unit: 'short', type: 'number', + alias: '', decimals: 2, colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], colorMode: null, @@ -118,6 +121,9 @@ class TablePanelCtrl extends MetricsPanelCtrl { render() { this.table = transformDataToTable(this.dataRaw, this.panel); this.table.sort(this.panel.sort); + + this.renderer = new TableRenderer(this.panel, this.table, this.dashboard.isTimezoneUtc(), this.$sanitize); + return super.render(this.table); } @@ -141,8 +147,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { } exportCsv() { - var renderer = new TableRenderer(this.panel, this.table, this.dashboard.isTimezoneUtc(), this.$sanitize); - FileExport.exportTableDataToCsv(renderer.render_values()); + FileExport.exportTableDataToCsv(this.renderer.render_values()); } link(scope, elem, attrs, ctrl) { @@ -162,9 +167,9 @@ class TablePanelCtrl extends MetricsPanelCtrl { } function appendTableRows(tbodyElem) { - var renderer = new TableRenderer(panel, data, ctrl.dashboard.isTimezoneUtc(), ctrl.$sanitize); + ctrl.renderer.setTable(data); tbodyElem.empty(); - tbodyElem.html(renderer.render(ctrl.pageIndex)); + tbodyElem.html(ctrl.renderer.render(ctrl.pageIndex)); } function switchPage(e) { diff --git a/public/app/plugins/panel/table/renderer.ts b/public/app/plugins/panel/table/renderer.ts index 2f5a51ab111..ae56e8dff12 100644 --- a/public/app/plugins/panel/table/renderer.ts +++ b/public/app/plugins/panel/table/renderer.ts @@ -5,12 +5,44 @@ import moment from 'moment'; import kbn from 'app/core/utils/kbn'; export class TableRenderer { - formaters: any[]; + formatters: any[]; colorState: any; constructor(private panel, private table, private isUtc, private sanitize) { - this.formaters = []; + this.initColumns(); + } + + setTable(table) { + this.table = table; + + this.initColumns(); + } + + initColumns() { + this.formatters = []; this.colorState = {}; + + for (let colIndex = 0; colIndex < this.table.columns.length; colIndex++) { + let column = this.table.columns[colIndex]; + column.title = column.text; + + for (let i = 0; i < this.panel.styles.length; i++) { + let style = this.panel.styles[i]; + + var regex = kbn.stringToJsRegex(style.pattern); + if (column.text.match(regex)) { + column.style = style; + + if (style.alias) { + column.title = column.text.replace(regex, style.alias); + } + + break; + } + } + + this.formatters[colIndex] = this.createColumnFormatter(column); + } } getColorForValue(value, style) { @@ -24,7 +56,7 @@ export class TableRenderer { return _.first(style.colors); } - defaultCellFormater(v, style) { + defaultCellFormatter(v, style) { if (v === null || v === void 0 || v === undefined) { return ''; } @@ -40,18 +72,18 @@ export class TableRenderer { } } - createColumnFormater(style, column) { - if (!style) { - return this.defaultCellFormater; + createColumnFormatter(column) { + if (!column.style) { + return this.defaultCellFormatter; } - if (style.type === 'hidden') { + if (column.style.type === 'hidden') { return v => { return undefined; }; } - if (style.type === 'date') { + if (column.style.type === 'date') { return v => { if (v === undefined || v === null) { return '-'; @@ -62,12 +94,12 @@ export class TableRenderer { if (this.isUtc) { date = date.utc(); } - return date.format(style.dateFormat); + return date.format(column.style.dateFormat); }; } - if (style.type === 'number') { - let valueFormater = kbn.valueFormats[column.unit || style.unit]; + if (column.style.type === 'number') { + let valueFormatter = kbn.valueFormats[column.unit || column.style.unit]; return v => { if (v === null || v === void 0) { @@ -75,39 +107,24 @@ export class TableRenderer { } if (_.isString(v)) { - return this.defaultCellFormater(v, style); + return this.defaultCellFormatter(v, column.style); } - if (style.colorMode) { - this.colorState[style.colorMode] = this.getColorForValue(v, style); + if (column.style.colorMode) { + this.colorState[column.style.colorMode] = this.getColorForValue(v, column.style); } - return valueFormater(v, style.decimals, null); + return valueFormatter(v, column.style.decimals, null); }; } return (value) => { - return this.defaultCellFormater(value, style); + return this.defaultCellFormatter(value, column.style); }; } formatColumnValue(colIndex, value) { - if (this.formaters[colIndex]) { - return this.formaters[colIndex](value); - } - - for (let i = 0; i < this.panel.styles.length; i++) { - let style = this.panel.styles[i]; - let column = this.table.columns[colIndex]; - var regex = kbn.stringToJsRegex(style.pattern); - if (column.text.match(regex)) { - this.formaters[colIndex] = this.createColumnFormater(style, column); - return this.formaters[colIndex](value); - } - } - - this.formaters[colIndex] = this.defaultCellFormater; - return this.formaters[colIndex](value); + return this.formatters[colIndex] ? this.formatters[colIndex](value) : value; } renderCell(columnIndex, value, addWidthHack = false) { @@ -126,7 +143,7 @@ export class TableRenderer { // this hack adds header content to cell (not visible) var widthHack = ''; if (addWidthHack) { - widthHack = '
' + this.table.columns[columnIndex].text + '
'; + widthHack = '
' + this.table.columns[columnIndex].title + '
'; } if (value === undefined) { diff --git a/public/app/plugins/panel/table/specs/renderer_specs.ts b/public/app/plugins/panel/table/specs/renderer_specs.ts index 46789f295d1..6b031d4bb91 100644 --- a/public/app/plugins/panel/table/specs/renderer_specs.ts +++ b/public/app/plugins/panel/table/specs/renderer_specs.ts @@ -22,13 +22,15 @@ describe('when rendering table', () => { { pattern: 'Time', type: 'date', - format: 'LLL' + format: 'LLL', + alias: 'Timestamp' }, { - pattern: 'Value', + pattern: '/(Val)ue/', type: 'number', unit: 'ms', decimals: 3, + alias: '$1' }, { pattern: 'Colored', @@ -132,6 +134,18 @@ describe('when rendering table', () => { var html = renderer.renderCell(6, 'text link'); expect(html).to.be('sanitized'); }); + + it('Time column title should be Timestamp', () => { + expect(table.columns[0].title).to.be('Timestamp'); + }); + + it('Value column title should be Val', () => { + expect(table.columns[1].title).to.be('Val'); + }); + + it('Colored column title should be Colored', () => { + expect(table.columns[2].title).to.be('Colored'); + }); }); }); diff --git a/public/app/plugins/panel/table/transformers.ts b/public/app/plugins/panel/table/transformers.ts index 15cc9e71134..0f793fa0434 100644 --- a/public/app/plugins/panel/table/transformers.ts +++ b/public/app/plugins/panel/table/transformers.ts @@ -229,7 +229,7 @@ function transformDataToTable(data, panel) { var transformer = transformers[panel.transform]; if (!transformer) { - throw {message: 'Transformer ' + panel.transformer + ' not found'}; + throw {message: 'Transformer ' + panel.transform + ' not found'}; } if (panel.filterNull) { @@ -239,6 +239,7 @@ function transformDataToTable(data, panel) { } transformer.transform(copyData, panel, model); + return model; } diff --git a/public/sass/components/_tabs.scss b/public/sass/components/_tabs.scss index 2426f70afee..32e395c4d73 100644 --- a/public/sass/components/_tabs.scss +++ b/public/sass/components/_tabs.scss @@ -68,3 +68,12 @@ top: 1px; } } + +.form-tabs-wrapper { + @include brand-bottom-border(); + @include clearfix(); +} + +.form-tabs-content { + padding: $spacer*2 $spacer; +}