From 9075954a1e7b9bedcc45fda505097e9cf570a1ff Mon Sep 17 00:00:00 2001 From: Robert Isele Date: Thu, 3 Sep 2026 16:07:12 +0200 Subject: [PATCH 1/2] Fix chain of unsaved-changes prompts when opening the formula editor for a new mapping After saving a new value mapping via the pen icon, the mapping list reloads while the formula editor mounts. Every list row issued a router navigation on mount and the editor reported unsaved changes until it had initialized, so each navigation raised the "unsaved changes" confirm, once per rule. - DraggableItem: rows that mount collapsed no longer touch the URL. - RuleEditorModel: no unsaved changes are reported while the editor is initializing. CMEM-8143 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_013CoCMG72HzZRnv4fzh6RtV --- .../containers/MappingRule/DraggableItem.jsx | 8 ++++++-- .../app/views/shared/RuleEditor/model/RuleEditorModel.tsx | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/workspace/src/app/views/pages/MappingEditor/HierarchicalMapping/containers/MappingRule/DraggableItem.jsx b/workspace/src/app/views/pages/MappingEditor/HierarchicalMapping/containers/MappingRule/DraggableItem.jsx index c4dfbd4da2..1589083dfd 100644 --- a/workspace/src/app/views/pages/MappingEditor/HierarchicalMapping/containers/MappingRule/DraggableItem.jsx +++ b/workspace/src/app/views/pages/MappingEditor/HierarchicalMapping/containers/MappingRule/DraggableItem.jsx @@ -94,9 +94,13 @@ const DraggableItem = (props) => { [expanded, props.type, props.id], ); - // Call updateQueryOnExpansion when expanded changes + // Call updateQueryOnExpansion when expanded changes; a row mounting collapsed would only re-set the parent id. + const mounted = useRef(false); useEffect(() => { - updateQueryOnExpansion(); + if (expanded || mounted.current) { + updateQueryOnExpansion(); + } + mounted.current = true; }, [expanded]); // Create provided and snapshot objects compatible with the existing MappingRule component diff --git a/workspace/src/app/views/shared/RuleEditor/model/RuleEditorModel.tsx b/workspace/src/app/views/shared/RuleEditor/model/RuleEditorModel.tsx index b61e99ba8e..4dc5620c0b 100644 --- a/workspace/src/app/views/shared/RuleEditor/model/RuleEditorModel.tsx +++ b/workspace/src/app/views/shared/RuleEditor/model/RuleEditorModel.tsx @@ -164,7 +164,9 @@ export const RuleEditorModel = ({ children }: RuleEditorModelProps) => { stickyNotes: StickyNote[]; externalSavedState?: unknown; }>(); - const unsavedChanges = savedStatePosition !== "current" || (!savedOnce && ruleEditorContext.saveInitiallyEnabled); + // Nothing can be unsaved before the editor has loaded; reporting it would arm the navigation prompts meanwhile. + const unsavedChanges = + !initializing && (savedStatePosition !== "current" || (!savedOnce && ruleEditorContext.saveInitiallyEnabled)); const hotkeyContext = React.useContext(ReactFlowHotkeyContext); const onRuleOperatorNodesChangeRef = React.useRef(ruleEditorContext.onRuleOperatorNodesChange); onRuleOperatorNodesChangeRef.current = ruleEditorContext.onRuleOperatorNodesChange; From 1180b13b4b7a35ca74a1eb5d20fe181cb83c53aa Mon Sep 17 00:00:00 2001 From: Robert Isele Date: Mon, 7 Sep 2026 12:52:43 +0200 Subject: [PATCH 2/2] Fix race in the RuleBlockEditor invalid-save integration test Since the editor no longer reports unsaved changes while initializing, the Save button is disabled from the first render, so waiting for it no longer waits for the rule to load. Wait for the node on the canvas instead. CMEM-8143 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01AAGwGNnrBJFSUwz1DwHmea --- .../ruleBlock/test/RuleBlockEditor.integration.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/workspace/src/app/views/taskViews/ruleBlock/test/RuleBlockEditor.integration.test.tsx b/workspace/src/app/views/taskViews/ruleBlock/test/RuleBlockEditor.integration.test.tsx index be2a513466..82587861db 100644 --- a/workspace/src/app/views/taskViews/ruleBlock/test/RuleBlockEditor.integration.test.tsx +++ b/workspace/src/app/views/taskViews/ruleBlock/test/RuleBlockEditor.integration.test.tsx @@ -83,6 +83,7 @@ describe("RuleBlockEditor integration", () => { , ); + await waitFor(() => expect(screen.getByRole("button", { name: "remove-normal-node" })).toBeInTheDocument()); await waitFor(() => expect(screen.getByRole("button", { name: "Save" })).toBeDisabled()); expect(screen.queryByTestId("context-overlay")).not.toBeInTheDocument();