Bug #644 Crear componente Input Number

This commit is contained in:
gerard 2018-09-13 11:27:38 +02:00
parent 8234780252
commit 094a6eda68
6 changed files with 331 additions and 1 deletions

View File

@ -40,3 +40,4 @@ import './table';
import './th'; import './th';
import './input-range'; import './input-range';
import './chip'; import './chip';
import './input-number';

View File

@ -0,0 +1,42 @@
<div class="container"
ng-class="{selected: $ctrl.hasFocus}">
<div class="textField">
<div class="leftIcons">
<vn-icon-button
icon="remove"
ng-click="$ctrl.remove()"
tabindex="-1"
title="Remove number">
</vn-icon-button>
</div>
<div class="infix">
<input
class="mdl-textfield__input"
type="number"
name="{{$ctrl.name}}"
ng-model="$ctrl.value"
ng-disabled="$ctrl.disabled"
ng-readonly="$ctrl.readonly"
ng-focus="$ctrl.hasFocus = true"
ng-blur="$ctrl.hasFocus = false"
tabindex="{{$ctrl.input.tabindex}}"/>
<label class="label" translate>{{::$ctrl.label}}</label>
</div>
<div class="underline"></div>
<div class="selected underline"></div>
<div class="suffix">
<vn-icon-button
icon="add"
ng-click="$ctrl.add()"
tabindex="-1"
title="Add number">
</vn-icon-button>
<i class="material-icons"
ng-if="$ctrl.hasInfo"
vn-tooltip="{{$ctrl.info}}">
info_outline
</i>
</div>
<div class="rightIcons"></div>
</div>
</div>

View File

@ -0,0 +1,104 @@
import ngModule from '../../module';
import Textfield from '../textfield/textfield';
import './style.scss';
export default class InputNumber extends Textfield {
constructor($element, $scope, $attrs, vnTemplate, $transclude) {
super($element, $scope, $attrs, vnTemplate, $transclude);
this.input.addEventListener('change', () => {
this.validateValue();
});
}
get value() {
return this._value;
}
set value(value) {
this._value = value;
this.hasValue = this._value !== null;
if (this.hasValue) this.element.classList.add('not-empty');
else this.element.classList.remove('not-empty');
this.element.querySelector('.infix').classList.remove('invalid', 'validated');
}
get max() {
return this.input.max;
}
set max(value) {
if (value)
this.input.max = value;
}
get min() {
return this.input.min;
}
set min(value) {
if (!value) value = 0;
this.input.min = value;
}
get step() {
return parseInt(this.input.step);
}
set step(value) {
this.input.step = value;
}
validateValue() {
if ((this.validate() !== undefined && !this.validate()) ||
(this.max && this.value > this.max) ||
(this.min && this.value < this.min) ||
(this.step && this.value % this.step != 0)) {
this.$element[0].querySelector('.infix').classList.add('invalid', 'validated');
}
if (this.onChange)
this.onChange();
}
add() {
if (this.step && this.value % this.step != 0) {
this.value += (this.step - this.value % this.step);
} else {
this.value += this.step;
}
this.validateValue();
}
remove() {
if (this.step && this.value % this.step != 0) {
this.value -= (this.step + this.value % this.step);
} else {
this.value -= this.step;
}
this.validateValue();
}
}
InputNumber.$inject = ['$element', '$scope', '$attrs', 'vnTemplate', '$transclude'];
ngModule.component('vnInputNumber', {
template: require('./index.html'),
controller: InputNumber,
bindings: {
label: '@?',
disabled: '<?',
min: '<?',
max: '<?',
step: '<?',
rule: '@?',
value: '=model',
validate: '&',
onChange: '&',
onClear: '&'
}
});

View File

