From d274d62d736e14a5be815578fd69e4f221f1c057 Mon Sep 17 00:00:00 2001 From: Darren Thornton <6361057+dthornton-hyl@users.noreply.github.com> Date: Thu, 12 Feb 2026 09:25:46 -0600 Subject: [PATCH] AAE-40604 Fix edge case for REST/Variable for dropdown showing required message (#11638) --- .../dropdown/dropdown-cloud.widget.spec.ts | 103 ++++++++++++++++++ .../widgets/dropdown/dropdown-cloud.widget.ts | 10 +- 2 files changed, 110 insertions(+), 3 deletions(-) diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.spec.ts index bfc3d492d9..5415f5a131 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.spec.ts @@ -303,6 +303,34 @@ describe('DropdownCloudWidgetComponent', () => { }); }); + describe('when options load successfully from restUrl', () => { + beforeEach(() => { + spyOn(formCloudService, 'getRestWidgetData').and.returnValue(of(fakeOptionList)); + widget.field.restUrl = 'https://fake-rest-url'; + widget.field.optionType = 'rest'; + widget.field.required = true; + widget.field.value = ''; + widget.field.isVisible = true; + }); + + it('should show required message for rest type when required and value is empty after touch', fakeAsync(() => { + widget.ngOnInit(); + fixture.detectChanges(); + tick(DROPDOWN_CLOUD_WIDGET_SET_VALUE_DEBOUNCE); + fixture.detectChanges(); + + expect(widget.isRestApiFailed).toBe(false); + expect(widget.dropdownControl.errors?.required).toBeTruthy(); + + widget.dropdownControl.markAsTouched(); + fixture.detectChanges(); + + const requiredErrorElement = fixture.debugElement.query(By.css('.adf-dropdown-required-message .adf-error-text')); + expect(requiredErrorElement).toBeTruthy(); + expect(requiredErrorElement.nativeElement.innerText).toEqual('FORM.FIELD.REQUIRED'); + })); + }); + it('should preselect dropdown widget value when Json (rest call) passed', async () => { widget.field.restUrl = 'https://fake-rest-url'; widget.field.optionType = 'rest'; @@ -1127,6 +1155,54 @@ describe('DropdownCloudWidgetComponent', () => { expect(widget.dropdownControl.value).toEqual({ id: 'testValueObj', name: 'testValueObjName' }); })); + it('should set dropdownControl value to null when field value is undefined', fakeAsync(() => { + widget.field = { + value: undefined, + options: [], + isVisible: true, + markAsValid: () => {} + } as FormFieldModel; + + fixture.detectChanges(); + widget['setFormControlValue'](); + + tick(DROPDOWN_CLOUD_WIDGET_SET_VALUE_DEBOUNCE); + + expect(widget.dropdownControl.value).toBeNull(); + })); + + it('should set dropdownControl value to null when field value is empty string', fakeAsync(() => { + widget.field = { + value: '', + options: [], + isVisible: true, + markAsValid: () => {} + } as FormFieldModel; + + fixture.detectChanges(); + widget['setFormControlValue'](); + + tick(DROPDOWN_CLOUD_WIDGET_SET_VALUE_DEBOUNCE); + + expect(widget.dropdownControl.value).toBeNull(); + })); + + it('should set dropdownControl value to null when field value is null', fakeAsync(() => { + widget.field = { + value: null, + options: [], + isVisible: true, + markAsValid: () => {} + } as FormFieldModel; + + fixture.detectChanges(); + widget['setFormControlValue'](); + + tick(DROPDOWN_CLOUD_WIDGET_SET_VALUE_DEBOUNCE); + + expect(widget.dropdownControl.value).toBeNull(); + })); + it('should display options persisted from process variable', async () => { widget.field = getVariableDropdownWidget( 'variables.json-variable', @@ -1212,6 +1288,33 @@ describe('DropdownCloudWidgetComponent', () => { expect(variableFailedElement).toBeTruthy(); }); + it('should show required message for variable type when options load successfully and required and value is empty after touch', fakeAsync(() => { + widget.field = getVariableDropdownWidget( + 'variables.json-variable', + 'response.people.players', + 'playerId', + 'playerFullName', + mockProcessVariablesWithJson + ); + widget.field.required = true; + widget.field.value = ''; + widget.field.isVisible = true; + widget.ngOnInit(); + fixture.detectChanges(); + tick(DROPDOWN_CLOUD_WIDGET_SET_VALUE_DEBOUNCE); + fixture.detectChanges(); + + expect(widget.variableOptionsFailed).toBe(false); + expect(widget.dropdownControl.errors?.required).toBeTruthy(); + + widget.dropdownControl.markAsTouched(); + fixture.detectChanges(); + + const requiredErrorElement = fixture.debugElement.query(By.css('.adf-dropdown-required-message .adf-error-text')); + expect(requiredErrorElement).toBeTruthy(); + expect(requiredErrorElement.nativeElement.innerText).toEqual('FORM.FIELD.REQUIRED'); + })); + it('should return empty array and display error when id is incorrect', () => { widget.field = getVariableDropdownWidget( 'variables.json-variable', diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.ts index e1d22ce633..deaa1c126f 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.ts @@ -143,8 +143,8 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI value = this.field?.value; } else if (this.field?.value && typeof this.field?.value === 'object') { value = { id: this.field?.value.id, name: this.field?.value.name }; - } else if (this.field.value === null) { - value = this.field.value; + } else if (this.field.value === null || this.field.value === undefined || this.field.value === '') { + value = null; } else { value = { id: this.field?.value, name: '' }; } @@ -519,7 +519,11 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI return event.field.type === FormFieldTypes.DROPDOWN; } - private setOptionValue(option: FormFieldOption | FormFieldOption[], field: FormFieldModel) { + private setOptionValue(option: FormFieldOption | FormFieldOption[] | null, field: FormFieldModel) { + if (option == null) { + field.value = undefined; + return; + } if (Array.isArray(option) || field.hasMultipleValues) { field.value = option; return;