diff --git a/public/app/components/kbn.js b/public/app/components/kbn.js index 973b28f40fd..ef1e45c23d3 100644 --- a/public/app/components/kbn.js +++ b/public/app/components/kbn.js @@ -404,6 +404,11 @@ function($, _, moment) { kbn.valueFormats.velocitymph = function(value, decimals) { return kbn.toFixed(value, decimals) + ' mph'; }; kbn.valueFormats.velocityknot = function(value, decimals) { return kbn.toFixed(value, decimals) + ' kn'; }; + kbn.roundValue = function (num, decimals) { + var n = Math.pow(10, decimals); + return Math.round((n * num).toFixed(decimals)) / n; + }; + kbn.toFixedScaled = function(value, decimals, scaledDecimals, additionalDecimals, ext) { if (scaledDecimals === null) { return kbn.toFixed(value, decimals) + ext; diff --git a/public/app/panels/singlestat/module.js b/public/app/panels/singlestat/module.js index a43ac2d0b69..3c698bfd553 100644 --- a/public/app/panels/singlestat/module.js +++ b/public/app/panels/singlestat/module.js @@ -170,17 +170,7 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) { $scope.render = function() { var data = {}; - if (!$scope.series || $scope.series.length === 0) { - data.flotpairs = []; - data.mainValue = Number.NaN; - data.mainValueFormated = $scope.getFormatedValue(null); - } - else { - var series = $scope.series[0]; - data.mainValue = series.stats[$scope.panel.valueName]; - data.mainValueFormated = $scope.getFormatedValue(data.mainValue); - data.flotpairs = series.flotpairs; - } + $scope.setValues(data); data.thresholds = $scope.panel.thresholds.split(',').map(function(strVale) { return Number(strVale.trim()); @@ -192,32 +182,41 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) { $scope.$broadcast('render'); }; - $scope.getFormatedValue = function(mainValue) { + $scope.setValues = function(data) { + data.flotpairs = []; + + if ($scope.series && $scope.series.length > 0) { + data.value = $scope.series[0].stats[$scope.panel.valueName]; + data.flotpairs = $scope.series[0].flotpairs; + } // first check value to text mappings for(var i = 0; i < $scope.panel.valueMaps.length; i++) { var map = $scope.panel.valueMaps[i]; // special null case if (map.value === 'null') { - if (mainValue === null || mainValue === void 0) { - return map.text; + if (data.value === null || data.value === void 0) { + data.valueFormated = map.text; + return; } continue; } // value/number to text mapping var value = parseFloat(map.value); - if (value === mainValue) { - return map.text; + if (value === data.value) { + data.valueFormated = map.text; + return; } } - if (mainValue === null || mainValue === void 0) { - return "no value"; + if (data.value === null || data.value === void 0) { + data.valueFormated = "no value"; } - var decimalInfo = $scope.getDecimalsForValue(mainValue); + var decimalInfo = $scope.getDecimalsForValue(data.value); var formatFunc = kbn.valueFormats[$scope.panel.format]; - return formatFunc(mainValue, decimalInfo.decimals, decimalInfo.scaledDecimals); + data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals); + data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals); }; $scope.removeValueMap = function(map) { diff --git a/public/app/panels/singlestat/singleStatPanel.js b/public/app/panels/singlestat/singleStatPanel.js index 49c248bc5cd..27a0167f05b 100644 --- a/public/app/panels/singlestat/singleStatPanel.js +++ b/public/app/panels/singlestat/singleStatPanel.js @@ -73,7 +73,7 @@ function (angular, app, _, $) { if (panel.prefix) { body += getSpan('singlestat-panel-prefix', panel.prefixFontSize, scope.panel.prefix); } - var value = applyColoringThresholds(data.mainValue, data.mainValueFormated); + var value = applyColoringThresholds(data.valueRounded, data.valueFormated); body += getSpan('singlestat-panel-value', panel.valueFontSize, value); if (panel.postfix) { body += getSpan('singlestat-panel-postfix', panel.postfixFontSize, panel.postfix); } @@ -147,8 +147,8 @@ function (angular, app, _, $) { var body = getBigValueHtml(); - if (panel.colorBackground && !isNaN(data.mainValue)) { - var color = getColorForValue(data.mainValue); + if (panel.colorBackground && !isNaN(data.valueRounded)) { + var color = getColorForValue(data.valueRounded); if (color) { $panelContainer.css('background-color', color); if (scope.fullscreen) { diff --git a/public/test/specs/singlestat-specs.js b/public/test/specs/singlestat-specs.js new file mode 100644 index 00000000000..a8cf0bc34c1 --- /dev/null +++ b/public/test/specs/singlestat-specs.js @@ -0,0 +1,85 @@ +define([ + 'helpers', + 'features/panel/panelSrv', + 'features/panel/panelHelper', + 'panels/singlestat/module' +], function(helpers) { + 'use strict'; + + describe('SingleStatCtrl', function() { + var ctx = new helpers.ControllerTestContext(); + + function singleStatScenario(desc, func) { + + describe(desc, function() { + + ctx.setup = function (setupFunc) { + + beforeEach(module('grafana.services')); + beforeEach(module('grafana.panels.singlestat')); + + beforeEach(ctx.providePhase()); + beforeEach(ctx.createControllerPhase('SingleStatCtrl')); + + beforeEach(function() { + setupFunc(); + ctx.datasource.query = sinon.stub().returns(ctx.$q.when({ + data: [ { target: 'test.cpu1', datapoints: ctx.datapoints } ] + })); + + ctx.scope.refreshData(ctx.datasource); + ctx.scope.$digest(); + ctx.data = ctx.scope.data; + }); + }; + + func(ctx); + }); + } + + singleStatScenario('with defaults', function(ctx) { + ctx.setup(function() { + ctx.datapoints = [[10,1], [20,2]]; + }); + + it('Should use series avg as default main value', function() { + expect(ctx.data.value).to.be(15); + expect(ctx.data.valueRounded).to.be(15); + }); + + it('should set formated falue', function() { + expect(ctx.data.valueFormated).to.be('15'); + }); + }); + + singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) { + ctx.setup(function() { + ctx.datapoints = [[99.999,1], [99.99999,2]]; + }); + + it('Should be rounded', function() { + expect(ctx.data.value).to.be(99.999495); + expect(ctx.data.valueRounded).to.be(100); + }); + + it('should set formated falue', function() { + expect(ctx.data.valueFormated).to.be('100'); + }); + }); + + singleStatScenario('When value to text mapping is specified', function(ctx) { + ctx.setup(function() { + ctx.datapoints = [[10,1]]; + ctx.scope.panel.valueMaps = [{value: '10', text: 'OK'}]; + }); + + it('Should replace value with text', function() { + expect(ctx.data.value).to.be(10); + expect(ctx.data.valueFormated).to.be('OK'); + }); + + }); + + }); +}); + diff --git a/public/test/test-main.js b/public/test/test-main.js index f96635e2209..c49c47429f9 100644 --- a/public/test/test-main.js +++ b/public/test/test-main.js @@ -140,6 +140,7 @@ require([ 'specs/dashboardSrv-specs', 'specs/dashboardViewStateSrv-specs', 'specs/soloPanelCtrl-specs', + 'specs/singlestat-specs', 'specs/dynamicDashboardSrv-specs', 'specs/unsavedChangesSrv-specs', ];