Visualization - Scale to Data Bounds with a value (#38774) (#39246)

Checking the Scale to Data Bound option may cause that the bar, which value equals to the lower bounds is hidden. To avoid that, you can define bottom margin. Via bottom margin you specify a value, which lowers the lower bounds when displaying the plot.

close #38536
This commit is contained in:
Marco Vettorello 2019-06-19 14:58:27 +02:00 committed by GitHub
parent 8ff7ea0b1e
commit 3cbf5b8a11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 148 additions and 4 deletions

View file

@ -62,7 +62,11 @@ Style all the Y-axes of the chart.
*Labels - Rotate*:::: You can enter the number in degrees for how much you want to rotate labels
*Labels - Truncate*:::: You can enter the size in pixels to which the label is truncated
*Scale to Data Bounds*:::: The default Y-axis bounds are zero and the maximum value returned in the data. Check
this box to change both upper and lower bounds to match the values returned in the data.
this box to change both upper and lower bounds to match the values returned in the data.
Checking this option may cause that the bar, which value equals to the lower bounds/
upper bounds (in case only negative values are depicted) is hidden.
To avoid that, you can define bounds margin. Via bounds margin you specify a value,
which decreases/increases the lower/upper bounds when displaying the plot.
*Custom Extents*:::: You can define custom minimum and maximum for each axis
[float]

View file

@ -255,7 +255,33 @@
i18n-default-message="Scale to Data Bounds"
></label>
<div class="visEditorSidebar__formControl">
<input class="kuiCheckBox" id="{ 'defaultYExtents' + $index }" type="checkbox" ng-model="axis.scale.defaultYExtents">
<input class="kuiCheckBox" id="{ 'defaultYExtents' + $index }" type="checkbox" ng-model="axis.scale.defaultYExtents" ng-change="updateBoundsMargin(axis)">
</div>
</div>
<div ng-if="axis.scale.defaultYExtents">
<div class="visEditorSidebar__formRow">
<label
class="visEditorSidebar__formLabel"
i18n-id="kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBounds.boundsMargin"
i18n-default-message="Bounds Margin"
></label>
<div class="visEditorSidebar__formControl">
<input
name="yBoundsMargin"
class="kuiInput visEditorSidebar__input"
type="number"
step="0.1"
greater-or-equal-than="0"
ng-model="axis.scale.boundsMargin">
</div>
</div>
<div ng-show="axis.scale.boundsMargin < 0">
<span
class="text-danger"
i18n-id="kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBounds.minNeededBoundsMargin"
i18n-default-message="Bounds Margin must be greater than or equal to 0"
></span>
</div>
</div>

View file

@ -125,6 +125,12 @@ module.directive('vislibValueAxes', function () {
}
};
$scope.updateBoundsMargin = function (axis) {
if (!axis.scale.defaultYExtents) {
delete axis.scale.boundsMargin;
}
};
$scope.updateAxisName = function (axis) {
const axisName = _.capitalize(axis.position) + 'Axis-';
axis.name = axisName + $scope.editorState.params.valueAxes.reduce((value, axis) => {

View file

@ -27,12 +27,14 @@ const updateVisualizationConfig = (stateConfig, config) => {
// update value axis options
const isUserDefinedYAxis = config.setYExtents;
const defaultYExtents = config.defaultYExtents;
const mode = ['stacked', 'overlap'].includes(config.mode) ? 'normal' : config.mode || 'normal';
config.valueAxes[0].scale = {
...config.valueAxes[0].scale,
type: config.scale || 'linear',
setYExtents: config.setYExtents || false,
defaultYExtents: config.defaultYExtents || false,
boundsMargin: defaultYExtents ? config.boundsMargin : 0,
min: isUserDefinedYAxis ? config.yAxis.min : undefined,
max: isUserDefinedYAxis ? config.yAxis.max : undefined,
mode: mode

View file

@ -225,5 +225,35 @@ _.forOwn(dataTypesArray, function (dataType, dataTypeName) {
});
});
});
[0, 2, 4, 8].forEach(function (boundsMarginValue) {
describe('defaultYExtents is true and boundsMargin is defined', function () {
beforeEach(function () {
vis.visConfigArgs.defaultYExtents = true;
vis.visConfigArgs.boundsMargin = boundsMarginValue;
vis.render(dataType, persistedState);
});
it('should return yAxis extents equal to data extents with boundsMargin', function () {
vis.handler.charts.forEach(function (chart) {
const yAxis = chart.handler.valueAxes[0];
const min = vis.handler.valueAxes[0].axisScale.getYMin();
const max = vis.handler.valueAxes[0].axisScale.getYMax();
const domain = yAxis.getScale().domain();
if (min < 0 && max < 0) {
expect(domain[0]).to.equal(min);
expect(domain[1] - boundsMarginValue).to.equal(max);
}
else if (min > 0 && max > 0) {
expect(domain[0] + boundsMarginValue).to.equal(min);
expect(domain[1]).to.equal(max);
}
else {
expect(domain[0]).to.equal(min);
expect(domain[1]).to.equal(max);
}
});
});
});
});
});
});

