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

View File

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

View File

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

View File

@ -3,7 +3,7 @@ import TableModel from 'app/core/table_model';
import { TableRenderer } from '../renderer'; import { TableRenderer } from '../renderer';
describe('when rendering table', () => { describe('when rendering table', () => {
describe('given 2 columns', () => { describe('given 13 columns', () => {
var table = new TableModel(); var table = new TableModel();
table.columns = [ table.columns = [
{ text: 'Time' }, { text: 'Time' },
@ -17,8 +17,12 @@ describe('when rendering table', () => {
{ text: 'Array' }, { text: 'Array' },
{ text: 'Mapping' }, { text: 'Mapping' },
{ text: 'RangeMapping' }, { 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 = { var panel = {
pageSize: 10, pageSize: 10,
@ -82,7 +86,7 @@ describe('when rendering table', () => {
pattern: 'Mapping', pattern: 'Mapping',
type: 'string', type: 'string',
mappingType: 1, mappingType: 1,
valueMappings: [ valueMaps: [
{ {
value: '1', value: '1',
text: 'on', text: 'on',
@ -91,13 +95,21 @@ describe('when rendering table', () => {
value: '0', value: '0',
text: 'off', text: 'off',
}, },
{
value: 'HELLO WORLD',
text: 'HELLO GRAFANA',
},
{
value: 'value1, value2',
text: 'value3, value4',
},
], ],
}, },
{ {
pattern: 'RangeMapping', pattern: 'RangeMapping',
type: 'string', type: 'string',
mappingType: 2, mappingType: 2,
rangeMappings: [ rangeMaps: [
{ {
from: '1', from: '1',
to: '3', 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>'); 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); var html = renderer.renderCell(9, 0, 1);
expect(html).toBe('<td>on</td>'); expect(html).toBe('<td>on</td>');
}); });
it('value should be mapped to text', () => { it('string numeric value should be mapped to text', () => {
var html = renderer.renderCell(9, 0, 0); var html = renderer.renderCell(9, 0, '0');
expect(html).toBe('<td>off</td>'); 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); var html = renderer.renderCell(10, 0, 2);
expect(html).toBe('<td>on</td>'); 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); var html = renderer.renderCell(10, 0, 5);
expect(html).toBe('<td>off</td>'); 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>');
});
}); });
}); });