Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fields): adiciona mensagem de erro nos componentes #2316

Merged
merged 2 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,17 @@ export interface PoDynamicFormField extends PoDynamicField {
* - pattern;
* - minValue;
* - maxValue;
* - required;
*
* > Esta mensagem não é apresentada quando o campo estiver vazio, mesmo que ele seja requerido.
* > Esta mensagem pode ser exibida quando o campo estiver vazio, caso seja requerido. Em casos de componentes como
* `po-datepicker`, `po-input`, `po-number`, `po-decimal`, `po-password`, é necessário que a propriedade
* `requiredFieldErrorMessage` esteja como `true` para que a mensagem seja exibida com o campo vazio. Componentes
* como `po-datepicker-range`, `po-select`, `po-checkbox-group`, `po-radio-group`, `po-multiselect`, `po-combo`,
* `po-lookup` e `po-textarea` não é necessário passar a propriedade `requiredFieldErrorMessage`.
*
* **Componentes compatíveis:** `po-datepicker`, `po-input`, `po-number`, `po-decimal`, `po-password`.
*
* **Componentes compatíveis:** `po-datepicker`, `po-input`, `po-number`, `po-decimal`, `po-password`, `po-datepicker-range`,
* `po-select`, `po-checkbox-group`, `po-radio-group`, `po-multiselect`, `po-combo`, `po-lookup`, `po-textarea`.
*/
errorMessage?: string;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
[p-optional]="field.optional"
[p-readonly]="field.readonly"
[p-required]="field.required"
[p-field-error-message]="field.errorMessage"
[p-show-required]="field.showRequired"
(p-change)="onChangeField(field)"
>
Expand Down Expand Up @@ -172,6 +173,7 @@
[p-optional]="field.optional"
[p-options]="field.options"
[p-required]="field.required"
[p-field-error-message]="field.errorMessage"
[p-show-required]="field.showRequired"
(p-change)="onChangeField(field)"
[p-placeholder]="field.placeholder"
Expand All @@ -193,6 +195,7 @@
[p-optional]="field.optional"
[p-options]="field.options"
[p-required]="field.required"
[p-field-error-message]="field.errorMessage"
[p-show-required]="field.showRequired"
(p-change)="onChangeField(field)"
>
Expand Down Expand Up @@ -253,6 +256,7 @@
[p-optional]="field.optional"
[p-sort]="field.sort"
[p-required]="field.required"
[p-field-error-message]="field.errorMessage"
[p-show-required]="field.showRequired"
(p-change)="onChangeField(field, $event)"
[p-icon]="field.icon"
Expand Down Expand Up @@ -292,6 +296,7 @@
[p-no-autocomplete]="field.noAutocomplete"
[p-optional]="field.optional"
[p-required]="field.required"
[p-field-error-message]="field.errorMessage"
[p-show-required]="field.showRequired"
(p-change)="onChangeField(field)"
[p-placeholder]="field.placeholder"
Expand All @@ -316,6 +321,7 @@
[p-options]="field.options"
[p-required]="field.required"
[p-show-required]="field.showRequired"
[p-field-error-message]="field.errorMessage"
(p-change)="onChangeField(field)"
>
</po-checkbox-group>
Expand All @@ -335,6 +341,7 @@
[p-optional]="field.optional"
[p-options]="field.options"
[p-required]="field.required"
[p-field-error-message]="field.errorMessage"
[p-show-required]="field.showRequired"
(p-change)="onChangeField(field)"
[p-placeholder]="field.placeholder"
Expand Down Expand Up @@ -365,6 +372,7 @@
[p-optional]="field.optional"
[p-readonly]="field.readonly"
[p-required]="field.required"
[p-field-error-message]="field.errorMessage"
[p-show-required]="field.showRequired"
[p-rows]="field.rows"
(p-change)="onChangeField(field)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import * as UtilsFunction from '../../../utils/util';
import * as ValidatorsFunctions from '../validators';
import { expectPropertiesValues } from './../../../util-test/util-expect.spec';

import { PoCheckboxGroupBaseComponent } from './po-checkbox-group-base.component';
import { PoCheckboxGroupOption } from './interfaces/po-checkbox-group-option.interface';
import { PoCheckboxGroupBaseComponent } from './po-checkbox-group-base.component';

describe('PoCheckboxGroupBaseComponent: ', () => {
let component: PoCheckboxGroupBaseComponent;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Directive, EventEmitter, Input, Output } from '@angular/core';
import { AbstractControl, ControlValueAccessor, Validator } from '@angular/forms';
import { AbstractControl, ControlValueAccessor, Validator, Validators } from '@angular/forms';

import { requiredFailed } from '../validators';
import { convertToBoolean, convertToInt, uuid } from './../../../utils/util';
Expand Down Expand Up @@ -78,6 +78,18 @@ export class PoCheckboxGroupBaseComponent implements ControlValueAccessor, Valid
*/
@Input('p-optional') optional: boolean;

/**
* @optional
*
* @description
*
* Exibe a mensagem setada se o campo estiver vazio e for requerido.
*
* > Necessário que a propriedade `p-required` esteja habilitada.
*
*/
@Input('p-field-error-message') fieldErrorMessage: string;

/**
* @optional
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@
</ul>
</div>

<po-field-container-bottom></po-field-container-bottom>
<po-field-container-bottom [p-error-pattern]="getErrorPattern()"></po-field-container-bottom>
</po-field-container>
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,24 @@ describe('PoCheckboxGroupComponent:', () => {
});
});

describe('getErrorPattern:', () => {
it('should return true in hasInvalidClass if fieldErrorMessage', () => {
component['el'].nativeElement.classList.add('ng-invalid');
component['el'].nativeElement.classList.add('ng-dirty');
component.fieldErrorMessage = 'Field Invalid';
component.required = true;
expect(component.hasInvalidClass()).toBeTruthy();
expect(component.getErrorPattern()).toBe('Field Invalid');
});

it('should return empty if fieldErrorMessage is undefined', () => {
component['el'].nativeElement.classList.add('ng-invalid');
component['el'].nativeElement.classList.add('ng-dirty');
component.fieldErrorMessage = undefined;
expect(component.getErrorPattern()).toBe('');
});
});

it('trackByFn: should return index', () => {
const index = 1;
expect(component.trackByFn(index)).toBe(index);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
forwardRef,
inject,
QueryList,
ViewChildren
} from '@angular/core';
Expand Down Expand Up @@ -54,6 +56,7 @@ import { PoCheckboxGroupBaseComponent } from './po-checkbox-group-base.component
export class PoCheckboxGroupComponent extends PoCheckboxGroupBaseComponent implements AfterViewChecked, AfterViewInit {
@ViewChildren('checkboxLabel') checkboxLabels: QueryList<PoCheckboxComponent>;

private el: ElementRef = inject(ElementRef);
constructor(private changeDetector: ChangeDetectorRef) {
super();
}
Expand Down Expand Up @@ -95,6 +98,16 @@ export class PoCheckboxGroupComponent extends PoCheckboxGroupBaseComponent imple
}
}

getErrorPattern() {
return this.fieldErrorMessage && this.hasInvalidClass() ? this.fieldErrorMessage : '';
}

hasInvalidClass() {
return (
this.el.nativeElement.classList.contains('ng-invalid') && this.el.nativeElement.classList.contains('ng-dirty')
);
}

onKeyDown(event: KeyboardEvent, option: PoCheckboxGroupOption) {
const spaceBar = 32;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
[p-optional]="properties.includes('optional')"
[p-options]="options"
[p-required]="properties.includes('required')"
[p-field-error-message]="fieldErrorMessage"
[p-show-required]="properties.includes('showRequired')"
(p-change)="changeEvent('p-change')"
>
Expand Down Expand Up @@ -46,6 +47,16 @@

<po-input class="po-lg-6" name="help" [(ngModel)]="help" p-clean p-label="Help"> </po-input>
</div>
<div class="po-row">
<po-input
class="po-md-6"
name="fieldErrorMessage"
[(ngModel)]="fieldErrorMessage"
p-clean
p-label="Field Error Message"
>
</po-input>
</div>

<div class="po-row">
<po-radio-group
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export class SamplePoCheckboxGroupLabsComponent implements OnInit {
option: PoCheckboxGroupOption;
options: Array<PoCheckboxGroupOption>;
properties: Array<string>;
fieldErrorMessage: string;

public readonly columnOptions: Array<PoRadioGroupOption> = [
{ label: '1 column', value: 1 },
Expand Down Expand Up @@ -56,6 +57,7 @@ export class SamplePoCheckboxGroupLabsComponent implements OnInit {
this.label = undefined;
this.options = [];
this.properties = [];
this.fieldErrorMessage = '';

this.clearOption();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { Directive } from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { FormControl, UntypedFormControl, Validators } from '@angular/forms';

import { Observable, of } from 'rxjs';

import { expectPropertiesValues, expectSettersMethod } from '../../../util-test/util-expect.spec';
import * as Utils from '../../../utils/util';
import * as ValidatorsFunctions from '../validators';
import { expectPropertiesValues, expectSettersMethod } from '../../../util-test/util-expect.spec';

import { PoLanguageService } from '../../../services/po-language/po-language.service';
import { poLocaleDefault } from '../../../services/po-language/po-language.constant';
import { PoLanguageService } from '../../../services/po-language/po-language.service';

import { PoComboBaseComponent } from './po-combo-base.component';
import { PoComboFilter } from './interfaces/po-combo-filter.interface';
import { PoComboFilterMode } from './po-combo-filter-mode.enum';
import { PoComboOption } from './interfaces/po-combo-option.interface';
import { poComboLiteralsDefault } from './interfaces/po-combo-literals-default.interface';
import { PoComboOption } from './interfaces/po-combo-option.interface';
import { PoComboBaseComponent } from './po-combo-base.component';
import { PoComboFilterMode } from './po-combo-filter-mode.enum';

@Directive()
class PoComboTest extends PoComboBaseComponent {
constructor() {
super(new PoLanguageService());
super(new PoLanguageService(), { detectChanges: () => {}, markForCheck: () => {} } as any);
}

getInputValue(): string {
Expand Down Expand Up @@ -814,6 +814,17 @@ describe('PoComboBaseComponent:', () => {
expect(ValidatorsFunctions.requiredFailed).toHaveBeenCalled();
});

it('validate: should set hasValidatorRequired to true if fieldErrorMessage is valid and control has required validator', () => {
component['hasValidatorRequired'] = false;
component.fieldErrorMessage = 'Field Invalid';

const controlMock = new FormControl('', Validators.required);

component.validate(controlMock);

expect(component['hasValidatorRequired']).toBeTrue();
});

it('validateModel: should call `validatorChange` when `validateModel` is a function.', () => {
component['validatorChange'] = () => {};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Directive, EventEmitter, Input, OnInit, Output, TemplateRef } from '@angular/core';
import { AbstractControl, ControlValueAccessor, Validator } from '@angular/forms';
import { ChangeDetectorRef, Directive, EventEmitter, Input, OnInit, Output, TemplateRef } from '@angular/core';
import { AbstractControl, ControlValueAccessor, Validator, Validators } from '@angular/forms';

import { poLocaleDefault } from '../../../services/po-language/po-language.constant';
import { PoLanguageService } from '../../../services/po-language/po-language.service';
Expand All @@ -14,6 +14,7 @@ import { PoComboOptionGroup } from './interfaces/po-combo-option-group.interface
import { PoComboOption } from './interfaces/po-combo-option.interface';
import { PoComboFilterMode } from './po-combo-filter-mode.enum';
import { PoComboFilterService } from './po-combo-filter.service';
import { Subscription } from 'rxjs';

const PO_COMBO_DEBOUNCE_TIME_DEFAULT = 400;
const PO_COMBO_FIELD_LABEL_DEFAULT = 'label';
Expand Down Expand Up @@ -238,6 +239,18 @@ export abstract class PoComboBaseComponent implements ControlValueAccessor, OnIn
*/
@Input('p-remove-initial-filter') removeInitialFilter: boolean = false;

/**
* @optional
*
* @description
*
* Exibe a mensagem setada se o campo estiver vazio e for requerido.
*
* > Necessário que a propriedade `p-required` esteja habilitada.
*
*/
@Input('p-field-error-message') fieldErrorMessage: string;

/**
* @optional
*
Expand Down Expand Up @@ -296,6 +309,7 @@ export abstract class PoComboBaseComponent implements ControlValueAccessor, OnIn
dynamicValue: string = 'value';
shouldApplyFocus: boolean = false;

protected hasValidatorRequired = false;
protected cacheStaticOptions: Array<any> = [];
protected comboOptionsList: Array<any> = [];
protected onModelTouched: any = null;
Expand Down Expand Up @@ -654,7 +668,10 @@ export abstract class PoComboBaseComponent implements ControlValueAccessor, OnIn
*/
@Input({ alias: 'p-append-in-body', transform: convertToBoolean }) appendBox?: boolean = false;

constructor(languageService: PoLanguageService) {
constructor(
languageService: PoLanguageService,
protected changeDetector: ChangeDetectorRef
) {
this.language = languageService.getShortLanguage();
}

Expand Down Expand Up @@ -910,7 +927,12 @@ export abstract class PoComboBaseComponent implements ControlValueAccessor, OnIn
}

validate(abstractControl: AbstractControl): { [key: string]: any } {
if (requiredFailed(this.required, this.disabled, abstractControl.value)) {
if (!this.hasValidatorRequired && this.fieldErrorMessage && abstractControl.hasValidator(Validators.required)) {
this.hasValidatorRequired = true;
}

if (requiredFailed(this.required || this.hasValidatorRequired, this.disabled, abstractControl.value)) {
this.changeDetector.markForCheck();
return {
required: {
valid: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@
</ng-template>
</ng-template>

<po-field-container-bottom [p-help]="help" [p-disabled]="disabled"></po-field-container-bottom>
<po-field-container-bottom
[p-help]="help"
[p-disabled]="disabled"
[p-error-pattern]="getErrorPattern()"
></po-field-container-bottom>
</po-field-container>

<ng-template #dropdownListbox>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,24 @@ describe('PoComboComponent:', () => {
});
});

describe('getErrorPattern:', () => {
it('should return true in hasInvalidClass if fieldErrorMessage', () => {
component.element.nativeElement.classList.add('ng-invalid');
component.element.nativeElement.classList.add('ng-dirty');
component.fieldErrorMessage = 'Field Invalid';
component.required = true;
expect(component.hasInvalidClass()).toBeTruthy();
expect(component.getErrorPattern()).toBe('Field Invalid');
});

it('should return empty if fieldErrorMessage is undefined', () => {
component.element.nativeElement.classList.add('ng-invalid');
component.element.nativeElement.classList.add('ng-dirty');
component.fieldErrorMessage = undefined;
expect(component.getErrorPattern()).toBe('');
});
});

it('should call controlComboVisibility with false', () => {
spyOn(component, 'controlComboVisibility');

Expand Down
Loading