From 1e692252a574dc8b7202ff744dc27e99594022d1 Mon Sep 17 00:00:00 2001 From: davidcanonieto Date: Thu, 30 Jul 2020 17:05:10 +0100 Subject: [PATCH] [AAE-612] Support for Form Outcome Visibility Conditions (#5934) --- .../widgets/core/form-outcome.model.spec.ts | 21 ++++++++++++++++- .../widgets/core/form-outcome.model.ts | 4 ++++ .../components/widgets/core/tab.model.spec.ts | 2 +- .../widget-visibility-cloud.service.spec.ts | 23 +++++++++++++++++-- .../services/widget-visibility.service.ts | 10 +++++++- .../form/components/form-cloud.component.html | 14 ++++++----- .../components/form-cloud.component.spec.ts | 23 +++++++++++++++++++ .../src/lib/form/mocks/cloud-form.mock.ts | 16 ++++++++++++- 8 files changed, 101 insertions(+), 12 deletions(-) diff --git a/lib/core/form/components/widgets/core/form-outcome.model.spec.ts b/lib/core/form/components/widgets/core/form-outcome.model.spec.ts index 6ccc497661..ab96d4e2a3 100644 --- a/lib/core/form/components/widgets/core/form-outcome.model.spec.ts +++ b/lib/core/form/components/widgets/core/form-outcome.model.spec.ts @@ -23,11 +23,30 @@ describe('FormOutcomeModel', () => { it('should setup with json config', () => { const json = { id: '', - name: '' + name: '', + visibilityCondition: { + leftType: 'field', + leftValue: 'TextOne', + operator: '==', + rightValue: 'showTab', + rightType: 'value', + nextConditionOperator: '', + nextCondition: null + } }; const model = new FormOutcomeModel(null, json); expect(model.id).toBe(json.id); expect(model.name).toBe(json.name); + expect(model.visibilityCondition).toBeDefined(); + }); + + it('should not setup with null json config', () => { + const model = new FormOutcomeModel(null, null); + expect(model.id).toBeUndefined(); + expect(model.name).toBeUndefined(); + expect(model.isVisible).toBeDefined(); + expect(model.isVisible).toBe(true); + expect(model.visibilityCondition).toBeUndefined(); }); it('should store the form reference', () => { diff --git a/lib/core/form/components/widgets/core/form-outcome.model.ts b/lib/core/form/components/widgets/core/form-outcome.model.ts index 31969c2421..4ed629adf3 100644 --- a/lib/core/form/components/widgets/core/form-outcome.model.ts +++ b/lib/core/form/components/widgets/core/form-outcome.model.ts @@ -19,6 +19,7 @@ import { FormWidgetModel } from './form-widget.model'; import { FormModel } from './form.model'; +import { WidgetVisibilityModel } from './../../../models/widget-visibility.model'; export class FormOutcomeModel extends FormWidgetModel { @@ -28,6 +29,8 @@ export class FormOutcomeModel extends FormWidgetModel { isSystem: boolean = false; isSelected: boolean = false; + isVisible: boolean = true; + visibilityCondition: WidgetVisibilityModel; constructor(form: FormModel, json?: any) { super(form, json); @@ -35,6 +38,7 @@ export class FormOutcomeModel extends FormWidgetModel { if (json) { this.isSystem = json.isSystem ? true : false; this.isSelected = form && json.name === form.selectedOutcome ? true : false; + this.visibilityCondition = new WidgetVisibilityModel(json.visibilityCondition); } } } diff --git a/lib/core/form/components/widgets/core/tab.model.spec.ts b/lib/core/form/components/widgets/core/tab.model.spec.ts index 1f313b0964..517b0298ee 100644 --- a/lib/core/form/components/widgets/core/tab.model.spec.ts +++ b/lib/core/form/components/widgets/core/tab.model.spec.ts @@ -35,7 +35,7 @@ describe('TabModel', () => { expect(model.isVisible).toBe(true); }); - it('should not setup with json config', () => { + it('should not setup with null json config', () => { const model = new TabModel(null, null); expect(model.id).toBeUndefined(); expect(model.title).toBeUndefined(); diff --git a/lib/core/form/services/widget-visibility-cloud.service.spec.ts b/lib/core/form/services/widget-visibility-cloud.service.spec.ts index 92ddb5d89e..5985ece2b7 100644 --- a/lib/core/form/services/widget-visibility-cloud.service.spec.ts +++ b/lib/core/form/services/widget-visibility-cloud.service.spec.ts @@ -21,7 +21,8 @@ import { FormFieldModel, FormFieldTypes, FormModel, - TabModel + TabModel, + FormOutcomeModel } from './../components/widgets/core/index'; import { TaskProcessVariableModel } from './../models/task-process-variable.model'; import { WidgetVisibilityModel, WidgetTypeEnum } from './../models/widget-visibility.model'; @@ -31,7 +32,7 @@ import { fakeFormJson, fakeTaskProcessVariableModels, formTest, formValues, complexVisibilityJsonVisible, nextConditionForm, complexVisibilityJsonNotVisible, - headerVisibilityCond } from 'core/mock/form/widget-visibility-cloud.service.mock'; + headerVisibilityCond } from '../../mock/form/widget-visibility-cloud.service.mock'; import { CoreTestingModule } from '../../testing/core.testing.module'; import { TranslateModule } from '@ngx-translate/core'; @@ -672,6 +673,24 @@ describe('WidgetVisibilityCloudService', () => { expect(fakeFormWithField.tabs[0].isVisible).toBeFalsy(); }); + it('should refresh the visibility for Outcomes in forms', () => { + visibilityObjTest.leftType = WidgetTypeEnum.field; + visibilityObjTest.leftValue = 'FIELD_TEST'; + visibilityObjTest.operator = '!='; + visibilityObjTest.rightValue = 'RIGHT_FORM_FIELD_VALUE'; + const outcome = new FormOutcomeModel(fakeFormWithField, { + isSystem: false, + isSelected: false, + isVisible: true + }); + + outcome.visibilityCondition = visibilityObjTest; + fakeFormWithField.outcomes.push(outcome); + service.refreshVisibility(fakeFormWithField); + const outcomeIndex = fakeFormWithField.outcomes.length - 1; + expect(fakeFormWithField.outcomes[outcomeIndex].isVisible).toBeFalsy(); + }); + it('should use the form value to evaluate the visibility condition if the form value is defined', (done) => { service.getTaskProcessVariable('9999').subscribe( (res: TaskProcessVariableModel[]) => { diff --git a/lib/core/form/services/widget-visibility.service.ts b/lib/core/form/services/widget-visibility.service.ts index af080159f1..cf785bbab8 100644 --- a/lib/core/form/services/widget-visibility.service.ts +++ b/lib/core/form/services/widget-visibility.service.ts @@ -20,7 +20,7 @@ import { LogService } from '../../services/log.service'; import { Injectable } from '@angular/core'; import moment from 'moment-es6'; import { Observable, from, throwError } from 'rxjs'; -import { FormFieldModel, FormModel, TabModel, ContainerModel } from '../components/widgets/core/index'; +import { FormFieldModel, FormModel, TabModel, ContainerModel, FormOutcomeModel } from '../components/widgets/core/index'; import { TaskProcessVariableModel } from '../models/task-process-variable.model'; import { WidgetVisibilityModel, WidgetTypeEnum } from '../models/widget-visibility.model'; import { map, catchError } from 'rxjs/operators'; @@ -43,6 +43,10 @@ export class WidgetVisibilityService { form.tabs.map((tabModel) => this.refreshEntityVisibility(tabModel)); } + if (form && form.outcomes && form.outcomes.length > 0) { + form.outcomes.map((outcomeModel) => this.refreshOutcomeVisibility(outcomeModel)); + } + if (form) { form.getFormFields().map((field) => this.refreshEntityVisibility(field)); } @@ -53,6 +57,10 @@ export class WidgetVisibilityService { element.isVisible = visible && this.isParentTabVisible(this.form, element); } + refreshOutcomeVisibility(element: FormOutcomeModel) { + element.isVisible = this.evaluateVisibility(element.form, element.visibilityCondition); + } + evaluateVisibility(form: FormModel, visibilityObj: WidgetVisibilityModel): boolean { const isLeftFieldPresent = visibilityObj && (visibilityObj.leftType || visibilityObj.leftValue); if (!isLeftFieldPresent || isLeftFieldPresent === 'null') { diff --git a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.html b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.html index daafd50875..5c1bc1c342 100644 --- a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.html +++ b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.html @@ -36,12 +36,14 @@ - + + + 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 bc276613a8..e5433522fb 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 @@ -991,6 +991,29 @@ describe('FormCloudComponent', () => { const label = fixture.debugElement.query(By.css(`${container} label`)); expect(label.nativeElement.innerText).toEqual('Attach file'); }); + + it('should be able to set visibility conditions for Outcomes', async () => { + spyOn(formCloudService, 'getForm').and.returnValue(of(conditionalUploadWidgetsMock)); + const formId = '123'; + const appName = 'test-app'; + formComponent.formId = formId; + formComponent.appVersion = 1; + + formComponent.ngOnChanges({ 'appName': new SimpleChange(null, appName, true) }); + expect(formCloudService.getForm).toHaveBeenCalledWith(appName, formId, 1); + + fixture.detectChanges(); + let outcome = fixture.debugElement.query(By.css(`#adf-form-custom_outcome`)); + expect(outcome).toBeNull(); + + const inputElement = fixture.debugElement.query(By.css('[id="field-Text0xlk8n-container"] input')); + inputElement.nativeElement.value = 'hi'; + inputElement.nativeElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + + outcome = fixture.debugElement.query(By.css(`#adf-form-custom_outcome`)); + expect(outcome.nativeElement.innerText).toEqual('CUSTOM OUTCOME'); + }); }); describe('Multilingual Form', () => { diff --git a/lib/process-services-cloud/src/lib/form/mocks/cloud-form.mock.ts b/lib/process-services-cloud/src/lib/form/mocks/cloud-form.mock.ts index b072b5145d..bbe762b9a7 100644 --- a/lib/process-services-cloud/src/lib/form/mocks/cloud-form.mock.ts +++ b/lib/process-services-cloud/src/lib/form/mocks/cloud-form.mock.ts @@ -826,7 +826,21 @@ export const conditionalUploadWidgetsMock = { } } ], - 'outcomes': [], + 'outcomes': [ + { + 'id': '5f2f1c2d-5a79-4ed1-a262-4fef190d41eb', + 'name': 'Custom Outcome', + 'visibilityCondition': { + 'leftType': 'field', + 'leftValue': 'Text0xlk8n', + 'operator': '==', + 'rightValue': 'hi', + 'rightType': 'value', + 'nextConditionOperator': '', + 'nextCondition': null + } + } + ], 'metadata': {}, 'variables': [] }