From 05288a4538f03edf873edf97df49f70f10e61d9e Mon Sep 17 00:00:00 2001 From: Jibin7Jose Date: Sun, 12 Jul 2026 22:52:15 +0530 Subject: [PATCH 1/9] fix: resync tracked properties editor state --- .../__tests__/simpledictionary.spec.tsx | 26 +++++++++++++++++++ .../dictionary/simpledictionary.tsx | 16 +++++++++--- .../lib/core/actions/bjsworkflow/settings.ts | 4 ++- 3 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 libs/designer-ui/src/lib/settings/settingsection/__tests__/simpledictionary.spec.tsx diff --git a/libs/designer-ui/src/lib/settings/settingsection/__tests__/simpledictionary.spec.tsx b/libs/designer-ui/src/lib/settings/settingsection/__tests__/simpledictionary.spec.tsx new file mode 100644 index 00000000000..edf2ed81b64 --- /dev/null +++ b/libs/designer-ui/src/lib/settings/settingsection/__tests__/simpledictionary.spec.tsx @@ -0,0 +1,26 @@ +import { SimpleDictionary } from '../dictionary/simpledictionary'; +import { IntlProvider } from 'react-intl'; +import { render, screen, waitFor } from '@testing-library/react'; +import type { ReactElement } from 'react'; +import { describe, expect, it, vi } from 'vitest'; + +const renderWithIntl = (ui: ReactElement) => render({ui}); + +describe('ui/settings/simpledictionary', () => { + it('keeps the editor in sync when the incoming dictionary value changes', async () => { + const onChange = vi.fn(); + + const { rerender } = renderWithIntl(); + + await waitFor(() => expect(screen.getByDisplayValue('first-value')).toBeInTheDocument()); + + rerender( + + + + ); + + await waitFor(() => expect(screen.getByDisplayValue('second-value')).toBeInTheDocument()); + expect(screen.queryByDisplayValue('first-value')).not.toBeInTheDocument(); + }); +}); diff --git a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx index eb85b481ed2..3afc1b2e555 100644 --- a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx +++ b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx @@ -24,16 +24,24 @@ export const SimpleDictionary: React.FC = ({ onChange, ariaLabel, }): JSX.Element => { - const [values, setValues] = useState([ - ...Object.entries(value ?? {}).map(([key, value], index) => ({ + const createValues = (dictionaryValue?: Record) => [ + ...Object.entries(dictionaryValue ?? {}).map(([key, value], index) => ({ key, value, index, })), - { key: '', value: '', index: Object.keys(value ?? {}).length }, - ]); + { key: '', value: '', index: Object.keys(dictionaryValue ?? {}).length }, + ]; + + const [values, setValues] = useState(createValues(value)); const intl = useIntl(); + + // Keep the editor in sync if the tracked-properties value is refreshed from the parent. + useEffect(() => { + setValues(createValues(value)); + }, [value]); + useEffect(() => { onChange?.( values diff --git a/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts b/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts index e86b9106b0a..d47e48dc54e 100644 --- a/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts +++ b/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts @@ -17,6 +17,7 @@ import type { } from '@microsoft/logic-apps-shared'; import { convertToStringLiteral, + clone, getSplitOnArrayAliasMetadata, equals, getObjectPropertyValue, @@ -859,7 +860,8 @@ const getDownloadChunkSize = (definition?: LogicAppsV2.OperationDefinition): num const getTrackedProperties = (isTrigger: boolean, manifest?: OperationManifest, definition?: LogicAppsV2.ActionDefinition): any => { const supported = areTrackedPropertiesSupported(isTrigger, manifest); - return supported && definition ? getPropertyValue(definition as any, 'trackedProperties') : undefined; + const trackedProperties = supported && definition ? getPropertyValue(definition as any, 'trackedProperties') : undefined; + return trackedProperties ? clone(trackedProperties) : trackedProperties; }; const areTrackedPropertiesSupported = (isTrigger: boolean, manifest?: OperationManifest): boolean => { From 976a21208082cd81c331dde45ce37b746a552b26 Mon Sep 17 00:00:00 2001 From: Jibin7Jose Date: Mon, 13 Jul 2026 10:07:55 +0530 Subject: [PATCH 2/9] fix: resync tracked properties editor state --- .../__tests__/simpledictionary.spec.tsx | 2 + .../dictionary/simpledictionary.tsx | 37 +++++++++++++------ .../lib/core/actions/bjsworkflow/settings.ts | 3 +- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/libs/designer-ui/src/lib/settings/settingsection/__tests__/simpledictionary.spec.tsx b/libs/designer-ui/src/lib/settings/settingsection/__tests__/simpledictionary.spec.tsx index edf2ed81b64..50ebcd95dc9 100644 --- a/libs/designer-ui/src/lib/settings/settingsection/__tests__/simpledictionary.spec.tsx +++ b/libs/designer-ui/src/lib/settings/settingsection/__tests__/simpledictionary.spec.tsx @@ -13,6 +13,7 @@ describe('ui/settings/simpledictionary', () => { const { rerender } = renderWithIntl(); await waitFor(() => expect(screen.getByDisplayValue('first-value')).toBeInTheDocument()); + expect(onChange).not.toHaveBeenCalled(); rerender( @@ -22,5 +23,6 @@ describe('ui/settings/simpledictionary', () => { await waitFor(() => expect(screen.getByDisplayValue('second-value')).toBeInTheDocument()); expect(screen.queryByDisplayValue('first-value')).not.toBeInTheDocument(); + expect(onChange).not.toHaveBeenCalled(); }); }); diff --git a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx index 3afc1b2e555..e4c182e4eef 100644 --- a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx +++ b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx @@ -3,9 +3,10 @@ import { useId } from '../../../useId'; import { SimpleDictionaryItem } from './simpledictionaryitem'; import type { SimpleDictionaryRowModel, SimpleDictionaryChangeModel } from './simpledictionaryitem'; import type React from 'react'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useIntl } from 'react-intl'; import { useStyles } from './simpledictionary.styles'; +import { equals } from '@microsoft/logic-apps-shared'; export interface SimpleDictionaryProps { disabled?: boolean; @@ -33,27 +34,41 @@ export const SimpleDictionary: React.FC = ({ { key: '', value: '', index: Object.keys(dictionaryValue ?? {}).length }, ]; + const valuesToDictionary = (dictionaryRows: SimpleDictionaryRowModel[]): Record | undefined => { + const nextDictionary = dictionaryRows.reduce((acc, row) => { + if (row.key) { + acc[row.key] = row.value; + } + return acc; + }, {} as Record); + + return Object.keys(nextDictionary).length > 0 ? nextDictionary : undefined; + }; + const [values, setValues] = useState(createValues(value)); + const valuesRef = useRef(values); const intl = useIntl(); // Keep the editor in sync if the tracked-properties value is refreshed from the parent. useEffect(() => { - setValues(createValues(value)); + const nextValues = createValues(value); + if (!equals(nextValues, valuesRef.current)) { + setValues(nextValues); + } }, [value]); useEffect(() => { - onChange?.( - values - .filter((x) => x.key && x.key !== '') - .reduce((acc: any, val) => { - acc[val.key] = val.value; - return acc; - }, {}) - ); - // eslint-disable-next-line react-hooks/exhaustive-deps + valuesRef.current = values; }, [values]); + useEffect(() => { + const nextDictionary = valuesToDictionary(values); + if (!equals(nextDictionary, value)) { + onChange?.(nextDictionary); + } + }, [onChange, value, values]); + const handleItemDelete = (e: SimpleDictionaryRowModel): void => { setValues((oldValues) => oldValues.filter((x) => x.index !== e.index).map((x, i) => ({ ...x, index: i }))); }; diff --git a/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts b/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts index d47e48dc54e..30c563140b9 100644 --- a/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts +++ b/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts @@ -22,6 +22,7 @@ import { equals, getObjectPropertyValue, getPropertyValue, + isObject, OperationOptions, SettingScope, ValidationErrorCode, @@ -861,7 +862,7 @@ const getDownloadChunkSize = (definition?: LogicAppsV2.OperationDefinition): num const getTrackedProperties = (isTrigger: boolean, manifest?: OperationManifest, definition?: LogicAppsV2.ActionDefinition): any => { const supported = areTrackedPropertiesSupported(isTrigger, manifest); const trackedProperties = supported && definition ? getPropertyValue(definition as any, 'trackedProperties') : undefined; - return trackedProperties ? clone(trackedProperties) : trackedProperties; + return isObject(trackedProperties) ? clone(trackedProperties) : trackedProperties; }; const areTrackedPropertiesSupported = (isTrigger: boolean, manifest?: OperationManifest): boolean => { From b9ea077d7334a6112d4e0069fcf888e90ce2f43c Mon Sep 17 00:00:00 2001 From: Jibin7Jose Date: Mon, 13 Jul 2026 10:47:45 +0530 Subject: [PATCH 3/9] fix: compare tracked properties safely --- .../settingsection/dictionary/simpledictionary.tsx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx index e4c182e4eef..90dfb711d0d 100644 --- a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx +++ b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx @@ -3,10 +3,10 @@ import { useId } from '../../../useId'; import { SimpleDictionaryItem } from './simpledictionaryitem'; import type { SimpleDictionaryRowModel, SimpleDictionaryChangeModel } from './simpledictionaryitem'; import type React from 'react'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; import { useStyles } from './simpledictionary.styles'; -import { equals } from '@microsoft/logic-apps-shared'; +import { deepCompareObjects } from '@microsoft/logic-apps-shared'; export interface SimpleDictionaryProps { disabled?: boolean; @@ -46,25 +46,20 @@ export const SimpleDictionary: React.FC = ({ }; const [values, setValues] = useState(createValues(value)); - const valuesRef = useRef(values); const intl = useIntl(); // Keep the editor in sync if the tracked-properties value is refreshed from the parent. useEffect(() => { const nextValues = createValues(value); - if (!equals(nextValues, valuesRef.current)) { + if (!deepCompareObjects(valuesToDictionary(nextValues), value)) { setValues(nextValues); } }, [value]); - useEffect(() => { - valuesRef.current = values; - }, [values]); - useEffect(() => { const nextDictionary = valuesToDictionary(values); - if (!equals(nextDictionary, value)) { + if (!deepCompareObjects(nextDictionary, value)) { onChange?.(nextDictionary); } }, [onChange, value, values]); From e7033e4ed8543d4051c5ec00e8a1ed736bf3e0ac Mon Sep 17 00:00:00 2001 From: Jibin7Jose Date: Mon, 13 Jul 2026 10:53:24 +0530 Subject: [PATCH 4/9] fix: hoist dictionary helpers --- .../dictionary/simpledictionary.tsx | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx index 90dfb711d0d..3a82cfaeaf4 100644 --- a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx +++ b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx @@ -17,6 +17,26 @@ export interface SimpleDictionaryProps { onChange?: EventHandler | undefined>; } +const createValues = (dictionaryValue?: Record): SimpleDictionaryRowModel[] => [ + ...Object.entries(dictionaryValue ?? {}).map(([key, value], index) => ({ + key, + value, + index, + })), + { key: '', value: '', index: Object.keys(dictionaryValue ?? {}).length }, +]; + +const valuesToDictionary = (dictionaryRows: SimpleDictionaryRowModel[]): Record | undefined => { + const nextDictionary = dictionaryRows.reduce((acc, row) => { + if (row.key) { + acc[row.key] = row.value; + } + return acc; + }, {} as Record); + + return Object.keys(nextDictionary).length > 0 ? nextDictionary : undefined; +}; + export const SimpleDictionary: React.FC = ({ disabled, customLabel, @@ -25,26 +45,6 @@ export const SimpleDictionary: React.FC = ({ onChange, ariaLabel, }): JSX.Element => { - const createValues = (dictionaryValue?: Record) => [ - ...Object.entries(dictionaryValue ?? {}).map(([key, value], index) => ({ - key, - value, - index, - })), - { key: '', value: '', index: Object.keys(dictionaryValue ?? {}).length }, - ]; - - const valuesToDictionary = (dictionaryRows: SimpleDictionaryRowModel[]): Record | undefined => { - const nextDictionary = dictionaryRows.reduce((acc, row) => { - if (row.key) { - acc[row.key] = row.value; - } - return acc; - }, {} as Record); - - return Object.keys(nextDictionary).length > 0 ? nextDictionary : undefined; - }; - const [values, setValues] = useState(createValues(value)); const intl = useIntl(); From c23b88dbce6e8989dff2bd679e67de733c24da4c Mon Sep 17 00:00:00 2001 From: Jibin7Jose Date: Mon, 13 Jul 2026 11:01:23 +0530 Subject: [PATCH 5/9] fix: guard dictionary sync effects --- .../dictionary/simpledictionary.tsx | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx index 3a82cfaeaf4..04fa8915410 100644 --- a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx +++ b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx @@ -3,7 +3,7 @@ import { useId } from '../../../useId'; import { SimpleDictionaryItem } from './simpledictionaryitem'; import type { SimpleDictionaryRowModel, SimpleDictionaryChangeModel } from './simpledictionaryitem'; import type React from 'react'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useIntl } from 'react-intl'; import { useStyles } from './simpledictionary.styles'; import { deepCompareObjects } from '@microsoft/logic-apps-shared'; @@ -46,23 +46,37 @@ export const SimpleDictionary: React.FC = ({ ariaLabel, }): JSX.Element => { const [values, setValues] = useState(createValues(value)); + const valuesRef = useRef(values); + const isInitialRenderRef = useRef(true); + const isSyncingFromParentRef = useRef(false); const intl = useIntl(); - // Keep the editor in sync if the tracked-properties value is refreshed from the parent. useEffect(() => { const nextValues = createValues(value); - if (!deepCompareObjects(valuesToDictionary(nextValues), value)) { + if (!deepCompareObjects(valuesToDictionary(valuesRef.current), value)) { + isSyncingFromParentRef.current = true; setValues(nextValues); } }, [value]); useEffect(() => { - const nextDictionary = valuesToDictionary(values); - if (!deepCompareObjects(nextDictionary, value)) { - onChange?.(nextDictionary); + valuesRef.current = values; + }, [values]); + + useEffect(() => { + if (isInitialRenderRef.current) { + isInitialRenderRef.current = false; + return; } - }, [onChange, value, values]); + + if (isSyncingFromParentRef.current) { + isSyncingFromParentRef.current = false; + return; + } + + onChange?.(valuesToDictionary(values)); + }, [onChange, values]); const handleItemDelete = (e: SimpleDictionaryRowModel): void => { setValues((oldValues) => oldValues.filter((x) => x.index !== e.index).map((x, i) => ({ ...x, index: i }))); From 04df9d69763b29019542e2f6dedd68c5ed25fb20 Mon Sep 17 00:00:00 2001 From: Jibin7Jose Date: Mon, 13 Jul 2026 11:09:16 +0530 Subject: [PATCH 6/9] fix: harden tracked properties sync --- .../dictionary/simpledictionary.tsx | 8 ++++++- .../lib/core/actions/bjsworkflow/settings.ts | 24 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx index 04fa8915410..798cb4fbce6 100644 --- a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx +++ b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx @@ -37,6 +37,9 @@ const valuesToDictionary = (dictionaryRows: SimpleDictionaryRowModel[]): Record< return Object.keys(nextDictionary).length > 0 ? nextDictionary : undefined; }; +const normalizeDictionary = (dictionary?: Record): Record | undefined => + Object.keys(dictionary ?? {}).length > 0 ? dictionary : undefined; + export const SimpleDictionary: React.FC = ({ disabled, customLabel, @@ -54,7 +57,10 @@ export const SimpleDictionary: React.FC = ({ useEffect(() => { const nextValues = createValues(value); - if (!deepCompareObjects(valuesToDictionary(valuesRef.current), value)) { + const currentDictionary = normalizeDictionary(valuesToDictionary(valuesRef.current)); + const nextDictionary = normalizeDictionary(value); + + if (!deepCompareObjects(currentDictionary, nextDictionary)) { isSyncingFromParentRef.current = true; setValues(nextValues); } diff --git a/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts b/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts index 30c563140b9..daded49730c 100644 --- a/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts +++ b/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts @@ -17,7 +17,6 @@ import type { } from '@microsoft/logic-apps-shared'; import { convertToStringLiteral, - clone, getSplitOnArrayAliasMetadata, equals, getObjectPropertyValue, @@ -862,7 +861,28 @@ const getDownloadChunkSize = (definition?: LogicAppsV2.OperationDefinition): num const getTrackedProperties = (isTrigger: boolean, manifest?: OperationManifest, definition?: LogicAppsV2.ActionDefinition): any => { const supported = areTrackedPropertiesSupported(isTrigger, manifest); const trackedProperties = supported && definition ? getPropertyValue(definition as any, 'trackedProperties') : undefined; - return isObject(trackedProperties) ? clone(trackedProperties) : trackedProperties; + return isObject(trackedProperties) ? cloneTrackedProperties(trackedProperties) : trackedProperties; +}; + +const cloneTrackedProperties = (trackedProperties: Record): Record => { + const safeClone: Record = {}; + + for (const key of Object.keys(trackedProperties)) { + if (key === '__proto__' || key === 'constructor' || key === 'prototype') { + continue; + } + + const value = trackedProperties[key]; + if (Array.isArray(value)) { + safeClone[key] = value.map((item) => (isObject(item) ? cloneTrackedProperties(item) : item)); + } else if (isObject(value)) { + safeClone[key] = cloneTrackedProperties(value); + } else { + safeClone[key] = value; + } + } + + return safeClone; }; const areTrackedPropertiesSupported = (isTrigger: boolean, manifest?: OperationManifest): boolean => { From 8430fdef714c73233fc3fe4cd289a422f806b314 Mon Sep 17 00:00:00 2001 From: Jibin7Jose Date: Mon, 13 Jul 2026 11:20:03 +0530 Subject: [PATCH 7/9] fix: ignore unsafe tracked property keys --- .../__tests__/simpledictionary.spec.tsx | 14 ++++++++++++++ .../settingsection/dictionary/simpledictionary.tsx | 4 +++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/libs/designer-ui/src/lib/settings/settingsection/__tests__/simpledictionary.spec.tsx b/libs/designer-ui/src/lib/settings/settingsection/__tests__/simpledictionary.spec.tsx index 50ebcd95dc9..56f8a71c0fd 100644 --- a/libs/designer-ui/src/lib/settings/settingsection/__tests__/simpledictionary.spec.tsx +++ b/libs/designer-ui/src/lib/settings/settingsection/__tests__/simpledictionary.spec.tsx @@ -1,5 +1,6 @@ import { SimpleDictionary } from '../dictionary/simpledictionary'; import { IntlProvider } from 'react-intl'; +import userEvent from '@testing-library/user-event'; import { render, screen, waitFor } from '@testing-library/react'; import type { ReactElement } from 'react'; import { describe, expect, it, vi } from 'vitest'; @@ -25,4 +26,17 @@ describe('ui/settings/simpledictionary', () => { expect(screen.queryByDisplayValue('first-value')).not.toBeInTheDocument(); expect(onChange).not.toHaveBeenCalled(); }); + + it('ignores unsafe dictionary keys when emitting changes', async () => { + const onChange = vi.fn(); + const user = userEvent.setup(); + + renderWithIntl(); + + const [keyInput] = screen.getAllByRole('textbox'); + await user.type(keyInput, '__proto__'); + + await waitFor(() => expect(onChange).toHaveBeenCalled()); + expect(onChange).toHaveBeenLastCalledWith(undefined); + }); }); diff --git a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx index 798cb4fbce6..b72a5a4db18 100644 --- a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx +++ b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx @@ -26,9 +26,11 @@ const createValues = (dictionaryValue?: Record): SimpleDictionar { key: '', value: '', index: Object.keys(dictionaryValue ?? {}).length }, ]; +const isSafeDictionaryKey = (key: string): boolean => key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; + const valuesToDictionary = (dictionaryRows: SimpleDictionaryRowModel[]): Record | undefined => { const nextDictionary = dictionaryRows.reduce((acc, row) => { - if (row.key) { + if (row.key && isSafeDictionaryKey(row.key)) { acc[row.key] = row.value; } return acc; From b013a193fc13b44b3b41ef50f288e3f8a1d489cb Mon Sep 17 00:00:00 2001 From: Jibin7Jose Date: Mon, 13 Jul 2026 11:25:08 +0530 Subject: [PATCH 8/9] fix: normalize dictionary sync comparison --- .../lib/settings/settingsection/dictionary/simpledictionary.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx index b72a5a4db18..e982003dc62 100644 --- a/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx +++ b/libs/designer-ui/src/lib/settings/settingsection/dictionary/simpledictionary.tsx @@ -60,7 +60,7 @@ export const SimpleDictionary: React.FC = ({ useEffect(() => { const nextValues = createValues(value); const currentDictionary = normalizeDictionary(valuesToDictionary(valuesRef.current)); - const nextDictionary = normalizeDictionary(value); + const nextDictionary = normalizeDictionary(valuesToDictionary(nextValues)); if (!deepCompareObjects(currentDictionary, nextDictionary)) { isSyncingFromParentRef.current = true; From 83d966dea7bcac51b98a7cb1d1f387e3d9ff7c46 Mon Sep 17 00:00:00 2001 From: Jibin7Jose Date: Mon, 13 Jul 2026 11:31:53 +0530 Subject: [PATCH 9/9] fix: recursively clone tracked properties arrays --- .../src/lib/core/actions/bjsworkflow/settings.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts b/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts index daded49730c..684e4ada810 100644 --- a/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts +++ b/libs/designer-v2/src/lib/core/actions/bjsworkflow/settings.ts @@ -874,7 +874,7 @@ const cloneTrackedProperties = (trackedProperties: Record): Record< const value = trackedProperties[key]; if (Array.isArray(value)) { - safeClone[key] = value.map((item) => (isObject(item) ? cloneTrackedProperties(item) : item)); + safeClone[key] = cloneTrackedPropertiesArray(value); } else if (isObject(value)) { safeClone[key] = cloneTrackedProperties(value); } else { @@ -885,6 +885,20 @@ const cloneTrackedProperties = (trackedProperties: Record): Record< return safeClone; }; +const cloneTrackedPropertiesArray = (trackedProperties: any[]): any[] => { + return trackedProperties.map((item) => { + if (Array.isArray(item)) { + return cloneTrackedPropertiesArray(item); + } + + if (isObject(item)) { + return cloneTrackedProperties(item); + } + + return item; + }); +}; + const areTrackedPropertiesSupported = (isTrigger: boolean, manifest?: OperationManifest): boolean => { // Tracked properties are never supported for triggers (backend limitation), // regardless of what the manifest declares. See Azure/logicapps#798.