diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.html b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.html index 0bf385425e..b45c34b1fd 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.html +++ b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.html @@ -7,8 +7,8 @@
- - {{ defaultOption.name }} + + {{ emptyValueOption.name }} {{opt.name}} {{field.value}} 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 cd28d70586..7051924e54 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 @@ -179,10 +179,10 @@ describe('DropdownCloudWidgetComponent', () => { expect(await (await dropdown.getOptions())[0].getText()).toEqual('default1_value'); }); - it('should preselect dropdown widget value when String (defined value) passed ', async () => { + it('should preselect dropdown widget value when value matches one of the fetched options', async () => { widget.field.restUrl = 'https://fake-rest-url'; widget.field.optionType = 'rest'; - widget.field.value = 'opt1'; + widget.field.value = { id: 'opt1', name: 'default1_value' }; spyOn(formCloudService, 'getRestWidgetData').and.returnValue( of([ @@ -245,7 +245,7 @@ describe('DropdownCloudWidgetComponent', () => { await dropdown.clickOptions({ selector: '[id="opt_1"]' }); expect(await dropdown.getValueText()).toEqual('option_1'); - expect(widget.fieldValue).toEqual('opt_1'); + expect(widget.fieldValue).toEqual({ id: 'opt_1', name: 'option_1' }); await dropdown.open(); await dropdown.clickOptions({ selector: '[id="empty"]' }); @@ -254,8 +254,8 @@ describe('DropdownCloudWidgetComponent', () => { const dropdownLabel = await formField.getLabel(); expect(dropdownLabel).toEqual('This is a mock none option'); - expect(widget.fieldValue).toEqual(undefined); - expect(await dropdown.getValueText()).toEqual(''); + expect(widget.fieldValue).toEqual({ id: 'empty', name: 'This is a mock none option' }); + expect(await dropdown.getValueText()).toEqual('This is a mock none option'); }); }); @@ -344,6 +344,22 @@ describe('DropdownCloudWidgetComponent', () => { }); }); + it('should NOT display a required error when dropdown is readonly', async () => { + widget.field.readOnly = true; + fixture.detectChanges(); + await fixture.whenStable(); + + expect(element.querySelector('.adf-invalid')).toBeFalsy(); + + const dropdownSelect = element.querySelector('.adf-select'); + dropdownSelect.dispatchEvent(new Event('blur')); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(element.querySelector('.adf-invalid')).toBeFalsy(); + }); + describe('filter', () => { beforeEach(() => { widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', readOnly: false }), { @@ -432,7 +448,7 @@ describe('DropdownCloudWidgetComponent', () => { }); it('should show preselected option for rest options', async () => { - widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', readOnly: 'false' }), { + widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', readOnly: false }), { id: 'dropdown-id', name: 'date-name', type: 'dropdown', @@ -471,7 +487,7 @@ describe('DropdownCloudWidgetComponent', () => { }); it('should support multiple options for rest options', async () => { - widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', readOnly: 'false' }), { + widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', readOnly: false }), { id: 'dropdown-id', name: 'date-name', type: 'dropdown', 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 3b852b85ad..ad3d6fa9eb 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 @@ -31,7 +31,7 @@ import { BehaviorSubject, combineLatest, Observable, of, Subject } from 'rxjs'; import { filter, map, takeUntil } from 'rxjs/operators'; import { TaskVariableCloud } from '../../../models/task-variable-cloud.model'; -export const DEFAULT_OPTION = { +export const DEFAULT_OPTION: FormFieldOption = { id: 'empty', name: 'Choose one...' }; @@ -218,16 +218,18 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI private buildBodyParam(): any { const bodyParam = Object.assign({}); if (this.isLinkedWidget()) { - const parentWidgetValue = this.getParentWidgetValue(); + const parentWidgetSelectedOption = this.getParentWidgetSelectedOption(); + const parentWidgetSelectedOptionId = this.field.isEmptyValueOption(parentWidgetSelectedOption) ? null : parentWidgetSelectedOption.id; + const parentWidgetId = this.getLinkedWidgetId(); - bodyParam[parentWidgetId] = parentWidgetValue; + bodyParam[parentWidgetId] = parentWidgetSelectedOptionId; } return bodyParam; } private loadFieldOptionsForLinkedWidget() { - const parentWidgetValue = this.getParentWidgetValue(); - this.parentValueChanged(parentWidgetValue); + const parentWidgetSelectedOption = this.getParentWidgetSelectedOption(); + this.parentValueChanged(parentWidgetSelectedOption); this.formService.formFieldValueChanged .pipe( @@ -240,16 +242,16 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI }); } - private getParentWidgetValue(): string { + private getParentWidgetSelectedOption(): FormFieldOption { const parentWidgetId = this.getLinkedWidgetId(); const parentWidget = this.getFormFieldById(parentWidgetId); return parentWidget?.value; } - private parentValueChanged(value: string) { - if (value && !this.isNoneValueSelected(value)) { + private parentValueChanged(value: FormFieldOption) { + if (value && !this.isEmptyValueOption(value)) { this.isValidRestType() ? this.persistFieldOptionsFromRestApi() : this.persistFieldOptionsFromManualList(value); - } else if (this.isNoneValueSelected(value)) { + } else if (this.isEmptyValueOption(value)) { this.resetRestApiErrorMessage(); this.resetOptions(); this.resetInvalidValue(); @@ -259,19 +261,23 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI } } - private isNoneValueSelected(value: string): boolean { - return value === undefined; + private isEmptyValueOptionSelected(): boolean { + return this.isEmptyValueOption(this.field.value); + } + + private isEmptyValueOption(value: FormFieldOption): boolean { + return this.field.isEmptyValueOption(value); } private getFormFieldById(fieldId): FormFieldModel { return this.field.form.getFormFields().filter((field: FormFieldModel) => field.id === fieldId)[0]; } - private persistFieldOptionsFromManualList(value: string) { + private persistFieldOptionsFromManualList(option: FormFieldOption) { if (this.hasRuleEntries()) { const rulesEntries = this.field.rule.entries; rulesEntries.forEach((ruleEntry: RuleEntry) => { - if (ruleEntry.key === value) { + if (ruleEntry.key === option.id) { this.field.options = ruleEntry.options; this.resetInvalidValue(); this.field.updateForm(); @@ -287,7 +293,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI } private resetValue() { - this.field.value = ''; + this.field.value = null; this.selectionChangedForField(this.field); this.updateOptions(); this.field.updateForm(); @@ -298,16 +304,15 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI } private isSelectedValueInOptions(): boolean { - if (Array.isArray(this.fieldValue)) { - const optionIdList = [...this.field.options].map((option) => option.id); - const fieldValueIds = this.fieldValue.map((valueOption) => valueOption.id); - return fieldValueIds.every((valueOptionId) => optionIdList.includes(valueOptionId)); - } else { - return [...this.field.options].map((option) => option.id).includes(this.fieldValue); - } + const selectedOptions: FormFieldOption[] = Array.isArray(this.fieldValue) ? this.fieldValue : [this.fieldValue]; + + return selectedOptions.every((selectedOption) => { + const isIncludedInOptions = this.field.options.some((option) => option.id === selectedOption.id); + return isIncludedInOptions; + }); } - get fieldValue(): string { + get fieldValue(): FormFieldOption { return this.field.value; } @@ -342,40 +347,12 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI return this.field?.rule?.ruleOn; } - compareDropdownValues(opt1: FormFieldOption | string, opt2: FormFieldOption | string): boolean { + compareDropdownValues(opt1: FormFieldOption, opt2: FormFieldOption): boolean { if (!opt1 || !opt2) { return false; } - if (typeof opt1 === 'string' && typeof opt2 === 'object') { - return opt1 === opt2.id || opt1 === opt2.name; - } - - if (typeof opt1 === 'object' && typeof opt2 === 'string') { - return opt1.id === opt2 || opt1.name === opt2; - } - - if (typeof opt1 === 'object' && typeof opt2 === 'object') { - return opt1.id === opt2.id || opt1.name === opt2.name; - } - - return opt1 === opt2; - } - - getOptionValue(option: FormFieldOption, fieldValue: string): string | FormFieldOption { - if (this.field.hasMultipleValues) { - return option; - } - - let optionValue: string = ''; - if (option.id === DEFAULT_OPTION.id) { - optionValue = undefined; - } else if (option.name !== fieldValue) { - optionValue = option.id; - } else { - optionValue = option.name; - } - return optionValue; + return opt1.id === opt2.id && opt1.name === opt2.name; } private isValidRestType(): boolean { @@ -404,11 +381,6 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI return this.field.type === 'readonly'; } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - updateOptions(): void { if (this.isReadOnly()) { this.list$ = of(this.field.options); @@ -448,14 +420,22 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI showRequiredMessage(): boolean { return ( - (this.isInvalidFieldRequired() || (this.isNoneValueSelected(this.field.value) && this.isRequired())) && + (this.isInvalidFieldRequired() || (this.isEmptyValueOptionSelected() && this.isRequired())) && this.isTouched() && !this.isRestApiFailed && !this.variableOptionsFailed ); } - getDefaultOption(options: FormFieldOption[]): FormFieldOption { - return options.find((option: FormFieldOption) => option.id === DEFAULT_OPTION.id); + getEmptyValueOption(options: FormFieldOption[]): null | FormFieldOption { + if (!this.field.hasEmptyValue) { + return null; + } + return options.find((option: FormFieldOption) => this.field.isEmptyValueOption(option)); + } + + ngOnDestroy() { + this.onDestroy$.next(true); + this.onDestroy$.complete(); } }