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;