[AAE-11480] - Fix required error is displayed for a non required drop… (#8021)

* [AAE-11480] - Fix required error is displayed for a non required dropdown

* Use alias for async pipe instead of subscribing 2 times
This commit is contained in:
Ardit Domi 2022-12-01 14:11:20 +00:00 committed by GitHub
parent e5489363a1
commit e343de5c13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 112 additions and 19 deletions

View File

@ -531,7 +531,7 @@ describe('FormFieldModel', () => {
expect(field.value).toBe(false); expect(field.value).toBe(false);
}); });
it('should delete empty dropdown value from the form values', () => { it('should set the value as null for a dropdown field that has the None value selected', () => {
const form = new FormModel(); const form = new FormModel();
const field = new FormFieldModel(form, { const field = new FormFieldModel(form, {
id: 'dropdown-1', id: 'dropdown-1',
@ -539,10 +539,13 @@ describe('FormFieldModel', () => {
}); });
field.value = 'empty'; field.value = 'empty';
expect(form.values['dropdown-1']).toBe(undefined); expect(form.values['dropdown-1']).toBe(null);
field.value = ''; field.value = '';
expect(form.values['dropdown-1']).toBe(undefined); expect(form.values['dropdown-1']).toBe(null);
field.value = undefined;
expect(form.values['dropdown-1']).toBe(null);
}); });
it('should update form with dropdown value', () => { it('should update form with dropdown value', () => {

View File

@ -361,6 +361,12 @@ export class FormFieldModel extends FormWidgetModel {
switch (this.type) { switch (this.type) {
case FormFieldTypes.DROPDOWN: case FormFieldTypes.DROPDOWN:
if (!this.value) {
this.form.values[this.id] = null;
break;
}
/* /*
This is needed due to Activiti reading dropdown values as string This is needed due to Activiti reading dropdown values as string
but saving back as object: { id: <id>, name: <name> } but saving back as object: { id: <id>, name: <name> }
@ -372,7 +378,7 @@ export class FormFieldModel extends FormWidgetModel {
if (typeof this.value === 'string') { if (typeof this.value === 'string') {
if (this.value === 'empty' || this.value === '') { if (this.value === 'empty' || this.value === '') {
delete this.form.values[this.id]; this.form.values[this.id] = null;
break; break;
} }

View File

@ -7,6 +7,9 @@
</div> </div>
<div> <div>
<mat-form-field> <mat-form-field>
<mat-label *ngIf="getDefaultOption(list$ | async) as defaultOption">
{{ defaultOption.name }}
</mat-label>
<mat-select class="adf-select" <mat-select class="adf-select"
[id]="field.id" [id]="field.id"
[(ngModel)]="field.value" [(ngModel)]="field.value"
@ -21,7 +24,7 @@
matTooltipShowDelay="1000" matTooltipShowDelay="1000"
[multiple]="field.hasMultipleValues"> [multiple]="field.hasMultipleValues">
<adf-select-filter-input *ngIf="showInputFilter" (change)="filter$.next($event)"></adf-select-filter-input> <adf-select-filter-input *ngIf="showInputFilter" (change)="filter$.next($event)"></adf-select-filter-input>
<mat-option *ngFor="let opt of list$ | async" <mat-option *ngFor="let opt of list$ | async"
[value]="getOptionValue(opt, field.value)" [value]="getOptionValue(opt, field.value)"
[id]="opt.id">{{opt.name}} [id]="opt.id">{{opt.name}}

View File

@ -232,6 +232,80 @@ describe('DropdownCloudWidgetComponent', () => {
expect(options[0].nativeElement.innerText).toBe('default1_value'); expect(options[0].nativeElement.innerText).toBe('default1_value');
expect(widget.field.form.values['dropdown-id']).toEqual({ id: 'opt1', name: 'default1_value' }); expect(widget.field.form.values['dropdown-id']).toEqual({ id: 'opt1', name: 'default1_value' });
}); });
it('should not display required error for a non required dropdown when selecting the none option', async () => {
widget.field.options = [
{ id: 'empty', name: 'Choose empty' },
...fakeOptionList
];
widget.ngOnInit();
fixture.detectChanges();
await openSelect();
const defaultOption: any = fixture.debugElement.query(By.css('[id="empty"]'));
widget.touched = true;
defaultOption.triggerEventHandler('click', null);
fixture.detectChanges();
const requiredErrorElement = fixture.debugElement.query(By.css('.adf-dropdown-required-message .adf-error-text'));
expect(requiredErrorElement).toBeFalsy();
});
it('should not display required error when selecting a valid option for a required dropdown', async () => {
widget.field.required = true;
widget.field.options = [
{ id: 'empty', name: 'Choose empty' },
...fakeOptionList
];
widget.ngOnInit();
fixture.detectChanges();
await openSelect();
const optionOne: any = fixture.debugElement.query(By.css('[id="opt_1"]'));
widget.touched = true;
optionOne.triggerEventHandler('click', null);
fixture.detectChanges();
const requiredErrorElement = fixture.debugElement.query(By.css('.adf-dropdown-required-message .adf-error-text'));
expect(requiredErrorElement).toBeFalsy();
});
it('should not have a value when switching from an available option to the None option', async () => {
widget.field.options = [
{ id: 'empty', name: 'This is a mock none option' },
...fakeOptionList
];
widget.ngOnInit();
fixture.detectChanges();
await openSelect();
const optionOne = fixture.debugElement.query(By.css('[id="opt_1"]'));
optionOne.triggerEventHandler('click', null);
fixture.detectChanges();
await fixture.whenStable();
let selectedValueElement = fixture.debugElement.query(By.css('.mat-select-value-text'));
expect(selectedValueElement.nativeElement.innerText).toEqual('option_1');
expect(widget.fieldValue).toEqual('opt_1');
await openSelect();
const defaultOption: any = fixture.debugElement.query(By.css('[id="empty"]'));
defaultOption.triggerEventHandler('click', null);
fixture.detectChanges();
await fixture.whenStable();
const dropdownLabel = fixture.debugElement.query(By.css('.adf-dropdown-widget mat-label'));
selectedValueElement = fixture.debugElement.query(By.css('.mat-select-value-text'));
expect(dropdownLabel.nativeNode.innerText).toEqual('This is a mock none option');
expect(widget.fieldValue).toEqual(undefined);
expect(selectedValueElement).toBeFalsy();
});
}); });
describe('when is required', () => { describe('when is required', () => {
@ -253,7 +327,7 @@ describe('DropdownCloudWidgetComponent', () => {
expect(asterisk.textContent).toEqual('*'); expect(asterisk.textContent).toEqual('*');
}); });
it('should be invalid if no default option after interaction', async () => { it('should display a required error when dropdown is required and has no value after an interaction', async () => {
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
@ -265,7 +339,8 @@ describe('DropdownCloudWidgetComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
expect(element.querySelector('.adf-invalid')).toBeTruthy(); const requiredErrorElement = fixture.debugElement.query(By.css('.adf-dropdown-required-message .adf-error-text'));
expect(requiredErrorElement.nativeElement.innerText).toEqual('FORM.FIELD.REQUIRED');
}); });
}); });
@ -431,7 +506,7 @@ describe('DropdownCloudWidgetComponent', () => {
it('should reset the options for a linked dropdown with restUrl when the parent dropdown selection changes to empty', async () => { it('should reset the options for a linked dropdown with restUrl when the parent dropdown selection changes to empty', async () => {
widget.field.options = mockConditionalEntries[1].options; widget.field.options = mockConditionalEntries[1].options;
parentDropdown.value = 'empty'; parentDropdown.value = undefined;
widget.selectionChangedForField(parentDropdown); widget.selectionChangedForField(parentDropdown);
fixture.detectChanges(); fixture.detectChanges();
@ -439,7 +514,7 @@ describe('DropdownCloudWidgetComponent', () => {
const defaultOption: any = fixture.debugElement.query(By.css('[id="empty"]')); const defaultOption: any = fixture.debugElement.query(By.css('[id="empty"]'));
expect(widget.field.options).toEqual([{ id: 'empty', name: 'Choose one...' }]); expect(widget.field.options).toEqual([{ id: 'empty', name: 'Choose one...' }]);
expect(defaultOption.context.value).toBe('empty'); expect(defaultOption.context.value).toBe(undefined);
expect(defaultOption.context.viewValue).toBe('Choose one...'); expect(defaultOption.context.viewValue).toBe('Choose one...');
}); });
@ -551,7 +626,7 @@ describe('DropdownCloudWidgetComponent', () => {
const optThree: any = fixture.debugElement.query(By.css('[id="SKG"]')); const optThree: any = fixture.debugElement.query(By.css('[id="SKG"]'));
expect(widget.field.options).toEqual(mockConditionalEntries[0].options); expect(widget.field.options).toEqual(mockConditionalEntries[0].options);
expect(optOne.context.value).toBe('empty'); expect(optOne.context.value).toBe(undefined);
expect(optOne.context.viewValue).toBe('Choose one...'); expect(optOne.context.viewValue).toBe('Choose one...');
expect(optTwo.context.value).toBe('ATH'); expect(optTwo.context.value).toBe('ATH');
expect(optTwo.context.viewValue).toBe('Athens'); expect(optTwo.context.viewValue).toBe('Athens');
@ -561,7 +636,7 @@ describe('DropdownCloudWidgetComponent', () => {
it('should reset the options for a linked dropdown when the parent dropdown selection changes to empty', async () => { it('should reset the options for a linked dropdown when the parent dropdown selection changes to empty', async () => {
widget.field.options = mockConditionalEntries[1].options; widget.field.options = mockConditionalEntries[1].options;
parentDropdown.value = 'empty'; parentDropdown.value = undefined;
widget.selectionChangedForField(parentDropdown); widget.selectionChangedForField(parentDropdown);
fixture.detectChanges(); fixture.detectChanges();
@ -569,7 +644,7 @@ describe('DropdownCloudWidgetComponent', () => {
const defaultOption: any = fixture.debugElement.query(By.css('[id="empty"]')); const defaultOption: any = fixture.debugElement.query(By.css('[id="empty"]'));
expect(widget.field.options).toEqual([{ id: 'empty', name: 'Choose one...' }]); expect(widget.field.options).toEqual([{ id: 'empty', name: 'Choose one...' }]);
expect(defaultOption.context.value).toBe('empty'); expect(defaultOption.context.value).toBe(undefined);
expect(defaultOption.context.viewValue).toBe('Choose one...'); expect(defaultOption.context.viewValue).toBe('Choose one...');
}); });

View File

@ -134,9 +134,9 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
} }
private parentValueChanged(value: string) { private parentValueChanged(value: string) {
if (value && !this.isDefaultValue(value)) { if (value && !this.isNoneValueSelected(value)) {
this.isValidRestType() ? this.persistFieldOptionsFromRestApi() : this.persistFieldOptionsFromManualList(value); this.isValidRestType() ? this.persistFieldOptionsFromRestApi() : this.persistFieldOptionsFromManualList(value);
} else if (this.isDefaultValue(value)) { } else if (this.isNoneValueSelected(value)) {
this.resetRestApiErrorMessage(); this.resetRestApiErrorMessage();
this.addDefaultOption(); this.addDefaultOption();
this.resetInvalidValue(); this.resetInvalidValue();
@ -146,8 +146,8 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
} }
} }
private isDefaultValue(value: string): boolean { private isNoneValueSelected(value: string): boolean {
return value === DEFAULT_OPTION.id; return value === undefined;
} }
private getFormFieldById(fieldId): FormFieldModel { private getFormFieldById(fieldId): FormFieldModel {
@ -181,7 +181,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
} }
private isValidValue(): boolean { private isValidValue(): boolean {
return this.fieldValue && !this.isDefaultValue(this.fieldValue) && this.isSelectedValueInOptions(); return this.fieldValue && this.isSelectedValueInOptions();
} }
private isSelectedValueInOptions(): boolean { private isSelectedValueInOptions(): boolean {
@ -249,7 +249,9 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
} }
let optionValue: string = ''; let optionValue: string = '';
if (option.id === DEFAULT_OPTION.id || option.name !== fieldValue) { if (option.id === DEFAULT_OPTION.id) {
optionValue = undefined;
} else if (option.name !== fieldValue) {
optionValue = option.id; optionValue = option.id;
} else { } else {
optionValue = option.name; optionValue = option.name;
@ -309,6 +311,10 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
} }
showRequiredMessage(): boolean { showRequiredMessage(): boolean {
return (this.isInvalidFieldRequired() || this.field.value === 'empty') && this.isTouched() && !this.isRestApiFailed; return (this.isInvalidFieldRequired() || (this.isNoneValueSelected(this.field.value) && this.isRequired())) && this.isTouched() && !this.isRestApiFailed;
}
getDefaultOption(options: FormFieldOption[]): FormFieldOption {
return options.find((option: FormFieldOption) => option.id === DEFAULT_OPTION.id);
} }
} }