View file

@ -208,6 +208,36 @@ dataTypesArray.forEach(function (dataType) {
});
});
});
[0, 2, 4, 8].forEach(function (boundsMarginValue) {
describe('defaultYExtents is true and boundsMargin is defined', function () {
beforeEach(function () {
vis.visConfigArgs.defaultYExtents = true;
vis.visConfigArgs.boundsMargin = boundsMarginValue;
vis.render(data, persistedState);
});
it('should return yAxis extents equal to data extents with boundsMargin', function () {
vis.handler.charts.forEach(function (chart) {
const yAxis = chart.handler.valueAxes[0];
const min = vis.handler.valueAxes[0].axisScale.getYMin();
const max = vis.handler.valueAxes[0].axisScale.getYMax();
const domain = yAxis.getScale().domain();
if (min < 0 && max < 0) {
expect(domain[0]).to.equal(min);
expect(domain[1] - boundsMarginValue).to.equal(max);
}
else if (min > 0 && max > 0) {
expect(domain[0] + boundsMarginValue).to.equal(min);
expect(domain[1]).to.equal(max);
}
else {
expect(domain[0]).to.equal(min);
expect(domain[1]).to.equal(max);
}
});
});
});
});
});
});

View file

@ -185,6 +185,36 @@ describe('Vislib Line Chart', function () {
});
});
});
[0, 2, 4, 8].forEach(function (boundsMarginValue) {
describe('defaultYExtents is true and boundsMargin is defined', function () {
beforeEach(function () {
vis.visConfigArgs.defaultYExtents = true;
vis.visConfigArgs.boundsMargin = boundsMarginValue;
vis.render(data, persistedState);
});
it('should return yAxis extents equal to data extents with boundsMargin', function () {
vis.handler.charts.forEach(function (chart) {
const yAxis = chart.handler.valueAxes[0];
const min = vis.handler.valueAxes[0].axisScale.getYMin();
const max = vis.handler.valueAxes[0].axisScale.getYMax();
const domain = yAxis.getScale().domain();
if (min < 0 && max < 0) {
expect(domain[0]).to.equal(min);
expect(domain[1] - boundsMarginValue).to.equal(max);
}
else if (min > 0 && max > 0) {
expect(domain[0] + boundsMarginValue).to.equal(min);
expect(domain[1]).to.equal(max);
}
else {
expect(domain[0]).to.equal(min);
expect(domain[1]).to.equal(max);
}
});
});
});
});
});
});
});

View file

@ -30,8 +30,9 @@ const defaults = {
type: 'linear',
expandLastBucket: true,
inverted: false,
setYExtents: null,
defaultYExtents: null,
boundsMargin: 0,
setYExtents: null,
min: null,
max: null,
mode: SCALE_MODES.NORMAL

View file

@ -151,7 +151,20 @@ export class AxisScale {
const domain = [min, max];
if (this.axisConfig.isUserDefined()) return this.validateUserExtents(domain);
if (this.axisConfig.isLogScale()) return this.logDomain(min, max);
if (this.axisConfig.isYExtents()) return domain;
if (this.axisConfig.isYExtents()) {
const scaleBoundsMargin = this.axisConfig.get('scale.boundsMargin');
if (scaleBoundsMargin === 0) {
return domain;
} else {
if (max < 0) {
domain[1] = domain[1] + scaleBoundsMargin;
}
if (min > 0) {
domain[0] = domain[0] - scaleBoundsMargin;
}
return domain;
}
}
return [Math.min(0, min), Math.max(0, max)];
}

View file

@ -85,6 +85,7 @@ function create(opts) {
return function (cfg, data) {
const isUserDefinedYAxis = cfg.setYExtents;
const defaultYExtents = cfg.defaultYExtents;
const config = _.cloneDeep(cfg);
_.defaultsDeep(config, {
chartTitle: {},
@ -110,6 +111,7 @@ function create(opts) {
type: config.scale,
setYExtents: config.setYExtents,
defaultYExtents: config.defaultYExtents,
boundsMargin: defaultYExtents ? config.boundsMargin : 0,
min: isUserDefinedYAxis ? config.yAxis.min : undefined,
max: isUserDefinedYAxis ? config.yAxis.max : undefined,
mode: mode