Commit f673ec16 by Marcus Efraimsson

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.
parent 67f03822
......@@ -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>
......
......@@ -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);
addRangeMap(style) {
if (!style.rangeMaps) {
style.rangeMaps = [];
}
style.rangeMaps.push({ from: '', to: '', text: '' });
this.panelCtrl.render();
}
addRangeMapping(style) {
if (!style.rangeMappings) {
style.rangeMappings = [];
}
style.rangeMappings.push({ from: '', to: '', text: '' });
removeRangeMap(style, index) {
style.rangeMaps.splice(index, 1);
this.panelCtrl.render();
}
}
......
......@@ -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;
......
......@@ -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,56 @@ 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',
text: 'on',
},
{
from: '3',
to: '6',
text: 'off',
},
],
},
{
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',
......@@ -109,6 +156,9 @@ describe('when rendering table', () => {
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>');
});
});
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment