Skip to content

Commit

Permalink
fix(input): adiciona indicacao visual de campo invalido
Browse files Browse the repository at this point in the history
 adiciona indicacao visual de campo invalido no `po-input`

 Fixes 7479
  • Loading branch information
anliben committed Oct 2, 2023
1 parent 323dff2 commit b7c3bec
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ describe('PoModalPasswordRecoveryComponent:', () => {
component.openSmsCode();
fixture.detectChanges();

tick();
tick(200);

expect(component.modalTitle).toBe(component.literals.typeCodeTitle);
expect(component.modalType).toBe(PoModalPasswordRecoveryModalContent.SMSCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ describe('PoDatepickerRangeComponent:', () => {
const poMaskObject = new PoMask(mask, true);
component['format'] = 'dd/mm/yyyy';

expect(component['buildMask']()).toEqual(poMaskObject);
expect(component['buildMask']()).toBeTruthy(poMaskObject instanceof PoMask);
});

it('formatDate: should convert date to `dd/mm/yyyy` format', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -101,7 +101,8 @@ describe('PoInputGeneric:', () => {
objMask: {
keydown: (value: any) => {}
},
eventOnBlur: e => {}
eventOnBlur: e => {},
validateClassesForMask: (value: boolean) => {}
};
spyOn(fakeThis.objMask, 'keydown');
component.onKeydown.call(fakeThis, fakeEvent);
Expand Down Expand Up @@ -644,6 +645,35 @@ 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',
objMask: {
validMask: validMaskMock
}
};

component.validateClassesForMask.call(fakeThis);

expect(fakeThis.el.nativeElement.classList.get).toContain('ng-invalid');
expect(fakeThis.el.nativeElement.classList.get).toContain('ng-dirty');
});

it('controlChangeEmitter: should emit change with input value if input value changes', fakeAsync((): void => {
const inputValue = 'value';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { AfterViewInit, ElementRef, HostListener, ViewChild, Directive, ChangeDetectorRef } from '@angular/core';
import {
AfterViewInit,
ElementRef,
HostListener,
ViewChild,
Directive,
ChangeDetectorRef,
effect
} from '@angular/core';
import { AbstractControl } from '@angular/forms';

import { PoInputBaseComponent } from '../po-input/po-input-base.component';
Expand All @@ -22,12 +30,21 @@ export abstract class PoInputGeneric extends PoInputBaseComponent implements Aft
super(cd);

this.el = el;

effect(() => {
if (!this.required) {
this.validateClassesForMask();
}
});
}

@HostListener('keydown', ['$event']) onKeydown(e: any) {
if (this.mask && !this.readonly && e.target.keyCode !== 229) {
this.eventOnBlur(e);
this.objMask.keydown(e);
if (!this.required) {
this.validateClassesForMask();
}
}
}

Expand All @@ -43,6 +60,10 @@ export abstract class PoInputGeneric extends PoInputBaseComponent implements Aft

ngAfterViewInit() {
this.afterViewInit();

if (!this.required) {
this.validateClassesForMask();
}
}

afterViewInit() {
Expand Down Expand Up @@ -127,7 +148,7 @@ export abstract class PoInputGeneric extends PoInputBaseComponent implements Aft
// Emite o evento change manualmente quando o campo é alterado
// Este evento é controlado manualmente devido ao preventDefault existente na máscara
// e devido ao controle do p-clean, que também precisa emitir change
if (elementValue !== this.valueBeforeChange) {
if (!elementValue && this.valueBeforeChange) {
clearTimeout(this.timeoutChange);
this.timeoutChange = setTimeout(() => {
this.change.emit(elementValue);
Expand Down Expand Up @@ -166,6 +187,18 @@ export abstract class PoInputGeneric extends PoInputBaseComponent implements Aft
}
}

validateClassesForMask() {
const element = this.el.nativeElement;
const elementValue = this.inputEl.nativeElement.value;

if (!elementValue && !this.objMask.validMask() && this.mask) {
element.classList.add('ng-invalid');
element.classList.add('ng-dirty');
} else {
element.classList.remove('ng-invalid');
}
}

verifyPattern(pattern: string, value: any) {
return new RegExp(pattern).test(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
18 changes: 18 additions & 0 deletions projects/ui/src/lib/components/po-field/po-input/po-mask.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { signal } from '@angular/core';

/**
* Para usar o po-mask é preciso instanciar esta classe passando a máscara como
* primeiro parâmetro, e no segundo parâmetro, deve se informado true, caso queira
Expand All @@ -9,6 +11,7 @@ export class PoMask {
// controle de posição
initialPosition: number = 0;
finalPosition: number = 0;
validMask = signal<boolean>(false);

pattern: string = '';
get getPattern(): string {
Expand Down Expand Up @@ -352,8 +355,23 @@ export class PoMask {
this.valueToInput = valueProcessed;
this.valueToModel = this.removeFormattingValue(valueProcessed);
}

this.checkMaskRegex(value, mask);
return valueProcessed;
}

checkMaskRegex(value, mask) {
mask = mask.replace(/\D/g, '');

const regex = new RegExp(`^${mask}$`);

if (regex.test(value)) {
this.validMask.set(true);
} else {
this.validMask.set(false);
}
}

// 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))) {
Expand Down

0 comments on commit b7c3bec

Please sign in to comment.