diff --git a/frontend/packages/ux-editor/src/testing/expressionMocks.ts b/frontend/packages/ux-editor/src/testing/expressionMocks.ts index b7e3a162019..4bddd52c988 100644 --- a/frontend/packages/ux-editor/src/testing/expressionMocks.ts +++ b/frontend/packages/ux-editor/src/testing/expressionMocks.ts @@ -4,9 +4,9 @@ import { ExpressionFunction, ExpressionPropertyBase, Operator, - SubExpression + SubExpression, } from '../types/Expressions'; -import { component1Mock } from "./layoutMock"; +import { component1Mock } from './layoutMock'; export const componentId = component1Mock.id; export const datamodelField = 'some-data-model-field'; @@ -17,7 +17,7 @@ export const booleanValue = true; export const baseInternalSubExpression: SubExpression = { id: 'some-sub-exp-id', function: ExpressionFunction.Equals, -} +}; export const subExpression0: SubExpression = { id: 'some-sub-exp-id-0', function: ExpressionFunction.Equals, @@ -25,7 +25,7 @@ export const subExpression0: SubExpression = { value: componentId, comparableDataSource: DataSource.String, comparableValue: stringValue, -} +}; export const subExpression1: SubExpression = { id: 'some-sub-exp-id-1', function: ExpressionFunction.Equals, @@ -33,7 +33,7 @@ export const subExpression1: SubExpression = { value: nullValue, comparableDataSource: DataSource.Number, comparableValue: numberValue, -} +}; export const subExpression2: SubExpression = { id: 'some-sub-exp-id-2', function: ExpressionFunction.Equals, @@ -41,83 +41,49 @@ export const subExpression2: SubExpression = { value: booleanValue, comparableDataSource: DataSource.Component, comparableValue: componentId, -} +}; export const baseInternalExpression: Expression = { id: 'some-id-0', property: ExpressionPropertyBase.Hidden, - subExpressions: [ - baseInternalSubExpression - ] -} + subExpressions: [baseInternalSubExpression], +}; export const simpleInternalExpression: Expression = { id: 'some-id-1', property: ExpressionPropertyBase.Hidden, - subExpressions: [ - subExpression0 - ] + subExpressions: [subExpression0], }; export const internalExpressionWithMultipleSubExpressions: Expression = { id: 'some-id-2', property: ExpressionPropertyBase.Hidden, operator: Operator.Or, - subExpressions: [ - subExpression1, - subExpression2 - ] + subExpressions: [subExpression1, subExpression2], }; export const equivalentExternalExpressionWithMultipleSubExpressions = [ - 'or', [ - 'equals', - nullValue, - numberValue - ], - [ - 'equals', - booleanValue, - [ - DataSource.Component, - componentId - ] - ] -] -export const parsableExternalExpression = [ + 'or', + ['equals', nullValue, numberValue], + ['equals', booleanValue, [DataSource.Component, componentId]], +]; +export const parsableExternalExpression: any = [ 'and', - [ - 'equals', - stringValue, - nullValue - ], - [ - 'equals', - numberValue, - booleanValue - ], - [ - 'not', - [ - DataSource.Component, - componentId - ], - [ - DataSource.DataModel, - datamodelField - ] - ] + ['equals', stringValue, nullValue], + ['equals', numberValue, booleanValue], + ['not', [DataSource.Component, componentId], [DataSource.DataModel, datamodelField]], ]; export const unParsableComplexExpression = '["equals, [datamodel, test, true]'; export const parsableComplexExpression = '["equals", ["datamodel", "test"], true]'; -export const parsableNotStudioFriendlyComplexExpression = ["dataModel", "some-field"]; -export const parsableNotStudioFriendlyLongComplexExpression = ["and", - ["equals", ["equals", ["dataModel", "some-field"], "true"], "true"], - ["equals", ["dataModel", "some-field"], "true"] +export const parsableNotStudioFriendlyComplexExpression: any = ['dataModel', 'some-field']; +export const parsableNotStudioFriendlyLongComplexExpression: any = [ + 'and', + ['equals', ['equals', ['dataModel', 'some-field'], 'true'], 'true'], + ['equals', ['dataModel', 'some-field'], 'true'], ]; export const internalUnParsableComplexExpression: Expression = { id: 'some-id-4', property: ExpressionPropertyBase.Hidden, complexExpression: unParsableComplexExpression, -} +}; export const internalParsableComplexExpression: Expression = { id: 'some-id-5', property: ExpressionPropertyBase.Hidden, complexExpression: parsableExternalExpression, -} +}; diff --git a/frontend/packages/ux-editor/src/types/Expressions.ts b/frontend/packages/ux-editor/src/types/Expressions.ts index 4e6907e3615..50953b2e98e 100644 --- a/frontend/packages/ux-editor/src/types/Expressions.ts +++ b/frontend/packages/ux-editor/src/types/Expressions.ts @@ -20,7 +20,7 @@ export interface SubExpression { export enum Operator { And = 'and', - Or = 'or' + Or = 'or', } export type ExpressionProperty = ExpressionPropertyBase | ExpressionPropertyForGroup; @@ -50,21 +50,25 @@ export enum ExpressionFunction { LessThanEq = 'lessThanEq', } -export enum DataSource { // comments reflects available values to select if choosing the specific datasource - Component = 'component', // get all components-ids in layoutset - DataModel = 'dataModel', // get all datamodel-ids in selected datamodel - InstanceContext = 'instanceContext', // restrict to only; instanceOwnerPartyId, instanceId, appId +export enum DataSource { + Component = 'component', + DataModel = 'dataModel', + InstanceContext = 'instanceContext', ApplicationSettings = 'applicationSettings', // get all fields from section "FrontEndSettings" in applicationSettings - String = 'string', // custom input field for string - Number = 'number', // custom input field for number - Boolean = 'boolean', // togglebuttons? - Null = 'null', // no additional field + String = 'string', + Number = 'number', + Boolean = 'boolean', + Null = 'null', } -export const getExpressionPropertiesBasedOnComponentType = (componentType: LayoutItemType.Component | LayoutItemType.Container): ExpressionProperty[] => { +export const getExpressionPropertiesBasedOnComponentType = ( + componentType: LayoutItemType.Component | LayoutItemType.Container, +): ExpressionProperty[] => { const expressionProperties = Object.values(ExpressionPropertyBase) as string[]; if (componentType === LayoutItemType.Container) { - return expressionProperties.concat(Object.values(ExpressionPropertyForGroup) as string[]) as ExpressionProperty[]; + return expressionProperties.concat( + Object.values(ExpressionPropertyForGroup) as string[], + ) as ExpressionProperty[]; } return expressionProperties as ExpressionProperty[]; }; @@ -83,20 +87,36 @@ export const expressionPropertyTexts = (t: UseText) => ({ [ExpressionPropertyBase.Hidden]: t('right_menu.expressions_property_hidden'), [ExpressionPropertyBase.ReadOnly]: t('right_menu.expressions_property_read_only'), [ExpressionPropertyBase.Required]: t('right_menu.expressions_property_required'), - [ExpressionPropertyForGroup.EditAddButton]: t('right_menu.expressions_group_property_show_add_button'), - [ExpressionPropertyForGroup.EditSaveAndNextButton]: t('right_menu.expressions_group_property_show_edit_button'), - [ExpressionPropertyForGroup.EditDeleteButton]: t('right_menu.expressions_group_property_show_delete_button'), - [ExpressionPropertyForGroup.EditSaveButton]: t('right_menu.expressions_group_property_show_save_button'), + [ExpressionPropertyForGroup.EditAddButton]: t( + 'right_menu.expressions_group_property_show_add_button', + ), + [ExpressionPropertyForGroup.EditSaveAndNextButton]: t( + 'right_menu.expressions_group_property_show_edit_button', + ), + [ExpressionPropertyForGroup.EditDeleteButton]: t( + 'right_menu.expressions_group_property_show_delete_button', + ), + [ExpressionPropertyForGroup.EditSaveButton]: t( + 'right_menu.expressions_group_property_show_save_button', + ), }); export const expressionInPreviewPropertyTexts = (t: UseText) => ({ [ExpressionPropertyBase.Hidden]: t('right_menu.expressions_property_preview_hidden'), [ExpressionPropertyBase.ReadOnly]: t('right_menu.expressions_property_preview_read_only'), [ExpressionPropertyBase.Required]: t('right_menu.expressions_property_preview_required'), - [ExpressionPropertyForGroup.EditAddButton]: t('right_menu.expressions_group_property_preview_show_add_button'), - [ExpressionPropertyForGroup.EditSaveAndNextButton]: t('right_menu.expressions_group_property_preview_show_edit_button'), - [ExpressionPropertyForGroup.EditDeleteButton]: t('right_menu.expressions_group_property_preview_show_delete_button'), - [ExpressionPropertyForGroup.EditSaveButton]: t('right_menu.expressions_group_property_preview_show_save_button'), + [ExpressionPropertyForGroup.EditAddButton]: t( + 'right_menu.expressions_group_property_preview_show_add_button', + ), + [ExpressionPropertyForGroup.EditSaveAndNextButton]: t( + 'right_menu.expressions_group_property_preview_show_edit_button', + ), + [ExpressionPropertyForGroup.EditDeleteButton]: t( + 'right_menu.expressions_group_property_preview_show_delete_button', + ), + [ExpressionPropertyForGroup.EditSaveButton]: t( + 'right_menu.expressions_group_property_preview_show_save_button', + ), }); export const expressionDataSourceTexts = (t: UseText) => ({ diff --git a/frontend/packages/ux-editor/src/types/FormComponent.ts b/frontend/packages/ux-editor/src/types/FormComponent.ts index c3a4b7c1772..c5d42a9be39 100644 --- a/frontend/packages/ux-editor/src/types/FormComponent.ts +++ b/frontend/packages/ux-editor/src/types/FormComponent.ts @@ -21,8 +21,7 @@ export interface FormComponentBase { handleDeleteElement?: () => void; handleUpdateFormData?: (formData: any) => void; handleUpdateDataModel?: (dataModelBinding: string) => void; - disabled?: boolean; // Add dynamic type? - // TODO: Figure out if it is necessary to have the Dynamic type here since the type is not actually added to the field? + disabled?: boolean; required?: boolean | any; hidden?: boolean | any; readOnly?: boolean | any; diff --git a/frontend/packages/ux-editor/src/utils/expressionsUtils.test.ts b/frontend/packages/ux-editor/src/utils/expressionsUtils.test.ts index ccddf340cba..9c32865cb4f 100644 --- a/frontend/packages/ux-editor/src/utils/expressionsUtils.test.ts +++ b/frontend/packages/ux-editor/src/utils/expressionsUtils.test.ts @@ -1,9 +1,10 @@ import { DataSource, + Expression, ExpressionFunction, ExpressionPropertyBase, Operator, - SubExpression + SubExpression, } from '../types/Expressions'; import { addProperty, @@ -18,7 +19,9 @@ import { addDataSource, addDataSourceValue, tryParseExpression, - stringifyValueForDisplay, deleteExpressionFromComponent, + stringifyValueForDisplay, + deleteExpressionFromComponent, + convertInternalSubExpressionToExternal, } from './expressionsUtils'; import { component1Mock } from '../testing/layoutMock'; import { @@ -26,82 +29,114 @@ import { baseInternalSubExpression, booleanValue, componentId, - datamodelField, equivalentExternalExpressionWithMultipleSubExpressions, - internalExpressionWithMultipleSubExpressions, internalParsableComplexExpression, + datamodelField, + equivalentExternalExpressionWithMultipleSubExpressions, + internalExpressionWithMultipleSubExpressions, + internalParsableComplexExpression, internalUnParsableComplexExpression, nullValue, - numberValue, parsableComplexExpression, + numberValue, + parsableComplexExpression, parsableExternalExpression, parsableNotStudioFriendlyComplexExpression, parsableNotStudioFriendlyLongComplexExpression, simpleInternalExpression, - stringValue, subExpression0, subExpression1, subExpression2, - unParsableComplexExpression - + stringValue, + subExpression0, + subExpression1, + subExpression2, + unParsableComplexExpression, } from '../testing/expressionMocks'; import { deepCopy } from 'app-shared/pure'; import { textMock } from '../../../../testing/mocks/i18nMock'; describe('expressionsUtils', () => { - describe('convertSubExpression', () => { it('converts first part of external subexpression in array format to internal subexpression where dataSource and dataSourceValue are set', () => { - const extSubExpression: any = ['component', 'test-comp']; - const convertedSubExpression: SubExpression = convertSubExpression(baseInternalSubExpression, extSubExpression, false); + const externalExpEl: [string, string] = ['component', 'test-comp']; + const convertedSubExpression: SubExpression = convertSubExpression( + baseInternalSubExpression, + externalExpEl, + false, + ); expect(convertedSubExpression.dataSource).toBe(DataSource.Component); - expect(convertedSubExpression.value).toBe(extSubExpression[1]); + expect(convertedSubExpression.value).toBe(externalExpEl[1]); expect(convertedSubExpression.comparableDataSource).toBe(undefined); expect(convertedSubExpression.comparableValue).toBe(undefined); }); it('converts comparable part of external subexpression in array format to internal subexpression where compDataSource and compDataSourceValue are set', () => { - const extSubExpression: any = ['component', 'test-comp']; - const convertedSubExpression: SubExpression = convertSubExpression(baseInternalSubExpression, extSubExpression, true); + const externalExpEl: [string, string] = ['component', 'test-comp']; + const convertedSubExpression: SubExpression = convertSubExpression( + baseInternalSubExpression, + externalExpEl, + true, + ); expect(convertedSubExpression.dataSource).toBe(undefined); expect(convertedSubExpression.value).toBe(undefined); expect(convertedSubExpression.comparableDataSource).toBe(DataSource.Component); - expect(convertedSubExpression.comparableValue).toBe(extSubExpression[1]); + expect(convertedSubExpression.comparableValue).toBe(externalExpEl[1]); }); it('converts first part of external subexpression in string format to internal subexpression where dataSource and dataSourceValue are set', () => { - const extSubExpression: any = 'test-string'; - const convertedSubExpression: SubExpression = convertSubExpression(baseInternalSubExpression, extSubExpression, false); + const externalExpEl = 'test-string'; + const convertedSubExpression: SubExpression = convertSubExpression( + baseInternalSubExpression, + externalExpEl, + false, + ); expect(convertedSubExpression.dataSource).toBe(DataSource.String); - expect(convertedSubExpression.value).toBe(extSubExpression); + expect(convertedSubExpression.value).toBe(externalExpEl); expect(convertedSubExpression.comparableDataSource).toBe(undefined); expect(convertedSubExpression.comparableValue).toBe(undefined); }); it('converts comparable part of external subexpression in string format to internal subexpression where compDataSource and compDataSourceValue are set', () => { - const extSubExpression: any = 'test-string'; - const convertedSubExpression: SubExpression = convertSubExpression(baseInternalSubExpression, extSubExpression, true); + const externalExpEl = 'test-string'; + const convertedSubExpression: SubExpression = convertSubExpression( + baseInternalSubExpression, + externalExpEl, + true, + ); expect(convertedSubExpression.dataSource).toBe(undefined); expect(convertedSubExpression.value).toBe(undefined); expect(convertedSubExpression.comparableDataSource).toBe(DataSource.String); - expect(convertedSubExpression.comparableValue).toBe(extSubExpression); + expect(convertedSubExpression.comparableValue).toBe(externalExpEl); }); it('converts first part of external subexpression in number format to internal subexpression where dataSource and dataSourceValue are set', () => { - const extSubExpression: any = 1024; - const convertedSubExpression: SubExpression = convertSubExpression(baseInternalSubExpression, extSubExpression, false); + const externalExpEl = 1024; + const convertedSubExpression: SubExpression = convertSubExpression( + baseInternalSubExpression, + externalExpEl, + false, + ); expect(convertedSubExpression.dataSource).toBe(DataSource.Number); - expect(convertedSubExpression.value).toBe(extSubExpression); + expect(convertedSubExpression.value).toBe(externalExpEl); expect(convertedSubExpression.comparableDataSource).toBe(undefined); expect(convertedSubExpression.comparableValue).toBe(undefined); }); it('converts comparable part of external subexpression in number format to internal subexpression where compDataSource and compDataSourceValue are set', () => { - const extSubExpression: any = 1024; - const convertedSubExpression: SubExpression = convertSubExpression(baseInternalSubExpression, extSubExpression, true); + const externalExpEl = 1024; + const convertedSubExpression: SubExpression = convertSubExpression( + baseInternalSubExpression, + externalExpEl, + true, + ); expect(convertedSubExpression.dataSource).toBe(undefined); expect(convertedSubExpression.value).toBe(undefined); expect(convertedSubExpression.comparableDataSource).toBe(DataSource.Number); - expect(convertedSubExpression.comparableValue).toBe(extSubExpression); + expect(convertedSubExpression.comparableValue).toBe(externalExpEl); }); it('converts first part of external subexpression as null to internal subexpression where dataSource is set', () => { - const extSubExpression: any = null; - const convertedSubExpression: SubExpression = convertSubExpression(baseInternalSubExpression, extSubExpression, false); + const externalExpEl = null; + const convertedSubExpression: SubExpression = convertSubExpression( + baseInternalSubExpression, + externalExpEl, + false, + ); expect(convertedSubExpression.dataSource).toBe(DataSource.Null); expect(convertedSubExpression.value).toBe(null); @@ -109,8 +144,12 @@ describe('expressionsUtils', () => { expect(convertedSubExpression.comparableValue).toBe(undefined); }); it('converts comparable part of external subexpression as null to internal subexpression where compDataSource is set', () => { - const extSubExpression: any = null; - const convertedSubExpression: SubExpression = convertSubExpression(baseInternalSubExpression, extSubExpression, true); + const externalExpEl = null; + const convertedSubExpression: SubExpression = convertSubExpression( + baseInternalSubExpression, + externalExpEl, + true, + ); expect(convertedSubExpression.dataSource).toBe(undefined); expect(convertedSubExpression.value).toBe(undefined); @@ -118,22 +157,30 @@ describe('expressionsUtils', () => { expect(convertedSubExpression.comparableValue).toBe(null); }); it('converts first part of external subexpression as boolean to internal subexpression where dataSource and dataSourceValue are set', () => { - const extSubExpression: any = true; - const convertedSubExpression: SubExpression = convertSubExpression(baseInternalSubExpression, extSubExpression, false); + const externalExpEl = true; + const convertedSubExpression: SubExpression = convertSubExpression( + baseInternalSubExpression, + externalExpEl, + false, + ); expect(convertedSubExpression.dataSource).toBe(DataSource.Boolean); - expect(convertedSubExpression.value).toBe(extSubExpression); + expect(convertedSubExpression.value).toBe(externalExpEl); expect(convertedSubExpression.comparableDataSource).toBe(undefined); expect(convertedSubExpression.comparableValue).toBe(undefined); }); it('converts comparable part of external subexpression as boolean to internal subexpression where compDataSource and compDataSourceValue are set', () => { - const extSubExpression: any = false; - const convertedSubExpression: SubExpression = convertSubExpression(baseInternalSubExpression, extSubExpression, true); + const externalExpEl = false; + const convertedSubExpression: SubExpression = convertSubExpression( + baseInternalSubExpression, + externalExpEl, + true, + ); expect(convertedSubExpression.dataSource).toBe(undefined); expect(convertedSubExpression.value).toBe(undefined); expect(convertedSubExpression.comparableDataSource).toBe(DataSource.Boolean); - expect(convertedSubExpression.comparableValue).toBe(extSubExpression); + expect(convertedSubExpression.comparableValue).toBe(externalExpEl); }); }); describe('convertInternalExpressionToExternal', () => { @@ -150,7 +197,9 @@ describe('expressionsUtils', () => { expect(externalExpression[2]).toBe(stringValue); }); it('converts internal expression with multiple subExpressions and boolean-, null- and number-usage.', () => { - const externalExpression = convertInternalExpressionToExternal(internalExpressionWithMultipleSubExpressions); + const externalExpression = convertInternalExpressionToExternal( + internalExpressionWithMultipleSubExpressions, + ); expect(externalExpression).toBeInstanceOf(Array); expect(externalExpression.length).toBe(3); @@ -177,23 +226,41 @@ describe('expressionsUtils', () => { expect(externalExpression[2]).toBe(nullValue); }); it('converts un-parsable internal complex expression to plain string', () => { - const externalExpression = convertInternalExpressionToExternal(internalUnParsableComplexExpression); + const externalExpression = convertInternalExpressionToExternal( + internalUnParsableComplexExpression, + ); expect(typeof externalExpression).toBe('string'); expect(externalExpression).toBe(unParsableComplexExpression); }); }); + describe('convertInternalSubExpressionToExternal', () => { + it('converts most basic valid internal sub expression', () => { + const externalExpression: any = + convertInternalSubExpressionToExternal(baseInternalSubExpression); + + expect(externalExpression).toBeInstanceOf(Array); + expect(externalExpression[0]).toBe(ExpressionFunction.Equals); + expect(externalExpression[1]).toBe(nullValue); + expect(externalExpression[2]).toBe(nullValue); + }); + it('converts valid internal sub expression', () => { + const externalExpression: any = convertInternalSubExpressionToExternal(subExpression0); + + expect(externalExpression).toBeInstanceOf(Array); + expect(externalExpression[0]).toBe(ExpressionFunction.Equals); + expect(externalExpression[1][0]).toBe(DataSource.Component); + expect(externalExpression[1][1]).toBe(componentId); + expect(externalExpression[2]).toBe(stringValue); + }); + }); describe('convertExternalExpressionToInternal', () => { it('converts expression with one subExpression where first part is array and second null to valid internal expression', () => { - const externalExpression = [ - 'equals', - [ - 'component', - componentId - ], - nullValue - ]; - const internalExpression = convertExternalExpressionToInternal(ExpressionPropertyBase.Hidden, externalExpression); + const externalExpression: any = ['equals', ['component', componentId], nullValue]; + const internalExpression: Expression = convertExternalExpressionToInternal( + ExpressionPropertyBase.Hidden, + externalExpression, + ); expect(internalExpression.complexExpression).toBe(undefined); expect(internalExpression.property).toBe(ExpressionPropertyBase.Hidden); @@ -205,12 +272,11 @@ describe('expressionsUtils', () => { expect(internalExpression.subExpressions[0].comparableValue).toBe(nullValue); }); it('converts expression with one subExpression where first part is string and second number to valid internal expression', () => { - const externalExpression = [ - 'equals', - stringValue, - numberValue - ]; - const internalExpression = convertExternalExpressionToInternal(ExpressionPropertyBase.Hidden, externalExpression); + const externalExpression: any = ['equals', stringValue, numberValue]; + const internalExpression: Expression = convertExternalExpressionToInternal( + ExpressionPropertyBase.Hidden, + externalExpression, + ); expect(internalExpression.complexExpression).toBe(undefined); expect(internalExpression.property).toBe(ExpressionPropertyBase.Hidden); @@ -222,12 +288,11 @@ describe('expressionsUtils', () => { expect(internalExpression.subExpressions[0].comparableValue).toBe(numberValue); }); it('converts expression with one subExpression where both parts are null number to valid internal expression', () => { - const externalExpression = [ - 'equals', - nullValue, - nullValue - ]; - const internalExpression = convertExternalExpressionToInternal(ExpressionPropertyBase.Hidden, externalExpression); + const externalExpression: any = ['equals', nullValue, nullValue]; + const internalExpression: Expression = convertExternalExpressionToInternal( + ExpressionPropertyBase.Hidden, + externalExpression, + ); expect(internalExpression.complexExpression).toBe(undefined); expect(internalExpression.operator).toBe(undefined); @@ -240,7 +305,10 @@ describe('expressionsUtils', () => { expect(internalExpression.subExpressions[0].comparableValue).toBe(nullValue); }); it('converts expression with multiple subExpressions to valid internal expression', () => { - const internalExpression = convertExternalExpressionToInternal(ExpressionPropertyBase.Hidden, parsableExternalExpression); + const internalExpression: Expression = convertExternalExpressionToInternal( + ExpressionPropertyBase.Hidden, + parsableExternalExpression, + ); expect(internalExpression.complexExpression).toBe(undefined); expect(internalExpression.operator).toBe(Operator.And); @@ -263,7 +331,10 @@ describe('expressionsUtils', () => { expect(internalExpression.subExpressions[2].comparableValue).toBe(datamodelField); }); it('converts non-studio-friendly expression to internal complex expression', () => { - const internalExpression = convertExternalExpressionToInternal(ExpressionPropertyBase.Hidden, parsableNotStudioFriendlyComplexExpression); + const internalExpression: Expression = convertExternalExpressionToInternal( + ExpressionPropertyBase.Hidden, + parsableNotStudioFriendlyComplexExpression, + ); expect(internalExpression.complexExpression).toBe(parsableNotStudioFriendlyComplexExpression); expect(internalExpression.property).toBe(ExpressionPropertyBase.Hidden); @@ -271,9 +342,14 @@ describe('expressionsUtils', () => { expect(internalExpression.subExpressions).toBe(undefined); }); it('converts expression with multiple nested subExpressions to internal complex expression', () => { - const internalExpression = convertExternalExpressionToInternal(ExpressionPropertyBase.Hidden, parsableNotStudioFriendlyLongComplexExpression); - - expect(internalExpression.complexExpression).toBe(parsableNotStudioFriendlyLongComplexExpression); + const internalExpression: Expression = convertExternalExpressionToInternal( + ExpressionPropertyBase.Hidden, + parsableNotStudioFriendlyLongComplexExpression, + ); + + expect(internalExpression.complexExpression).toBe( + parsableNotStudioFriendlyLongComplexExpression, + ); expect(internalExpression.property).toBe(ExpressionPropertyBase.Hidden); expect(internalExpression.operator).toBe(undefined); expect(internalExpression.subExpressions).toBe(undefined); @@ -281,18 +357,29 @@ describe('expressionsUtils', () => { }); describe('convertAndAddExpressionToComponent', () => { it('converted expression is set on form component hidden property', async () => { - const updatedComponent = convertAndAddExpressionToComponent(component1Mock, internalExpressionWithMultipleSubExpressions); + const updatedComponent = convertAndAddExpressionToComponent( + component1Mock, + internalExpressionWithMultipleSubExpressions, + ); - expect(updatedComponent.hidden).toStrictEqual(equivalentExternalExpressionWithMultipleSubExpressions); + expect(updatedComponent.hidden).toStrictEqual( + equivalentExternalExpressionWithMultipleSubExpressions, + ); }); it('converted and parsed complex expression is set as array on form component hidden property', () => { - const updatedComponent = convertAndAddExpressionToComponent(component1Mock, internalParsableComplexExpression); + const updatedComponent = convertAndAddExpressionToComponent( + component1Mock, + internalParsableComplexExpression, + ); expect(updatedComponent.hidden).toStrictEqual(parsableExternalExpression); expect(updatedComponent.hidden).toBeInstanceOf(Array); }); it('converted complex expression is set as string on form component hidden property', () => { - const updatedComponent = convertAndAddExpressionToComponent(component1Mock, internalUnParsableComplexExpression); + const updatedComponent = convertAndAddExpressionToComponent( + component1Mock, + internalUnParsableComplexExpression, + ); expect(updatedComponent.hidden).toStrictEqual(unParsableComplexExpression); expect(typeof updatedComponent.hidden).toBe('string'); @@ -300,7 +387,10 @@ describe('expressionsUtils', () => { }); describe('deleteExpressionFromComponent', () => { it('should delete the property on the form component connected to the expression', () => { - const newExpressions = deleteExpressionFromComponent(component1Mock, internalExpressionWithMultipleSubExpressions); + const newExpressions = deleteExpressionFromComponent( + component1Mock, + internalExpressionWithMultipleSubExpressions, + ); expect(newExpressions.hidden).toBeUndefined(); }); @@ -324,7 +414,10 @@ describe('expressionsUtils', () => { component1Mock.hidden = internalExpressionWithMultipleSubExpressions; const expressionToDelete = internalExpressionWithMultipleSubExpressions; const oldExpressions = [expressionToDelete]; - const updatedExpressions = deleteExpressionAndAddDefaultIfEmpty(expressionToDelete, oldExpressions); + const updatedExpressions = deleteExpressionAndAddDefaultIfEmpty( + expressionToDelete, + oldExpressions, + ); expect(updatedExpressions).toHaveLength(1); expect(updatedExpressions[0].id).not.toBe(internalExpressionWithMultipleSubExpressions.id); @@ -334,7 +427,10 @@ describe('expressionsUtils', () => { component1Mock.hidden = internalExpressionWithMultipleSubExpressions; const expressionToDelete = internalExpressionWithMultipleSubExpressions; const oldExpressions = [expressionToDelete, internalParsableComplexExpression]; - const updatedExpressions = deleteExpressionAndAddDefaultIfEmpty(expressionToDelete, oldExpressions); + const updatedExpressions = deleteExpressionAndAddDefaultIfEmpty( + expressionToDelete, + oldExpressions, + ); expect(updatedExpressions).toHaveLength(1); expect(updatedExpressions[0]).toStrictEqual(internalParsableComplexExpression); @@ -357,17 +453,25 @@ describe('expressionsUtils', () => { describe('removeSubExpressionAndAdaptParentProps', () => { it('should remove a subExpression and do nothing more with parent properties when there are more than 2 subExpressions to start with', () => { const internalExpressionCopy = deepCopy(internalExpressionWithMultipleSubExpressions); - internalExpressionCopy.subExpressions.push(subExpression0) - const newExpression = removeSubExpressionAndAdaptParentProps(internalExpressionCopy, subExpression0); + internalExpressionCopy.subExpressions.push(subExpression0); + const newExpression = removeSubExpressionAndAdaptParentProps( + internalExpressionCopy, + subExpression0, + ); expect(newExpression.operator).toBe(Operator.Or); expect(newExpression.property).toBe(ExpressionPropertyBase.Hidden); expect(newExpression.subExpressions).toHaveLength(2); - expect(newExpression.subExpressions).toStrictEqual(internalExpressionWithMultipleSubExpressions.subExpressions); + expect(newExpression.subExpressions).toStrictEqual( + internalExpressionWithMultipleSubExpressions.subExpressions, + ); }); it('should remove a subExpression and clear operator when there is only one subExpression left', () => { const internalExpressionCopy = deepCopy(internalExpressionWithMultipleSubExpressions); - const newExpression = removeSubExpressionAndAdaptParentProps(internalExpressionCopy, subExpression1); + const newExpression = removeSubExpressionAndAdaptParentProps( + internalExpressionCopy, + subExpression1, + ); expect(newExpression.operator).toBeUndefined(); expect(newExpression.property).toBe(ExpressionPropertyBase.Hidden); @@ -377,7 +481,10 @@ describe('expressionsUtils', () => { it('should have no subExpressions and clear operator and property when there is no subExpressions left', () => { const internalExpressionCopy = deepCopy(internalExpressionWithMultipleSubExpressions); internalExpressionCopy.subExpressions.pop(); - const newExpression = removeSubExpressionAndAdaptParentProps(internalExpressionCopy, subExpression1); + const newExpression = removeSubExpressionAndAdaptParentProps( + internalExpressionCopy, + subExpression1, + ); expect(newExpression.operator).toBeUndefined(); expect(newExpression.property).toBeUndefined(); @@ -386,11 +493,16 @@ describe('expressionsUtils', () => { }); describe('addProperty', () => { it('should add an action to the expression when action is not "default"', () => { - const newExpression = addProperty(internalExpressionWithMultipleSubExpressions, ExpressionPropertyBase.Required); + const newExpression = addProperty( + internalExpressionWithMultipleSubExpressions, + ExpressionPropertyBase.Required, + ); expect(newExpression).toBeDefined(); expect(newExpression.operator).toBe(Operator.Or); - expect(newExpression.property).not.toBe(internalExpressionWithMultipleSubExpressions.property); + expect(newExpression.property).not.toBe( + internalExpressionWithMultipleSubExpressions.property, + ); expect(newExpression.property).toBe(ExpressionPropertyBase.Required); expect(newExpression.subExpressions).toHaveLength(2); expect(newExpression.subExpressions[0].id).toBe(subExpression1.id); @@ -398,7 +510,10 @@ describe('expressionsUtils', () => { }); it('should return nothing when action is "default"', () => { const propertyToAdd = 'default'; - const newExpression = addProperty(internalExpressionWithMultipleSubExpressions, propertyToAdd); + const newExpression = addProperty( + internalExpressionWithMultipleSubExpressions, + propertyToAdd, + ); expect(newExpression).toStrictEqual(internalExpressionWithMultipleSubExpressions); }); @@ -517,13 +632,19 @@ describe('expressionsUtils', () => { }); describe('tryParseExpression', () => { it('should parse valid JSON complexExpression', () => { - const newExpression = tryParseExpression(baseInternalExpression, parsableComplexExpression); + const newExpression: Expression = tryParseExpression( + baseInternalExpression, + parsableComplexExpression, + ); const parsedComplexExpression = JSON.parse(parsableComplexExpression); expect(newExpression.complexExpression).toStrictEqual(parsedComplexExpression); }); it('should handle invalid JSON complexExpression and keep it as a string', () => { - const newExpression = tryParseExpression(baseInternalExpression, unParsableComplexExpression); + const newExpression: Expression = tryParseExpression( + baseInternalExpression, + unParsableComplexExpression, + ); expect(newExpression.complexExpression).toStrictEqual(unParsableComplexExpression); }); @@ -551,9 +672,7 @@ describe('expressionsUtils', () => { }); it('should return string representation for numeric value', () => { const result = stringifyValueForDisplay(textMock, numberValue); - expect - (result).toBe('1024'); + expect(result).toBe('1024'); }); }); }); - diff --git a/frontend/packages/ux-editor/src/utils/expressionsUtils.ts b/frontend/packages/ux-editor/src/utils/expressionsUtils.ts index 3d28d2f7a3d..007208c77dc 100644 --- a/frontend/packages/ux-editor/src/utils/expressionsUtils.ts +++ b/frontend/packages/ux-editor/src/utils/expressionsUtils.ts @@ -21,40 +21,47 @@ export const convertInternalExpressionToExternal = (expression: Expression): any if (complexExpressionIsSet(expression.complexExpression)) { return expression.complexExpression; } - const subExpressions: any[] = []; if (!expression.subExpressions || expression.subExpressions.length === 0) { - return subExpressions; + return []; } - expression.subExpressions.map((subEXp) => { - const expressionObject = []; - expressionObject[0] = subEXp.function; - if ( - subEXp.dataSource === DataSource.ApplicationSettings || - subEXp.dataSource === DataSource.Component || - subEXp.dataSource === DataSource.DataModel || - subEXp.dataSource === DataSource.InstanceContext - ) { - expressionObject[1] = [subEXp.dataSource, subEXp.value]; - } else if (!subEXp.dataSource) { - expressionObject[1] = null; - } else { - expressionObject[1] = subEXp.value; - } - if ( - subEXp.comparableDataSource === DataSource.ApplicationSettings || - subEXp.comparableDataSource === DataSource.Component || - subEXp.comparableDataSource === DataSource.DataModel || - subEXp.comparableDataSource === DataSource.InstanceContext - ) { - expressionObject[2] = [subEXp.comparableDataSource, subEXp.comparableValue]; - } else if (!subEXp.comparableDataSource) { - expressionObject[2] = null; - } else { - expressionObject[2] = subEXp.comparableValue; - } - subExpressions.push(expressionObject); + if (expression.subExpressions.length === 1) { + return convertInternalSubExpressionToExternal(expression.subExpressions[0]); + } + const multiExpression: any = [expression.operator]; + expression.subExpressions.map((subExp) => { + const convertedSubExpression = convertInternalSubExpressionToExternal(subExp); + multiExpression.push(convertedSubExpression); }); - return expression.operator ? [expression.operator].concat(subExpressions) : subExpressions[0]; + return multiExpression; +}; + +export const convertInternalSubExpressionToExternal = (subExp: SubExpression): any => { + const expressionObject: any = [subExp.function]; + if ( + subExp.dataSource === DataSource.ApplicationSettings || + subExp.dataSource === DataSource.Component || + subExp.dataSource === DataSource.DataModel || + subExp.dataSource === DataSource.InstanceContext + ) { + expressionObject[1] = [subExp.dataSource, subExp.value]; + } else if (!subExp.dataSource) { + expressionObject[1] = null; + } else { + expressionObject[1] = subExp.value; + } + if ( + subExp.comparableDataSource === DataSource.ApplicationSettings || + subExp.comparableDataSource === DataSource.Component || + subExp.comparableDataSource === DataSource.DataModel || + subExp.comparableDataSource === DataSource.InstanceContext + ) { + expressionObject[2] = [subExp.comparableDataSource, subExp.comparableValue]; + } else if (!subExp.comparableDataSource) { + expressionObject[2] = null; + } else { + expressionObject[2] = subExp.comparableValue; + } + return expressionObject; }; export const isStudioFriendlyExpression = (expression: any): boolean => { @@ -110,7 +117,7 @@ export const convertExternalExpressionToInternal = ( ); return convertedExpression; } else { - convertedExpression.operator = expression[0]; + convertedExpression.operator = expression[0] as Operator; expression.slice(1).map((expEl) => { const exp: SubExpression = { id: uuidv4(), @@ -126,32 +133,32 @@ export const convertExternalExpressionToInternal = ( }; export function convertSubExpression( - internalExpEl: SubExpression, + internalSubExp: SubExpression, externalExpEl: any, isComparable: boolean, ): SubExpression { - const newInternalExpEl = deepCopy(internalExpEl); + const newInternalSubExp = deepCopy(internalSubExp); if (Array.isArray(externalExpEl)) { isComparable - ? (newInternalExpEl.comparableDataSource = externalExpEl[0] as DataSource) - : (newInternalExpEl.dataSource = externalExpEl[0] as DataSource); + ? (newInternalSubExp.comparableDataSource = externalExpEl[0] as DataSource) + : (newInternalSubExp.dataSource = externalExpEl[0] as DataSource); isComparable - ? (newInternalExpEl.comparableValue = externalExpEl[1]) - : (newInternalExpEl.value = externalExpEl[1]); + ? (newInternalSubExp.comparableValue = externalExpEl[1]) + : (newInternalSubExp.value = externalExpEl[1]); } else if (externalExpEl === null) { isComparable - ? (newInternalExpEl.comparableDataSource = DataSource.Null) - : (newInternalExpEl.dataSource = DataSource.Null); - isComparable ? (newInternalExpEl.comparableValue = null) : (newInternalExpEl.value = null); + ? (newInternalSubExp.comparableDataSource = DataSource.Null) + : (newInternalSubExp.dataSource = DataSource.Null); + isComparable ? (newInternalSubExp.comparableValue = null) : (newInternalSubExp.value = null); } else { isComparable - ? (newInternalExpEl.comparableDataSource = typeof externalExpEl as DataSource) - : (newInternalExpEl.dataSource = typeof externalExpEl as DataSource); // to string. Can be string, number, boolean + ? (newInternalSubExp.comparableDataSource = typeof externalExpEl as DataSource) + : (newInternalSubExp.dataSource = typeof externalExpEl as DataSource); // to string. Can be string, number, boolean isComparable - ? (newInternalExpEl.comparableValue = externalExpEl) - : (newInternalExpEl.value = externalExpEl); + ? (newInternalSubExp.comparableValue = externalExpEl) + : (newInternalSubExp.value = externalExpEl); } - return newInternalExpEl; + return newInternalSubExp; } export const convertAndAddExpressionToComponent = ( @@ -260,9 +267,9 @@ export const updateExpression = ( oldExpression: Expression, index: number, subExpression: SubExpression, -) => { +): Expression => { const newExpression = deepCopy(oldExpression); - newExpression.subExpressions[index] = { id: uuidv4(), ...subExpression }; + newExpression.subExpressions[index] = subExpression; return newExpression; }; @@ -295,7 +302,11 @@ export const removeSubExpressionAndAdaptParentProps = ( return newExpression; }; -export const addDataSource = (expEl: SubExpression, dataSource: string, isComparable: boolean) => { +export const addDataSource = ( + expEl: SubExpression, + dataSource: string, + isComparable: boolean, +): SubExpression => { const newExpEl = deepCopy(expEl); if (dataSource === 'default') { isComparable ? delete newExpEl.comparableDataSource : delete newExpEl.dataSource; @@ -322,7 +333,7 @@ export const addDataSourceValue = ( expEl: SubExpression, dataSourceValue: string, isComparable: boolean, -) => { +): SubExpression => { const newExpEl = deepCopy(expEl); // TODO: Remove check for 'NotImplementedYet' when applicationSettings can be retrieved. Issue #10856 if (dataSourceValue === 'default' || dataSourceValue === 'NotImplementedYet') { @@ -363,18 +374,21 @@ export const stringifyValueForDisplay = ( return dataSourceValue.toString(); }; -export const tryParseExpression = (oldExpression: Expression, complexExpression: string) => { +export const tryParseExpression = ( + oldExpression: Expression, + complexExpression: any, +): Expression => { // TODO: Try format expression for better readability const newExpression = deepCopy(oldExpression); try { - newExpression.complexExpression = JSON.parse(complexExpression); + newExpression.complexExpression = JSON.parse(complexExpression as string); } catch (error) { newExpression.complexExpression = complexExpression; } return newExpression; }; -export const complexExpressionIsSet = (complexExpression: string) => { +export const complexExpressionIsSet = (complexExpression: string): boolean => { // ComplexExpression can be empty string return complexExpression !== undefined && complexExpression !== null; }; @@ -405,7 +419,7 @@ export const getAllComponentPropertiesThatCanHaveExpressions = ( }; }; -export const getAllConvertedExpressions = (form: FormComponent | FormContainer) => { +export const getAllConvertedExpressions = (form: FormComponent | FormContainer): Expression[] => { const { generalProperties, propertiesForGroup } = getAllComponentPropertiesThatCanHaveExpressions(form); const potentialConvertedExternalExpressionsForGroupProperties = propertiesForGroup.map(