diff --git a/docs/process-services-cloud/components/form-cloud.component.md b/docs/process-services-cloud/components/form-cloud.component.md index 16520e687c..709cad1d37 100644 --- a/docs/process-services-cloud/components/form-cloud.component.md +++ b/docs/process-services-cloud/components/form-cloud.component.md @@ -96,6 +96,7 @@ The template defined inside `empty-form` will be shown when no form definition i | showValidationIcon | `boolean` | true | Toggle rendering of the validation icon next to the form title. | | taskId | `string` | | Task id to fetch corresponding form and values. | | displayModeConfigurations | [`FormCloudDisplayModeConfiguration`](../../../lib/process-services-cloud/src/lib/services/form-fields.interfaces.ts)`[]` | | The available display configurations for the form | +| enableParentVisibilityCheck | `boolean` | false | Toggle to enable parent visibility check for validation. When enabled, fields inside hidden groups/sections will skip validation. | ### Events diff --git a/docs/process-services-cloud/components/start-process-cloud.component.md b/docs/process-services-cloud/components/start-process-cloud.component.md index cd2052622b..c7b24e52a8 100644 --- a/docs/process-services-cloud/components/start-process-cloud.component.md +++ b/docs/process-services-cloud/components/start-process-cloud.component.md @@ -42,6 +42,9 @@ Starts a process. | processDefinitionName | `string` | | Name of the process definition. | | showSelectProcessDropdown | `boolean` | true | Show/hide the process dropdown list. | | showTitle | `boolean` | true | Show/hide title. | +| showCancelButton | `boolean` | true | Show/hide cancel button. | +| displayModeConfigurations | [`FormCloudDisplayModeConfiguration`](../../../lib/process-services-cloud/src/lib/services/form-fields.interfaces.ts)`[]` | | The available display configurations for the form (start process event can have assigned form). | +| enableParentVisibilityCheck | `boolean` | false | Toggle to enable parent visibility check for validation. When enabled, fields inside hidden groups/sections will skip validation. | | values | [`TaskVariableCloud`](../../../lib/process-services-cloud/src/lib/form/models/task-variable-cloud.model.ts)`[]` | | Parameter to pass form field values in the start form if one is associated. | | variables | `any` | | Variables to attach to the payload. | diff --git a/docs/process-services-cloud/components/task-form-cloud.component.md b/docs/process-services-cloud/components/task-form-cloud.component.md index 9b69fe39e8..e8ace6ad1e 100644 --- a/docs/process-services-cloud/components/task-form-cloud.component.md +++ b/docs/process-services-cloud/components/task-form-cloud.component.md @@ -44,6 +44,7 @@ Save and Complete buttons get disabled when at least one of the form's inputs ar | showValidationIcon | `boolean` | true | Toggle rendering of the `Validation` icon. | | taskId | `string` | | Task id to fetch corresponding form and values. | | displayModeConfigurations | `FormCloudDisplayModeConfiguration[]` | | The available display configurations for the form | +| enableParentVisibilityCheck | `boolean` | false | Toggle to enable parent visibility check for validation. When enabled, fields inside hidden groups/sections will skip validation. | ### Events diff --git a/docs/process-services-cloud/components/user-task-cloud.component.md b/docs/process-services-cloud/components/user-task-cloud.component.md index 2170128d4b..da4beee6c2 100644 --- a/docs/process-services-cloud/components/user-task-cloud.component.md +++ b/docs/process-services-cloud/components/user-task-cloud.component.md @@ -45,6 +45,7 @@ Based on property taskDetails: TaskDetailsCloudModel shows a form or a screen. | showValidationIcon | `boolean` | true | Toggle rendering of the `Validation` icon. | | taskId | `string` | | Task id to fetch corresponding form and values. | | displayModeConfigurations | `FormCloudDisplayModeConfiguration[]` | | The available display configurations for the form | +| enableParentVisibilityCheck | `boolean` | false | Toggle to enable parent visibility check for validation. When enabled, fields inside hidden groups/sections will skip validation. | ### Events diff --git a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts index c426423ec0..76ccf04919 100644 --- a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts @@ -461,6 +461,138 @@ describe('FormCloudComponent', () => { expect(formComponent.getFormById).not.toHaveBeenCalled(); }); + describe('enableParentVisibilityCheck', () => { + let formModel: FormModel; + let field1: FormFieldModel; + let field2: FormFieldModel; + + beforeEach(() => { + const formJson = { + id: 'test-form', + name: 'Test Form', + fields: [ + { + id: 'group1', + type: FormFieldTypes.GROUP, + numberOfColumns: 1, + fields: { + 1: [ + { id: 'field1', type: FormFieldTypes.TEXT, required: true }, + { id: 'field2', type: FormFieldTypes.TEXT, required: true } + ] + } + } + ] + }; + formModel = new FormModel(formJson); + formComponent.form = formModel; + field1 = formModel.getFieldById('field1'); + field2 = formModel.getFieldById('field2'); + }); + + it('should set flags on form and fields when enableParentVisibilityCheck changes from false to true', () => { + formComponent.enableParentVisibilityCheck = false; + expect(formModel.enableParentVisibilityCheck).toBe(false); + expect(field1.checkParentVisibilityForValidation).toBe(false); + expect(field2.checkParentVisibilityForValidation).toBe(false); + + const change = new SimpleChange(false, true, false); + formComponent.enableParentVisibilityCheck = true; + spyOn(formModel, 'validateForm').and.stub(); + + formComponent.ngOnChanges({ enableParentVisibilityCheck: change }); + + expect(formModel.enableParentVisibilityCheck).toBe(true); + expect(field1.checkParentVisibilityForValidation).toBe(true); + expect(field2.checkParentVisibilityForValidation).toBe(true); + expect(formModel.validateForm).toHaveBeenCalled(); + }); + + it('should not set flags when enableParentVisibilityCheck changes but form is null', () => { + formComponent.form = null; + formComponent.enableParentVisibilityCheck = false; + + const change = new SimpleChange(false, true, false); + formComponent.ngOnChanges({ enableParentVisibilityCheck: change }); + + expect(formComponent.form).toBeNull(); + }); + + it('should reset flags when enableParentVisibilityCheck changes from true to false', () => { + formModel.enableParentVisibilityCheck = true; + field1.checkParentVisibilityForValidation = true; + field2.checkParentVisibilityForValidation = true; + formComponent.enableParentVisibilityCheck = false; + + const change = new SimpleChange(true, false, false); + spyOn(formModel, 'validateForm').and.stub(); + + formComponent.ngOnChanges({ enableParentVisibilityCheck: change }); + + expect(formModel.enableParentVisibilityCheck).toBe(false); + expect(field1.checkParentVisibilityForValidation).toBe(false); + expect(field2.checkParentVisibilityForValidation).toBe(false); + expect(formModel.validateForm).toHaveBeenCalled(); + }); + + it('should set flags on all fields including nested fields', () => { + const formJsonWithNestedFields = { + id: 'test-form', + name: 'Test Form', + fields: [ + { + id: 'group1', + type: FormFieldTypes.GROUP, + numberOfColumns: 1, + fields: { + 1: [{ id: 'field1', type: FormFieldTypes.TEXT }] + } + }, + { + id: 'section1', + type: FormFieldTypes.SECTION, + numberOfColumns: 1, + fields: { + 1: [ + { + id: 'group2', + type: FormFieldTypes.GROUP, + numberOfColumns: 1, + fields: { + 1: [{ id: 'field2', type: FormFieldTypes.TEXT }] + } + } + ] + } + }, + { id: 'field3', type: FormFieldTypes.TEXT } + ] + }; + const nestedFormModel = new FormModel(formJsonWithNestedFields); + formComponent.form = nestedFormModel; + formComponent.enableParentVisibilityCheck = true; + + const change = new SimpleChange(false, true, false); + formComponent.ngOnChanges({ enableParentVisibilityCheck: change }); + + const allFields = nestedFormModel.getFormFields(); + allFields.forEach((field) => { + expect(field.checkParentVisibilityForValidation).toBe(true); + }); + expect(nestedFormModel.enableParentVisibilityCheck).toBe(true); + }); + + it('should re-validate form after setting flags', () => { + formComponent.enableParentVisibilityCheck = true; + spyOn(formModel, 'validateForm').and.stub(); + + const change = new SimpleChange(false, true, false); + formComponent.ngOnChanges({ enableParentVisibilityCheck: change }); + + expect(formModel.validateForm).toHaveBeenCalled(); + }); + }); + it('should complete form on custom outcome click', () => { const formModel = new FormModel(); const outcomeName = 'Custom Action'; diff --git a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts index f121e76041..7cc5e00333 100644 --- a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts @@ -135,6 +135,13 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, @Input() customCompleteButtonText: string = ''; + /** + * Toggle to enable parent visibility check for validation. + * When enabled, fields inside hidden groups/sections will skip validation. + */ + @Input() + enableParentVisibilityCheck: boolean = false; + /** Emitted when the form is submitted with the `Save` or custom outcomes. */ @Output() formSaved = new EventEmitter(); @@ -254,6 +261,12 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, this.onFormLoaded(this.form); return; } + + const enableParentVisibilityCheckChange = changes['enableParentVisibilityCheck']; + if (enableParentVisibilityCheckChange && this.form) { + this.setCheckParentVisibilityForValidationOnFields(); + this.form.validateForm(); + } } ngOnInit(): void { @@ -338,8 +351,9 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, const parsedForm = this.parseForm(this.formCloudRepresentationJSON); this.visibilityService.refreshVisibility(parsedForm, this.data); - parsedForm.validateForm(); this.form = parsedForm; + this.setCheckParentVisibilityForValidationOnFields(); + parsedForm.validateForm(); this.form.nodeId = '-my-'; this.onFormLoaded(this.form); resolve(this.form); @@ -369,8 +383,9 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, this.formCloudRepresentationJSON.processVariables = this.data || []; const parsedForm = this.parseForm(form); this.visibilityService.refreshVisibility(parsedForm); - parsedForm?.validateForm(); this.form = parsedForm; + this.setCheckParentVisibilityForValidationOnFields(); + parsedForm?.validateForm(); this.form.nodeId = '-my-'; this.onFormLoaded(this.form); }, @@ -467,11 +482,29 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, private refreshFormData() { this.form = this.parseForm(this.formCloudRepresentationJSON); if (this.form) { + this.setCheckParentVisibilityForValidationOnFields(); this.onFormLoaded(this.form); this.onFormDataRefreshed(this.form); } } + /** + * Sets the parent visibility check flag on all form fields. + * When enabled, fields inside hidden groups/sections will skip validation. + * When disabled, resets flags to false to revert to default behavior. + */ + private setCheckParentVisibilityForValidationOnFields(): void { + if (!this.form) { + return; + } + + this.form.enableParentVisibilityCheck = this.enableParentVisibilityCheck; + const allFields = this.form.getFormFields(); + allFields.forEach((field) => { + field.checkParentVisibilityForValidation = this.enableParentVisibilityCheck; + }); + } + protected onFormLoaded(form: FormModel) { if (form) { this.displayModeConfigurations = this.displayModeService.getDisplayModeConfigurations(this.displayModeConfigurations); diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html index 9f8fa48947..5293ce43d9 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html @@ -96,6 +96,7 @@ [data]="resolvedValues" [formId]="processDefinitionCurrent.formKey" [displayModeConfigurations]="displayModeConfigurations" + [enableParentVisibilityCheck]="enableParentVisibilityCheck" [showSaveButton]="showSaveButton" [showCompleteButton]="showCompleteButton" [showRefreshButton]="false" diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts index d14682df64..a8695a818c 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts @@ -21,6 +21,7 @@ import { FormModel, FormOutcomeEvent, FormOutcomeModel } from '@alfresco/adf-cor import { of, throwError } from 'rxjs'; import { StartProcessCloudService } from '../services/start-process-cloud.service'; import { FormCloudService } from '../../../form/services/form-cloud.service'; +import { FormCloudComponent } from '../../../form/components/form-cloud.component'; import { StartProcessCloudComponent } from './start-process-cloud.component'; import { fakeProcessDefinitions, @@ -629,6 +630,51 @@ describe('StartProcessCloudComponent', () => { expect(processForm).not.toBeNull(); }); + it('should pass enableParentVisibilityCheck to adf-cloud-form', async () => { + getProcessDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[2]])); + formDefinitionSpy.and.returnValue(of(fakeStartForm)); + component.enableParentVisibilityCheck = true; + const change = new SimpleChange('myApp', 'myApp1', true); + component.ngOnChanges({ appName: change }); + fixture.detectChanges(); + await fixture.whenStable(); + + await selectOptionByName('processwithform'); + + component.processDefinitionName = fakeProcessDefinitions[2].name; + component.setProcessDefinitionOnForm(fakeProcessDefinitions[2].name); + fixture.detectChanges(); + await fixture.whenStable(); + + const cloudFormElement = fixture.debugElement.query(By.css('adf-cloud-form')); + expect(cloudFormElement).toBeDefined(); + const cloudFormComponent = cloudFormElement.componentInstance as FormCloudComponent; + expect(cloudFormComponent).toBeDefined(); + expect(cloudFormComponent.enableParentVisibilityCheck).toBe(true); + }); + + it('should pass enableParentVisibilityCheck as false by default to adf-cloud-form', async () => { + getProcessDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[2]])); + formDefinitionSpy.and.returnValue(of(fakeStartForm)); + const change = new SimpleChange('myApp', 'myApp1', true); + component.ngOnChanges({ appName: change }); + fixture.detectChanges(); + await fixture.whenStable(); + + await selectOptionByName('processwithform'); + + component.processDefinitionName = fakeProcessDefinitions[2].name; + component.setProcessDefinitionOnForm(fakeProcessDefinitions[2].name); + fixture.detectChanges(); + await fixture.whenStable(); + + const cloudFormElement = fixture.debugElement.query(By.css('adf-cloud-form')); + expect(cloudFormElement).toBeDefined(); + const cloudFormComponent = cloudFormElement.componentInstance as FormCloudComponent; + expect(cloudFormComponent).toBeDefined(); + expect(cloudFormComponent.enableParentVisibilityCheck).toBe(false); + }); + it('should not select automatically any processDefinition if the app contain multiple process and does not have any processDefinition as input', async () => { getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions)); component.appName = 'myApp'; diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts index 61bfaaed6a..78ddabbf49 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts @@ -142,6 +142,13 @@ export class StartProcessCloudComponent implements OnChanges, OnInit { @Input() displayModeConfigurations: FormCloudDisplayModeConfiguration[]; + /** + * Toggle to enable parent visibility check for validation. + * When enabled, fields inside hidden groups/sections will skip validation. + */ + @Input() + enableParentVisibilityCheck: boolean = false; + /** Emitted when the process is successfully started. */ @Output() success = new EventEmitter(); diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html index 0d9a3c64c7..6fe34da70c 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html @@ -14,6 +14,7 @@ [customSaveButtonText]="customSaveButtonText" [customCompleteButtonText]="customCompleteButtonText" [displayModeConfigurations]="displayModeConfigurations" + [enableParentVisibilityCheck]="enableParentVisibilityCheck" (formLoaded)="onFormLoaded($event)" (formSaved)="onFormSaved($event)" (formCompleted)="onFormCompleted($event)" diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.spec.ts index 965976e097..43258b1837 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.spec.ts @@ -276,6 +276,31 @@ describe('TaskFormCloudComponent', () => { expect(cloudFormComponent).toBeDefined(); expect(cloudFormComponent.customCompleteButtonText).toBe(customText); }); + + it('should pass enableParentVisibilityCheck to adf-cloud-form', () => { + component.appName = 'app1'; + component.taskId = 'task1'; + component.enableParentVisibilityCheck = true; + fixture.detectChanges(); + + const cloudFormElement = fixture.debugElement.query(By.css('adf-cloud-form')); + expect(cloudFormElement).toBeDefined(); + const cloudFormComponent = cloudFormElement.componentInstance as FormCloudComponent; + expect(cloudFormComponent).toBeDefined(); + expect(cloudFormComponent.enableParentVisibilityCheck).toBe(true); + }); + + it('should pass enableParentVisibilityCheck as false by default to adf-cloud-form', () => { + component.appName = 'app1'; + component.taskId = 'task1'; + fixture.detectChanges(); + + const cloudFormElement = fixture.debugElement.query(By.css('adf-cloud-form')); + expect(cloudFormElement).toBeDefined(); + const cloudFormComponent = cloudFormElement.componentInstance as FormCloudComponent; + expect(cloudFormComponent).toBeDefined(); + expect(cloudFormComponent.enableParentVisibilityCheck).toBe(false); + }); }); describe('Events', () => { diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts index 33bb10d48c..5e453db1ce 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts @@ -107,6 +107,13 @@ export class TaskFormCloudComponent { @Input() displayModeConfigurations: FormCloudDisplayModeConfiguration[]; + /** + * Toggle to enable parent visibility check for validation. + * When enabled, fields inside hidden groups/sections will skip validation. + */ + @Input() + enableParentVisibilityCheck: boolean = false; + /** Task details. */ @Input() taskDetails: TaskDetailsCloudModel; diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html index e5e408b5f2..80a15f8e0e 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html @@ -8,6 +8,7 @@ [candidateUsers]="candidateUsers" [candidateGroups]="candidateGroups" [displayModeConfigurations]="displayModeConfigurations" + [enableParentVisibilityCheck]="enableParentVisibilityCheck" [showValidationIcon]="showValidationIcon" [showTitle]="showTitle" [taskId]="taskId" diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts index 5ffae88624..cd81be2f48 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts @@ -736,6 +736,25 @@ describe('UserTaskCloudComponent', () => { expect(taskFormComponent.customCompleteButtonText).toBe(customText); }); + + it('should pass enableParentVisibilityCheck to task form when task type is Form', () => { + fixture.componentRef.setInput('enableParentVisibilityCheck', true); + fixture.detectChanges(); + + const taskFormElement = fixture.debugElement.query(By.css('adf-cloud-task-form')); + const taskFormComponent = taskFormElement?.componentInstance as TaskFormCloudComponent; + + expect(taskFormComponent.enableParentVisibilityCheck).toBe(true); + }); + + it('should pass enableParentVisibilityCheck as false by default to task form', () => { + fixture.detectChanges(); + + const taskFormElement = fixture.debugElement.query(By.css('adf-cloud-task-form')); + const taskFormComponent = taskFormElement?.componentInstance as TaskFormCloudComponent; + + expect(taskFormComponent.enableParentVisibilityCheck).toBe(false); + }); }); }); diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts index 9e3530d4f8..648d1dcd53 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts @@ -67,6 +67,13 @@ export class UserTaskCloudComponent implements OnInit, OnChanges { @Input() displayModeConfigurations: FormCloudDisplayModeConfiguration[]; + /** + * Toggle to enable parent visibility check for validation. + * When enabled, fields inside hidden groups/sections will skip validation. + */ + @Input() + enableParentVisibilityCheck: boolean = false; + /** Toggle readonly state of the task. */ @Input() readOnly = false;