mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
SingleStat: use DataFrame results rather than TimeSeries/TableData (#18580)
This commit is contained in:
@@ -501,6 +501,237 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"valueName": "current"
|
"valueName": "current"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cacheTimeout": null,
|
||||||
|
"colorBackground": false,
|
||||||
|
"colorPrefix": false,
|
||||||
|
"colorValue": false,
|
||||||
|
"colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"],
|
||||||
|
"datasource": "gdev-testdata",
|
||||||
|
"decimals": null,
|
||||||
|
"description": "",
|
||||||
|
"format": "none",
|
||||||
|
"gauge": {
|
||||||
|
"maxValue": 150,
|
||||||
|
"minValue": 0,
|
||||||
|
"show": false,
|
||||||
|
"thresholdLabels": false,
|
||||||
|
"thresholdMarkers": true
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 4,
|
||||||
|
"w": 8,
|
||||||
|
"x": 0,
|
||||||
|
"y": 14
|
||||||
|
},
|
||||||
|
"id": 8,
|
||||||
|
"interval": null,
|
||||||
|
"links": [],
|
||||||
|
"mappingType": 1,
|
||||||
|
"mappingTypes": [
|
||||||
|
{
|
||||||
|
"name": "value to text",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "range to text",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"maxDataPoints": 100,
|
||||||
|
"nullPointMode": "connected",
|
||||||
|
"nullText": null,
|
||||||
|
"options": {},
|
||||||
|
"postfix": "",
|
||||||
|
"postfixFontSize": "50%",
|
||||||
|
"prefix": "",
|
||||||
|
"prefixFontSize": "50%",
|
||||||
|
"rangeMaps": [
|
||||||
|
{
|
||||||
|
"from": "null",
|
||||||
|
"text": "N/A",
|
||||||
|
"to": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sparkline": {
|
||||||
|
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||||
|
"full": true,
|
||||||
|
"lineColor": "rgb(31, 120, 193)",
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
"tableColumn": "Info",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"alias": "",
|
||||||
|
"expr": "",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 1,
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk_table",
|
||||||
|
"stringInput": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": "81,90",
|
||||||
|
"title": "TableData 'Info' string Column",
|
||||||
|
"type": "singlestat",
|
||||||
|
"valueFontSize": "80%",
|
||||||
|
"valueMaps": [],
|
||||||
|
"valueName": "current"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cacheTimeout": null,
|
||||||
|
"colorBackground": false,
|
||||||
|
"colorPrefix": false,
|
||||||
|
"colorValue": false,
|
||||||
|
"colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"],
|
||||||
|
"datasource": "gdev-testdata",
|
||||||
|
"decimals": 2,
|
||||||
|
"description": "",
|
||||||
|
"format": "celsius",
|
||||||
|
"gauge": {
|
||||||
|
"maxValue": 150,
|
||||||
|
"minValue": 0,
|
||||||
|
"show": false,
|
||||||
|
"thresholdLabels": false,
|
||||||
|
"thresholdMarkers": true
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 4,
|
||||||
|
"w": 8,
|
||||||
|
"x": 8,
|
||||||
|
"y": 14
|
||||||
|
},
|
||||||
|
"id": 9,
|
||||||
|
"interval": null,
|
||||||
|
"links": [],
|
||||||
|
"mappingType": 1,
|
||||||
|
"mappingTypes": [
|
||||||
|
{
|
||||||
|
"name": "value to text",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "range to text",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"maxDataPoints": 100,
|
||||||
|
"nullPointMode": "connected",
|
||||||
|
"nullText": null,
|
||||||
|
"options": {},
|
||||||
|
"postfix": "",
|
||||||
|
"postfixFontSize": "50%",
|
||||||
|
"prefix": "",
|
||||||
|
"prefixFontSize": "50%",
|
||||||
|
"rangeMaps": [
|
||||||
|
{
|
||||||
|
"from": "null",
|
||||||
|
"text": "N/A",
|
||||||
|
"to": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sparkline": {
|
||||||
|
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||||
|
"full": true,
|
||||||
|
"lineColor": "rgb(31, 120, 193)",
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
"tableColumn": "Min",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"alias": "",
|
||||||
|
"expr": "",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 1,
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk_table",
|
||||||
|
"stringInput": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": "81,90",
|
||||||
|
"title": "TableData 'Value' as temp Column",
|
||||||
|
"type": "singlestat",
|
||||||
|
"valueFontSize": "80%",
|
||||||
|
"valueMaps": [],
|
||||||
|
"valueName": "current"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cacheTimeout": null,
|
||||||
|
"colorBackground": false,
|
||||||
|
"colorPrefix": false,
|
||||||
|
"colorValue": false,
|
||||||
|
"colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"],
|
||||||
|
"datasource": "gdev-testdata",
|
||||||
|
"decimals": null,
|
||||||
|
"description": "",
|
||||||
|
"format": "dateTimeFromNow",
|
||||||
|
"gauge": {
|
||||||
|
"maxValue": 150,
|
||||||
|
"minValue": 0,
|
||||||
|
"show": false,
|
||||||
|
"thresholdLabels": false,
|
||||||
|
"thresholdMarkers": true
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 4,
|
||||||
|
"w": 8,
|
||||||
|
"x": 16,
|
||||||
|
"y": 14
|
||||||
|
},
|
||||||
|
"id": 10,
|
||||||
|
"interval": null,
|
||||||
|
"links": [],
|
||||||
|
"mappingType": 1,
|
||||||
|
"mappingTypes": [
|
||||||
|
{
|
||||||
|
"name": "value to text",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "range to text",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"maxDataPoints": 100,
|
||||||
|
"nullPointMode": "connected",
|
||||||
|
"nullText": null,
|
||||||
|
"options": {},
|
||||||
|
"postfix": "",
|
||||||
|
"postfixFontSize": "50%",
|
||||||
|
"prefix": "",
|
||||||
|
"prefixFontSize": "50%",
|
||||||
|
"rangeMaps": [
|
||||||
|
{
|
||||||
|
"from": "null",
|
||||||
|
"text": "N/A",
|
||||||
|
"to": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sparkline": {
|
||||||
|
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||||
|
"full": true,
|
||||||
|
"lineColor": "rgb(31, 120, 193)",
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
"tableColumn": "time",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"alias": "",
|
||||||
|
"expr": "",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 1,
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"stringInput": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": "81,90",
|
||||||
|
"title": "last_time display (a few seconds ago)",
|
||||||
|
"type": "singlestat",
|
||||||
|
"valueFontSize": "80%",
|
||||||
|
"valueMaps": [],
|
||||||
|
"valueName": "last_time"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"refresh": false,
|
"refresh": false,
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ export function migrateOldThresholds(thresholds?: any[]): Threshold[] | undefine
|
|||||||
/**
|
/**
|
||||||
* Convert the angular single stat mapping to new react style
|
* Convert the angular single stat mapping to new react style
|
||||||
*/
|
*/
|
||||||
function convertOldAngulrValueMapping(panel: any): ValueMapping[] {
|
export function convertOldAngulrValueMapping(panel: any): ValueMapping[] {
|
||||||
const mappings: ValueMapping[] = [];
|
const mappings: ValueMapping[] = [];
|
||||||
|
|
||||||
// Guess the right type based on options
|
// Guess the right type based on options
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ export {
|
|||||||
SingleStatBaseOptions,
|
SingleStatBaseOptions,
|
||||||
sharedSingleStatPanelChangedHandler,
|
sharedSingleStatPanelChangedHandler,
|
||||||
sharedSingleStatMigrationHandler,
|
sharedSingleStatMigrationHandler,
|
||||||
|
convertOldAngulrValueMapping,
|
||||||
} from './SingleStatBaseOptions';
|
} from './SingleStatBaseOptions';
|
||||||
|
|||||||
@@ -58,6 +58,14 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
|
|||||||
if (shouldFormat && !_.isBoolean(value)) {
|
if (shouldFormat && !_.isBoolean(value)) {
|
||||||
const { decimals, scaledDecimals } = getDecimalsForValue(value, field.decimals);
|
const { decimals, scaledDecimals } = getDecimalsForValue(value, field.decimals);
|
||||||
text = formatFunc(numeric, decimals, scaledDecimals, options.isUtc);
|
text = formatFunc(numeric, decimals, scaledDecimals, options.isUtc);
|
||||||
|
|
||||||
|
// Check if the formatted text mapped to a different value
|
||||||
|
if (mappings && mappings.length > 0) {
|
||||||
|
const mappedValue = getMappedValue(mappings, text);
|
||||||
|
if (mappedValue) {
|
||||||
|
text = mappedValue.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (thresholds && thresholds.length) {
|
if (thresholds && thresholds.length) {
|
||||||
color = getColorFromThreshold(numeric, thresholds, theme);
|
color = getColorFromThreshold(numeric, thresholds, theme);
|
||||||
|
|||||||
@@ -40,16 +40,18 @@
|
|||||||
<h5 class="section-heading">Value</h5>
|
<h5 class="section-heading">Value</h5>
|
||||||
|
|
||||||
<div class="gf-form-inline">
|
<div class="gf-form-inline">
|
||||||
<div class="gf-form" ng-show="ctrl.dataType === 'timeseries'">
|
<div class="gf-form" ng-if="ctrl.fieldNames.length > 1">
|
||||||
<label class="gf-form-label width-6">Stat</label>
|
<label class="gf-form-label width-6">Field</label>
|
||||||
<div class="gf-form-select-wrapper width-12">
|
<div class="gf-form-select-wrapper width-12">
|
||||||
<select class="gf-form-input" ng-model="ctrl.panel.valueName" ng-options="f.value as f.text for f in ctrl.valueNameOptions" ng-change="ctrl.refresh()"></select>
|
<select class="gf-form-input" ng-model="ctrl.panel.tableColumn" ng-options="f for f in ctrl.fieldNames" ng-change="ctrl.refresh()"></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form" ng-show="ctrl.dataType === 'table'">
|
</div>
|
||||||
<label class="gf-form-label width-6">Column</label>
|
<div class="gf-form-inline">
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-6">Show</label>
|
||||||
<div class="gf-form-select-wrapper width-12">
|
<div class="gf-form-select-wrapper width-12">
|
||||||
<select class="gf-form-input" ng-model="ctrl.panel.tableColumn" ng-options="f for f in ctrl.tableColumnOptions" ng-change="ctrl.refresh()"></select>
|
<select class="gf-form-input" ng-model="ctrl.panel.valueName" ng-options="f.value as f.text for f in ctrl.valueNameOptions" ng-change="ctrl.refresh()"></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
@@ -64,19 +66,24 @@
|
|||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<label class="gf-form-label width-6">Prefix</label>
|
<label class="gf-form-label width-6">Prefix</label>
|
||||||
<input type="text" class="gf-form-input width-12" ng-model="ctrl.panel.prefix" ng-change="ctrl.render()" ng-model-onblur>
|
<input type="text" class="gf-form-input width-12" ng-model="ctrl.panel.prefix" ng-change="ctrl.render()" ng-model-onblur>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
<label class="gf-form-label width-6">Font size</label>
|
<label class="gf-form-label width-6">Font size</label>
|
||||||
<div class="gf-form-select-wrapper">
|
<div class="gf-form-select-wrapper">
|
||||||
<select class="gf-form-input" ng-model="ctrl.panel.prefixFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()" ng-disabled="!ctrl.canModifyText()"></select>
|
<select class="gf-form-input" ng-model="ctrl.panel.prefixFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()" ng-disabled="!ctrl.canModifyText()"></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="gf-form-inline">
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<label class="gf-form-label width-6">Postfix</label>
|
<label class="gf-form-label width-6">Postfix</label>
|
||||||
<input type="text" class="gf-form-input width-12" ng-model="ctrl.panel.postfix" ng-change="ctrl.render()" ng-model-onblur>
|
<input type="text" class="gf-form-input width-12" ng-model="ctrl.panel.postfix" ng-change="ctrl.render()" ng-model-onblur>
|
||||||
<label class="gf-form-label width-6">Font size</label>
|
</div>
|
||||||
<div class="gf-form-select-wrapper">
|
<div class="gf-form">
|
||||||
<select class="input-small gf-form-input" ng-model="ctrl.panel.postfixFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()" ng-disabled="!ctrl.canModifyText()"></select>
|
<label class="gf-form-label width-6">Font size</label>
|
||||||
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="input-small gf-form-input" ng-model="ctrl.panel.postfixFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()" ng-disabled="!ctrl.canModifyText()"></select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
@@ -122,7 +129,7 @@
|
|||||||
|
|
||||||
<div class="section gf-form-group">
|
<div class="section gf-form-group">
|
||||||
<h5 class="section-heading">Spark lines</h5>
|
<h5 class="section-heading">Spark lines</h5>
|
||||||
<gf-form-switch class="gf-form" label-class="width-9" label="Show" checked="ctrl.panel.sparkline.show" on-change="ctrl.render()"></gf-form-switch>
|
<gf-form-switch class="gf-form" label-class="width-9" label="Show" checked="ctrl.panel.sparkline.show" on-change="ctrl.refresh()"></gf-form-switch>
|
||||||
<div ng-if="ctrl.panel.sparkline.show">
|
<div ng-if="ctrl.panel.sparkline.show">
|
||||||
<gf-form-switch class="gf-form" label-class="width-9" label="Full height" checked="ctrl.panel.sparkline.full" on-change="ctrl.render()"></gf-form-switch>
|
<gf-form-switch class="gf-form" label-class="width-9" label="Full height" checked="ctrl.panel.sparkline.full" on-change="ctrl.render()"></gf-form-switch>
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<div class="gf-form-select-wrapper">
|
<div class="gf-form-select-wrapper">
|
||||||
<select class="gf-form-input" ng-model="ctrl.panel.mappingType"
|
<select class="gf-form-input" ng-model="ctrl.panel.mappingType"
|
||||||
ng-options="f.value as f.name for f in ctrl.panel.mappingTypes" ng-change="ctrl.render()"></select>
|
ng-options="f.value as f.name for f in ctrl.panel.mappingTypes" ng-change="ctrl.refresh()"></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,11 +18,11 @@
|
|||||||
<span class="gf-form-label">
|
<span class="gf-form-label">
|
||||||
<i class="fa fa-remove pointer" ng-click="ctrl.removeValueMap(map)"></i>
|
<i class="fa fa-remove pointer" ng-click="ctrl.removeValueMap(map)"></i>
|
||||||
</span>
|
</span>
|
||||||
<input type="text" ng-model="map.value" placeholder="value" class="gf-form-input max-width-6" ng-blur="ctrl.render()">
|
<input type="text" ng-model="map.value" placeholder="value" class="gf-form-input max-width-6" ng-blur="ctrl.refresh()">
|
||||||
<span class="gf-form-label">
|
<span class="gf-form-label">
|
||||||
<i class="fa fa-arrow-right"></i>
|
<i class="fa fa-arrow-right"></i>
|
||||||
</span>
|
</span>
|
||||||
<input type="text" placeholder="text" ng-model="map.text" class="gf-form-input max-width-8" ng-blur="ctrl.render()">
|
<input type="text" placeholder="text" ng-model="map.text" class="gf-form-input max-width-8" ng-blur="ctrl.refresh()">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form-button-row">
|
<div class="gf-form-button-row">
|
||||||
@@ -41,11 +41,11 @@
|
|||||||
<i class="fa fa-remove pointer" ng-click="ctrl.removeRangeMap(rangeMap)"></i>
|
<i class="fa fa-remove pointer" ng-click="ctrl.removeRangeMap(rangeMap)"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="gf-form-label">From</span>
|
<span class="gf-form-label">From</span>
|
||||||
<input type="text" ng-model="rangeMap.from" class="gf-form-input max-width-6" ng-blur="ctrl.render()">
|
<input type="text" ng-model="rangeMap.from" class="gf-form-input max-width-6" ng-blur="ctrl.refresh()">
|
||||||
<span class="gf-form-label">To</span>
|
<span class="gf-form-label">To</span>
|
||||||
<input type="text" ng-model="rangeMap.to" class="gf-form-input max-width-6" ng-blur="ctrl.render()">
|
<input type="text" ng-model="rangeMap.to" class="gf-form-input max-width-6" ng-blur="ctrl.refresh()">
|
||||||
<span class="gf-form-label">Text</span>
|
<span class="gf-form-label">Text</span>
|
||||||
<input type="text" ng-model="rangeMap.text" class="gf-form-input max-width-8" ng-blur="ctrl.render()">
|
<input type="text" ng-model="rangeMap.text" class="gf-form-input max-width-8" ng-blur="ctrl.refresh()">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form-button-row">
|
<div class="gf-form-button-row">
|
||||||
|
|||||||
@@ -3,34 +3,56 @@ import $ from 'jquery';
|
|||||||
import 'vendor/flot/jquery.flot';
|
import 'vendor/flot/jquery.flot';
|
||||||
import 'vendor/flot/jquery.flot.gauge';
|
import 'vendor/flot/jquery.flot.gauge';
|
||||||
import 'app/features/panel/panellinks/link_srv';
|
import 'app/features/panel/panellinks/link_srv';
|
||||||
import { getDecimalsForValue } from '@grafana/ui';
|
import {
|
||||||
|
LegacyResponseData,
|
||||||
|
getFlotPairs,
|
||||||
|
getDisplayProcessor,
|
||||||
|
convertOldAngulrValueMapping,
|
||||||
|
getColorFromHexRgbOrName,
|
||||||
|
} from '@grafana/ui';
|
||||||
|
|
||||||
import kbn from 'app/core/utils/kbn';
|
import kbn from 'app/core/utils/kbn';
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import TimeSeries from 'app/core/time_series2';
|
|
||||||
import { MetricsPanelCtrl } from 'app/plugins/sdk';
|
import { MetricsPanelCtrl } from 'app/plugins/sdk';
|
||||||
import { isTableData } from '@grafana/data';
|
import {
|
||||||
import { GrafanaThemeType, getValueFormat, getColorFromHexRgbOrName } from '@grafana/ui';
|
DataFrame,
|
||||||
|
FieldType,
|
||||||
|
reduceField,
|
||||||
|
ReducerID,
|
||||||
|
Field,
|
||||||
|
GraphSeriesValue,
|
||||||
|
DisplayValue,
|
||||||
|
fieldReducers,
|
||||||
|
KeyValue,
|
||||||
|
} from '@grafana/data';
|
||||||
import { auto } from 'angular';
|
import { auto } from 'angular';
|
||||||
import { LinkSrv, LinkModel } from 'app/features/panel/panellinks/link_srv';
|
import { LinkSrv, LinkModel } from 'app/features/panel/panellinks/link_srv';
|
||||||
import TableModel from 'app/core/table_model';
|
import { PanelQueryRunnerFormat } from 'app/features/dashboard/state/PanelQueryRunner';
|
||||||
|
import { getProcessedDataFrames } from 'app/features/dashboard/state/PanelQueryState';
|
||||||
|
|
||||||
const BASE_FONT_SIZE = 38;
|
const BASE_FONT_SIZE = 38;
|
||||||
|
|
||||||
interface DataFormat {
|
export interface ShowData {
|
||||||
value: string | number;
|
field: Field;
|
||||||
valueFormatted: string;
|
value: any;
|
||||||
valueRounded: number;
|
sparkline: GraphSeriesValue[][];
|
||||||
|
display: DisplayValue;
|
||||||
|
|
||||||
|
scopedVars: any;
|
||||||
|
|
||||||
|
thresholds: any[];
|
||||||
|
colorMap: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SingleStatCtrl extends MetricsPanelCtrl {
|
class SingleStatCtrl extends MetricsPanelCtrl {
|
||||||
static templateUrl = 'module.html';
|
static templateUrl = 'module.html';
|
||||||
|
|
||||||
dataType = 'timeseries';
|
data: Partial<ShowData> = {};
|
||||||
series: any[];
|
|
||||||
data: any;
|
|
||||||
fontSizes: any[];
|
fontSizes: any[];
|
||||||
unitFormats: any[];
|
unitFormats: any[];
|
||||||
|
fieldNames: string[] = [];
|
||||||
|
|
||||||
invalidGaugeRange: boolean;
|
invalidGaugeRange: boolean;
|
||||||
panel: any;
|
panel: any;
|
||||||
events: any;
|
events: any;
|
||||||
@@ -47,7 +69,6 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
{ value: 'range', text: 'Range' },
|
{ value: 'range', text: 'Range' },
|
||||||
{ value: 'last_time', text: 'Time of last point' },
|
{ value: 'last_time', text: 'Time of last point' },
|
||||||
];
|
];
|
||||||
tableColumnOptions: any;
|
|
||||||
|
|
||||||
// Set and populate defaults
|
// Set and populate defaults
|
||||||
panelDefaults: any = {
|
panelDefaults: any = {
|
||||||
@@ -102,6 +123,8 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
this.events.on('data-snapshot-load', this.onDataReceived.bind(this));
|
this.events.on('data-snapshot-load', this.onDataReceived.bind(this));
|
||||||
this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
|
this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
|
||||||
|
|
||||||
|
this.dataFormat = PanelQueryRunnerFormat.frames;
|
||||||
|
|
||||||
this.onSparklineColorChange = this.onSparklineColorChange.bind(this);
|
this.onSparklineColorChange = this.onSparklineColorChange.bind(this);
|
||||||
this.onSparklineFillChange = this.onSparklineFillChange.bind(this);
|
this.onSparklineFillChange = this.onSparklineFillChange.bind(this);
|
||||||
}
|
}
|
||||||
@@ -128,104 +151,115 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onDataError(err: any) {
|
onDataError(err: any) {
|
||||||
this.onDataReceived([]);
|
this.handleDataFrames([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDataReceived(dataList: any[]) {
|
// This should only be called from the snapshot callback
|
||||||
const data: any = {
|
onDataReceived(dataList: LegacyResponseData[]) {
|
||||||
scopedVars: _.extend({}, this.panel.scopedVars),
|
this.handleDataFrames(getProcessedDataFrames(dataList));
|
||||||
};
|
}
|
||||||
|
|
||||||
if (dataList.length > 0 && isTableData(dataList[0])) {
|
// Directly support DataFrame skipping event callbacks
|
||||||
this.dataType = 'table';
|
handleDataFrames(frames: DataFrame[]) {
|
||||||
const tableData = dataList.map(this.tableHandler.bind(this));
|
const { panel } = this;
|
||||||
this.setTableValues(tableData, data);
|
super.handleDataFrames(frames);
|
||||||
} else {
|
this.loading = false;
|
||||||
this.dataType = 'timeseries';
|
|
||||||
this.series = dataList.map(this.seriesHandler.bind(this));
|
const distinct = getDistinctNames(frames);
|
||||||
this.setValues(data);
|
let fieldInfo = distinct.byName[panel.tableColumn]; //
|
||||||
|
this.fieldNames = distinct.names;
|
||||||
|
|
||||||
|
if (!fieldInfo) {
|
||||||
|
fieldInfo = distinct.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fieldInfo) {
|
||||||
|
// When we don't have any field
|
||||||
|
this.data = {
|
||||||
|
value: 'No Data',
|
||||||
|
display: {
|
||||||
|
text: 'No Data',
|
||||||
|
numeric: NaN,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.data = this.processField(fieldInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.data = data;
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
seriesHandler(dataFrame: any) {
|
processField(fieldInfo: FieldInfo) {
|
||||||
const series = new TimeSeries({
|
const { panel, dashboard } = this;
|
||||||
datapoints: dataFrame.datapoints || [],
|
|
||||||
alias: dataFrame.target,
|
|
||||||
});
|
|
||||||
|
|
||||||
series.flotpairs = series.getFlotPairs(this.panel.nullPointMode);
|
const name = fieldInfo.field.config.title || fieldInfo.field.name;
|
||||||
return series;
|
let calc = panel.valueName;
|
||||||
}
|
let calcField = fieldInfo.field;
|
||||||
|
let val: any = undefined;
|
||||||
|
|
||||||
tableHandler(tableData: TableModel) {
|
if ('name' === calc) {
|
||||||
const datapoints: any[] = [];
|
val = name;
|
||||||
const columnNames: string[] = [];
|
} else {
|
||||||
|
if ('last_time' === calc) {
|
||||||
|
if (fieldInfo.frame.firstTimeField) {
|
||||||
|
calcField = fieldInfo.frame.firstTimeField;
|
||||||
|
calc = ReducerID.last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tableData.columns.forEach((column, columnIndex) => {
|
// Normalize functions (avg -> mean, etc)
|
||||||
columnNames[columnIndex] = column.text;
|
const r = fieldReducers.getIfExists(calc);
|
||||||
});
|
if (r) {
|
||||||
|
calc = r.id;
|
||||||
|
// With strings, don't accidentally use a math function
|
||||||
|
if (calcField.type === FieldType.string) {
|
||||||
|
const avoid = [ReducerID.mean, ReducerID.sum];
|
||||||
|
if (avoid.includes(calc)) {
|
||||||
|
calc = panel.valueName = ReducerID.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
calc = ReducerID.lastNotNull;
|
||||||
|
}
|
||||||
|
|
||||||
this.tableColumnOptions = columnNames;
|
// Calculate the value
|
||||||
if (!_.find(tableData.columns, ['text', this.panel.tableColumn])) {
|
val = reduceField({
|
||||||
this.setTableColumnToSensibleDefault(tableData);
|
field: calcField,
|
||||||
|
reducers: [calc],
|
||||||
|
})[calc];
|
||||||
}
|
}
|
||||||
|
|
||||||
tableData.rows.forEach(row => {
|
const processor = getDisplayProcessor({
|
||||||
const datapoint: any = {};
|
field: {
|
||||||
|
...fieldInfo.field.config,
|
||||||
|
unit: panel.format,
|
||||||
|
decimals: panel.decimals,
|
||||||
|
mappings: convertOldAngulrValueMapping(panel),
|
||||||
|
},
|
||||||
|
theme: config.theme,
|
||||||
|
isUtc: dashboard.isTimezoneUtc && dashboard.isTimezoneUtc(),
|
||||||
|
});
|
||||||
|
|
||||||
row.forEach((value: any, columnIndex: number) => {
|
const data = {
|
||||||
const key = columnNames[columnIndex];
|
field: fieldInfo.field,
|
||||||
datapoint[key] = value;
|
value: val,
|
||||||
|
display: processor(val),
|
||||||
|
scopedVars: _.extend({}, panel.scopedVars),
|
||||||
|
};
|
||||||
|
|
||||||
|
data.scopedVars['__name'] = name;
|
||||||
|
panel.tableColumn = this.fieldNames.length > 1 ? name : '';
|
||||||
|
|
||||||
|
// Get the fields for a sparkline
|
||||||
|
if (panel.sparkline && panel.sparkline.show && fieldInfo.frame.firstTimeField) {
|
||||||
|
this.data.sparkline = getFlotPairs({
|
||||||
|
xField: fieldInfo.frame.firstTimeField,
|
||||||
|
yField: fieldInfo.field,
|
||||||
|
nullValueMode: panel.nullPointMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
datapoints.push(datapoint);
|
|
||||||
});
|
|
||||||
|
|
||||||
return datapoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTableColumnToSensibleDefault(tableData: TableModel) {
|
|
||||||
if (tableData.columns.length === 1) {
|
|
||||||
this.panel.tableColumn = tableData.columns[0].text;
|
|
||||||
} else {
|
|
||||||
this.panel.tableColumn = _.find(tableData.columns, col => {
|
|
||||||
return col.type !== 'time';
|
|
||||||
}).text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTableValues(tableData: any[], data: DataFormat) {
|
|
||||||
if (!tableData || tableData.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tableData[0].length === 0 || tableData[0][0][this.panel.tableColumn] === undefined) {
|
return data;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const datapoint = tableData[0][0];
|
|
||||||
data.value = datapoint[this.panel.tableColumn];
|
|
||||||
|
|
||||||
if (_.isString(data.value)) {
|
|
||||||
data.valueFormatted = _.escape(data.value);
|
|
||||||
data.value = 0;
|
|
||||||
data.valueRounded = 0;
|
|
||||||
} else {
|
|
||||||
const decimalInfo = getDecimalsForValue(data.value, this.panel.decimals);
|
|
||||||
const formatFunc = getValueFormat(this.panel.format);
|
|
||||||
|
|
||||||
data.valueFormatted = formatFunc(
|
|
||||||
datapoint[this.panel.tableColumn],
|
|
||||||
decimalInfo.decimals,
|
|
||||||
decimalInfo.scaledDecimals
|
|
||||||
);
|
|
||||||
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setValueMapping(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canModifyText() {
|
canModifyText() {
|
||||||
@@ -267,106 +301,6 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
setValues(data: any) {
|
|
||||||
data.flotpairs = [];
|
|
||||||
|
|
||||||
if (this.series.length > 1) {
|
|
||||||
const error: any = new Error();
|
|
||||||
error.message = 'Multiple Series Error';
|
|
||||||
error.data =
|
|
||||||
'Metric query returns ' +
|
|
||||||
this.series.length +
|
|
||||||
' series. Single Stat Panel expects a single series.\n\nResponse:\n' +
|
|
||||||
JSON.stringify(this.series);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.series && this.series.length > 0) {
|
|
||||||
const lastPoint: any = _.last(this.series[0].datapoints);
|
|
||||||
const lastValue = _.isArray(lastPoint) ? lastPoint[0] : null;
|
|
||||||
const formatFunc = getValueFormat(this.panel.format);
|
|
||||||
|
|
||||||
if (this.panel.valueName === 'name') {
|
|
||||||
data.value = 0;
|
|
||||||
data.valueRounded = 0;
|
|
||||||
data.valueFormatted = this.series[0].alias;
|
|
||||||
} else if (_.isString(lastValue)) {
|
|
||||||
data.value = 0;
|
|
||||||
data.valueFormatted = _.escape(lastValue);
|
|
||||||
data.valueRounded = 0;
|
|
||||||
} else if (this.panel.valueName === 'last_time') {
|
|
||||||
data.value = lastPoint[1];
|
|
||||||
data.valueRounded = data.value;
|
|
||||||
data.valueFormatted = formatFunc(data.value, 0, 0, this.dashboard.isTimezoneUtc());
|
|
||||||
} else {
|
|
||||||
data.value = this.series[0].stats[this.panel.valueName];
|
|
||||||
data.flotpairs = this.series[0].flotpairs;
|
|
||||||
|
|
||||||
const decimalInfo = getDecimalsForValue(data.value, this.panel.decimals);
|
|
||||||
|
|
||||||
data.valueFormatted = formatFunc(
|
|
||||||
data.value,
|
|
||||||
decimalInfo.decimals,
|
|
||||||
decimalInfo.scaledDecimals,
|
|
||||||
this.dashboard.isTimezoneUtc()
|
|
||||||
);
|
|
||||||
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add $__name variable for using in prefix or postfix
|
|
||||||
data.scopedVars['__name'] = { value: this.series[0].label };
|
|
||||||
}
|
|
||||||
this.setValueMapping(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
setValueMapping(data: DataFormat) {
|
|
||||||
// check value to text mappings if its enabled
|
|
||||||
if (this.panel.mappingType === 1) {
|
|
||||||
for (let i = 0; i < this.panel.valueMaps.length; i++) {
|
|
||||||
const map = this.panel.valueMaps[i];
|
|
||||||
// special null case
|
|
||||||
if (map.value === 'null') {
|
|
||||||
if (data.value === null || data.value === void 0) {
|
|
||||||
data.valueFormatted = map.text;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// value/number to text mapping
|
|
||||||
const value = parseFloat(map.value);
|
|
||||||
if (value === data.valueRounded) {
|
|
||||||
data.valueFormatted = map.text;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (this.panel.mappingType === 2) {
|
|
||||||
for (let i = 0; i < this.panel.rangeMaps.length; i++) {
|
|
||||||
const map = this.panel.rangeMaps[i];
|
|
||||||
// special null case
|
|
||||||
if (map.from === 'null' && map.to === 'null') {
|
|
||||||
if (data.value === null || data.value === void 0) {
|
|
||||||
data.valueFormatted = map.text;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// value/number to range mapping
|
|
||||||
const from = parseFloat(map.from);
|
|
||||||
const to = parseFloat(map.to);
|
|
||||||
if (to >= data.valueRounded && from <= data.valueRounded) {
|
|
||||||
data.valueFormatted = map.text;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.value === null || data.value === void 0) {
|
|
||||||
data.valueFormatted = 'no value';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeValueMap(map: any) {
|
removeValueMap(map: any) {
|
||||||
const index = _.indexOf(this.panel.valueMaps, map);
|
const index = _.indexOf(this.panel.valueMaps, map);
|
||||||
this.panel.valueMaps.splice(index, 1);
|
this.panel.valueMaps.splice(index, 1);
|
||||||
@@ -394,12 +328,12 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
const $sanitize = this.$sanitize;
|
const $sanitize = this.$sanitize;
|
||||||
const panel = ctrl.panel;
|
const panel = ctrl.panel;
|
||||||
const templateSrv = this.templateSrv;
|
const templateSrv = this.templateSrv;
|
||||||
let data: any;
|
|
||||||
let linkInfo: LinkModel | null = null;
|
let linkInfo: LinkModel | null = null;
|
||||||
const $panelContainer = elem.find('.panel-container');
|
const $panelContainer = elem.find('.panel-container');
|
||||||
elem = elem.find('.singlestat-panel');
|
elem = elem.find('.singlestat-panel');
|
||||||
|
|
||||||
function applyColoringThresholds(valueString: string) {
|
function applyColoringThresholds(valueString: string) {
|
||||||
|
const data = ctrl.data;
|
||||||
const color = getColorForValue(data, data.value);
|
const color = getColorForValue(data, data.value);
|
||||||
if (color) {
|
if (color) {
|
||||||
return '<span style="color:' + color + '">' + valueString + '</span>';
|
return '<span style="color:' + color + '">' + valueString + '</span>';
|
||||||
@@ -409,20 +343,21 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getSpan(className: string, fontSizePercent: string, applyColoring: any, value: string) {
|
function getSpan(className: string, fontSizePercent: string, applyColoring: any, value: string) {
|
||||||
value = $sanitize(templateSrv.replace(value, data.scopedVars));
|
value = $sanitize(templateSrv.replace(value, ctrl.data.scopedVars));
|
||||||
value = applyColoring ? applyColoringThresholds(value) : value;
|
value = applyColoring ? applyColoringThresholds(value) : value;
|
||||||
const pixelSize = (parseInt(fontSizePercent, 10) / 100) * BASE_FONT_SIZE;
|
const pixelSize = (parseInt(fontSizePercent, 10) / 100) * BASE_FONT_SIZE;
|
||||||
return '<span class="' + className + '" style="font-size:' + pixelSize + 'px">' + value + '</span>';
|
return '<span class="' + className + '" style="font-size:' + pixelSize + 'px">' + value + '</span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBigValueHtml() {
|
function getBigValueHtml() {
|
||||||
|
const data: ShowData = ctrl.data;
|
||||||
let body = '<div class="singlestat-panel-value-container">';
|
let body = '<div class="singlestat-panel-value-container">';
|
||||||
|
|
||||||
if (panel.prefix) {
|
if (panel.prefix) {
|
||||||
body += getSpan('singlestat-panel-prefix', panel.prefixFontSize, panel.colorPrefix, panel.prefix);
|
body += getSpan('singlestat-panel-prefix', panel.prefixFontSize, panel.colorPrefix, panel.prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
body += getSpan('singlestat-panel-value', panel.valueFontSize, panel.colorValue, data.valueFormatted);
|
body += getSpan('singlestat-panel-value', panel.valueFontSize, panel.colorValue, data.display.text);
|
||||||
|
|
||||||
if (panel.postfix) {
|
if (panel.postfix) {
|
||||||
body += getSpan('singlestat-panel-postfix', panel.postfixFontSize, panel.colorPostfix, panel.postfix);
|
body += getSpan('singlestat-panel-postfix', panel.postfixFontSize, panel.colorPostfix, panel.postfix);
|
||||||
@@ -434,14 +369,16 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getValueText() {
|
function getValueText() {
|
||||||
|
const data: ShowData = ctrl.data;
|
||||||
let result = panel.prefix ? templateSrv.replace(panel.prefix, data.scopedVars) : '';
|
let result = panel.prefix ? templateSrv.replace(panel.prefix, data.scopedVars) : '';
|
||||||
result += data.valueFormatted;
|
result += data.display.text;
|
||||||
result += panel.postfix ? templateSrv.replace(panel.postfix, data.scopedVars) : '';
|
result += panel.postfix ? templateSrv.replace(panel.postfix, data.scopedVars) : '';
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addGauge() {
|
function addGauge() {
|
||||||
|
const data: ShowData = ctrl.data;
|
||||||
const width = elem.width();
|
const width = elem.width();
|
||||||
const height = elem.height();
|
const height = elem.height();
|
||||||
// Allow to use a bit more space for wide gauges
|
// Allow to use a bit more space for wide gauges
|
||||||
@@ -513,7 +450,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
width: thresholdMarkersWidth,
|
width: thresholdMarkersWidth,
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
color: panel.colorValue ? getColorForValue(data, data.valueRounded) : null,
|
color: panel.colorValue ? getColorForValue(data, data.display.numeric) : null,
|
||||||
formatter: () => {
|
formatter: () => {
|
||||||
return getValueText();
|
return getValueText();
|
||||||
},
|
},
|
||||||
@@ -537,6 +474,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addSparkline() {
|
function addSparkline() {
|
||||||
|
const data: ShowData = ctrl.data;
|
||||||
const width = elem.width();
|
const width = elem.width();
|
||||||
if (width < 30) {
|
if (width < 30) {
|
||||||
// element has not gotten it's width yet
|
// element has not gotten it's width yet
|
||||||
@@ -544,6 +482,10 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
setTimeout(addSparkline, 30);
|
setTimeout(addSparkline, 30);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!data.sparkline || !data.sparkline.length) {
|
||||||
|
// no sparkline data
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const height = ctrl.height;
|
const height = ctrl.height;
|
||||||
const plotCanvas = $('<div></div>');
|
const plotCanvas = $('<div></div>');
|
||||||
@@ -592,7 +534,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
elem.append(plotCanvas);
|
elem.append(plotCanvas);
|
||||||
|
|
||||||
const plotSeries = {
|
const plotSeries = {
|
||||||
data: data.flotpairs,
|
data: data.sparkline,
|
||||||
color: getColorFromHexRgbOrName(panel.sparkline.lineColor, config.theme.type),
|
color: getColorFromHexRgbOrName(panel.sparkline.lineColor, config.theme.type),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -603,26 +545,24 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
|||||||
if (!ctrl.data) {
|
if (!ctrl.data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data = ctrl.data;
|
const { data, panel } = ctrl;
|
||||||
|
|
||||||
// get thresholds
|
// get thresholds
|
||||||
data.thresholds = panel.thresholds.split(',').map((strVale: string) => {
|
data.thresholds = panel.thresholds
|
||||||
return Number(strVale.trim());
|
? panel.thresholds.split(',').map((strVale: string) => {
|
||||||
});
|
return Number(strVale.trim());
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
// Map panel colors to hex or rgb/a values
|
// Map panel colors to hex or rgb/a values
|
||||||
data.colorMap = panel.colors.map((color: string) =>
|
if (panel.colors) {
|
||||||
getColorFromHexRgbOrName(
|
data.colorMap = panel.colors.map((color: string) => getColorFromHexRgbOrName(color, config.theme.type));
|
||||||
color,
|
}
|
||||||
config.bootData.user.lightTheme ? GrafanaThemeType.Light : GrafanaThemeType.Dark
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const body = panel.gauge.show ? '' : getBigValueHtml();
|
const body = panel.gauge.show ? '' : getBigValueHtml();
|
||||||
|
|
||||||
if (panel.colorBackground) {
|
if (panel.colorBackground) {
|
||||||
const color = getColorForValue(data, data.value);
|
const color = getColorForValue(data, data.display.numeric);
|
||||||
console.log(color);
|
|
||||||
if (color) {
|
if (color) {
|
||||||
$panelContainer.css('background-color', color);
|
$panelContainer.css('background-color', color);
|
||||||
if (scope.fullscreen) {
|
if (scope.fullscreen) {
|
||||||
@@ -729,4 +669,59 @@ function getColorForValue(data: any, value: number) {
|
|||||||
return _.first(data.colorMap);
|
return _.first(data.colorMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------
|
||||||
|
// Private utility functions
|
||||||
|
// Somethign like this should be avaliable in a
|
||||||
|
// DataFrame[] abstraction helper
|
||||||
|
//------------------------------------------------
|
||||||
|
|
||||||
|
interface FrameInfo {
|
||||||
|
firstTimeField?: Field;
|
||||||
|
frame: DataFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FieldInfo {
|
||||||
|
field: Field;
|
||||||
|
frame: FrameInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DistinctFieldsInfo {
|
||||||
|
first?: FieldInfo;
|
||||||
|
byName: KeyValue<FieldInfo>;
|
||||||
|
names: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDistinctNames(data: DataFrame[]): DistinctFieldsInfo {
|
||||||
|
const distinct: DistinctFieldsInfo = {
|
||||||
|
byName: {},
|
||||||
|
names: [],
|
||||||
|
};
|
||||||
|
for (const frame of data) {
|
||||||
|
const info: FrameInfo = { frame };
|
||||||
|
for (const field of frame.fields) {
|
||||||
|
if (field.type === FieldType.time) {
|
||||||
|
if (!info.firstTimeField) {
|
||||||
|
info.firstTimeField = field;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const f = { field, frame: info };
|
||||||
|
if (!distinct.first) {
|
||||||
|
distinct.first = f;
|
||||||
|
}
|
||||||
|
let t = field.config.title;
|
||||||
|
if (t && !distinct.byName[t]) {
|
||||||
|
distinct.byName[t] = f;
|
||||||
|
distinct.names.push(t);
|
||||||
|
}
|
||||||
|
t = field.name;
|
||||||
|
if (t && !distinct.byName[t]) {
|
||||||
|
distinct.byName[t] = f;
|
||||||
|
distinct.names.push(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return distinct;
|
||||||
|
}
|
||||||
|
|
||||||
export { SingleStatCtrl, SingleStatCtrl as PanelCtrl, getColorForValue };
|
export { SingleStatCtrl, SingleStatCtrl as PanelCtrl, getColorForValue };
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
import { SingleStatCtrl } from '../module';
|
import { SingleStatCtrl, ShowData } from '../module';
|
||||||
import { dateTime } from '@grafana/data';
|
import { dateTime, ReducerID } from '@grafana/data';
|
||||||
import { LinkSrv } from 'app/features/panel/panellinks/link_srv';
|
import { LinkSrv } from 'app/features/panel/panellinks/link_srv';
|
||||||
|
import { LegacyResponseData } from '@grafana/ui';
|
||||||
|
|
||||||
|
interface TestContext {
|
||||||
|
ctrl: SingleStatCtrl;
|
||||||
|
input: LegacyResponseData[];
|
||||||
|
data: Partial<ShowData>;
|
||||||
|
setup: (setupFunc: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
describe('SingleStatCtrl', () => {
|
describe('SingleStatCtrl', () => {
|
||||||
const ctx = {} as any;
|
const ctx: TestContext = {} as TestContext;
|
||||||
const epoch = 1505826363746;
|
const epoch = 1505826363746;
|
||||||
Date.now = () => epoch;
|
Date.now = () => epoch;
|
||||||
|
|
||||||
@@ -37,7 +45,7 @@ describe('SingleStatCtrl', () => {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
ctx.ctrl = new SingleStatCtrl($scope, $injector, {} as LinkSrv, $sanitize);
|
ctx.ctrl = new SingleStatCtrl($scope, $injector, {} as LinkSrv, $sanitize);
|
||||||
setupFunc();
|
setupFunc();
|
||||||
ctx.ctrl.onDataReceived(ctx.data);
|
ctx.ctrl.onDataReceived(ctx.input);
|
||||||
ctx.data = ctx.ctrl.data;
|
ctx.data = ctx.ctrl.data;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -46,40 +54,38 @@ describe('SingleStatCtrl', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
singleStatScenario('with defaults', (ctx: any) => {
|
singleStatScenario('with defaults', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 1], [20, 2]] }];
|
ctx.input = [{ target: 'test.cpu1', datapoints: [[10, 1], [20, 2]] }];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should use series avg as default main value', () => {
|
it('Should use series avg as default main value', () => {
|
||||||
expect(ctx.data.value).toBe(15);
|
expect(ctx.data.value).toBe(15);
|
||||||
expect(ctx.data.valueRounded).toBe(15);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set formatted falue', () => {
|
it('should set formatted falue', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('15');
|
expect(ctx.data.display.text).toBe('15');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('showing serie name instead of value', (ctx: any) => {
|
singleStatScenario('showing serie name instead of value', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 1], [20, 2]] }];
|
ctx.input = [{ target: 'test.cpu1', datapoints: [[10, 1], [20, 2]] }];
|
||||||
ctx.ctrl.panel.valueName = 'name';
|
ctx.ctrl.panel.valueName = 'name';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should use series avg as default main value', () => {
|
it('Should use series avg as default main value', () => {
|
||||||
expect(ctx.data.value).toBe(0);
|
expect(ctx.data.value).toBe('test.cpu1');
|
||||||
expect(ctx.data.valueRounded).toBe(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set formatted value', () => {
|
it('should set formatted value', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('test.cpu1');
|
expect(ctx.data.display.text).toBe('test.cpu1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('showing last iso time instead of value', (ctx: any) => {
|
singleStatScenario('showing last iso time instead of value', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }];
|
ctx.input = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }];
|
||||||
ctx.ctrl.panel.valueName = 'last_time';
|
ctx.ctrl.panel.valueName = 'last_time';
|
||||||
ctx.ctrl.panel.format = 'dateTimeAsIso';
|
ctx.ctrl.panel.format = 'dateTimeAsIso';
|
||||||
ctx.ctrl.dashboard.isTimezoneUtc = () => false;
|
ctx.ctrl.dashboard.isTimezoneUtc = () => false;
|
||||||
@@ -87,30 +93,29 @@ describe('SingleStatCtrl', () => {
|
|||||||
|
|
||||||
it('Should use time instead of value', () => {
|
it('Should use time instead of value', () => {
|
||||||
expect(ctx.data.value).toBe(1505634997920);
|
expect(ctx.data.value).toBe(1505634997920);
|
||||||
expect(ctx.data.valueRounded).toBe(1505634997920);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set formatted value', () => {
|
it('should set formatted value', () => {
|
||||||
expect(dateTime(ctx.data.valueFormatted).valueOf()).toBe(1505634997000);
|
expect(dateTime(ctx.data.display.text).valueOf()).toBe(1505634997000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('showing last iso time instead of value (in UTC)', (ctx: any) => {
|
singleStatScenario('showing last iso time instead of value (in UTC)', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 5000]] }];
|
ctx.input = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 5000]] }];
|
||||||
ctx.ctrl.panel.valueName = 'last_time';
|
ctx.ctrl.panel.valueName = 'last_time';
|
||||||
ctx.ctrl.panel.format = 'dateTimeAsIso';
|
ctx.ctrl.panel.format = 'dateTimeAsIso';
|
||||||
ctx.ctrl.dashboard.isTimezoneUtc = () => true;
|
ctx.ctrl.dashboard.isTimezoneUtc = () => true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set value', () => {
|
it('should set value', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('1970-01-01 00:00:05');
|
expect(ctx.data.display.text).toBe('1970-01-01 00:00:05');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('showing last us time instead of value', (ctx: any) => {
|
singleStatScenario('showing last us time instead of value', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }];
|
ctx.input = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }];
|
||||||
ctx.ctrl.panel.valueName = 'last_time';
|
ctx.ctrl.panel.valueName = 'last_time';
|
||||||
ctx.ctrl.panel.format = 'dateTimeAsUS';
|
ctx.ctrl.panel.format = 'dateTimeAsUS';
|
||||||
ctx.ctrl.dashboard.isTimezoneUtc = () => false;
|
ctx.ctrl.dashboard.isTimezoneUtc = () => false;
|
||||||
@@ -118,79 +123,76 @@ describe('SingleStatCtrl', () => {
|
|||||||
|
|
||||||
it('Should use time instead of value', () => {
|
it('Should use time instead of value', () => {
|
||||||
expect(ctx.data.value).toBe(1505634997920);
|
expect(ctx.data.value).toBe(1505634997920);
|
||||||
expect(ctx.data.valueRounded).toBe(1505634997920);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set formatted value', () => {
|
it('should set formatted value', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe(dateTime(1505634997920).format('MM/DD/YYYY h:mm:ss a'));
|
expect(ctx.data.display.text).toBe(dateTime(1505634997920).format('MM/DD/YYYY h:mm:ss a'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('showing last us time instead of value (in UTC)', (ctx: any) => {
|
singleStatScenario('showing last us time instead of value (in UTC)', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 5000]] }];
|
ctx.input = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 5000]] }];
|
||||||
ctx.ctrl.panel.valueName = 'last_time';
|
ctx.ctrl.panel.valueName = 'last_time';
|
||||||
ctx.ctrl.panel.format = 'dateTimeAsUS';
|
ctx.ctrl.panel.format = 'dateTimeAsUS';
|
||||||
ctx.ctrl.dashboard.isTimezoneUtc = () => true;
|
ctx.ctrl.dashboard.isTimezoneUtc = () => true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set formatted value', () => {
|
it('should set formatted value', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('01/01/1970 12:00:05 am');
|
expect(ctx.data.display.text).toBe('01/01/1970 12:00:05 am');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('showing last time from now instead of value', (ctx: any) => {
|
singleStatScenario('showing last time from now instead of value', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }];
|
ctx.input = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }];
|
||||||
ctx.ctrl.panel.valueName = 'last_time';
|
ctx.ctrl.panel.valueName = 'last_time';
|
||||||
ctx.ctrl.panel.format = 'dateTimeFromNow';
|
ctx.ctrl.panel.format = 'dateTimeFromNow';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should use time instead of value', () => {
|
it('Should use time instead of value', () => {
|
||||||
expect(ctx.data.value).toBe(1505634997920);
|
expect(ctx.data.value).toBe(1505634997920);
|
||||||
expect(ctx.data.valueRounded).toBe(1505634997920);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set formatted value', () => {
|
it('should set formatted value', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('2 days ago');
|
expect(ctx.data.display.text).toBe('2 days ago');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('showing last time from now instead of value (in UTC)', (ctx: any) => {
|
singleStatScenario('showing last time from now instead of value (in UTC)', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }];
|
ctx.input = [{ target: 'test.cpu1', datapoints: [[10, 12], [20, 1505634997920]] }];
|
||||||
ctx.ctrl.panel.valueName = 'last_time';
|
ctx.ctrl.panel.valueName = 'last_time';
|
||||||
ctx.ctrl.panel.format = 'dateTimeFromNow';
|
ctx.ctrl.panel.format = 'dateTimeFromNow';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set formatted value', () => {
|
it('should set formatted value', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('2 days ago');
|
expect(ctx.data.display.text).toBe('2 days ago');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario(
|
singleStatScenario(
|
||||||
'MainValue should use same number for decimals as displayed when checking thresholds',
|
'MainValue should use same number for decimals as displayed when checking thresholds',
|
||||||
(ctx: any) => {
|
(ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = [{ target: 'test.cpu1', datapoints: [[99.999, 1], [99.99999, 2]] }];
|
ctx.input = [{ target: 'test.cpu1', datapoints: [[99.999, 1], [99.99999, 2]] }];
|
||||||
ctx.ctrl.panel.valueName = 'avg';
|
ctx.ctrl.panel.valueName = 'avg';
|
||||||
ctx.ctrl.panel.format = 'none';
|
ctx.ctrl.panel.format = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be rounded', () => {
|
it('Should be rounded', () => {
|
||||||
expect(ctx.data.value).toBe(99.999495);
|
expect(ctx.data.value).toBe(99.999495);
|
||||||
expect(ctx.data.valueRounded).toBe(100);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set formatted value', () => {
|
it('should set formatted value', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('100');
|
expect(ctx.data.display.text).toBe('100');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
singleStatScenario('When value to text mapping is specified', (ctx: any) => {
|
singleStatScenario('When value to text mapping is specified', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = [{ target: 'test.cpu1', datapoints: [[9.9, 1]] }];
|
ctx.input = [{ target: 'test.cpu1', datapoints: [[9.9, 1]] }];
|
||||||
ctx.ctrl.panel.valueMaps = [{ value: '10', text: 'OK' }];
|
ctx.ctrl.panel.valueMaps = [{ value: '10', text: 'OK' }];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -198,36 +200,32 @@ describe('SingleStatCtrl', () => {
|
|||||||
expect(ctx.data.value).toBe(9.9);
|
expect(ctx.data.value).toBe(9.9);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('round should be rounded up', () => {
|
|
||||||
expect(ctx.data.valueRounded).toBe(10);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should replace value with text', () => {
|
it('Should replace value with text', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('OK');
|
expect(ctx.data.display.text).toBe('OK');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('When range to text mapping is specified for first range', (ctx: any) => {
|
singleStatScenario('When range to text mapping is specified for first range', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = [{ target: 'test.cpu1', datapoints: [[41, 50]] }];
|
ctx.input = [{ target: 'test.cpu1', datapoints: [[41, 50]] }];
|
||||||
ctx.ctrl.panel.mappingType = 2;
|
ctx.ctrl.panel.mappingType = 2;
|
||||||
ctx.ctrl.panel.rangeMaps = [{ from: '10', to: '50', text: 'OK' }, { from: '51', to: '100', text: 'NOT OK' }];
|
ctx.ctrl.panel.rangeMaps = [{ from: '10', to: '50', text: 'OK' }, { from: '51', to: '100', text: 'NOT OK' }];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should replace value with text OK', () => {
|
it('Should replace value with text OK', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('OK');
|
expect(ctx.data.display.text).toBe('OK');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('When range to text mapping is specified for other ranges', (ctx: any) => {
|
singleStatScenario('When range to text mapping is specified for other ranges', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = [{ target: 'test.cpu1', datapoints: [[65, 75]] }];
|
ctx.input = [{ target: 'test.cpu1', datapoints: [[65, 75]] }];
|
||||||
ctx.ctrl.panel.mappingType = 2;
|
ctx.ctrl.panel.mappingType = 2;
|
||||||
ctx.ctrl.panel.rangeMaps = [{ from: '10', to: '50', text: 'OK' }, { from: '51', to: '100', text: 'NOT OK' }];
|
ctx.ctrl.panel.rangeMaps = [{ from: '10', to: '50', text: 'OK' }, { from: '51', to: '100', text: 'NOT OK' }];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should replace value with text NOT OK', () => {
|
it('Should replace value with text NOT OK', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('NOT OK');
|
expect(ctx.data.display.text).toBe('NOT OK');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -240,9 +238,9 @@ describe('SingleStatCtrl', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
singleStatScenario('with default values', (ctx: any) => {
|
singleStatScenario('with default values', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = tableData;
|
ctx.input = tableData;
|
||||||
ctx.ctrl.panel = {
|
ctx.ctrl.panel = {
|
||||||
emit: () => {},
|
emit: () => {},
|
||||||
};
|
};
|
||||||
@@ -252,17 +250,16 @@ describe('SingleStatCtrl', () => {
|
|||||||
|
|
||||||
it('Should use first rows value as default main value', () => {
|
it('Should use first rows value as default main value', () => {
|
||||||
expect(ctx.data.value).toBe(15);
|
expect(ctx.data.value).toBe(15);
|
||||||
expect(ctx.data.valueRounded).toBe(15);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set formatted value', () => {
|
it('should set formatted value', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('15');
|
expect(ctx.data.display.text).toBe('15');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('When table data has multiple columns', (ctx: any) => {
|
singleStatScenario('When table data has multiple columns', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = tableData;
|
ctx.input = tableData;
|
||||||
ctx.ctrl.panel.tableColumn = '';
|
ctx.ctrl.panel.tableColumn = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -273,29 +270,28 @@ describe('SingleStatCtrl', () => {
|
|||||||
|
|
||||||
singleStatScenario(
|
singleStatScenario(
|
||||||
'MainValue should use same number for decimals as displayed when checking thresholds',
|
'MainValue should use same number for decimals as displayed when checking thresholds',
|
||||||
(ctx: any) => {
|
(ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = tableData;
|
ctx.input = tableData;
|
||||||
ctx.data[0].rows[0] = [1492759673649, 'ignore1', 99.99999, 'ignore2'];
|
ctx.input[0].rows[0] = [1492759673649, 'ignore1', 99.99999, 'ignore2'];
|
||||||
ctx.ctrl.panel.mappingType = 0;
|
ctx.ctrl.panel.mappingType = 0;
|
||||||
ctx.ctrl.panel.tableColumn = 'mean';
|
ctx.ctrl.panel.tableColumn = 'mean';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be rounded', () => {
|
it('Should be rounded', () => {
|
||||||
expect(ctx.data.value).toBe(99.99999);
|
expect(ctx.data.value).toBe(99.99999);
|
||||||
expect(ctx.data.valueRounded).toBe(100);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set formatted falue', () => {
|
it('should set formatted falue', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('100');
|
expect(ctx.data.display.text).toBe('100');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
singleStatScenario('When value to text mapping is specified', (ctx: any) => {
|
singleStatScenario('When value to text mapping is specified', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = tableData;
|
ctx.input = tableData;
|
||||||
ctx.data[0].rows[0] = [1492759673649, 'ignore1', 9.9, 'ignore2'];
|
ctx.input[0].rows[0] = [1492759673649, 'ignore1', 9.9, 'ignore2'];
|
||||||
ctx.ctrl.panel.mappingType = 2;
|
ctx.ctrl.panel.mappingType = 2;
|
||||||
ctx.ctrl.panel.tableColumn = 'mean';
|
ctx.ctrl.panel.tableColumn = 'mean';
|
||||||
ctx.ctrl.panel.valueMaps = [{ value: '10', text: 'OK' }];
|
ctx.ctrl.panel.valueMaps = [{ value: '10', text: 'OK' }];
|
||||||
@@ -305,59 +301,60 @@ describe('SingleStatCtrl', () => {
|
|||||||
expect(ctx.data.value).toBe(9.9);
|
expect(ctx.data.value).toBe(9.9);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('round should be rounded up', () => {
|
// it('round should be rounded up', () => {
|
||||||
expect(ctx.data.valueRounded).toBe(10);
|
// expect(ctx.data.valueRounded).toBe(10);
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('Should replace value with text', () => {
|
it('Should replace value with text', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('OK');
|
expect(ctx.data.display.text).toBe('OK');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('When range to text mapping is specified for first range', (ctx: any) => {
|
singleStatScenario('When range to text mapping is specified for first range', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = tableData;
|
ctx.input = tableData;
|
||||||
ctx.data[0].rows[0] = [1492759673649, 'ignore1', 41, 'ignore2'];
|
ctx.input[0].rows[0] = [1492759673649, 'ignore1', 41, 'ignore2'];
|
||||||
ctx.ctrl.panel.tableColumn = 'mean';
|
ctx.ctrl.panel.tableColumn = 'mean';
|
||||||
ctx.ctrl.panel.mappingType = 2;
|
ctx.ctrl.panel.mappingType = 2;
|
||||||
ctx.ctrl.panel.rangeMaps = [{ from: '10', to: '50', text: 'OK' }, { from: '51', to: '100', text: 'NOT OK' }];
|
ctx.ctrl.panel.rangeMaps = [{ from: '10', to: '50', text: 'OK' }, { from: '51', to: '100', text: 'NOT OK' }];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should replace value with text OK', () => {
|
it('Should replace value with text OK', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('OK');
|
expect(ctx.data.display.text).toBe('OK');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('When range to text mapping is specified for other ranges', (ctx: any) => {
|
singleStatScenario('When range to text mapping is specified for other ranges', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = tableData;
|
ctx.input = tableData;
|
||||||
ctx.data[0].rows[0] = [1492759673649, 'ignore1', 65, 'ignore2'];
|
ctx.input[0].rows[0] = [1492759673649, 'ignore1', 65, 'ignore2'];
|
||||||
ctx.ctrl.panel.tableColumn = 'mean';
|
ctx.ctrl.panel.tableColumn = 'mean';
|
||||||
ctx.ctrl.panel.mappingType = 2;
|
ctx.ctrl.panel.mappingType = 2;
|
||||||
ctx.ctrl.panel.rangeMaps = [{ from: '10', to: '50', text: 'OK' }, { from: '51', to: '100', text: 'NOT OK' }];
|
ctx.ctrl.panel.rangeMaps = [{ from: '10', to: '50', text: 'OK' }, { from: '51', to: '100', text: 'NOT OK' }];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should replace value with text NOT OK', () => {
|
it('Should replace value with text NOT OK', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('NOT OK');
|
expect(ctx.data.display.text).toBe('NOT OK');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('When value is string', (ctx: any) => {
|
singleStatScenario('When value is string', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = tableData;
|
ctx.input = tableData;
|
||||||
ctx.data[0].rows[0] = [1492759673649, 'ignore1', 65, 'ignore2'];
|
ctx.input[0].rows[0] = [1492759673649, 'ignore1', 65, 'ignore2'];
|
||||||
ctx.ctrl.panel.tableColumn = 'test1';
|
ctx.ctrl.panel.tableColumn = 'test1';
|
||||||
|
ctx.ctrl.panel.valueName = ReducerID.first;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should replace value with text NOT OK', () => {
|
it('Should replace value with text NOT OK', () => {
|
||||||
expect(ctx.data.valueFormatted).toBe('ignore1');
|
expect(ctx.data.display.text).toBe('ignore1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
singleStatScenario('When value is zero', (ctx: any) => {
|
singleStatScenario('When value is zero', (ctx: TestContext) => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.data = tableData;
|
ctx.input = tableData;
|
||||||
ctx.data[0].rows[0] = [1492759673649, 'ignore1', 0, 'ignore2'];
|
ctx.input[0].rows[0] = [1492759673649, 'ignore1', 0, 'ignore2'];
|
||||||
ctx.ctrl.panel.tableColumn = 'mean';
|
ctx.ctrl.panel.tableColumn = 'mean';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user