diff --git a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap
index c419c05b2ea..8fe42b3af72 100644
--- a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap
+++ b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap
@@ -78,7 +78,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
],
"refresh": undefined,
"revision": undefined,
- "schemaVersion": 21,
+ "schemaVersion": 22,
"snapshot": undefined,
"style": "dark",
"tags": Array [],
@@ -191,7 +191,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
],
"refresh": undefined,
"revision": undefined,
- "schemaVersion": 21,
+ "schemaVersion": 22,
"snapshot": undefined,
"style": "dark",
"tags": Array [],
@@ -283,7 +283,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
],
"refresh": undefined,
"revision": undefined,
- "schemaVersion": 21,
+ "schemaVersion": 22,
"snapshot": undefined,
"style": "dark",
"tags": Array [],
@@ -407,7 +407,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
],
"refresh": undefined,
"revision": undefined,
- "schemaVersion": 21,
+ "schemaVersion": 22,
"snapshot": undefined,
"style": "dark",
"tags": Array [],
@@ -518,7 +518,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
],
"refresh": undefined,
"revision": undefined,
- "schemaVersion": 21,
+ "schemaVersion": 22,
"snapshot": undefined,
"style": "dark",
"tags": Array [],
@@ -613,7 +613,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
],
"refresh": undefined,
"revision": undefined,
- "schemaVersion": 21,
+ "schemaVersion": 22,
"snapshot": undefined,
"style": "dark",
"tags": Array [],
@@ -705,7 +705,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
],
"refresh": undefined,
"revision": undefined,
- "schemaVersion": 21,
+ "schemaVersion": 22,
"snapshot": undefined,
"style": "dark",
"tags": Array [],
diff --git a/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap b/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap
index 1bbc17c6073..774608195c4 100644
--- a/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap
+++ b/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap
@@ -232,7 +232,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
],
"refresh": undefined,
"revision": undefined,
- "schemaVersion": 21,
+ "schemaVersion": 22,
"snapshot": undefined,
"style": "dark",
"tags": Array [],
@@ -469,7 +469,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
],
"refresh": undefined,
"revision": undefined,
- "schemaVersion": 21,
+ "schemaVersion": 22,
"snapshot": undefined,
"style": "dark",
"tags": Array [],
@@ -706,7 +706,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
],
"refresh": undefined,
"revision": undefined,
- "schemaVersion": 21,
+ "schemaVersion": 22,
"snapshot": undefined,
"style": "dark",
"tags": Array [],
@@ -943,7 +943,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
],
"refresh": undefined,
"revision": undefined,
- "schemaVersion": 21,
+ "schemaVersion": 22,
"snapshot": undefined,
"style": "dark",
"tags": Array [],
diff --git a/public/app/features/dashboard/state/DashboardMigrator.test.ts b/public/app/features/dashboard/state/DashboardMigrator.test.ts
index 73ac0b909ed..1e26c477ec1 100644
--- a/public/app/features/dashboard/state/DashboardMigrator.test.ts
+++ b/public/app/features/dashboard/state/DashboardMigrator.test.ts
@@ -128,7 +128,7 @@ describe('DashboardModel', () => {
});
it('dashboard schema version should be set to latest', () => {
- expect(model.schemaVersion).toBe(21);
+ expect(model.schemaVersion).toBe(22);
});
it('graph thresholds should be migrated', () => {
diff --git a/public/app/features/dashboard/state/DashboardMigrator.ts b/public/app/features/dashboard/state/DashboardMigrator.ts
index ca8100902d2..358f9086635 100644
--- a/public/app/features/dashboard/state/DashboardMigrator.ts
+++ b/public/app/features/dashboard/state/DashboardMigrator.ts
@@ -33,7 +33,7 @@ export class DashboardMigrator {
let i, j, k, n;
const oldVersion = this.dashboard.schemaVersion;
const panelUpgrades = [];
- this.dashboard.schemaVersion = 21;
+ this.dashboard.schemaVersion = 22;
if (oldVersion === this.dashboard.schemaVersion) {
return;
@@ -487,6 +487,18 @@ export class DashboardMigrator {
});
}
+ if (oldVersion < 22) {
+ panelUpgrades.push((panel: any) => {
+ if (panel.type !== 'table') {
+ return;
+ }
+
+ _.each(panel.styles, style => {
+ style.align = 'auto';
+ });
+ });
+ }
+
if (panelUpgrades.length === 0) {
return;
}
diff --git a/public/app/plugins/panel/table/column_options.html b/public/app/plugins/panel/table/column_options.html
index 56fab80599d..709066bdd4b 100644
--- a/public/app/plugins/panel/table/column_options.html
+++ b/public/app/plugins/panel/table/column_options.html
@@ -36,6 +36,20 @@
+
+
diff --git a/public/app/plugins/panel/table/column_options.ts b/public/app/plugins/panel/table/column_options.ts
index 1e496da0539..0274a575211 100644
--- a/public/app/plugins/panel/table/column_options.ts
+++ b/public/app/plugins/panel/table/column_options.ts
@@ -15,6 +15,14 @@ export class ColumnOptionsCtrl {
activeStyleIndex: number;
mappingTypes: any;
+ alignTypes: any;
+ static readonly alignTypesEnum = [
+ { text: 'auto', value: '' },
+ { text: 'left', value: 'left' },
+ { text: 'center', value: 'center' },
+ { text: 'right', value: 'right' },
+ ];
+
/** @ngInject */
constructor($scope: any) {
$scope.editor = this;
@@ -47,6 +55,7 @@ export class ColumnOptionsCtrl {
{ text: 'Value to text', value: 1 },
{ text: 'Range to text', value: 2 },
];
+ this.alignTypes = ColumnOptionsCtrl.alignTypesEnum;
this.getColumnNames = () => {
if (!this.panelCtrl.table) {
@@ -81,6 +90,7 @@ export class ColumnOptionsCtrl {
dateFormat: 'YYYY-MM-DD HH:mm:ss',
thresholds: [],
mappingType: 1,
+ align: 'auto',
};
const styles = this.panel.styles;
diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts
index f7f887b77a6..fa5fdd3c034 100644
--- a/public/app/plugins/panel/table/module.ts
+++ b/public/app/plugins/panel/table/module.ts
@@ -30,6 +30,7 @@ class TablePanelCtrl extends MetricsPanelCtrl {
pattern: 'Time',
alias: 'Time',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
+ align: 'auto',
},
{
unit: 'short',
@@ -40,6 +41,7 @@ class TablePanelCtrl extends MetricsPanelCtrl {
colorMode: null,
pattern: '/.*/',
thresholds: [],
+ align: 'right',
},
],
columns: [],
diff --git a/public/app/plugins/panel/table/renderer.ts b/public/app/plugins/panel/table/renderer.ts
index ae631d35fc4..8b9623ba4a9 100644
--- a/public/app/plugins/panel/table/renderer.ts
+++ b/public/app/plugins/panel/table/renderer.ts
@@ -13,6 +13,7 @@ import {
} from '@grafana/data';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { ColumnRender, TableRenderModel, ColumnStyle } from './types';
+import { ColumnOptionsCtrl } from './column_options';
export class TableRenderer {
formatters: any[];
@@ -243,17 +244,17 @@ export class TableRenderer {
value = this.formatColumnValue(columnIndex, value);
const column = this.table.columns[columnIndex];
+ const cellStyles = [];
let cellStyle = '';
- let textStyle = '';
const cellClasses = [];
let cellClass = '';
if (this.colorState.cell) {
- cellStyle = ' style="background-color:' + this.colorState.cell + '"';
+ cellStyles.push('background-color:' + this.colorState.cell);
cellClasses.push('table-panel-color-cell');
this.colorState.cell = null;
} else if (this.colorState.value) {
- textStyle = ' style="color:' + this.colorState.value + '"';
+ cellStyles.push('color:' + this.colorState.value);
this.colorState.value = null;
}
// because of the fixed table headers css only solution
@@ -265,7 +266,7 @@ export class TableRenderer {
}
if (value === undefined) {
- cellStyle = ' style="display:none;"';
+ cellStyles.push('display:none');
column.hidden = true;
} else {
column.hidden = false;
@@ -279,6 +280,17 @@ export class TableRenderer {
cellClasses.push('table-panel-cell-pre');
}
+ if (column.style && column.style.align) {
+ const textAlign = _.find(ColumnOptionsCtrl.alignTypesEnum, ['text', column.style.align]);
+ if (textAlign && textAlign['value']) {
+ cellStyles.push(`text-align:${textAlign['value']}`);
+ }
+ }
+
+ if (cellStyles.length) {
+ cellStyle = ' style="' + cellStyles.join(';') + '"';
+ }
+
if (column.style && column.style.link) {
// Render cell as link
const scopedVars = this.renderRowVariables(rowIndex);
@@ -291,7 +303,7 @@ export class TableRenderer {
cellClasses.push('table-panel-cell-link');
columnHtml += `
-
+
${value}
`;
@@ -316,7 +328,7 @@ export class TableRenderer {
cellClass = ' class="' + cellClasses.join(' ') + '"';
}
- columnHtml = '
' + columnHtml + ' | ';
+ columnHtml = '
' + columnHtml + ' | ';
return columnHtml;
}
diff --git a/public/app/plugins/panel/table/specs/renderer.test.ts b/public/app/plugins/panel/table/specs/renderer.test.ts
index 09cc7e34bbd..a2e4b1d8bf4 100644
--- a/public/app/plugins/panel/table/specs/renderer.test.ts
+++ b/public/app/plugins/panel/table/specs/renderer.test.ts
@@ -40,9 +40,26 @@ describe('when rendering table', () => {
{ text: 'MappingColored' },
{ text: 'RangeMappingColored' },
{ text: 'HiddenType' },
+ { text: 'RightAligned' },
];
table.rows = [
- [1388556366666, 1230, 40, undefined, '', '', 'my.host.com', 'host1', ['value1', 'value2'], 1, 2, 1, 2, 'ignored'],
+ [
+ 1388556366666,
+ 1230,
+ 40,
+ undefined,
+ '',
+ '',
+ 'my.host.com',
+ 'host1',
+ ['value1', 'value2'],
+ 1,
+ 2,
+ 1,
+ 2,
+ 'ignored',
+ 42,
+ ],
];
const panel = {
@@ -185,13 +202,17 @@ describe('when rendering table', () => {
pattern: 'HiddenType',
type: 'hidden',
},
+ {
+ pattern: 'RightAligned',
+ align: 'right',
+ },
],
};
//@ts-ignore
const renderer = new TableRenderer(panel, table, 'utc', sanitize, templateSrv);
- it('time column should be formated', () => {
+ it('time column should be formatted', () => {
const html = renderer.renderCell(0, 0, 1388556366666);
expect(html).toBe('
2014-01-01T06:06:06Z | ');
});
@@ -396,6 +417,11 @@ describe('when rendering table', () => {
expect(html).toBe('');
});
+ it('right aligned column should have correct text-align style', () => {
+ const html = renderer.renderCell(14, 0, 42);
+ expect(html).toBe('
42 | ');
+ });
+
it('render_values should ignore hidden columns', () => {
renderer.render(0); // this computes the hidden markers on the columns
const { columns, rows } = renderer.render_values();
@@ -451,6 +477,76 @@ describe('when rendering table with different patterns', () => {
);
});
+describe('when rendering cells with different alignment options', () => {
+ const cases = [
+ //align, preserve fmt, color mode, expected
+ ['', false, null, '
42 | '],
+ ['invalid_option', false, null, '
42 | '],
+ ['alert("no xss");', false, null, '
42 | '],
+ ['auto', false, null, '
42 | '],
+ ['justify', false, null, '
42 | '],
+ ['auto', true, null, '
42 | '],
+ ['left', false, null, '
42 | '],
+ ['left', true, null, '
42 | '],
+ ['center', false, null, '
42 | '],
+ [
+ 'center',
+ true,
+ 'cell',
+ '
42 | ',
+ ],
+ [
+ 'right',
+ false,
+ 'cell',
+ '
42 | ',
+ ],
+ [
+ 'right',
+ true,
+ 'cell',
+ '
42 | ',
+ ],
+ ];
+
+ it.each(cases)(
+ 'align option:"%s", preformatted:%s columns should be formatted with correct style',
+ (align: string, preserveFormat: boolean, colorMode, expected: string) => {
+ const table = new TableModel();
+ table.columns = [{ text: 'Time' }, { text: align }];
+ table.rows = [[0, 42]];
+
+ const panel = {
+ pageSize: 10,
+ styles: [
+ {
+ pattern: 'Time',
+ type: 'date',
+ format: 'LLL',
+ alias: 'Timestamp',
+ },
+ {
+ pattern: `/${align}/`,
+ align: align,
+ type: 'number',
+ unit: 'none',
+ preserveFormat: preserveFormat,
+ colorMode: colorMode,
+ thresholds: [1, 2],
+ colors: ['rgba(245, 54, 54, 0.9)', 'rgba(237, 129, 40, 0.89)', 'rgba(50, 172, 45, 0.97)'],
+ },
+ ],
+ };
+
+ //@ts-ignore
+ const renderer = new TableRenderer(panel, table, 'utc', sanitize, templateSrv);
+ const html = renderer.renderCell(1, 0, 42);
+
+ expect(html).toBe(expected);
+ }
+ );
+});
+
function normalize(str: string) {
return str.replace(/\s+/gm, ' ').trim();
}
diff --git a/public/app/plugins/panel/table/types.ts b/public/app/plugins/panel/table/types.ts
index 01ba33e0692..21c49166de0 100644
--- a/public/app/plugins/panel/table/types.ts
+++ b/public/app/plugins/panel/table/types.ts
@@ -33,7 +33,7 @@ export interface ColumnStyle {
mappingType?: any;
valueMaps?: any;
rangeMaps?: any;
-
+ align?: 'auto' | 'left' | 'center' | 'right';
link?: any;
linkUrl?: any;
linkTooltip?: any;