Skip to content

Commit e270b50

Browse files
samir-ayoubjhosefmarks
authored andcommitted
fix(number): valida campo no evento blur
Se houver valor inválido na saída do campo, o mesmo fica inválido e o usuário recebe o feedback Fixes DTHFUI-3968
1 parent c991f7f commit e270b50

File tree

5 files changed

+152
-55
lines changed

5 files changed

+152
-55
lines changed

projects/ui/src/lib/components/po-field/po-number/po-number-base.component.spec.ts

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -55,77 +55,63 @@ describe('PoNumberBaseComponent', () => {
5555
expect(component).toBeTruthy();
5656
});
5757

58-
it('should call keyup from mask with keyCode different 229', () => {
58+
it('onBlur: shouldn`t call `callOnChange` if event.target.value is empty and event.target.validity.valid is true', () => {
59+
fakeEvent.target.value = '';
60+
fakeEvent.target.validity.valid = true;
61+
5962
const fakeThis = {
60-
isMobile: () => false,
61-
mask: '(999)',
62-
objMask: {
63-
keyup: (value: any) => {},
64-
valueToModel: ''
65-
},
66-
callOnChange: () => {},
63+
invalidInputValueOnBlur: false,
64+
callOnChange: (v: any) => {},
6765
eventOnBlur: e => {}
6866
};
6967

7068
spyOn(fakeThis, 'callOnChange');
71-
spyOn(fakeThis.objMask, 'keyup');
7269
spyOn(fakeThis, 'eventOnBlur');
7370

74-
component.onKeyup.call(fakeThis, fakeEvent);
71+
component.onBlur.call(fakeThis, fakeEvent);
7572

76-
expect(fakeThis.callOnChange).toHaveBeenCalled();
77-
expect(fakeThis.objMask.keyup).toHaveBeenCalled();
78-
expect(fakeThis.eventOnBlur).toHaveBeenCalled();
73+
expect(fakeThis.callOnChange).not.toHaveBeenCalled();
74+
expect(fakeThis.eventOnBlur).toHaveBeenCalledWith(fakeEvent);
7975
});
8076

81-
it('shouldn`t call keyup from mask with keyCode equal to 229', () => {
77+
it('onBlur: shouldn`t call `callOnChange` if event.target.value has value and event.target.validity.valid is true', () => {
78+
fakeEvent.target.value = '1234567890';
79+
fakeEvent.target.validity.valid = true;
80+
8281
const fakeThis = {
83-
isMobile: () => true,
84-
mask: '(999)',
85-
objMask: {
86-
keyup: (value: any) => {},
87-
valueToModel: ''
88-
},
89-
callOnChange: () => {},
82+
invalidInputValueOnBlur: false,
83+
callOnChange: (v: any) => {},
9084
eventOnBlur: e => {}
9185
};
9286

93-
const fakeEventLocal = {
94-
target: {
95-
value: '',
96-
keyCode: 229
97-
}
98-
};
99-
100-
spyOn(fakeThis.objMask, 'keyup');
87+
spyOn(fakeThis, 'callOnChange');
10188
spyOn(fakeThis, 'eventOnBlur');
10289

103-
component.onKeyup.call(fakeThis, fakeEventLocal);
90+
component.onBlur.call(fakeThis, fakeEvent);
10491

105-
expect(fakeThis.objMask.keyup).not.toHaveBeenCalled();
106-
expect(fakeThis.eventOnBlur).not.toHaveBeenCalled();
92+
expect(fakeThis.callOnChange).not.toHaveBeenCalled();
93+
expect(fakeThis.eventOnBlur).toHaveBeenCalledWith(fakeEvent);
10794
});
10895

109-
it('should not call keyup when the mask is empty and keyCode is different of 229', () => {
96+
it('onBlur: should call `callOnChange` if event.target.value is empty and event.target.validity.valid is false', () => {
97+
fakeEvent.target.value = '';
98+
fakeEvent.target.validity.valid = false;
11099
const fakeThis = {
111-
mask: '',
112-
objMask: {
113-
keyup: (value: any) => {},
114-
valueToModel: ''
115-
},
116-
callOnChange: () => {}
100+
invalidInputValueOnBlur: false,
101+
callOnChange: (v: any) => {},
102+
eventOnBlur: e => {}
117103
};
118104

119105
spyOn(fakeThis, 'callOnChange');
120-
spyOn(fakeThis.objMask, 'keyup');
106+
spyOn(fakeThis, 'eventOnBlur');
121107

122-
component.onKeyup.call(fakeThis, event);
108+
component.onBlur.call(fakeThis, fakeEvent);
123109

124-
expect(fakeThis.callOnChange).not.toHaveBeenCalled();
125-
expect(fakeThis.objMask.keyup).not.toHaveBeenCalled();
110+
expect(fakeThis.callOnChange).toHaveBeenCalledWith('Valor Inválido');
111+
expect(fakeThis.eventOnBlur).toHaveBeenCalledWith(fakeEvent);
126112
});
127113

128-
it('should call "callOnChange" eventOnInput without mask', () => {
114+
it('eventOnInput: should call "callOnChange" if doesn`t contain mask and set invalidInputValueOnBlur with false', () => {
129115
fakeEvent.target.value = '1234567890';
130116
const fakeThis = {
131117
mask: false,
@@ -134,6 +120,7 @@ describe('PoNumberBaseComponent', () => {
134120
maxlength: 5,
135121
formatNumber: component['formatNumber'],
136122
inputEl: component.inputEl,
123+
invalidInputValueOnBlur: true,
137124
isEndWithDot: () => {}
138125
};
139126

@@ -142,17 +129,19 @@ describe('PoNumberBaseComponent', () => {
142129
component.eventOnInput.call(fakeThis, fakeEvent);
143130

144131
expect(fakeThis.callOnChange).toHaveBeenCalledWith(12345);
132+
expect(fakeThis.invalidInputValueOnBlur).toBe(false);
145133
expect(fakeThis.inputEl.nativeElement.value).toBe('12345');
146134
});
147135

148-
it('should call "callOnChange" eventOnInput without mask and maxlength', () => {
136+
it('eventOnInput: should call "callOnChange" if doesn`t contain mask and maxlength', () => {
149137
fakeEvent.target.value = '12345';
150138
const fakeThis = {
151139
mask: false,
152140
callOnChange: (v: any) => {},
153141
validMaxLength: component.validMaxLength,
154142
formatNumber: component['formatNumber'],
155143
inputEl: component.inputEl,
144+
invalidInputValueOnBlur: true,
156145
isEndWithDot: () => {}
157146
};
158147

@@ -163,7 +152,7 @@ describe('PoNumberBaseComponent', () => {
163152
expect(fakeThis.callOnChange).toHaveBeenCalledWith(12345);
164153
});
165154

166-
it('should not call "callOnChange" on eventOnInput with mask', () => {
155+
it('eventOnInput: should not call "callOnChange" if has mask', () => {
167156
const fakeThis = {
168157
mask: true,
169158
callOnChange: () => {},

projects/ui/src/lib/components/po-field/po-number/po-number-base.component.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { PoInputGeneric } from '../po-input-generic/po-input-generic';
66
export abstract class PoNumberBaseComponent extends PoInputGeneric {
77
type = 'number';
88

9+
protected invalidInputValueOnBlur = false;
10+
911
/* istanbul ignore next */
1012
constructor(elementRef: ElementRef) {
1113
super(elementRef);
@@ -15,6 +17,7 @@ export abstract class PoNumberBaseComponent extends PoInputGeneric {
1517
if (!this.mask) {
1618
let value = e.target.value;
1719
const valueMaxlength = this.validMaxLength(this.maxlength, value);
20+
this.invalidInputValueOnBlur = false;
1821

1922
if (value !== valueMaxlength) {
2023
value = valueMaxlength;
@@ -26,6 +29,17 @@ export abstract class PoNumberBaseComponent extends PoInputGeneric {
2629
}
2730
}
2831

32+
onBlur(event: any) {
33+
const target = event.target;
34+
this.invalidInputValueOnBlur = target.value === '' && !target.validity.valid;
35+
36+
if (this.invalidInputValueOnBlur) {
37+
this.callOnChange('Valor Inválido');
38+
}
39+
40+
this.eventOnBlur(event);
41+
}
42+
2943
validMaxLength(maxlength: number, value: string) {
3044
if (maxlength && value.length > maxlength) {
3145
const substringValue = value.toString().substring(0, maxlength);

projects/ui/src/lib/components/po-field/po-number/po-number.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
[readonly]="readonly"
1616
[required]="required"
1717
[tabindex]="disabled ? -1 : 0"
18-
(blur)="eventOnBlur($event)"
18+
(blur)="onBlur($event)"
1919
(focus)="eventOnFocus($event)"
2020
(input)="eventOnInput($event)"
2121
/>
@@ -25,5 +25,5 @@
2525
</div>
2626
</div>
2727

28-
<po-field-container-bottom [p-error-pattern]="getErrorPattern()"> </po-field-container-bottom>
28+
<po-field-container-bottom [p-error-pattern]="getErrorPatternMessage()"> </po-field-container-bottom>
2929
</po-field-container>

projects/ui/src/lib/components/po-field/po-number/po-number.component.spec.ts

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,6 @@ describe('PoNumberComponent:', () => {
5555
expectSettersMethod(component, 'setMin', '10', 'min', 10);
5656
});
5757

58-
it('should return null in extraValidation()', () => {
59-
component.min = 0;
60-
component.max = 0;
61-
62-
expect(component.extraValidation(new FormControl(null))).toBeNull();
63-
});
64-
6558
it('should call minFailed', () => {
6659
component.min = 4;
6760

@@ -74,6 +67,81 @@ describe('PoNumberComponent:', () => {
7467
expect(component.validate(new FormControl('10'))).not.toBeNull();
7568
});
7669

70+
describe('Methods: ', () => {
71+
it('extraValidation: should invalidate number if invalidInputValueOnBlur is true and set errorPattern with a defined value', () => {
72+
component['invalidInputValueOnBlur'] = true;
73+
component.errorPattern = 'errorPattern';
74+
75+
const expectedReturn = { number: { valid: false } };
76+
const result = component.validate(new FormControl('2e'));
77+
78+
expect(result).toEqual(expectedReturn);
79+
expect(component.errorPattern).toBe('errorPattern');
80+
});
81+
82+
it('extraValidation: should invalidate number if invalidInputValueOnBlur is true and set errorPattern with default value', () => {
83+
component['invalidInputValueOnBlur'] = true;
84+
component.errorPattern = 'Valor Inválido';
85+
86+
const expectedReturn = { number: { valid: false } };
87+
const result = component.validate(new FormControl('2e'));
88+
89+
expect(result).toEqual(expectedReturn);
90+
expect(component.errorPattern).toBe('Valor Inválido');
91+
});
92+
93+
it('extraValidation: should return null in extraValidation()', () => {
94+
component.min = 0;
95+
component.max = 0;
96+
component['invalidInputValueOnBlur'] = false;
97+
98+
expect(component.extraValidation(new FormControl(null))).toBeNull();
99+
});
100+
101+
describe('getErrorPatternMessage: ', () => {
102+
it('should return errorPattern value if errorPattern has value and containsInvalidClass returns true and show the properly message in template', () => {
103+
component.el.nativeElement.value = '1e';
104+
component.errorPattern = 'erro';
105+
component.el.nativeElement.classList.add('ng-invalid');
106+
component.el.nativeElement.classList.add('ng-dirty');
107+
component['invalidInputValueOnBlur'] = true;
108+
109+
const expectedResult = component.getErrorPatternMessage();
110+
111+
expect(expectedResult).toBe('erro');
112+
113+
fixture.detectChanges();
114+
const content = fixture.debugElement.nativeElement
115+
.querySelector('.po-field-container-bottom-text-error')
116+
.innerHTML.toString();
117+
118+
expect(content.indexOf('erro') > -1).toBeTruthy();
119+
});
120+
121+
it('should return empty string if errorPattern is empty', () => {
122+
component.errorPattern = '';
123+
124+
const expectedResult = component.getErrorPatternMessage();
125+
126+
expect(expectedResult).toBe('');
127+
expect(fixture.debugElement.nativeElement.querySelector('.po-field-container-bottom-text-error')).toBeNull();
128+
});
129+
130+
it('should return empty string if errorPattern has value but containsInvalidClass returns false', () => {
131+
component.el.nativeElement.value = '';
132+
component.errorPattern = 'error';
133+
component.el.nativeElement.classList.add('ng-invalid');
134+
component.el.nativeElement.classList.add('ng-dirty');
135+
component['invalidInputValueOnBlur'] = false;
136+
137+
const expectedResult = component.getErrorPatternMessage();
138+
139+
expect(expectedResult).toBe('');
140+
expect(fixture.debugElement.nativeElement.querySelector('.po-field-container-bottom-text-error')).toBeNull();
141+
});
142+
});
143+
});
144+
77145
describe('Templates: ', () => {
78146
it('tabindex: should set tabindex to -1 when `po-number` is disabled.', () => {
79147
component.disabled = true;

projects/ui/src/lib/components/po-field/po-number/po-number.component.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ export class PoNumberComponent extends PoNumberBaseComponent {
8181
}
8282

8383
extraValidation(abstractControl: AbstractControl): { [key: string]: any } {
84+
// Verifica se já possui algum error pattern padrão.
85+
this.errorPattern = this.errorPattern !== 'Valor Inválido' ? this.errorPattern : '';
86+
8487
if (minFailed(this.min, abstractControl.value)) {
8588
return {
8689
min: {
@@ -97,6 +100,29 @@ export class PoNumberComponent extends PoNumberBaseComponent {
97100
};
98101
}
99102

103+
if (this.invalidInputValueOnBlur) {
104+
this.errorPattern = this.errorPattern || 'Valor Inválido';
105+
106+
return {
107+
number: {
108+
valid: false
109+
}
110+
};
111+
}
112+
100113
return null;
101114
}
115+
116+
getErrorPatternMessage() {
117+
return this.errorPattern !== '' && this.containsInvalidClass() ? this.errorPattern : '';
118+
}
119+
120+
private containsInvalidClass(): boolean {
121+
return (
122+
(this.el.nativeElement.classList.contains('ng-invalid') &&
123+
this.el.nativeElement.classList.contains('ng-dirty') &&
124+
this.inputEl.nativeElement.value !== '') ||
125+
this.invalidInputValueOnBlur
126+
);
127+
}
102128
}

0 commit comments

Comments
 (0)