From b4eee9d6315a22815cc6b6a55ba4c4be10f54a06 Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Thu, 15 May 2025 21:26:17 +0200 Subject: [PATCH] AAE-34675 Fix default selection required (#10860) * fix default selection required * rename test * dropdown form field return object * dropdown form field return object * dropdown form field return object * fix test * Update lib/core/src/lib/form/components/widgets/core/form-field.model.spec.ts Co-authored-by: Ehsan Rezaei * Update lib/core/src/lib/form/components/widgets/core/form-field.model.ts Co-authored-by: Ehsan Rezaei --------- Co-authored-by: Ehsan Rezaei --- .../widgets/core/form-field.model.spec.ts | 20 ++++-- .../widgets/core/form-field.model.ts | 15 ++++- .../dropdown/dropdown-cloud.widget.spec.ts | 28 +++++++++ .../widgets/dropdown/dropdown-cloud.widget.ts | 16 ++++- .../widgets/dropdown/validator.spec.ts | 62 +++++++++++++++++++ .../components/widgets/dropdown/validators.ts | 1 + .../src/lib/form/form.component.spec.ts | 7 ++- 7 files changed, 138 insertions(+), 11 deletions(-) create mode 100644 lib/process-services-cloud/src/lib/form/components/widgets/dropdown/validator.spec.ts diff --git a/lib/core/src/lib/form/components/widgets/core/form-field.model.spec.ts b/lib/core/src/lib/form/components/widgets/core/form-field.model.spec.ts index 27e97de0dc..1293dc4a4a 100644 --- a/lib/core/src/lib/form/components/widgets/core/form-field.model.spec.ts +++ b/lib/core/src/lib/form/components/widgets/core/form-field.model.spec.ts @@ -146,7 +146,7 @@ describe('FormFieldModel', () => { }); expect(field.options).toEqual([{ id: 'id_one', name: 'One' }]); - expect(field.value).toEqual('id_one'); + expect(field.value).toEqual({ id: 'id_one', name: 'One' }); }); it('should add value (selected options) to field options if NOT present (multiple selection)', () => { @@ -176,7 +176,7 @@ describe('FormFieldModel', () => { expect(field.hasEmptyValue).toBe(true); expect(field.emptyOption).toEqual({ id: 'empty', name: 'Chose one...' }); - expect(field.value).toEqual('empty'); + expect(field.value).toEqual({ id: 'empty', name: 'Chose one...' }); }); it('should set hasEmptyValue to true if "empty" option is present in options', () => { @@ -272,7 +272,8 @@ describe('FormFieldModel', () => { options: [], value: { id: 'delayed-option-id', name: 'Delayed option' } }); - expect(field.value).toBe('delayed-option-id'); + + expect(field.value).toEqual({ id: 'delayed-option-id', name: 'Delayed option' }); }); }); }); @@ -770,7 +771,7 @@ describe('FormFieldModel', () => { ]; }); - it('should update form with selected option and options from which we chose', () => { + it('should update form with selected option and options from which we chose when is a string', () => { field.value = 'restOpt2'; field.updateForm(); @@ -1023,7 +1024,7 @@ describe('FormFieldModel', () => { expect(field.options).toEqual(staticOptions); }); - it('should selected option appear in form values', () => { + it('should selected option appear in form values string', () => { const field = getFieldConfig('manual', staticOptions, 'opt2'); field.updateForm(); @@ -1031,6 +1032,15 @@ describe('FormFieldModel', () => { expect(field.value).toEqual('opt2'); expect(field.form.values['dropdown_field']).toEqual({ id: 'opt2', name: 'Option 2' }); }); + + it('should selected option appear in form values obj', () => { + const field = getFieldConfig('manual', staticOptions, { id: 'opt3', name: 'opt3' }); + + field.updateForm(); + + expect(field.value).toEqual({ id: 'opt3', name: 'opt3' }); + expect(field.form.values['dropdown_field']).toEqual({ id: 'opt3', name: 'opt3' }); + }); }); describe('radio buttons field', () => { diff --git a/lib/core/src/lib/form/components/widgets/core/form-field.model.ts b/lib/core/src/lib/form/components/widgets/core/form-field.model.ts index 89d2fc6a06..d3a7f60aec 100644 --- a/lib/core/src/lib/form/components/widgets/core/form-field.model.ts +++ b/lib/core/src/lib/form/components/widgets/core/form-field.model.ts @@ -331,13 +331,13 @@ export class FormFieldModel extends FormWidgetModel { const isEmptyValue = !value || [this.emptyOption.id, this.emptyOption.name].includes(value); if (isEmptyValue) { - return this.emptyOption.id; + return this.emptyOption; } } if (this.isValidOption(value)) { this.addOption({ id: value.id, name: value.name }); - return value.id; + return value; } if (this.hasMultipleValues) { @@ -436,6 +436,17 @@ export class FormFieldModel extends FormWidgetModel { this.form.values[this.id] = matchingOption || null; } + + if (typeof this.value === 'object') { + if (this.value.id === 'empty' || this.value.id === '') { + this.form.values[this.id] = null; + break; + } + + const matchingOption: FormFieldOption = this.options.find((opt) => opt.id === this.value.id); + + this.form.values[this.id] = matchingOption; + } break; } case FormFieldTypes.RADIO_BUTTONS: { 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 7335b4bb96..20c9fceceb 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 @@ -983,6 +983,34 @@ describe('DropdownCloudWidgetComponent', () => { expect(widget.field.options.length).toEqual(0); }; + it('should set dropdownControl value without emitting events if the mapping is a string', () => { + widget.field = { + value: 'testValue', + options: [], + isVisible: true + } as any; // Mock field + spyOn(widget.dropdownControl, 'setValue').and.callThrough(); + + widget['setFormControlValue'](); + + expect(widget.dropdownControl.setValue).toHaveBeenCalledWith({ id: 'testValue', name: '' }, { emitEvent: false }); + expect(widget.dropdownControl.value).toEqual({ id: 'testValue', name: '' }); + }); + + it('should set dropdownControl value without emitting events if is an object', () => { + widget.field = { + value: { id: 'testValueObj', name: 'testValueObjName' }, + options: [], + isVisible: true + } as any; // Mock field + spyOn(widget.dropdownControl, 'setValue').and.callThrough(); + + widget['setFormControlValue'](); + + expect(widget.dropdownControl.setValue).toHaveBeenCalledWith({ id: 'testValueObj', name: 'testValueObjName' }, { emitEvent: false }); + expect(widget.dropdownControl.value).toEqual({ id: 'testValueObj', name: 'testValueObjName' }); + }); + it('should display options persisted from process variable', async () => { 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 df3b5eadca..37e8399abe 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 @@ -195,7 +195,15 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI } private setFormControlValue(): void { - this.dropdownControl.setValue(this.field?.value, { emitEvent: false }); + if (Array.isArray(this.field.value)) { + this.dropdownControl.setValue(this.field?.value, { emitEvent: false }); + } else if (this.field?.value && typeof this.field?.value === 'object') { + this.dropdownControl.setValue({ id: this.field?.value.id, name: this.field?.value.name }, { emitEvent: false }); + } else if (this.field.value === null) { + this.dropdownControl.setValue(this.field?.value, { emitEvent: false }); + } else { + this.dropdownControl.setValue({ id: this.field?.value, name: '' }, { emitEvent: false }); + } } private updateFormControlState(): void { @@ -469,7 +477,11 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI const fieldValueIds = this.field.value.map((valueOption) => valueOption.id); return fieldValueIds.every((valueOptionId) => optionIdList.includes(valueOptionId)); } else { - return [...this.field.options].map((option) => option.id).includes(this.field.value); + if (this.field?.value && typeof this.field?.value === 'object') { + return [...this.field.options].map((option) => option.id).includes(this.field.value.id); + } else { + return [...this.field.options].map((option) => option.id).includes(this.field.value); + } } } diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/validator.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/validator.spec.ts new file mode 100644 index 0000000000..d7bbd5fb26 --- /dev/null +++ b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/validator.spec.ts @@ -0,0 +1,62 @@ +/*! + * @license + * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FormFieldModel } from '@alfresco/adf-core'; +import { FormControl } from '@angular/forms'; +import { defaultValueValidator } from './validators'; +import { DEFAULT_OPTION } from './dropdown-cloud.widget'; + +describe('defaultValueValidator', () => { + let mockField: FormFieldModel; + + beforeEach(() => { + mockField = new FormFieldModel(null, { + options: [ + { id: DEFAULT_OPTION.id, name: DEFAULT_OPTION.name }, + { id: 'opt_1', name: 'Option 1' }, + { id: 'opt_2', name: 'Option 2' } + ] + }); + }); + + it('should return null when a valid option is selected', () => { + const validator = defaultValueValidator(mockField); + const control = new FormControl({ id: 'opt_1' }); + + const result = validator(control); + + expect(result).toBeNull(); + }); + + it('should return a required error when no valid option is selected', () => { + const validator = defaultValueValidator(mockField); + const control = new FormControl(null); + + const result = validator(control); + + expect(result).toEqual({ required: true }); + }); + + it('should return a required error when the default "choose one" option is selected', () => { + const validator = defaultValueValidator(mockField); + const control = new FormControl(DEFAULT_OPTION.id); + + const result = validator(control); + + expect(result).toEqual({ required: true }); + }); +}); diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/validators.ts b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/validators.ts index d3f153a08b..6b0c6fb872 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/validators.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/validators.ts @@ -30,6 +30,7 @@ export const defaultValueValidator = const isSomeOptionSelected = optionsWithNoDefaultValue.some((dropdownOption) => { const isOptionSelected = dropdownOption.id === control.value?.id; + return isOptionSelected; }); diff --git a/lib/process-services/src/lib/form/form.component.spec.ts b/lib/process-services/src/lib/form/form.component.spec.ts index e0361f3700..9c139bf91d 100644 --- a/lib/process-services/src/lib/form/form.component.spec.ts +++ b/lib/process-services/src/lib/form/form.component.spec.ts @@ -941,7 +941,7 @@ describe('FormComponent', () => { let dropdownField = formFields.find((field) => field.id === 'dropdownId'); let radioField = formFields.find((field) => field.id === 'radio'); - expect(dropdownField.value).toBe('empty'); + expect(dropdownField.value).toEqual({ id: 'empty', name: 'Choose one...' }); expect(radioField.value).toBeNull(); const formValues: any = {}; @@ -961,7 +961,10 @@ describe('FormComponent', () => { dropdownField = formFields.find((field) => field.id === 'dropdownId'); radioField = formFields.find((field) => field.id === 'radio'); - expect(dropdownField.value).toBe('dropdown_option_2'); + expect(dropdownField.value).toEqual({ + id: 'dropdown_option_2', + name: 'Dropdown option 2' + }); expect(radioField.value).toBe('radio_option_3'); });