Bug #644 Crear componente Input Number
This commit is contained in:
parent
8234780252
commit
094a6eda68
|
@ -40,3 +40,4 @@ import './table';
|
||||||
import './th';
|
import './th';
|
||||||
import './input-range';
|
import './input-range';
|
||||||
import './chip';
|
import './chip';
|
||||||
|
import './input-number';
|
||||||
|
|
|
@ -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>
|
|
@ -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: '&'
|
||||||
|
}
|
||||||
|
});
|
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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: '&'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue