[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);
});
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 field = new FormFieldModel(form, {
id: 'dropdown-1',
@ -539,10 +539,13 @@ describe('FormFieldModel', () => {
});
field.value = 'empty';
expect(form.values['dropdown-1']).toBe(undefined);
expect(form.values['dropdown-1']).toBe(null);
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', () => {

View File

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

View File

@ -7,6 +7,9 @@
</div>
<div>
<mat-form-field>
<mat-label *ngIf="getDefaultOption(list$ | async) as defaultOption">
{{ defaultOption.name }}
</mat-label>
<mat-select class="adf-select"
[id]="field.id"
[(ngModel)]="field.value"

View File

@ -232,6 +232,80 @@ describe('DropdownCloudWidgetComponent', () => {
expect(options[0].nativeElement.innerText).toBe('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', () => {
@ -253,7 +327,7 @@ describe('DropdownCloudWidgetComponent', () => {
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();
await fixture.whenStable();
@ -265,7 +339,8 @@ describe('DropdownCloudWidgetComponent', () => {
fixture.detectChanges();
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 () => {
widget.field.options = mockConditionalEntries[1].options;
parentDropdown.value = 'empty';
parentDropdown.value = undefined;
widget.selectionChangedForField(parentDropdown);
fixture.detectChanges();
@ -439,7 +514,7 @@ describe('DropdownCloudWidgetComponent', () => {
const defaultOption: any = fixture.debugElement.query(By.css('[id="empty"]'));
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...');
});
@ -551,7 +626,7 @@ describe('DropdownCloudWidgetComponent', () => {
const optThree: any = fixture.debugElement.query(By.css('[id="SKG"]'));
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(optTwo.context.value).toBe('ATH');
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 () => {
widget.field.options = mockConditionalEntries[1].options;
parentDropdown.value = 'empty';
parentDropdown.value = undefined;
widget.selectionChangedForField(parentDropdown);
fixture.detectChanges();
@ -569,7 +644,7 @@ describe('DropdownCloudWidgetComponent', () => {
const defaultOption: any = fixture.debugElement.query(By.css('[id="empty"]'));
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...');
});

View File

@ -134,9 +134,9 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
}
private parentValueChanged(value: string) {
if (value && !this.isDefaultValue(value)) {
if (value && !this.isNoneValueSelected(value)) {
this.isValidRestType() ? this.persistFieldOptionsFromRestApi() : this.persistFieldOptionsFromManualList(value);
} else if (this.isDefaultValue(value)) {
} else if (this.isNoneValueSelected(value)) {
this.resetRestApiErrorMessage();
this.addDefaultOption();
this.resetInvalidValue();
@ -146,8 +146,8 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
}
}
private isDefaultValue(value: string): boolean {
return value === DEFAULT_OPTION.id;
private isNoneValueSelected(value: string): boolean {
return value === undefined;
}
private getFormFieldById(fieldId): FormFieldModel {
@ -181,7 +181,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
}
private isValidValue(): boolean {
return this.fieldValue && !this.isDefaultValue(this.fieldValue) && this.isSelectedValueInOptions();
return this.fieldValue && this.isSelectedValueInOptions();
}
private isSelectedValueInOptions(): boolean {
@ -249,7 +249,9 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
}
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;
} else {
optionValue = option.name;
@ -309,6 +311,10 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
}
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);
}
}