From 094a6eda68ae5e903f1aa32b72ca182ea0b4b86a Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 13 Sep 2018 11:27:38 +0200 Subject: [PATCH] Bug #644 Crear componente Input Number --- client/core/src/components/index.js | 1 + .../src/components/input-number/index.html | 42 +++++ .../core/src/components/input-number/index.js | 104 ++++++++++++ .../src/components/input-number/index.spec.js | 160 ++++++++++++++++++ .../src/components/input-number/style.scss | 21 +++ .../src/components/textfield/textfield.js | 4 +- 6 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 client/core/src/components/input-number/index.html create mode 100644 client/core/src/components/input-number/index.js create mode 100644 client/core/src/components/input-number/index.spec.js create mode 100644 client/core/src/components/input-number/style.scss diff --git a/client/core/src/components/index.js b/client/core/src/components/index.js index dbba202d09..6ca4fd0e51 100644 --- a/client/core/src/components/index.js +++ b/client/core/src/components/index.js @@ -40,3 +40,4 @@ import './table'; import './th'; import './input-range'; import './chip'; +import './input-number'; diff --git a/client/core/src/components/input-number/index.html b/client/core/src/components/input-number/index.html new file mode 100644 index 0000000000..1eb20fda30 --- /dev/null +++ b/client/core/src/components/input-number/index.html @@ -0,0 +1,42 @@ +
+
+
+ + +
+
+ + +
+
+
+
+ + + + info_outline + +
+
+
+
diff --git a/client/core/src/components/input-number/index.js b/client/core/src/components/input-number/index.js new file mode 100644 index 0000000000..e344abd88a --- /dev/null +++ b/client/core/src/components/input-number/index.js @@ -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: ' { + 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('
'); + 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(); + }); + }); +}); diff --git a/client/core/src/components/input-number/style.scss b/client/core/src/components/input-number/style.scss new file mode 100644 index 0000000000..2b7749d263 --- /dev/null +++ b/client/core/src/components/input-number/style.scss @@ -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; + } + } +} \ No newline at end of file diff --git a/client/core/src/components/textfield/textfield.js b/client/core/src/components/textfield/textfield.js index 64b91faa09..3817ab0d51 100644 --- a/client/core/src/components/textfield/textfield.js +++ b/client/core/src/components/textfield/textfield.js @@ -81,6 +81,7 @@ export default class Textfield extends Input { clear() { this.saveOldValue(); this.value = null; + if (this.onClear) this.onClear(); this.input.focus(); } } @@ -102,6 +103,7 @@ ngModule.component('vnTextfield', { rule: '@?', type: '@?', vnTabIndex: '@?', - onChange: '&' + onChange: '&', + onClear: '&' } });