diff --git a/projects/ui/src/lib/components/po-field/po-input-generic/po-input-generic.spec.ts b/projects/ui/src/lib/components/po-field/po-input-generic/po-input-generic.spec.ts index d7dbcbb9c0..0703a3718d 100644 --- a/projects/ui/src/lib/components/po-field/po-input-generic/po-input-generic.spec.ts +++ b/projects/ui/src/lib/components/po-field/po-input-generic/po-input-generic.spec.ts @@ -1,6 +1,6 @@ import { AbstractControl } from '@angular/forms'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; -import { Component, ElementRef } from '@angular/core'; +import { Component, ElementRef, signal } from '@angular/core'; import { configureTestSuite } from './../../../util-test/util-expect.spec'; @@ -55,6 +55,18 @@ describe('PoInputGeneric:', () => { expect(component).toBeTruthy(); }); + it('validateClassesForMask: should called if mask exists', (): void => { + const fakeThis = { + mask: '99999-999', + validateClassesForMask: () => {} + }; + spyOn(fakeThis, 'validateClassesForMask'); + + component.validateInitMask.call(fakeThis); + + expect(fakeThis.validateClassesForMask).toHaveBeenCalled(); + }); + it('should call afterViewInit', () => { spyOn(component, 'afterViewInit'); component.ngAfterViewInit(); @@ -98,10 +110,12 @@ describe('PoInputGeneric:', () => { it('should call keydown from mask with keyCode different 229', () => { const fakeThis = { mask: '(999)', + passedWriteValue: true, objMask: { keydown: (value: any) => {} }, - eventOnBlur: e => {} + eventOnBlur: e => {}, + validateClassesForMask: (value: boolean) => {} }; spyOn(fakeThis.objMask, 'keydown'); component.onKeydown.call(fakeThis, fakeEvent); @@ -111,6 +125,7 @@ describe('PoInputGeneric:', () => { it('should not call keydown from mask with keyCode different 229', () => { const fakeThis = { mask: '', + passedWriteValue: true, objMask: { keydown: (value: any) => {} }, @@ -126,6 +141,7 @@ describe('PoInputGeneric:', () => { it('should not call keydown when the mask is empty and keyCode is different of 229', () => { const fakeThis = { mask: '999', + passedWriteValue: true, objMask: { keydown: (value: any) => {} }, @@ -644,6 +660,117 @@ describe('PoInputGeneric:', () => { expect(component.el.nativeElement.classList).not.toContain('ng-invalid'); }); + it('validateClassesForMask: should add invalid classes if maskValid validation failed.', (): void => { + const validMaskMock = signal(false); + + const fakeThis = { + inputEl: { + nativeElement: { + value: undefined + } + }, + el: { + nativeElement: { + classList: { + add: value => {}, + get: 'ng-invalid ng-dirty' + } + } + }, + mask: '99999-999' + }; + + component.validateClassesForMask.call(fakeThis); + + expect(fakeThis.el.nativeElement.classList.get).toContain('ng-invalid'); + expect(fakeThis.el.nativeElement.classList.get).toContain('ng-dirty'); + }); + + it('validateClassesForMask: should remove invalid classes if maskValid validation sucess.', (): void => { + const validMaskMock = signal(true); + + const fakeThis = { + inputEl: { + nativeElement: { + value: undefined + } + }, + el: { + nativeElement: { + classList: { + add: value => {}, + get: 'ng-invalid ng-dirty', + remove: value => {} + } + } + }, + mask: '99999-999', + }; + spyOn(fakeThis.el.nativeElement.classList, 'remove'); + + component.validateClassesForMask.call(fakeThis); + + expect(fakeThis.el.nativeElement.classList.remove).toHaveBeenCalledWith('ng-invalid'); + }); + + it('validateClassesForMask: should add invalid classes if < minlength and maxlenght >.', (): void => { + const validMaskMock = signal(true); + + const fakeThis = { + inputEl: { + nativeElement: { + value: '12345-678' + } + }, + el: { + nativeElement: { + classList: { + add: value => {}, + get: 'ng-invalid ng-dirty', + remove: value => {} + } + } + }, + mask: undefined, + minlength: 4, + maxlength: 4 + }; + spyOn(fakeThis.el.nativeElement.classList, 'add'); + + component.validateClassesForMask.call(fakeThis); + + expect(fakeThis.el.nativeElement.classList.add).toHaveBeenCalledWith('ng-invalid'); + }); + + it('validateClassesForMask: should remove invalid classes if minlength > and maxlenght >.', (): void => { + const validMaskMock = signal(false); + + const fakeThis = { + inputEl: { + nativeElement: { + value: '12345-678' + } + }, + el: { + nativeElement: { + classList: { + add: value => {}, + get: 'ng-invalid ng-dirty', + remove: value => {} + } + } + }, + mask: undefined, + minlength: 7, + maxlength: 11 + }; + spyOn(fakeThis.el.nativeElement.classList, 'remove'); + + component.validateClassesForMask.call(fakeThis); + + expect(fakeThis.el.nativeElement.classList.remove).toHaveBeenCalledWith('ng-invalid'); + }); + it('controlChangeEmitter: should emit change with input value if input value changes', fakeAsync((): void => { const inputValue = 'value'; @@ -690,7 +817,9 @@ describe('PoInputGeneric:', () => { inputEl: '', mask: '', changeModel: component.changeModel, - passedWriteValue: false + passedWriteValue: false, + validateClassesForMask: () => {}, + validateInitMask: () => {} }; spyOn(component.changeModel, 'emit'); component.writeValueModel.call(fakeThis, value); @@ -703,7 +832,8 @@ describe('PoInputGeneric:', () => { inputEl: '', mask: '', changeModel: component.changeModel, - passedWriteValue: false + passedWriteValue: false, + validateClassesForMask: () => {} }; spyOn(component.changeModel, 'emit'); component.writeValueModel.call(fakeThis, ''); @@ -716,7 +846,9 @@ describe('PoInputGeneric:', () => { inputEl: component.inputEl, mask: '', changeModel: component.changeModel, - passedWriteValue: false + passedWriteValue: false, + validateClassesForMask: () => {}, + validateInitMask: () => {} }; component.writeValueModel.call(fakeThis, 'valor'); expect(component.inputEl.nativeElement.value).toBe('valor'); @@ -732,7 +864,9 @@ describe('PoInputGeneric:', () => { _formatModel: false }, changeModel: component.changeModel, - passedWriteValue: false + passedWriteValue: false, + validateClassesForMask: () => {}, + validateInitMask: () => {} }; component.writeValueModel.call(fakeThis, 'valor'); expect(component.inputEl.nativeElement.value).toBe('valor formatted'); @@ -749,7 +883,9 @@ describe('PoInputGeneric:', () => { }, changeModel: component.changeModel, callUpdateModelWithTimeout: component.callUpdateModelWithTimeout, - passedWriteValue: false + passedWriteValue: false, + validateClassesForMask: () => {}, + validateInitMask: () => {} }; const callUpdateModelWithTimeout = spyOn(fakeThis, 'callUpdateModelWithTimeout'); component.writeValueModel.call(fakeThis, 'valor'); diff --git a/projects/ui/src/lib/components/po-field/po-input-generic/po-input-generic.ts b/projects/ui/src/lib/components/po-field/po-input-generic/po-input-generic.ts index 5a84eac6cd..92186576fc 100644 --- a/projects/ui/src/lib/components/po-field/po-input-generic/po-input-generic.ts +++ b/projects/ui/src/lib/components/po-field/po-input-generic/po-input-generic.ts @@ -1,4 +1,11 @@ -import { AfterViewInit, ElementRef, HostListener, ViewChild, Directive, ChangeDetectorRef } from '@angular/core'; +import { + AfterViewInit, + ElementRef, + HostListener, + ViewChild, + Directive, + ChangeDetectorRef +} from '@angular/core'; import { AbstractControl } from '@angular/forms'; import { PoInputBaseComponent } from '../po-input/po-input-base.component'; @@ -28,6 +35,9 @@ export abstract class PoInputGeneric extends PoInputBaseComponent implements Aft if (this.mask && !this.readonly && e.target.keyCode !== 229) { this.eventOnBlur(e); this.objMask.keydown(e); + if (this.passedWriteValue) { + this.validateClassesForMask(); + } } } @@ -166,6 +176,24 @@ export abstract class PoInputGeneric extends PoInputBaseComponent implements Aft } } + validateClassesForMask() { + const element = this.el.nativeElement; + const elementValue = this.inputEl.nativeElement.value; + + if (!elementValue && this.mask) { + element.classList.add('ng-invalid'); + element.classList.add('ng-dirty'); + } else if (this.minlength || this.maxlength) { + if (elementValue.length < this.minlength || elementValue.length > this.maxlength) { + element.classList.add('ng-invalid'); + } else { + element.classList.remove('ng-invalid'); + } + } else { + element.classList.remove('ng-invalid'); + } + } + verifyPattern(pattern: string, value: any) { return new RegExp(pattern).test(value); } @@ -197,6 +225,7 @@ export abstract class PoInputGeneric extends PoInputBaseComponent implements Aft // Emite evento quando o model é atualizado, inclusive a primeira vez if (value) { + this.validateInitMask(); this.changeModel.emit(value); } } @@ -212,5 +241,11 @@ export abstract class PoInputGeneric extends PoInputBaseComponent implements Aft } } + validateInitMask() { + if (this.mask) { + this.validateClassesForMask(); + } + } + abstract extraValidation(c: AbstractControl): { [key: string]: any }; } diff --git a/projects/ui/src/lib/components/po-field/po-input/po-input-base.component.ts b/projects/ui/src/lib/components/po-field/po-input/po-input-base.component.ts index 43bd1cd82f..e1dbd92195 100644 --- a/projects/ui/src/lib/components/po-field/po-input/po-input-base.component.ts +++ b/projects/ui/src/lib/components/po-field/po-input/po-input-base.component.ts @@ -343,7 +343,9 @@ export abstract class PoInputBaseComponent implements ControlValueAccessor, Vali } } - constructor(private cd?: ChangeDetectorRef) {} + constructor(private cd?: ChangeDetectorRef) { + this.objMask = new PoMask(this.mask, this.maskFormatModel); + } callOnChange(value: any) { this.updateModel(value); diff --git a/projects/ui/src/lib/components/po-field/po-input/po-mask.ts b/projects/ui/src/lib/components/po-field/po-input/po-mask.ts index ad5647535c..120f493279 100644 --- a/projects/ui/src/lib/components/po-field/po-input/po-mask.ts +++ b/projects/ui/src/lib/components/po-field/po-input/po-mask.ts @@ -352,8 +352,10 @@ export class PoMask { this.valueToInput = valueProcessed; this.valueToModel = this.removeFormattingValue(valueProcessed); } + return valueProcessed; } + // verifica se tem algum caracter de mascara antes do cursor checkMaskBefore($event: any, position: number) { if (this.isFixedCharacterGuide($event.target.value.toString().charAt(this.initialPosition - 1))) {