From 905953e30e765a71261c4581ba85f5a63ea7183f Mon Sep 17 00:00:00 2001 From: David Olson <157068235+DavidOlson-Hyland@users.noreply.github.com> Date: Mon, 6 Apr 2026 09:44:23 -0500 Subject: [PATCH] AAE-40741 Form rule `Validate form` doesn't do anything (#11788) --- .../widgets/core/form.model.spec.ts | 5 +++ .../components/widgets/core/form.model.ts | 1 + .../date-time/date-time.widget.spec.ts | 34 +++++++++++++++++ .../widgets/date-time/date-time.widget.ts | 3 ++ .../widgets/date/date.widget.spec.ts | 34 +++++++++++++++++ .../components/widgets/date/date.widget.ts | 3 ++ .../widgets/widget.component.spec.ts | 27 +++++++++++++ .../components/widgets/widget.component.ts | 2 +- .../widgets/date/date-cloud.widget.spec.ts | 34 +++++++++++++++++ .../widgets/date/date-cloud.widget.ts | 3 ++ .../dropdown/dropdown-cloud.widget.spec.ts | 38 +++++++++++++++++++ .../widgets/dropdown/dropdown-cloud.widget.ts | 3 ++ .../widgets/dropdown/dropdown.widget.spec.ts | 38 +++++++++++++++++++ .../form/widgets/dropdown/dropdown.widget.ts | 3 ++ 14 files changed, 227 insertions(+), 1 deletion(-) diff --git a/lib/core/src/lib/form/components/widgets/core/form.model.spec.ts b/lib/core/src/lib/form/components/widgets/core/form.model.spec.ts index 6883a2e65b..48af29fafc 100644 --- a/lib/core/src/lib/form/components/widgets/core/form.model.spec.ts +++ b/lib/core/src/lib/form/components/widgets/core/form.model.spec.ts @@ -79,6 +79,11 @@ describe('FormModel', () => { expect(form.readOnly).toBeTruthy(); }); + it('should have showAllValidationErrors default to false', () => { + const form = new FormModel({}); + expect(form.showAllValidationErrors).toBe(false); + }); + it('should set form values when variable value is 0', () => { const variables = { pfx_property_one: 0 diff --git a/lib/core/src/lib/form/components/widgets/core/form.model.ts b/lib/core/src/lib/form/components/widgets/core/form.model.ts index 9fd0841353..a330838c5b 100644 --- a/lib/core/src/lib/form/components/widgets/core/form.model.ts +++ b/lib/core/src/lib/form/components/widgets/core/form.model.ts @@ -94,6 +94,7 @@ export class FormModel implements ProcessFormModel { className: string; readOnly = false; isValid = true; + showAllValidationErrors = false; processVariables: ProcessVariableModel[] = []; variables: FormVariableModel[] = []; enableParentVisibilityCheck: boolean = false; diff --git a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.spec.ts b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.spec.ts index e04eadece1..df98ec202e 100644 --- a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.spec.ts +++ b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.spec.ts @@ -526,4 +526,38 @@ describe('DateTimeWidgetComponent', () => { expect(asterisk?.textContent).toEqual('*'); }); }); + + describe('showAllValidationErrors', () => { + it('should mark datetimeInputControl as touched when showAllValidationErrors is true', () => { + widget.field = new FormFieldModel(form, { + id: 'datetime-id', + name: 'datetime-name', + type: FormFieldTypes.DATETIME, + required: true + }); + fixture.detectChanges(); + + expect(widget.datetimeInputControl.touched).toBe(false); + + form.showAllValidationErrors = true; + widget.updateReactiveFormControl(); + + expect(widget.datetimeInputControl.touched).toBe(true); + }); + + it('should not mark datetimeInputControl as touched when showAllValidationErrors is false', () => { + widget.field = new FormFieldModel(form, { + id: 'datetime-id', + name: 'datetime-name', + type: FormFieldTypes.DATETIME, + required: true + }); + fixture.detectChanges(); + + form.showAllValidationErrors = false; + widget.updateReactiveFormControl(); + + expect(widget.datetimeInputControl.touched).toBe(false); + }); + }); }); diff --git a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts index 82d8a5d013..07dabf5a6f 100644 --- a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts +++ b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts @@ -71,6 +71,9 @@ export class DateTimeWidgetComponent extends WidgetComponent implements OnInit, updateReactiveFormControl(): void { this.setFormControlValue(); this.updateFormControlState(); + if (this.field?.form?.showAllValidationErrors) { + this.datetimeInputControl.markAsTouched(); + } this.validateField(); } diff --git a/lib/core/src/lib/form/components/widgets/date/date.widget.spec.ts b/lib/core/src/lib/form/components/widgets/date/date.widget.spec.ts index 1b2632230c..a2ce4f4fa1 100644 --- a/lib/core/src/lib/form/components/widgets/date/date.widget.spec.ts +++ b/lib/core/src/lib/form/components/widgets/date/date.widget.spec.ts @@ -340,4 +340,38 @@ describe('DateWidgetComponent', () => { expect(dateElement.value).toContain('03-02-2020'); }); + + describe('showAllValidationErrors', () => { + it('should mark dateInputControl as touched when showAllValidationErrors is true', () => { + widget.field = new FormFieldModel(form, { + id: 'date-field-id', + name: 'date-name', + type: FormFieldTypes.DATE, + required: true + }); + fixture.detectChanges(); + + expect(widget.dateInputControl.touched).toBe(false); + + form.showAllValidationErrors = true; + widget.updateReactiveFormControl(); + + expect(widget.dateInputControl.touched).toBe(true); + }); + + it('should not mark dateInputControl as touched when showAllValidationErrors is false', () => { + widget.field = new FormFieldModel(form, { + id: 'date-field-id', + name: 'date-name', + type: FormFieldTypes.DATE, + required: true + }); + fixture.detectChanges(); + + form.showAllValidationErrors = false; + widget.updateReactiveFormControl(); + + expect(widget.dateInputControl.touched).toBe(false); + }); + }); }); diff --git a/lib/core/src/lib/form/components/widgets/date/date.widget.ts b/lib/core/src/lib/form/components/widgets/date/date.widget.ts index f7dee18a6f..a4a2b64eee 100644 --- a/lib/core/src/lib/form/components/widgets/date/date.widget.ts +++ b/lib/core/src/lib/form/components/widgets/date/date.widget.ts @@ -80,6 +80,9 @@ export class DateWidgetComponent extends WidgetComponent implements OnInit, Reac updateReactiveFormControl(): void { this.updateFormControlState(); + if (this.field?.form?.showAllValidationErrors) { + this.dateInputControl.markAsTouched(); + } this.validateField(); } diff --git a/lib/core/src/lib/form/components/widgets/widget.component.spec.ts b/lib/core/src/lib/form/components/widgets/widget.component.spec.ts index d894a7c349..5d9d8a40c4 100644 --- a/lib/core/src/lib/form/components/widgets/widget.component.spec.ts +++ b/lib/core/src/lib/form/components/widgets/widget.component.spec.ts @@ -152,4 +152,31 @@ describe('WidgetComponent', () => { expect(widget.isInvalidFieldRequired()).toBe(false); }); }); + + describe('isTouched', () => { + it('should return false by default', () => { + widget.field = new FormFieldModel(new FormModel(), { type: 'text' }); + expect(widget.isTouched()).toBe(false); + }); + + it('should return true when touched is set', () => { + widget.field = new FormFieldModel(new FormModel(), { type: 'text' }); + widget.markAsTouched(); + expect(widget.isTouched()).toBe(true); + }); + + it('should return true when showAllValidationErrors is set on the form', () => { + const form = new FormModel(); + form.showAllValidationErrors = true; + widget.field = new FormFieldModel(form, { type: 'text' }); + expect(widget.isTouched()).toBe(true); + }); + + it('should return false when showAllValidationErrors is false and not touched', () => { + const form = new FormModel(); + form.showAllValidationErrors = false; + widget.field = new FormFieldModel(form, { type: 'text' }); + expect(widget.isTouched()).toBe(false); + }); + }); }); diff --git a/lib/core/src/lib/form/components/widgets/widget.component.ts b/lib/core/src/lib/form/components/widgets/widget.component.ts index f5e16d4b50..bd271099c3 100644 --- a/lib/core/src/lib/form/components/widgets/widget.component.ts +++ b/lib/core/src/lib/form/components/widgets/widget.component.ts @@ -80,7 +80,7 @@ export class WidgetComponent implements AfterViewInit { } isTouched(): boolean { - return this.touched; + return this.touched || !!this.field?.form?.showAllValidationErrors; } hasValue(): boolean { diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.spec.ts index b175b69549..01e71f457d 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.spec.ts @@ -510,4 +510,38 @@ describe('DateCloudWidgetComponent', () => { expect(widget.field.validationSummary.message).toBe('FORM.FIELD.REQUIRED'); }); }); + + describe('showAllValidationErrors', () => { + it('should mark dateInputControl as touched when showAllValidationErrors is true', () => { + widget.field = new FormFieldModel(form, { + id: 'date-field-id', + name: 'date-name', + type: 'date', + required: true + }); + fixture.detectChanges(); + + expect(widget.dateInputControl.touched).toBe(false); + + form.showAllValidationErrors = true; + widget.updateReactiveFormControl(); + + expect(widget.dateInputControl.touched).toBe(true); + }); + + it('should not mark dateInputControl as touched when showAllValidationErrors is false', () => { + widget.field = new FormFieldModel(form, { + id: 'date-field-id', + name: 'date-name', + type: 'date', + required: true + }); + fixture.detectChanges(); + + form.showAllValidationErrors = false; + widget.updateReactiveFormControl(); + + expect(widget.dateInputControl.touched).toBe(false); + }); + }); }); diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.ts index 9df21acd41..4afc01f7ea 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.ts @@ -88,6 +88,9 @@ export class DateCloudWidgetComponent extends WidgetComponent implements OnInit, updateReactiveFormControl(): void { this.setFormControlValue(); this.updateFormControlState(); + if (this.field?.form?.showAllValidationErrors) { + this.dateInputControl.markAsTouched(); + } this.validateField(); } 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 311c5115a0..aef71b54bd 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 @@ -1410,6 +1410,44 @@ describe('DropdownCloudWidgetComponent', () => { expect(allOptions.length).toEqual(1); }); }); + + describe('showAllValidationErrors', () => { + it('should mark dropdownControl as touched when showAllValidationErrors is true', () => { + const form = new FormModel(); + widget.field = new FormFieldModel(form, { + id: 'dropdown-id', + name: 'dropdown-name', + type: FormFieldTypes.DROPDOWN, + required: true, + options: fakeOptionList + }); + fixture.detectChanges(); + + expect(widget.dropdownControl.touched).toBe(false); + + form.showAllValidationErrors = true; + widget.updateReactiveFormControl(); + + expect(widget.dropdownControl.touched).toBe(true); + }); + + it('should not mark dropdownControl as touched when showAllValidationErrors is false', () => { + const form = new FormModel(); + widget.field = new FormFieldModel(form, { + id: 'dropdown-id', + name: 'dropdown-name', + type: FormFieldTypes.DROPDOWN, + required: true, + options: fakeOptionList + }); + fixture.detectChanges(); + + form.showAllValidationErrors = false; + widget.updateReactiveFormControl(); + + expect(widget.dropdownControl.touched).toBe(false); + }); + }); }); describe('DropdownCloudWidgetComponent instantiated by FormFieldComponent wrapper', () => { 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 ee9eaa4d84..682a6391b9 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 @@ -165,6 +165,9 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI this.setFormControlValue(); this.updateFormControlState(); + if (this.field?.form?.showAllValidationErrors) { + this.dropdownControl.markAsTouched(); + } this.handleErrors(); } diff --git a/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.spec.ts b/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.spec.ts index 57d191c937..9c37576010 100644 --- a/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.spec.ts +++ b/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.spec.ts @@ -349,4 +349,42 @@ describe('DropdownWidgetComponent', () => { }); }); }); + + describe('showAllValidationErrors', () => { + it('should mark dropdownControl as touched when showAllValidationErrors is true', () => { + const form = new FormModel(); + widget.field = new FormFieldModel(form, { + id: 'dropdown-id', + name: 'dropdown-name', + type: FormFieldTypes.DROPDOWN, + required: true, + options: fakeOptionList + }); + fixture.detectChanges(); + + expect(widget.dropdownControl.touched).toBe(false); + + form.showAllValidationErrors = true; + widget.updateReactiveFormControl(); + + expect(widget.dropdownControl.touched).toBe(true); + }); + + it('should not mark dropdownControl as touched when showAllValidationErrors is false', () => { + const form = new FormModel(); + widget.field = new FormFieldModel(form, { + id: 'dropdown-id', + name: 'dropdown-name', + type: FormFieldTypes.DROPDOWN, + required: true, + options: fakeOptionList + }); + fixture.detectChanges(); + + form.showAllValidationErrors = false; + widget.updateReactiveFormControl(); + + expect(widget.dropdownControl.touched).toBe(false); + }); + }); }); diff --git a/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.ts b/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.ts index 5082b00e62..cc250dd712 100644 --- a/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.ts +++ b/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.ts @@ -100,6 +100,9 @@ export class DropdownWidgetComponent extends WidgetComponent implements OnInit, updateReactiveFormControl(): void { this.updateFormControlState(); + if (this.field?.form?.showAllValidationErrors) { + this.dropdownControl.markAsTouched(); + } this.handleErrors(); }