Commit 79120a0b by Torkel Ödegaard

Merge branch 'master' of github.com:grafana/grafana

parents 8d072de5 fdc97010
......@@ -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.
......@@ -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; });
......
......@@ -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
......
......@@ -119,7 +119,7 @@ function (queryDef) {
}
query.script_fields = {},
query.fielddata_fields = [this.timeField];
query.docvalue_fields = [this.timeField];
return query;
};
......
......@@ -11,7 +11,7 @@
<label class="gf-form-label query-keyword width-7">FROM</label>
<metric-segment segment="ctrl.policySegment" get-options="ctrl.getPolicySegments()" on-change="ctrl.policyChanged()"></metric-segment>
<metric-segment segment="ctrl.measurementSegment" get-options="ctrl.getMeasurements(measurementFilter)" on-change="ctrl.measurementChanged()"></metric-segment>
<metric-segment segment="ctrl.measurementSegment" get-options="ctrl.getMeasurements($query)" on-change="ctrl.measurementChanged()"></metric-segment>
</div>
<div class="gf-form">
......
......@@ -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);
}
......@@ -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) {
......
......@@ -7,7 +7,7 @@
<tr>
<th ng-repeat="col in ctrl.table.columns" ng-hide="col.hidden">
<div class="table-panel-table-header-inner pointer" ng-click="ctrl.toggleColumnSort(col, $index)">
{{col.text}}
{{col.title}}
<span class="table-panel-table-header-controls" ng-if="col.sort">
<i class="fa fa-caret-down" ng-show="col.desc"></i>
<i class="fa fa-caret-up" ng-hide="col.desc"></i>
......
......@@ -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) {
......
......@@ -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 = '<div class="table-panel-width-hack">' + this.table.columns[columnIndex].text + '</div>';
widthHack = '<div class="table-panel-width-hack">' + this.table.columns[columnIndex].title + '</div>';
}
if (value === undefined) {
......
......@@ -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 <a href="http://google.com">link</a>');
expect(html).to.be('<td>sanitized</td>');
});
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');
});
});
});
......
......@@ -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;
}
......
......@@ -68,3 +68,12 @@
top: 1px;
}
}
.form-tabs-wrapper {
@include brand-bottom-border();
@include clearfix();
}
.form-tabs-content {
padding: $spacer*2 $spacer;
}
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