mirror of
https://github.com/grafana/grafana.git
synced 2024-11-30 20:54:22 -06:00
Refactor: flexible Y-Min and Y-Max settings (#6051)
* Feature: Flexible Y-Min and Y-Max settings. Y-Min and Y-Max is now string. New usage for both Y-Min and Y-Max (Where X is a real number): >X Y-Max/Y-Max is auto if data is above X else X <X Y-Max/Y-Min is auto if data is below X else X =X Y-Max/Y-Min is scaled to current value +/- X ~X Y-Max/Y-Min is caled to average value +/- X Example: Y-Min: <100 Y-Max: >200 If all points are within 100 and 200: Y-Min is 100 and Y-Max is 200 If some points are above 200: Y-Min is 100 and Y-Max is auto If some points are below 100: Y-Min is auto and Y-Max is 200 if some points are below 100 and above 200: Y-Min and Y-Max is auto Tests for new settings added * Use parseFloat instead of implicit conversion * feat(flexible_y-min/max): refactor. * feat(flexible_y-min/max): added more tests.
This commit is contained in:
parent
a95808a7c3
commit
4fa22e2158
@ -339,6 +339,75 @@ function (angular, $, moment, _, kbn, GraphTooltip, thresholdManExports) {
|
||||
};
|
||||
}
|
||||
|
||||
//Override min/max to provide more flexible autoscaling
|
||||
function autoscaleSpanOverride(yaxis, data, options) {
|
||||
var expr;
|
||||
if (yaxis.min != null && data != null) {
|
||||
expr = parseThresholdExpr(yaxis.min);
|
||||
options.min = autoscaleYAxisMin(expr, data.stats);
|
||||
}
|
||||
if (yaxis.max != null && data != null) {
|
||||
expr = parseThresholdExpr(yaxis.max);
|
||||
options.max = autoscaleYAxisMax(expr, data.stats);
|
||||
}
|
||||
}
|
||||
|
||||
function parseThresholdExpr(expr) {
|
||||
var match, operator, value, precision;
|
||||
match = expr.match(/\s*([<=>~]*)\W*(\d+(\.\d+)?)/);
|
||||
if (match) {
|
||||
operator = match[1];
|
||||
value = parseFloat(match[2]);
|
||||
//Precision based on input
|
||||
precision = match[3] ? match[3].length - 1 : 0;
|
||||
return {
|
||||
operator: operator,
|
||||
value: value,
|
||||
precision: precision
|
||||
};
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function autoscaleYAxisMax(expr, dataStats) {
|
||||
var operator = expr.operator,
|
||||
value = expr.value,
|
||||
precision = expr.precision;
|
||||
if (operator === ">") {
|
||||
return dataStats.max < value ? value : null;
|
||||
} else if (operator === "<") {
|
||||
return dataStats.max > value ? value : null;
|
||||
} else if (operator === "~") {
|
||||
return kbn.roundValue(dataStats.avg + value, precision);
|
||||
} else if (operator === "=") {
|
||||
return kbn.roundValue(dataStats.current + value, precision);
|
||||
} else if (!operator && !isNaN(value)) {
|
||||
return kbn.roundValue(value, precision);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function autoscaleYAxisMin(expr, dataStats) {
|
||||
var operator = expr.operator,
|
||||
value = expr.value,
|
||||
precision = expr.precision;
|
||||
if (operator === ">") {
|
||||
return dataStats.min < value ? value : null;
|
||||
} else if (operator === "<") {
|
||||
return dataStats.min > value ? value : null;
|
||||
} else if (operator === "~") {
|
||||
return kbn.roundValue(dataStats.avg - value, precision);
|
||||
} else if (operator === "=") {
|
||||
return kbn.roundValue(dataStats.current - value, precision);
|
||||
} else if (!operator && !isNaN(value)) {
|
||||
return kbn.roundValue(value, precision);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function configureAxisOptions(data, options) {
|
||||
var defaults = {
|
||||
position: 'left',
|
||||
@ -349,6 +418,7 @@ function (angular, $, moment, _, kbn, GraphTooltip, thresholdManExports) {
|
||||
max: panel.percentage && panel.stack ? 100 : panel.yaxes[0].max,
|
||||
};
|
||||
|
||||
autoscaleSpanOverride(panel.yaxes[0], data[0], defaults);
|
||||
options.yaxes.push(defaults);
|
||||
|
||||
if (_.find(data, {yaxis: 2})) {
|
||||
@ -359,6 +429,7 @@ function (angular, $, moment, _, kbn, GraphTooltip, thresholdManExports) {
|
||||
secondY.position = 'right';
|
||||
secondY.min = panel.yaxes[1].min;
|
||||
secondY.max = panel.percentage && panel.stack ? 100 : panel.yaxes[1].max;
|
||||
autoscaleSpanOverride(panel.yaxes[1], data[1], secondY);
|
||||
options.yaxes.push(secondY);
|
||||
|
||||
applyLogScale(options.yaxes[1], data);
|
||||
|
@ -218,4 +218,99 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
|
||||
}, 10);
|
||||
|
||||
graphScenario('when using flexible Y-Min and Y-Max settings', function(ctx) {
|
||||
describe('and Y-Min is <100 and Y-Max is >200 and values within range', function() {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.yaxes[0].min = '<100';
|
||||
ctrl.panel.yaxes[0].max = '>200';
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[120,10],[160,20]],
|
||||
alias: 'series1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should set min to 100 and max to 200', function() {
|
||||
expect(ctx.plotOptions.yaxes[0].min).to.be(100);
|
||||
expect(ctx.plotOptions.yaxes[0].max).to.be(200);
|
||||
});
|
||||
});
|
||||
describe('and Y-Min is <100 and Y-Max is >200 and values outside range', function() {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.yaxes[0].min = '<100';
|
||||
ctrl.panel.yaxes[0].max = '>200';
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[99,10],[201,20]],
|
||||
alias: 'series1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should set min to auto and max to auto', function() {
|
||||
expect(ctx.plotOptions.yaxes[0].min).to.be(null);
|
||||
expect(ctx.plotOptions.yaxes[0].max).to.be(null);
|
||||
});
|
||||
});
|
||||
describe('and Y-Min is =10.5 and Y-Max is =10.5', function() {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.yaxes[0].min = '=10.5';
|
||||
ctrl.panel.yaxes[0].max = '=10.5';
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[100,10],[120,20], [110,30]],
|
||||
alias: 'series1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should set min to last value + 10.5 and max to last value + 10.5', function() {
|
||||
expect(ctx.plotOptions.yaxes[0].min).to.be(99.5);
|
||||
expect(ctx.plotOptions.yaxes[0].max).to.be(120.5);
|
||||
});
|
||||
});
|
||||
describe('and Y-Min is ~10.5 and Y-Max is ~10.5', function() {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.yaxes[0].min = '~10.5';
|
||||
ctrl.panel.yaxes[0].max = '~10.5';
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[102,10],[104,20], [110,30]], //Also checks precision
|
||||
alias: 'series1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should set min to average value + 10.5 and max to average value + 10.5', function() {
|
||||
expect(ctx.plotOptions.yaxes[0].min).to.be(94.8);
|
||||
expect(ctx.plotOptions.yaxes[0].max).to.be(115.8);
|
||||
});
|
||||
});
|
||||
});
|
||||
graphScenario('when using regular Y-Min and Y-Max settings', function(ctx) {
|
||||
describe('and Y-Min is 100 and Y-Max is 200', function() {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.yaxes[0].min = '100';
|
||||
ctrl.panel.yaxes[0].max = '200';
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[120,10],[160,20]],
|
||||
alias: 'series1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should set min to 100 and max to 200', function() {
|
||||
expect(ctx.plotOptions.yaxes[0].min).to.be(100);
|
||||
expect(ctx.plotOptions.yaxes[0].max).to.be(200);
|
||||
});
|
||||
});
|
||||
describe('and Y-Min is 0 and Y-Max is 0', function() {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.yaxes[0].min = '0';
|
||||
ctrl.panel.yaxes[0].max = '0';
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[120,10],[160,20]],
|
||||
alias: 'series1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should set min to 0 and max to 0', function() {
|
||||
expect(ctx.plotOptions.yaxes[0].min).to.be(0);
|
||||
expect(ctx.plotOptions.yaxes[0].max).to.be(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -22,11 +22,11 @@
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-10">
|
||||
<label class="gf-form-label width-5">Y-Min</label>
|
||||
<input type="number" class="gf-form-input" placeholder="auto" empty-to-null ng-model="yaxis.min" ng-change="ctrl.render()" ng-model-onblur>
|
||||
<input type="text" class="gf-form-input" placeholder="auto" empty-to-null ng-model="yaxis.min" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</div>
|
||||
<div class="gf-form max-width-10">
|
||||
<label class="gf-form-label width-5">Y-Max</label>
|
||||
<input type="number" class="gf-form-input" placeholder="auto" empty-to-null ng-model="yaxis.max" ng-change="ctrl.render()" ng-model-onblur>
|
||||
<input type="text" class="gf-form-input" placeholder="auto" empty-to-null ng-model="yaxis.max" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user