table panel: additional fixes for value to text mapping

Make the value to text mapping more similar to singlestat solution.
Adds more tests.
Also, allows string to be mapped to other string besides numeric values.
This commit is contained in:
Marcus Efraimsson 2018-03-27 19:04:45 +02:00
parent 67f0382222
commit f673ec16d1
4 changed files with 211 additions and 88 deletions

View File

@ -70,7 +70,7 @@
</div>
<div class="section gf-form-group" ng-if="style.type === 'string'">
<h5 class="section-heading">Mapping</h5>
<h5 class="section-heading">Value Mappings</h5>
<div class="editor-row">
<div class="gf-form-group">
<div class="gf-form">
@ -83,38 +83,37 @@
</div>
</div>
<div class="gf-form-group" ng-if="style.mappingType==1">
<div class="gf-form" ng-repeat="mapping in style.valueMappings">
<input type="text" class="gf-form-input width-8" ng-model="mapping.value" placeholder="Value" ng-blur="editor.render()" array-join>
<label class="gf-form-label width-3">=></label>
<input type="text" class="gf-form-input width-9" ng-model="mapping.text" placeholder="Text" ng-blur="editor.render()" array-join>
<div class="gf-form" ng-repeat="map in style.valueMaps">
<span class="gf-form-label">
<i class="fa fa-remove pointer" ng-click="editor.removeValueMap(style, $index)"></i>
</span>
<input type="text" class="gf-form-input max-width-6" ng-model="map.value" placeholder="Value" ng-blur="editor.render()">
<label class="gf-form-label">
<a class="pointer" tabindex="1" ng-click="editor.removeValueMapping(style, $index)">
<i class="fa fa-trash"></i>
</a>
<i class="fa fa-arrow-right"></i>
</label>
<input type="text" class="gf-form-input max-width-8" ng-model="map.text" placeholder="Text" ng-blur="editor.render()">
</div>
<div class="gf-form">
<label class="gf-form-label">
<a class="pointer" ng-click="editor.addValueMapping(style)"><i class="fa fa-plus"></i></a>
<a class="pointer" ng-click="editor.addValueMap(style)"><i class="fa fa-plus"></i></a>
</label>
</div>
</div>
<div class="gf-form-group" ng-if="style.mappingType==2">
<div class="gf-form" ng-repeat="mapping in style.rangeMappings">
<input type="text" class="gf-form-input width-5" ng-model="mapping.from" placeholder="From" ng-blur="editor.render()" array-join>
<label class="gf-form-label width-2">-</label>
<input type="text" class="gf-form-input width-5" ng-model="mapping.to" placeholder="To" ng-blur="editor.render()" array-join>
<label class="gf-form-label width-3">=></label>
<input type="text" class="gf-form-input width-5" ng-model="mapping.text" placeholder="Text" ng-blur="editor.render()" array-join>
<label class="gf-form-label">
<a class="pointer" tabindex="1" ng-click="editor.removeRangeMapping(style, $index)">
<i class="fa fa-trash"></i>
</a>
</label>
<div class="gf-form" ng-repeat="rangeMap in style.rangeMaps">
<span class="gf-form-label">
<i class="fa fa-remove pointer" ng-click="editor.removeRangeMap(style, $index)"></i>
</span>
<span class="gf-form-label">From</span>
<input type="text" ng-model="rangeMap.from" class="gf-form-input max-width-6" ng-blur="editor.render()">
<span class="gf-form-label">To</span>
<input type="text" ng-model="rangeMap.to" class="gf-form-input max-width-6" ng-blur="editor.render()">
<span class="gf-form-label">Text</span>
<input type="text" ng-model="rangeMap.text" class="gf-form-input max-width-8" ng-blur="editor.render()">
</div>
<div class="gf-form">
<label class="gf-form-label">
<a class="pointer" ng-click="editor.addRangeMapping(style)"><i class="fa fa-plus"></i></a>
<a class="pointer" ng-click="editor.addRangeMap(style)"><i class="fa fa-plus"></i></a>
</label>
</div>
</div>

View File

