mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Table: Added text align option to column styles (#21175)
* Added text align option to column styles, tests * table panel migrations * added tests * default column style is now auto * tests and fixtures... * wrong comments need removing * xss guard * test * Some test for invalid options, formatting. * Remote branch tracking.
This commit is contained in:
parent
7784b519a0
commit
0bf9e9bc28
@ -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 [],
|
||||
|
@ -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 [],
|
||||
|
@ -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', () => {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -36,6 +36,20 @@
|
||||
<gf-form-switch class="gf-form" label-class="width-10" ng-if="style.type === 'string'" label="Sanitize HTML" checked="style.sanitize"
|
||||
on-change="editor.render()"></gf-form-switch>
|
||||
</div>
|
||||
|
||||
<div ng-if="style.type !== 'hidden'">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-10">Align</label>
|
||||
<gf-form-dropdown model="style.align"
|
||||
css-class="gf-form-input width-16"
|
||||
lookup-text="true"
|
||||
get-options="editor.alignTypes"
|
||||
allow-custom="false"
|
||||
on-change="editor.render()"
|
||||
allow-custom="false"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="style.type === 'string'">
|
||||
<gf-form-switch class="gf-form" label-class="width-10" ng-if="style.type === 'string'" label="Preserve Formatting" checked="style.preserveFormat"
|
||||
on-change="editor.render()"></gf-form-switch>
|
||||
|
@ -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;
|
||||
|
@ -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: [],
|
||||
|
@ -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 += `
|
||||
<a href="${cellLink}" target="${cellTarget}" data-link-tooltip data-original-title="${cellLinkTooltip}" data-placement="right"${textStyle}>
|
||||
<a href="${cellLink}" target="${cellTarget}" data-link-tooltip data-original-title="${cellLinkTooltip}" data-placement="right"${cellStyle}>
|
||||
${value}
|
||||
</a>
|
||||
`;
|
||||
@ -316,7 +328,7 @@ export class TableRenderer {
|
||||
cellClass = ' class="' + cellClasses.join(' ') + '"';
|
||||
}
|
||||
|
||||
columnHtml = '<td' + cellClass + cellStyle + textStyle + '>' + columnHtml + '</td>';
|
||||
columnHtml = '<td' + cellClass + cellStyle + '>' + columnHtml + '</td>';
|
||||
return columnHtml;
|
||||
}
|
||||
|
||||
|
@ -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('<td>2014-01-01T06:06:06Z</td>');
|
||||
});
|
||||
@ -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('<td style="text-align:right">42</td>');
|
||||
});
|
||||
|
||||
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, '<td>42</td>'],
|
||||
['invalid_option', false, null, '<td>42</td>'],
|
||||
['alert("no xss");', false, null, '<td>42</td>'],
|
||||
['auto', false, null, '<td>42</td>'],
|
||||
['justify', false, null, '<td>42</td>'],
|
||||
['auto', true, null, '<td class="table-panel-cell-pre">42</td>'],
|
||||
['left', false, null, '<td style="text-align:left">42</td>'],
|
||||
['left', true, null, '<td class="table-panel-cell-pre" style="text-align:left">42</td>'],
|
||||
['center', false, null, '<td style="text-align:center">42</td>'],
|
||||
[
|
||||
'center',
|
||||
true,
|
||||
'cell',
|
||||
'<td class="table-panel-color-cell table-panel-cell-pre" style="background-color:rgba(50, 172, 45, 0.97);text-align:center">42</td>',
|
||||
],
|
||||
[
|
||||
'right',
|
||||
false,
|
||||
'cell',
|
||||
'<td class="table-panel-color-cell" style="background-color:rgba(50, 172, 45, 0.97);text-align:right">42</td>',
|
||||
],
|
||||
[
|
||||
'right',
|
||||
true,
|
||||
'cell',
|
||||
'<td class="table-panel-color-cell table-panel-cell-pre" style="background-color:rgba(50, 172, 45, 0.97);text-align:right">42</td>',
|
||||
],
|
||||
];
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ export interface ColumnStyle {
|
||||
mappingType?: any;
|
||||
valueMaps?: any;
|
||||
rangeMaps?: any;
|
||||
|
||||
align?: 'auto' | 'left' | 'center' | 'right';
|
||||
link?: any;
|
||||
linkUrl?: any;
|
||||
linkTooltip?: any;
|
||||
|
Loading…
Reference in New Issue
Block a user