diff --git a/lib/core/src/lib/form/components/form-renderer.component.spec.ts b/lib/core/src/lib/form/components/form-renderer.component.spec.ts index 34606c6c41..b1e6c255e1 100644 --- a/lib/core/src/lib/form/components/form-renderer.component.spec.ts +++ b/lib/core/src/lib/form/components/form-renderer.component.spec.ts @@ -815,7 +815,7 @@ describe('Form Renderer Component', () => { params: { initialNumberOfRows: 2, allowInitialRowsDelete: true, - newRowsLimit: 1 + maxNumberOfRows: 3 }, numberOfColumns: 2, fields: { diff --git a/lib/core/src/lib/form/components/mock/form-renderer.component.mock.ts b/lib/core/src/lib/form/components/mock/form-renderer.component.mock.ts index 58f5e60509..aaaa9b551a 100644 --- a/lib/core/src/lib/form/components/mock/form-renderer.component.mock.ts +++ b/lib/core/src/lib/form/components/mock/form-renderer.component.mock.ts @@ -2332,7 +2332,7 @@ export const mockRepeatableSectionForm01 = { params: { initialNumberOfRows: 2, allowInitialRowsDelete: true, - newRowsLimit: 3 + maxNumberOfRows: 5 }, numberOfColumns: 2, fields: { @@ -2403,7 +2403,7 @@ export const mockRepeatableSectionForm02 = { params: { initialNumberOfRows: 2, allowInitialRowsDelete: false, - newRowsLimit: 3 + maxNumberOfRows: 5 }, numberOfColumns: 2, fields: { 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 8d963f45e0..91eb41beff 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 @@ -1492,6 +1492,16 @@ describe('FormFieldModel', () => { field.addRow(field.fields, form); expect(field.rows.length).toBe(5); }); + + it('should call onRepeatableSectionChanged', () => { + spyOn(field.form, 'onRepeatableSectionChanged').and.callThrough(); + + expect(field.form.onRepeatableSectionChanged).not.toHaveBeenCalled(); + + field.addRow(field.fields, form); + + expect(field.form.onRepeatableSectionChanged).toHaveBeenCalled(); + }); }); describe('remove row', () => { @@ -1560,6 +1570,16 @@ describe('FormFieldModel', () => { expect(field.form.onFormFieldChanged).not.toHaveBeenCalled(); }); + + it('should call onRepeatableSectionChanged', () => { + spyOn(field.form, 'onRepeatableSectionChanged').and.callThrough(); + + expect(field.form.onRepeatableSectionChanged).not.toHaveBeenCalled(); + + field.removeRow(1); + + expect(field.form.onRepeatableSectionChanged).toHaveBeenCalled(); + }); }); describe('disabled state', () => { 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 cacbd6009a..e5a0e00362 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 @@ -33,6 +33,7 @@ import { isValid as isValidDate } from 'date-fns'; import { ContainerRowModel } from './container-row.model'; import { RepeatableSectionModel, ROW_ID_PREFIX } from './repeatable-section.model'; import { formFieldRuleHandler } from './handlers/form-field-rule.handler'; +import { formFieldVisibilityConditionHandler } from './handlers/form-field-visibility-condition.handler'; export type FieldOptionType = 'rest' | 'manual' | 'variable'; export type FieldSelectionType = 'single' | 'multiple'; @@ -217,7 +218,7 @@ export class FormFieldModel extends FormWidgetModel { this.params = json.params || {}; this.hyperlinkUrl = json.hyperlinkUrl; this.displayText = json.displayText; - this.visibilityCondition = json.visibilityCondition ? new WidgetVisibilityModel(json.visibilityCondition) : undefined; + this.visibilityCondition = formFieldVisibilityConditionHandler.getVisibilityCondition(this.id, json.visibilityCondition, parent); this.enableFractions = json.enableFractions; this.currency = json.currency; this.dateDisplayFormat = json.dateDisplayFormat || this.getDefaultDateFormat(json); @@ -354,7 +355,7 @@ export class FormFieldModel extends FormWidgetModel { this.columns = this.rows[0].columns; } - private getNumberOfRows(initialNrRows: number = 1, maxNrRows?: number, value?: any) { + private getNumberOfRows(initialNrRows: number = 1, maxNrRows: number | null = null, value?: any) { return value?.length ? (maxNrRows ? Math.min(value.length, maxNrRows) : value.length) : initialNrRows; } @@ -469,6 +470,7 @@ export class FormFieldModel extends FormWidgetModel { } this.rows.push(this.createRow(fields, form, this.rows.length)); + this.form.onRepeatableSectionChanged(); } private shouldAddRow(): boolean { @@ -482,6 +484,7 @@ export class FormFieldModel extends FormWidgetModel { this.rows.splice(index, 1); this.updateChildrenFieldsRowIndex(); + this.form.onRepeatableSectionChanged(); if (!this.form.values[this.id]) { return; 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 2654453061..009b4e5103 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 @@ -147,6 +147,10 @@ export class FormModel implements ProcessFormModel { } } + onRepeatableSectionChanged() { + this.fieldsCache = this.getFormFields([], true); + } + /** * Validates entire form and all form fields. */ @@ -343,8 +347,8 @@ export class FormModel implements ProcessFormModel { return this.fieldsCache.find((field) => field.id === fieldId); } - getFormFields(filterTypes?: string[]): FormFieldModel[] { - if (this.fieldsCache?.length) { + getFormFields(filterTypes?: string[], isDynamic: boolean = false): FormFieldModel[] { + if (this.fieldsCache?.length && !isDynamic) { return this.filterFieldsByType(this.fieldsCache, filterTypes); } @@ -355,7 +359,9 @@ export class FormModel implements ProcessFormModel { private processFields(fields: (ContainerModel | FormFieldModel)[], formFieldModel: FormFieldModel[]): void { fields.forEach((field) => { - if (this.isContainerField(field)) { + if (this.isRepeatableSectionField(field)) { + this.handleRepeatableSectionField(field, formFieldModel); + } else if (this.isContainerField(field)) { this.handleContainerField(field, formFieldModel); } else if (this.isSectionField(field)) { this.handleSectionField(field, formFieldModel); @@ -377,6 +383,10 @@ export class FormModel implements ProcessFormModel { return field.type === FormFieldTypes.SECTION; } + private isRepeatableSectionField(field: ContainerModel | FormFieldModel): field is ContainerModel { + return field.type === FormFieldTypes.REPEATABLE_SECTION; + } + private handleSectionField(section: FormFieldModel, formFieldModel: FormFieldModel[]): void { formFieldModel.push(section); section.columns.forEach((column) => { @@ -384,6 +394,15 @@ export class FormModel implements ProcessFormModel { }); } + private handleRepeatableSectionField(repeatableSection: ContainerModel, formFieldModel: FormFieldModel[]): void { + formFieldModel.push(repeatableSection.field); + for (const row of repeatableSection.field.rows) { + for (const column of row.columns) { + this.processFields(column.fields, formFieldModel); + } + } + } + private handleContainerField(container: ContainerModel, formFieldModel: FormFieldModel[]): void { formFieldModel.push(container.field); container.field.columns.forEach((column) => { diff --git a/lib/core/src/lib/form/components/widgets/core/handlers/form-field-rule.handler.spec.ts b/lib/core/src/lib/form/components/widgets/core/handlers/form-field-rule.handler.spec.ts index b63daf75c6..503ffd0dfd 100644 --- a/lib/core/src/lib/form/components/widgets/core/handlers/form-field-rule.handler.spec.ts +++ b/lib/core/src/lib/form/components/widgets/core/handlers/form-field-rule.handler.spec.ts @@ -17,13 +17,8 @@ import { formFieldRuleHandler } from './form-field-rule.handler'; import { FormFieldRule } from '../form-field-rule'; -import { - mockParentRule, - mockParentWithFields, - mockParentWithoutFields, - mockParentWithSectionFields, - mockRule -} from '../mocks/form-field-rule.handler.mock'; +import { mockParentRule, mockRule } from '../mocks/form-field-rule.handler.mock'; +import { mockParentWithoutFields, mockParentWithFields, mockParentWithSectionFields } from '../mocks/repeatable-section-model.mock'; describe('formFieldRuleHandler', () => { it('should return null if provided rule is null', () => { diff --git a/lib/core/src/lib/form/components/widgets/core/handlers/form-field-rule.handler.ts b/lib/core/src/lib/form/components/widgets/core/handlers/form-field-rule.handler.ts index 127bde6f86..c4c00c9547 100644 --- a/lib/core/src/lib/form/components/widgets/core/handlers/form-field-rule.handler.ts +++ b/lib/core/src/lib/form/components/widgets/core/handlers/form-field-rule.handler.ts @@ -31,13 +31,7 @@ const getRuleOn = (id: string, ruleOn: string, fields: any): string => { for (const column of Object.values(fields)) { for (const field of column as any) { if (field.type === FormFieldTypes.SECTION) { - for (const sectionColumn of Object.values(field.fields)) { - for (const sectionField of sectionColumn as any) { - if (sectionField.id === ruleOn) { - return getRepeatableSectionChildRuleOn(id, ruleOn); - } - } - } + return getRuleOn(id, ruleOn, field.fields); } if (field.id === ruleOn) { diff --git a/lib/core/src/lib/form/components/widgets/core/handlers/form-field-visibility-condition.handler.spec.ts b/lib/core/src/lib/form/components/widgets/core/handlers/form-field-visibility-condition.handler.spec.ts new file mode 100644 index 0000000000..9f97a7f47c --- /dev/null +++ b/lib/core/src/lib/form/components/widgets/core/handlers/form-field-visibility-condition.handler.spec.ts @@ -0,0 +1,238 @@ +/*! + * @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 { formFieldVisibilityConditionHandler } from './form-field-visibility-condition.handler'; +import { mockParentWithoutFields, mockParentWithFields, mockParentWithSectionFields } from '../mocks/repeatable-section-model.mock'; +import { + mockParentVisibilityCondition, + mockParentVisibilityConditionWithLeftValue, + mockParentVisibilityConditionWithRightValue, + mockVisibilityCondition, + mockVisibilityConditionLeftValue, + mockVisibilityConditionRightValue +} from '../mocks/form-field-visibility-condition.handler.mock'; +import { WidgetVisibilityModel } from '../../../../models/widget-visibility.model'; + +describe('formFieldVisibilityConditionHandler', () => { + it('should return undefined if provided visibility condition json is null', () => { + expect(formFieldVisibilityConditionHandler.getVisibilityCondition('mock-id', null)).toBe(undefined); + }); + + it('should return undefined if provided visibility condition json is undefined', () => { + expect(formFieldVisibilityConditionHandler.getVisibilityCondition('mock-id', undefined)).toBe(undefined); + }); + + it('should return provided visibility condition if json is provided and no parent is provided', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + leftType: 'field', + leftValue: 'mock-left-value', + rightType: 'field', + rightValue: 'mock-right-value' + }); + + expect(formFieldVisibilityConditionHandler.getVisibilityCondition('mock-id', mockVisibilityCondition)).toEqual(expectedVisibilityCondition); + }); + + describe('rule and parent provided', () => { + describe('leftValue provided', () => { + it('should return provided visibility condition if parent has no fields', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + leftType: 'field', + leftValue: 'mock-left-value' + }); + + expect( + formFieldVisibilityConditionHandler.getVisibilityCondition( + 'mock-id-Row123456789', + mockVisibilityConditionLeftValue, + mockParentWithoutFields + ) + ).toEqual(expectedVisibilityCondition); + }); + + it('should return provided visibility condition if leftValue does not belong to parent fields', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + leftType: 'field', + leftValue: 'mock-left-value' + }); + + expect( + formFieldVisibilityConditionHandler.getVisibilityCondition( + 'mock-id-Row123456789', + mockVisibilityConditionLeftValue, + mockParentWithFields + ) + ).toEqual(expectedVisibilityCondition); + }); + it('should return visibility condition with parent leftValue property if leftValue belongs to parent fields', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + leftType: 'field', + leftValue: 'Text0c0ydk-Row123456789' + }); + + expect( + formFieldVisibilityConditionHandler.getVisibilityCondition( + 'mock-id-Row123456789', + mockParentVisibilityConditionWithLeftValue, + mockParentWithFields + ) + ).toEqual(expectedVisibilityCondition); + }); + + it('should return visibility condition with parent leftValue property if leftValue belongs to parent fields and sections are present', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + leftType: 'field', + leftValue: 'Text0c0ydk-Row123456789' + }); + + expect( + formFieldVisibilityConditionHandler.getVisibilityCondition( + 'mock-id-Row123456789', + mockParentVisibilityConditionWithLeftValue, + mockParentWithSectionFields + ) + ).toEqual(expectedVisibilityCondition); + }); + }); + + describe('rightValue provided', () => { + it('should return provided visibility condition if parent has no fields', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + rightType: 'field', + rightValue: 'mock-right-value' + }); + + expect( + formFieldVisibilityConditionHandler.getVisibilityCondition( + 'mock-id-Row123456789', + mockVisibilityConditionRightValue, + mockParentWithoutFields + ) + ).toEqual(expectedVisibilityCondition); + }); + + it('should return provided visibility condition if rightValue does not belong to parent fields', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + rightType: 'field', + rightValue: 'mock-right-value' + }); + + expect( + formFieldVisibilityConditionHandler.getVisibilityCondition( + 'mock-id-Row123456789', + mockVisibilityConditionRightValue, + mockParentWithFields + ) + ).toEqual(expectedVisibilityCondition); + }); + it('should return visibility condition with parent rightValue property if rightValue belongs to parent fields', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + rightType: 'field', + rightValue: 'Text0c0ydk-Row123456789' + }); + + expect( + formFieldVisibilityConditionHandler.getVisibilityCondition( + 'mock-id-Row123456789', + mockParentVisibilityConditionWithRightValue, + mockParentWithFields + ) + ).toEqual(expectedVisibilityCondition); + }); + + it('should return visibility condition with parent rightValue property if rightValue belongs to parent fields and sections are present', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + rightType: 'field', + rightValue: 'Text0c0ydk-Row123456789' + }); + + expect( + formFieldVisibilityConditionHandler.getVisibilityCondition( + 'mock-id-Row123456789', + mockParentVisibilityConditionWithRightValue, + mockParentWithSectionFields + ) + ).toEqual(expectedVisibilityCondition); + }); + }); + + describe('leftValue and rightValue provided', () => { + it('should return provided visibility condition if parent has no fields', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + leftType: 'field', + leftValue: 'mock-left-value', + rightType: 'field', + rightValue: 'mock-right-value' + }); + + expect( + formFieldVisibilityConditionHandler.getVisibilityCondition( + 'mock-id-Row123456789', + mockVisibilityCondition, + mockParentWithoutFields + ) + ).toEqual(expectedVisibilityCondition); + }); + + it('should return provided visibility condition if leftValue and rightValue do not belong to parent fields', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + leftType: 'field', + leftValue: 'mock-left-value', + rightType: 'field', + rightValue: 'mock-right-value' + }); + + expect( + formFieldVisibilityConditionHandler.getVisibilityCondition('mock-id-Row123456789', mockVisibilityCondition, mockParentWithFields) + ).toEqual(expectedVisibilityCondition); + }); + it('should return visibility condition with parent leftValue and rightValue property if values belong to parent fields', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + leftType: 'field', + leftValue: 'Text0c0ydk-Row123456789', + rightType: 'field', + rightValue: 'Text0c26k4-Row123456789' + }); + + expect( + formFieldVisibilityConditionHandler.getVisibilityCondition( + 'mock-id-Row123456789', + mockParentVisibilityCondition, + mockParentWithFields + ) + ).toEqual(expectedVisibilityCondition); + }); + + it('should return visibility condition with parent leftValue and rightValue property if values belong to parent fields and sections are present', () => { + const expectedVisibilityCondition = new WidgetVisibilityModel({ + leftType: 'field', + leftValue: 'Text0c0ydk-Row123456789', + rightType: 'field', + rightValue: 'Text0c26k4-Row123456789' + }); + + expect( + formFieldVisibilityConditionHandler.getVisibilityCondition( + 'mock-id-Row123456789', + mockParentVisibilityCondition, + mockParentWithSectionFields + ) + ).toEqual(expectedVisibilityCondition); + }); + }); + }); +}); diff --git a/lib/core/src/lib/form/components/widgets/core/handlers/form-field-visibility-condition.handler.ts b/lib/core/src/lib/form/components/widgets/core/handlers/form-field-visibility-condition.handler.ts new file mode 100644 index 0000000000..b2bc41f6b6 --- /dev/null +++ b/lib/core/src/lib/form/components/widgets/core/handlers/form-field-visibility-condition.handler.ts @@ -0,0 +1,70 @@ +/*! + * @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 { RepeatableSectionModel, ROW_ID_PREFIX } from '../repeatable-section.model'; +import { WidgetTypeEnum, WidgetVisibilityModel } from '../../../../models/widget-visibility.model'; +import { FormFieldTypes } from '../form-field-types'; + +const getVisibilityCondition = (id: string, json?: any, parent?: RepeatableSectionModel): WidgetVisibilityModel | undefined => { + if (!json) { + return undefined; + } + + return new WidgetVisibilityModel( + parent?.fields + ? { + ...json, + ...(shouldUpdateValue(json.leftType, json.leftValue) && { + leftValue: getValue(id, json.leftValue, parent.fields) + }), + ...(shouldUpdateValue(json.rightType, json.rightValue) && { + rightValue: getValue(id, json.rightValue, parent.fields) + }) + } + : json + ); +}; + +const shouldUpdateValue = (type: WidgetTypeEnum, value: string): boolean => type === WidgetTypeEnum.field && !!value; + +const getValue = (id: string, value: string, fields: any): string => { + for (const column of Object.values(fields)) { + for (const field of column as any) { + if (field.type === FormFieldTypes.SECTION) { + return getValue(id, value, field.fields); + } + + if (field.id === value) { + return getRepeatableSectionChildValue(id, value); + } + } + } + + return value; +}; + +const getRepeatableSectionChildValue = (id: string, value: string): string => value + ROW_ID_PREFIX + getRowId(id); + +const getRowId = (id: string) => { + const split = id.split(ROW_ID_PREFIX); + + return split.length > 1 ? split[1] : ''; +}; + +export const formFieldVisibilityConditionHandler = { + getVisibilityCondition +}; diff --git a/lib/core/src/lib/form/components/widgets/core/index.ts b/lib/core/src/lib/form/components/widgets/core/index.ts index 8b51b53e48..e36dec85cc 100644 --- a/lib/core/src/lib/form/components/widgets/core/index.ts +++ b/lib/core/src/lib/form/components/widgets/core/index.ts @@ -48,3 +48,4 @@ export * from './widget-schema.model'; export * from './theme.model'; export * from './predefined-theme'; export * from './displayable-cm-properties.model'; +export * from './repeatable-section.model'; diff --git a/lib/core/src/lib/form/components/widgets/core/mocks/form-field-rule.handler.mock.ts b/lib/core/src/lib/form/components/widgets/core/mocks/form-field-rule.handler.mock.ts index b652aebbdc..dbf5064cd6 100644 --- a/lib/core/src/lib/form/components/widgets/core/mocks/form-field-rule.handler.mock.ts +++ b/lib/core/src/lib/form/components/widgets/core/mocks/form-field-rule.handler.mock.ts @@ -16,7 +16,6 @@ */ import { FormFieldRule } from '../form-field-rule'; -import { RepeatableSectionModel } from '../repeatable-section.model'; export const mockRule: FormFieldRule = { ruleOn: 'mock-rule-on', @@ -27,135 +26,3 @@ export const mockParentRule: FormFieldRule = { ruleOn: 'Text0c0ydk', entries: [] }; - -export const mockParentWithoutFields: RepeatableSectionModel = { - id: 'mock-parent-id', - uid: 'mock-id-Row123456789', - fields: {}, - rowIndex: 0 -}; - -export const mockParentWithFields: RepeatableSectionModel = { - id: 'mock-parent-id', - uid: 'mock-id-Row123456789', - fields: { - '1': [ - { - id: 'Text0c0ydk', - name: 'Text', - type: 'text', - readOnly: false, - required: false, - colspan: 1, - rowspan: 1, - placeholder: null, - minLength: 0, - maxLength: 0, - regexPattern: null, - visibilityCondition: null, - params: { - existingColspan: 1, - maxColspan: 2 - } - } - ], - '2': [ - { - id: 'Text0c26k4', - name: 'Text', - type: 'text', - readOnly: false, - required: false, - colspan: 1, - rowspan: 1, - placeholder: null, - minLength: 0, - maxLength: 0, - regexPattern: null, - visibilityCondition: null, - params: { - existingColspan: 1, - maxColspan: 2 - } - } - ] - }, - rowIndex: 0 -}; - -export const mockParentWithSectionFields: RepeatableSectionModel = { - id: 'mock-parent-id', - uid: 'mock-id-Row123456789', - fields: { - '1': [ - { - id: 'd376200a-7d22-4b36-bbcd-b5f11a462b3e', - name: 'Section', - type: 'section', - numberOfColumns: 2, - fields: { - '1': [ - { - id: 'Text0c0ydk', - name: 'Text', - type: 'text', - readOnly: false, - required: false, - colspan: 1, - rowspan: 1, - placeholder: null, - minLength: 0, - maxLength: 0, - regexPattern: null, - visibilityCondition: null, - params: { - existingColspan: 1, - maxColspan: 2 - } - } - ], - '2': [ - { - id: 'Text0c26k4', - name: 'Text', - type: 'text', - readOnly: false, - required: false, - colspan: 1, - rowspan: 1, - placeholder: null, - minLength: 0, - maxLength: 0, - regexPattern: null, - visibilityCondition: null, - params: { - existingColspan: 1, - maxColspan: 2 - } - } - ] - }, - colspan: 1 - }, - { - id: 'Text08wpzg', - name: 'Text', - type: 'text', - readOnly: false, - required: false, - colspan: 1, - rowspan: 1, - placeholder: null, - minLength: 0, - maxLength: 0, - regexPattern: null, - visibilityCondition: null, - params: { - existingColspan: 1, - maxColspan: 2 - } - } - ] - }, - rowIndex: 0 -}; diff --git a/lib/core/src/lib/form/components/widgets/core/mocks/form-field-visibility-condition.handler.mock.ts b/lib/core/src/lib/form/components/widgets/core/mocks/form-field-visibility-condition.handler.mock.ts new file mode 100644 index 0000000000..2434883436 --- /dev/null +++ b/lib/core/src/lib/form/components/widgets/core/mocks/form-field-visibility-condition.handler.mock.ts @@ -0,0 +1,50 @@ +/*! + * @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. + */ + +export const mockVisibilityCondition = { + leftType: 'field', + leftValue: 'mock-left-value', + rightType: 'field', + rightValue: 'mock-right-value' +}; + +export const mockVisibilityConditionLeftValue = { + leftType: 'field', + leftValue: 'mock-left-value' +}; + +export const mockVisibilityConditionRightValue = { + rightType: 'field', + rightValue: 'mock-right-value' +}; + +export const mockParentVisibilityConditionWithLeftValue = { + leftType: 'field', + leftValue: 'Text0c0ydk' +}; + +export const mockParentVisibilityConditionWithRightValue = { + rightType: 'field', + rightValue: 'Text0c0ydk' +}; + +export const mockParentVisibilityCondition = { + leftType: 'field', + leftValue: 'Text0c0ydk', + rightType: 'field', + rightValue: 'Text0c26k4' +}; diff --git a/lib/core/src/lib/form/components/widgets/core/mocks/repeatable-section-model.mock.ts b/lib/core/src/lib/form/components/widgets/core/mocks/repeatable-section-model.mock.ts new file mode 100644 index 0000000000..5da38685f6 --- /dev/null +++ b/lib/core/src/lib/form/components/widgets/core/mocks/repeatable-section-model.mock.ts @@ -0,0 +1,150 @@ +/*! + * @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 { RepeatableSectionModel } from '../repeatable-section.model'; + +export const mockParentWithoutFields: RepeatableSectionModel = { + id: 'mock-parent-id', + uid: 'mock-id-Row123456789', + fields: {}, + rowIndex: 0 +}; + +export const mockParentWithFields: RepeatableSectionModel = { + id: 'mock-parent-id', + uid: 'mock-id-Row123456789', + fields: { + '1': [ + { + id: 'Text0c0ydk', + name: 'Text', + type: 'text', + readOnly: false, + required: false, + colspan: 1, + rowspan: 1, + placeholder: null, + minLength: 0, + maxLength: 0, + regexPattern: null, + visibilityCondition: null, + params: { + existingColspan: 1, + maxColspan: 2 + } + } + ], + '2': [ + { + id: 'Text0c26k4', + name: 'Text', + type: 'text', + readOnly: false, + required: false, + colspan: 1, + rowspan: 1, + placeholder: null, + minLength: 0, + maxLength: 0, + regexPattern: null, + visibilityCondition: null, + params: { + existingColspan: 1, + maxColspan: 2 + } + } + ] + }, + rowIndex: 0 +}; + +export const mockParentWithSectionFields: RepeatableSectionModel = { + id: 'mock-parent-id', + uid: 'mock-id-Row123456789', + fields: { + '1': [ + { + id: 'd376200a-7d22-4b36-bbcd-b5f11a462b3e', + name: 'Section', + type: 'section', + numberOfColumns: 2, + fields: { + '1': [ + { + id: 'Text0c0ydk', + name: 'Text', + type: 'text', + readOnly: false, + required: false, + colspan: 1, + rowspan: 1, + placeholder: null, + minLength: 0, + maxLength: 0, + regexPattern: null, + visibilityCondition: null, + params: { + existingColspan: 1, + maxColspan: 2 + } + } + ], + '2': [ + { + id: 'Text0c26k4', + name: 'Text', + type: 'text', + readOnly: false, + required: false, + colspan: 1, + rowspan: 1, + placeholder: null, + minLength: 0, + maxLength: 0, + regexPattern: null, + visibilityCondition: null, + params: { + existingColspan: 1, + maxColspan: 2 + } + } + ] + }, + colspan: 1 + }, + { + id: 'Text08wpzg', + name: 'Text', + type: 'text', + readOnly: false, + required: false, + colspan: 1, + rowspan: 1, + placeholder: null, + minLength: 0, + maxLength: 0, + regexPattern: null, + visibilityCondition: null, + params: { + existingColspan: 1, + maxColspan: 2 + } + } + ] + }, + rowIndex: 0 +}; diff --git a/lib/core/src/lib/form/components/widgets/repeat/repeat.widget.spec.ts b/lib/core/src/lib/form/components/widgets/repeat/repeat.widget.spec.ts index 1041dde171..199a8448e4 100644 --- a/lib/core/src/lib/form/components/widgets/repeat/repeat.widget.spec.ts +++ b/lib/core/src/lib/form/components/widgets/repeat/repeat.widget.spec.ts @@ -34,7 +34,7 @@ describe('RepeatWidgetComponent', () => { * @param allowInitialRowsDelete should allow deleting rows * @returns repeatable section json based on params */ - function getFormFieldJson(initialNumberOfRows: number = 2, maxNumberOfRows?: number, allowInitialRowsDelete: boolean = true) { + function getFormFieldJson(initialNumberOfRows: number = 2, maxNumberOfRows: number | null = null, allowInitialRowsDelete: boolean = true) { return { id: 'RepeatableSection0tbw2y', name: 'Repeatable Section', diff --git a/lib/core/src/lib/mock/form/widget-visibility.service.mock.ts b/lib/core/src/lib/mock/form/widget-visibility.service.mock.ts index 0688d4c5ee..c7b77d2dac 100644 --- a/lib/core/src/lib/mock/form/widget-visibility.service.mock.ts +++ b/lib/core/src/lib/mock/form/widget-visibility.service.mock.ts @@ -1836,3 +1836,129 @@ export const tabInvalidFormVisibility: any = { variables: [] } }; + +export const repeatableSectionFormVisibility: any = { + id: 'orm-875b6fe8-a84d-475b-a7e1-a3647955a0a6', + name: 'repeatable-section', + description: '', + version: 0, + tabs: [], + formDefinition: { + fields: [ + { + id: 'RepeatableSection0009v6', + name: 'Repeatable Section', + type: 'repeatable-section', + tab: null, + params: { + initialNumberOfRows: 1, + allowInitialRowsDelete: true, + maxNumberOfRows: null + }, + numberOfColumns: 2, + fields: { + '1': [ + { + id: 'text1', + name: 'Text', + type: 'text', + readOnly: false, + required: false, + colspan: 1, + rowspan: 1, + placeholder: null, + minLength: 0, + maxLength: 0, + regexPattern: null, + visibilityCondition: null, + params: { + existingColspan: 1, + maxColspan: 2 + } + } + ], + '2': [ + { + id: 'text2', + name: 'Text', + type: 'text', + readOnly: false, + required: false, + colspan: 1, + rowspan: 1, + placeholder: null, + minLength: 0, + maxLength: 0, + regexPattern: null, + visibilityCondition: { + leftType: 'field', + leftValue: 'text4', + operator: '!empty', + rightValue: null, + rightType: null, + nextConditionOperator: '', + nextCondition: null + }, + params: { + existingColspan: 1, + maxColspan: 2 + } + } + ] + } + }, + { + id: '5a3474a8-22e1-4408-8a43-2eb1fc8300ac', + name: 'Section', + tab: null, + type: 'section', + numberOfColumns: 2, + fields: { + '1': [ + { + id: 'text3', + name: 'Text', + type: 'text', + readOnly: false, + required: false, + colspan: 1, + rowspan: 1, + placeholder: null, + minLength: 0, + maxLength: 0, + regexPattern: null, + visibilityCondition: null, + params: { + existingColspan: 1, + maxColspan: 2 + } + } + ], + '2': [ + { + id: 'text4', + name: 'Text', + type: 'text', + readOnly: false, + required: false, + colspan: 1, + rowspan: 1, + placeholder: null, + minLength: 0, + maxLength: 0, + regexPattern: null, + visibilityCondition: null, + params: { + existingColspan: 1, + maxColspan: 2 + } + } + ] + } + } + ], + outcomes: [], + metadata: {}, + variables: [] + } +};