@ -113,29 +113,30 @@ export class ColumnOptionsCtrl {
this.render();
};
}
addValueMapping(style) {
if (!style.valueMappings) {
style.valueMappings = [];
addValueMap(style) {
if (!style.valueMaps) {
style.valueMaps = [];
}
style.valueMappings.push({ value: '', text: '' });
style.valueMaps.push({ value: '', text: '' });
this.panelCtrl.render();
}
removeValueMapping(style, index) {
style.valueMappings.splice(index, 1);
removeValueMap(style, index) {
style.valueMaps.splice(index, 1);
this.panelCtrl.render();
}
removeRangeMapping(style, index) {
style.rangeMappings.splice(index, 1);
this.panelCtrl.render();
}
addRangeMapping(style) {
if (!style.rangeMappings) {
style.rangeMappings = [];
addRangeMap(style) {
if (!style.rangeMaps) {
style.rangeMaps = [];
}
style.rangeMappings.push({ from: '', to: '', text: '' });
style.rangeMaps.push({ from: '', to: '', text: '' });
this.panelCtrl.render();
}
removeRangeMap(style, index) {
style.rangeMaps.splice(index, 1);
this.panelCtrl.render();
}
}

View File

@ -47,7 +47,6 @@ export class TableRenderer {
if (!style.thresholds) {
return null;
}
value = Number(value);
for (var i = style.thresholds.length; i > 0; i--) {
if (value >= style.thresholds[i - 1]) {
return style.colors[i];
@ -102,54 +101,54 @@ export class TableRenderer {
if (column.style.type === 'string') {
return v => {
if (column.style.valueMappings && column.style.mappingType && column.style.mappingType === 1) {
for (let i = 0; i < column.style.valueMappings.length; i++) {
let mapping = column.style.valueMappings[i];
var value = Number(mapping.value);
if (v === null && mapping.value[0] === 'null') {
return mapping.text;
}
if (v !== null && !_.isArray(v)) {
if (Number(v) === value) {
if (!_.isString(v) && !_.isArray(v)) {
this.colorState[column.style.colorMode] = this.getColorForValue(v, column.style);
}
return this.defaultCellFormatter(mapping.text, column.style);
if (_.isArray(v)) {
v = v.join(', ');
}
const mappingType = column.style.mappingType || 0;
if (mappingType === 1 && column.style.valueMaps) {
for (let i = 0; i < column.style.valueMaps.length; i++) {
const map = column.style.valueMaps[i];
if (v === null) {
if (map.value === 'null') {
return map.text;
}
continue;
}
// Allow both numeric and string values to be mapped
if ((!_.isString(v) && Number(map.value) === Number(v)) || map.value === v) {
this.setColorState(v, column.style);
return this.defaultCellFormatter(map.text, column.style);
}
}
if (v !== null && v !== void 0 && !_.isString(v) && !_.isArray(v)) {
this.colorState[column.style.colorMode] = this.getColorForValue(v, column.style);
}
}
if (column.style.rangeMappings && column.style.mappingType && column.style.mappingType === 2) {
for (let i = 0; i < column.style.rangeMappings.length; i++) {
let mapping = column.style.rangeMappings[i];
var from = mapping.from;
var to = mapping.to;
if (v === null && mapping.from[0] === 'null' && mapping.to[0] === 'null') {
return mapping.text;
if (mappingType === 2 && column.style.rangeMaps) {
for (let i = 0; i < column.style.rangeMaps.length; i++) {
const map = column.style.rangeMaps[i];
if (v === null) {
if (map.from === 'null' && map.to === 'null') {
return map.text;
}
continue;
}
if (
v !== null &&
!_.isString(v) &&
!_.isArray(v) &&
from !== '' &&
to !== '' &&
Number(from[0]) <= v &&
Number(to[0]) >= v
) {
this.colorState[column.style.colorMode] = this.getColorForValue(v, column.style);
return this.defaultCellFormatter(mapping.text, column.style);
if (Number(map.from) <= Number(v) && Number(map.to) >= Number(v)) {
this.setColorState(v, column.style);
return this.defaultCellFormatter(map.text, column.style);
}
}
if (v !== null && v !== void 0 && !_.isString(v) && !_.isArray(v)) {
this.colorState[column.style.colorMode] = this.getColorForValue(v, column.style);
}
}
if (v === null) {
if (v === null || v === void 0) {
return '-';
}
this.setColorState(v, column.style);
return this.defaultCellFormatter(v, column.style);
};
}
@ -166,10 +165,7 @@ export class TableRenderer {
return this.defaultCellFormatter(v, column.style);
}
if (column.style.colorMode) {
this.colorState[column.style.colorMode] = this.getColorForValue(v, column.style);
}
this.setColorState(v, column.style);
return valueFormatter(v, column.style.decimals, null);
};
}
@ -179,6 +175,23 @@ export class TableRenderer {
};
}
setColorState(value, style) {
if (!style.colorMode) {
return;
}
if (value === null || value === void 0 || _.isArray(value)) {
return;
}
var numericValue = Number(value);
if (numericValue === NaN) {
return;
}
this.colorState[style.colorMode] = this.getColorForValue(numericValue, style);
}
renderRowVariables(rowIndex) {
let scopedVars = {};
let cell_variable;

View File

@ -3,7 +3,7 @@ import TableModel from 'app/core/table_model';
import { TableRenderer } from '../renderer';
describe('when rendering table', () => {
describe('given 2 columns', () => {
describe('given 13 columns', () => {
var table = new TableModel();
table.columns = [
{ text: 'Time' },
@ -17,8 +17,12 @@ describe('when rendering table', () => {
{ text: 'Array' },
{ text: 'Mapping' },
{ text: 'RangeMapping' },
{ text: 'MappingColored' },
{ text: 'RangeMappingColored' },
];
table.rows = [
[1388556366666, 1230, 40, undefined, '', '', 'my.host.com', 'host1', ['value1', 'value2'], 1, 2, 1, 2],
];
table.rows = [[1388556366666, 1230, 40, undefined, '', '', 'my.host.com', 'host1', ['value1', 'value2'], 1, 2]];
var panel = {
pageSize: 10,
@ -82,7 +86,7 @@ describe('when rendering table', () => {
pattern: 'Mapping',
type: 'string',
mappingType: 1,
valueMappings: [
valueMaps: [
{
value: '1',
text: 'on',
@ -91,13 +95,21 @@ describe('when rendering table', () => {
value: '0',
text: 'off',
},
{
value: 'HELLO WORLD',
text: 'HELLO GRAFANA',
},
{
value: 'value1, value2',
text: 'value3, value4',
},
],
},
{
pattern: 'RangeMapping',
type: 'string',
mappingType: 2,
rangeMappings: [
rangeMaps: [
{
from: '1',
to: '3',
@ -110,6 +122,44 @@ describe('when rendering table', () => {
},
],
},
{
pattern: 'MappingColored',
type: 'string',
mappingType: 1,
valueMaps: [
{
value: '1',
text: 'on',
},
{
value: '0',
text: 'off',
},
],
colorMode: 'value',
thresholds: [1, 2],
colors: ['green', 'orange', 'red'],
},
{
pattern: 'RangeMappingColored',
type: 'string',
mappingType: 2,
rangeMaps: [
{
from: '1',
to: '3',
text: 'on',
},
{
from: '3',
to: '6',
text: 'off',
},
],
colorMode: 'value',
thresholds: [2, 5],
colors: ['green', 'orange', 'red'],
},
],
};
@ -231,25 +281,85 @@ describe('when rendering table', () => {
expect(html).toBe('<td>value1, value2</td>');
});
it('value should be mapped to text', () => {
it('numeric value should be mapped to text', () => {
var html = renderer.renderCell(9, 0, 1);
expect(html).toBe('<td>on</td>');
});
it('value should be mapped to text', () => {
var html = renderer.renderCell(9, 0, 0);
it('string numeric value should be mapped to text', () => {
var html = renderer.renderCell(9, 0, '0');
expect(html).toBe('<td>off</td>');
});
it('value should be mapped to text(range)', () => {
it('string value should be mapped to text', () => {
var html = renderer.renderCell(9, 0, 'HELLO WORLD');
expect(html).toBe('<td>HELLO GRAFANA</td>');
});
it('array column value should be mapped to text', () => {
var html = renderer.renderCell(9, 0, ['value1', 'value2']);
expect(html).toBe('<td>value3, value4</td>');
});
it('value should be mapped to text (range)', () => {
var html = renderer.renderCell(10, 0, 2);
expect(html).toBe('<td>on</td>');
});
it('value should be mapped to text(range)', () => {
it('value should be mapped to text (range)', () => {
var html = renderer.renderCell(10, 0, 5);
expect(html).toBe('<td>off</td>');
});
it('array column value should not be mapped to text', () => {
var html = renderer.renderCell(10, 0, ['value1', 'value2']);
expect(html).toBe('<td>value1, value2</td>');
});
it('value should be mapped to text and colored cell should have style', () => {
var html = renderer.renderCell(11, 0, 1);
expect(html).toBe('<td style="color:orange">on</td>');
});
it('value should be mapped to text and colored cell should have style', () => {
var html = renderer.renderCell(11, 0, '1');
expect(html).toBe('<td style="color:orange">on</td>');
});
it('value should be mapped to text and colored cell should have style', () => {
var html = renderer.renderCell(11, 0, 0);
expect(html).toBe('<td style="color:green">off</td>');
});
it('value should be mapped to text and colored cell should have style', () => {
var html = renderer.renderCell(11, 0, '0');
expect(html).toBe('<td style="color:green">off</td>');
});
it('value should be mapped to text and colored cell should have style', () => {
var html = renderer.renderCell(11, 0, '2.1');
expect(html).toBe('<td style="color:red">2.1</td>');
});
it('value should be mapped to text (range) and colored cell should have style', () => {
var html = renderer.renderCell(12, 0, 0);
expect(html).toBe('<td style="color:green">0</td>');
});
it('value should be mapped to text (range) and colored cell should have style', () => {
var html = renderer.renderCell(12, 0, 1);
expect(html).toBe('<td style="color:green">on</td>');
});
it('value should be mapped to text (range) and colored cell should have style', () => {
var html = renderer.renderCell(12, 0, 4);
expect(html).toBe('<td style="color:orange">off</td>');
});
it('value should be mapped to text (range) and colored cell should have style', () => {
var html = renderer.renderCell(12, 0, '7.1');
expect(html).toBe('<td style="color:red">7.1</td>');
});
});
});