Allow negative values for custom ranges in metric and gauge visualizations (#33814)

* Fix inequality directives to handle negative values

* Fix test
This commit is contained in:
Lukas Olson 2019-03-28 10:13:10 -07:00 committed by GitHub
parent 9d60ad3831
commit b4bafa5ff6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 61 additions and 23 deletions

View file

@ -173,7 +173,7 @@
type="number"
class="form-control"
name="range.from"
greater-or-equal-than="{{getGreaterThan($index)}}"
greater-or-equal-than="getGreaterThan($index)"
required
step="any" />
</td>
@ -184,7 +184,7 @@
type="number"
class="form-control"
name="range.to"
greater-or-equal-than="{{range.from}}"
greater-or-equal-than="range.from"
required
step="any" />
</td>

View file

@ -81,7 +81,7 @@ module.directive('gaugeOptions', function (i18n) {
};
$scope.getGreaterThan = function (index) {
if (index === 0) return 0;
if (index === 0) return -Infinity;
return $scope.editorState.params.gauge.colorsRange[index - 1].to;
};

View file

@ -161,7 +161,7 @@
type="number"
class="form-control"
name="range.from"
greater-or-equal-than="{{getGreaterThan($index)}}"
greater-or-equal-than="getGreaterThan($index)"
step="any" />
</td>
<td>

View file

@ -64,7 +64,7 @@ module.directive('heatmapOptions', function (i18n) {
};
$scope.getGreaterThan = function (index) {
if (index === 0) return;
if (index === 0) return -Infinity;
return $scope.editorState.params.colorsRange[index - 1].to;
};

View file

@ -284,6 +284,7 @@
class="kuiInput visEditorSidebar__input"
type="number"
step="0.1"
greater-than="axis.scale.min"
ng-model="axis.scale.max"
>
</div>
@ -301,7 +302,8 @@
class="kuiInput visEditorSidebar__input"
type="number"
step="0.1"
greater-than="{{axis.scale.type === 'log' ? 0 : ''}}"
greater-than="axis.scale.type === 'log' ? 0 : undefined"
less-than="axis.scale.max"
ng-model="axis.scale.min"
>
</div>

View file

@ -17,7 +17,7 @@
class="form-control"
type="number"
step="0.1"
greater-than="{{editorState.params.yAxis.min}}"
greater-than="editorState.params.yAxis.min"
ng-model="editorState.params.yAxis.max"
ng-required="editorState.params.setYExtents">
</label>
@ -30,8 +30,8 @@
class="form-control"
type="number"
step="0.1"
less-than="{{editorState.params.yAxis.max}}"
greater-than="{{editorState.params.scale === 'log' ? 0 : ''}}"
less-than="editorState.params.yAxis.max"
greater-than="editorState.params.scale === 'log' ? 0 : undefined"
ng-model="editorState.params.yAxis.min"
ng-required="editorState.params.setYExtents">
</label>

View file

@ -78,7 +78,7 @@
type="number"
class="form-control"
name="range.from"
greater-or-equal-than="{{getGreaterThan($index)}}"
greater-or-equal-than="getGreaterThan($index)"
required
step="any" />
</td>
@ -89,7 +89,7 @@
type="number"
class="form-control"
name="range.to"
greater-or-equal-than="{{range.from}}"
greater-or-equal-than="range.from"
required
step="any" />
</td>

View file

@ -55,7 +55,7 @@ module.directive('metricVisParams', function (i18n) {
};
$scope.getGreaterThan = function (index) {
if (index === 0) return 0;
if (index === 0) return -Infinity;
return $scope.editorState.params.metric.colorsRange[index - 1].to;
};

View file

@ -33,7 +33,6 @@ describe('greater_than model validator directive', function () {
$rootScope = _$rootScope_;
}));
// no value is the same as 0
describe('without value', function () {
let element;
beforeEach(function () {
@ -60,6 +59,33 @@ describe('greater_than model validator directive', function () {
});
});
describe('with string values', function () {
let element;
beforeEach(function () {
html = `<input type="text" ng-model="value" greater-than="'10'" />`;
element = $compile(html)($rootScope);
});
it('should be valid for greater than 10', function () {
$rootScope.value = '15';
$rootScope.$digest();
expect(element.hasClass('ng-valid')).to.be.ok();
});
it('should be invalid for 10', function () {
$rootScope.value = '10';
$rootScope.$digest();
expect(element.hasClass('ng-invalid')).to.be.ok();
});
// Edge case because '5' > '10' as strings
it('should be invalid less than 10', function () {
$rootScope.value = '5';
$rootScope.$digest();
expect(element.hasClass('ng-invalid')).to.be.ok();
});
});
[0, 1, 10, 42, -12].forEach(function (num) {
describe('with value ' + num, function () {
let element;
@ -85,7 +111,14 @@ describe('greater_than model validator directive', function () {
$rootScope.$digest();
expect(element.hasClass('ng-invalid')).to.be.ok();
});
it('should be valid for empty model values', () => {
[undefined, null, ''].forEach(val => {
$rootScope.value = val;
$rootScope.$digest();
expect(element.hasClass('ng-valid')).to.be.ok();
});
});
});
});
});

View file

@ -24,12 +24,7 @@ function makeDirectiveDef(id, compare) {
return {
require: 'ngModel',
link: function ($scope, $el, $attr, ngModel) {
const getBound = function () { return $parse($attr[id])(); };
const defaultVal = {
'greaterThan': -Infinity,
'greaterOrEqualThan': -Infinity,
'lessThan': Infinity
}[id];
const getBound = function () { return $parse($attr[id])($scope); };
ngModel.$parsers.push(validate);
ngModel.$formatters.push(validate);
@ -38,12 +33,20 @@ function makeDirectiveDef(id, compare) {
validate(ngModel.$viewValue);
});
// We only set it to invalid when both the model value and the value to compare against are
// provided, and the values do not meet the condition.
function validate(val) {
const bound = !isNaN(getBound()) ? +getBound() : defaultVal;
const valid = !isNaN(bound) && !isNaN(val) && compare(val, bound);
ngModel.$setValidity(id, valid);
const bound = getBound();
const left = parseFloat(val);
const right = parseFloat(bound);
const isValid = isEmpty(val) || isEmpty(bound) || compare(left, right);
ngModel.$setValidity(id, isValid);
return val;
}
function isEmpty(val) {
return typeof val === 'undefined' || val === null || val === '';
}
}
};
};