@ -0,0 +1,160 @@
import './index.js';
describe('Component vnInputNumber', () => {
let $componentController;
let $scope;
let $attrs;
let $timeout;
let $element;
let controller;
beforeEach(() => {
angular.mock.module('claim');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_, _$timeout_) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$attrs = {};
$timeout = _$timeout_;
$element = angular.element('<div><input><div class="infix invalid validated"><div class="rightIcons"></div></div>');
controller = $componentController('vnInputNumber', {$element, $scope, $attrs, $timeout, $transclude: () => {}});
}));
describe('value() setter', () => {
it(`should set _value to a given value, add the class not-empty and remove invalid and validated`, () => {
controller.value = 'pepino';
let classes = controller.element.classList.toString();
expect(classes).toContain('not-empty');
expect(controller._value).toEqual('pepino');
classes = controller.element.querySelector('.infix').classList.toString();
expect(classes).not.toContain('invalid validated');
});
it(`should set _value to a given value and not add the class not-empty if the given value is null`, () => {
controller.value = null;
let classes = controller.element.classList.toString();
expect(classes).not.toContain('not-empty');
expect(controller._value).toEqual(null);
});
});
describe('max() setter', () => {
it(`should set input.max to a given value`, () => {
controller.max = 10;
expect(controller.input.max).toEqual('10');
});
});
describe('min() setter', () => {
it(`should set input.min if theres a given value`, () => {
controller.min = 1;
expect(controller.input.min).toEqual('1');
});
it(`should set input.min to 0 if theres not a given value`, () => {
controller.min = null;
expect(controller.input.min).toEqual('0');
});
});
describe('step() setter/getter', () => {
it(`should set input.step to a given value`, () => {
controller.step = 50;
expect(controller.input.step).toEqual('50');
});
it(`should return a number`, () => {
controller.step = 50;
expect(controller.step).toEqual(50);
expect(typeof controller.step).toEqual('number');
});
});
describe('validateValue()', () => {
it(`should add the classes invalid and validated if the value is less than min`, () => {
controller.validate = () => {};
controller.min = 0;
controller.value = -7;
let classes = controller.element.querySelector('.infix').classList.toString();
expect(classes).not.toContain('invalid validated');
expect(controller.value).toEqual(-7);
controller.validateValue();
classes = controller.element.querySelector('.infix').classList.toString();
expect(classes).toContain('infix invalid validated');
});
it(`should add the classes invalid and validated if the value is greater than max`, () => {
controller.validate = () => {};
controller.max = 10;
controller.value = 15;
let classes = controller.element.querySelector('.infix').classList.toString();
expect(classes).not.toContain('invalid validated');
expect(controller.value).toEqual(15);
controller.validateValue();
classes = controller.element.querySelector('.infix').classList.toString();
expect(classes).toContain('infix invalid validated');
});
it(`should add the classes invalid and validated if the value is not a valid step`, () => {
controller.validate = () => {};
controller.step = 5;
controller.value = 7;
let classes = controller.element.querySelector('.infix').classList.toString();
expect(classes).not.toContain('invalid validated');
expect(controller.value).toEqual(7);
controller.validateValue();
classes = controller.element.querySelector('.infix').classList.toString();
expect(classes).toContain('infix invalid validated');
});
it(`should add the classes invalid and validated if the function validate returns false`, () => {
controller.validate = () => {
return false;
};
controller.step = 5;
controller.value = 7;
let classes = controller.element.querySelector('.infix').classList.toString();
expect(classes).not.toContain('invalid validated');
expect(controller.value).toEqual(7);
controller.validateValue();
classes = controller.element.querySelector('.infix').classList.toString();
expect(classes).toContain('infix invalid validated');
});
});
describe('add()', () => {
it(`should set value to the next possible step and call validateValue`, () => {
spyOn(controller, 'validateValue');
controller.step = 50;
controller.value = -1;
controller.remove();
expect(controller.value).toEqual(-50);
expect(controller.validateValue).toHaveBeenCalledWith();
});
});
});

View File

@ -0,0 +1,21 @@
@import 'colors';
@import '../textfield/style.scss';
vn-input-number {
@extend vn-textfield;
input {
text-align: center!important;
}
vn-icon[icon=add],
vn-icon[icon=remove]{
&:not(:hover){
color: $secondary-font-color;
}
i {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
}
}

View File

@ -81,6 +81,7 @@ export default class Textfield extends Input {
clear() { clear() {
this.saveOldValue(); this.saveOldValue();
this.value = null; this.value = null;
if (this.onClear) this.onClear();
this.input.focus(); this.input.focus();
} }
} }
@ -102,6 +103,7 @@ ngModule.component('vnTextfield', {
rule: '@?', rule: '@?',
type: '@?', type: '@?',
vnTabIndex: '@?', vnTabIndex: '@?',
onChange: '&' onChange: '&',
onClear: '&'
} }
}); });