Skip to content

refactor(DesignerV2): State simplification#9017

Closed
rllyy97 wants to merge 8 commits intomainfrom
riley/state-simplification
Closed

refactor(DesignerV2): State simplification#9017
rllyy97 wants to merge 8 commits intomainfrom
riley/state-simplification

Conversation

@rllyy97
Copy link
Copy Markdown
Contributor

@rllyy97 rllyy97 commented Apr 6, 2026

Commit Type

  • feature - New functionality
  • fix - Bug fix
  • refactor - Code restructuring without behavior change
  • perf - Performance improvement
  • docs - Documentation update
  • test - Test-related changes
  • chore - Maintenance/tooling

Risk Level

  • Low - Minor changes, limited scope
  • Medium - Moderate changes, some user impact
  • High - Major changes, significant user/system impact

What & Why

Simplifies the Redux state architecture in designer-v2 by reducing slice count, eliminating duplicated state, and moving non-global/non-serializable state out of Redux. This is a 6-commit series:

  • Merge staticResults into operationMetadata — Removes a standalone slice by folding staticResults into the existing operationMetadata slice, since both are keyed by operation ID.
  • Move designerView to React context — Moves rarely-changing, app-level view config (dark mode, read-only, panel location) from Redux to a React context provider, reducing unnecessary selector evaluations.
  • Move modal to React context — Removes non-serializable callbacks (resolveCombineVariable) from Redux state, fixing a Redux anti-pattern. Modal state is now managed via React context.
  • Exclude panel from undo/redo snapshots — Stops serializing/compressing the large panel state on every undoable action, since panel state is transient UI and irrelevant to undo/redo semantics.
  • Consolidate node description — Eliminates dual storage of operation descriptions (was in both workflow.operations and operations.operationMetadata), establishing a single source of truth.
  • Hoist selectedNodeIds to top-level panel state — Removes triplication of selectedNodeIds across three panel sub-states (connectionContent, discoveryContent, operationContent) that were always kept in sync.

Impact of Change

  • Users: No user-facing behavior changes. Panel selection, undo/redo, modals, and static results all function identically.
  • Developers:
    • staticResultsSlice is removed — use operationMetadataSlice instead
    • designerViewSlice is removed — use DesignerViewContext instead
    • modalSlice is removed — use ModalContext instead
    • setNodeDescription is removed — use updateOperationDescription instead
    • Panel selectedNodeIds is now at state.panel.selectedNodeIds instead of per-sub-state
    • UndoRedoPartialRootState no longer includes panel
  • System:
    • Smaller undo/redo snapshots (panel state excluded from compression) — performance improvement on every undoable action
    • Fewer Redux selector evaluations for view config reads
    • Net reduction of ~3 Redux slices and ~57 lines of state code

Test Plan

  • Unit tests added/updated
  • E2E tests added/updated
  • Manual testing completed
  • Tested in:

Contributors

@rllyy97

Screenshots/Videos

N/A

@rllyy97 rllyy97 added the risk:medium Medium risk change with potential impact label Apr 6, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

📊 Coverage check completed. See workflow run for details.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

🤖 AI PR Validation Report

PR Review Results

Thank you for your submission! Here's detailed feedback on your PR title and body compliance:

PR Title

  • Current: refactor(DesignerV2): State simplification
  • Issue: None — title is concise and follows conventional commit style. It communicates scope (refactor), area (DesignerV2) and intent (state simplification).
  • Recommendation: Accept as-is. If you want to be more specific, you can append a short parenthetical e.g. refactor(DesignerV2): state simplification (move modal/designerView to React context, consolidate staticResults) but this is optional.

Commit Type

  • Properly selected (refactor).
  • Note: Only one commit type is selected which is correct for this PR.

Risk Level

  • Risk label in PR: risk:medium and PR body selects Medium.
  • Assessment: Matches the scope and breadth of changes in the diff (many slices removed/merged, contexts introduced, undo/redo snapshot change). Advised risk: Medium (matches submitter). No change recommended.

What & Why

  • Current: "Simplifies the Redux state architecture in designer-v2 by reducing slice count, eliminating duplicated state, and moving non-global/non-serializable state out of Redux..." (detailed list included)
  • Issue: None — clear, succinct, describes the 6 commit series and motivation.
  • Recommendation: Accept as-is. Consider adding a one-line note calling out any backward-compatibility/migration considerations for internal consumers (e.g., if any public APIs changed) if applicable.

Impact of Change

  • Good: Sections for Users, Developers and System are present and specific.
  • Recommendation: For Developers, consider adding a short migration note with explicit replacement guidance for any exported APIs (e.g., resetDesignerView -> useResetDesignerView) so consumers scanning the PR can quickly update call sites. You already list the replacements — you could add example usage for the new context hooks if you expect other teams to use them.

Test Plan

  • Assessment: PR body indicates Unit tests added/updated and Manual testing completed. The diff shows multiple test files updated to reflect the new context-based APIs and merged slices, so this claim is supported.
  • Recommendation: If there are any scenarios not covered by unit tests (edge cases around undo/redo snapshots, modal promise resolution paths), consider calling them out and adding tests or a brief justification for manual testing only. E2E tests are unchecked — if this change could influence integrated user flows, consider an E2E or integration test in follow-up.

Contributors

  • Contributors section lists @rllyy97.
  • Assessment: Present and acceptable. If there were PMs/designers/reviewers who contributed, consider mentioning them as a courtesy, but not required.

⚠️ Screenshots/Videos

  • Assessment: N/A — acceptable since there are no user-facing visual changes. If you changed UX behavior/modal flows, consider adding a short recording or screenshots in future PRs for reviewers, but not required here.

Summary Table

Section Status Recommendation
Title No change needed.
Commit Type Correct (refactor).
Risk Level risk:medium appropriate.
What & Why Clear; consider a short migration note.
Impact of Change Good; add example usage for new hooks if helpful.
Test Plan Unit tests present in diff; consider E2E if needed.
Contributors OK.
Screenshots/Videos ⚠️ N/A is fine; add visuals only if UI behavior changed.

Final Message
This PR body and title largely follow the required template and are high quality. The risk label matches the code changes and the diff shows unit tests updated to reflect the refactor. A few small suggestions to make review and future maintenance easier:

  • Add a very short "Migration / Developer guidance" subsection (1–3 examples) in the PR body showing old → new usage for the most common external hooks/actions that changed (e.g., resetDesignerView dispatch → useResetDesignerView() usage, setNodeDescriptionupdateOperationDescription({ id, description })). This helps consumers update call sites quickly.
  • If there are known edge-case behaviors (undo/redo, modal promise resolution), either add targeted unit tests or call them out explicitly in Test Plan with rationale for manual testing.
  • Optionally expand the PR title with a parenthetical list of the biggest changes for quick scanning, though the current title is acceptable.

Please update only if you want to add migration examples or explicitly call out any remaining manual test coverage; otherwise this PR passes the PR title/body checklist. Thank you for the thorough description and for updating tests along with the refactor!


Last updated: Tue, 07 Apr 2026 19:04:49 GMT

@rllyy97 rllyy97 marked this pull request as ready for review April 7, 2026 18:50
Copilot AI review requested due to automatic review settings April 7, 2026 18:51
@rllyy97
Copy link
Copy Markdown
Contributor Author

rllyy97 commented Apr 7, 2026

Closing this, will be going with a different approach.

@rllyy97 rllyy97 closed this Apr 7, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Refactors designer-v2 state management by removing several Redux slices (designerView, modal, staticResultsSchema) and consolidating state into React contexts and the existing operationMetadata slice, while simplifying panel selection state and reducing undo/redo snapshot size.

Changes:

  • Moved designer view + modal state from Redux slices into React context providers (DesignerViewContext, ModalContext), updating UI call sites and tests.
  • Folded static result schema/properties into operationMetadataSlice and updated selectors, serializer/deserializer, and copy/paste paths.
  • Simplified panel selection to a single top-level panel.selectedNodeIds and excluded panel from undo/redo compression.

Reviewed changes

Copilot reviewed 59 out of 59 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
libs/designer-v2/src/lib/ui/panel/nodeDetailsPanel/usePanelTabs.tsx Switches schema hooks to operation selectors after static results consolidation.
libs/designer-v2/src/lib/ui/panel/nodeDetailsPanel/tabs/testingTab/index.tsx Repoints static-result actions/selectors to operation metadata/state.
libs/designer-v2/src/lib/ui/panel/nodeDetailsPanel/nodeDetailsPanel.tsx Uses DesignerViewContext + operationMetadata description update action.
libs/designer-v2/src/lib/ui/panel/nodeDetailsPanel/tests/usePanelTabs.spec.tsx Updates mocks for moved schema selector.
libs/designer-v2/src/lib/ui/dialog/triggerDescriptionDialog.tsx Migrates modal open/close to ModalContext and description to operation metadata.
libs/designer-v2/src/lib/ui/dialog/combineVariableDialog.tsx Migrates combine-variable modal close/resolve to ModalContext.
libs/designer-v2/src/lib/ui/connections/handoffEdge.tsx Moves edge context-menu state to DesignerViewContext.
libs/designer-v2/src/lib/ui/connections/dropzone.tsx Moves edge context-menu state to DesignerViewContext.
libs/designer-v2/src/lib/ui/common/KindChangeDialog/test/KindChangeDialog.spec.tsx Updates test to ModalContext close hook.
libs/designer-v2/src/lib/ui/common/KindChangeDialog/KindChangeDialog.tsx Uses ModalContext instead of Redux modal slice.
libs/designer-v2/src/lib/ui/common/DesignerContextualMenu/DesignerContextualMenu.tsx Uses DesignerViewContext for delete modal state.
libs/designer-v2/src/lib/ui/common/DeleteModal/DeleteModal.tsx Uses DesignerViewContext to dismiss delete modal.
libs/designer-v2/src/lib/ui/DesignerReactFlow.tsx Moves node context-menu state to DesignerViewContext.
libs/designer-v2/src/lib/ui/CustomNodes/test/SubgraphCardNode.spec.tsx Updates mocks for DesignerViewContext hooks.
libs/designer-v2/src/lib/ui/CustomNodes/test/ScopeCardNode.spec.tsx Updates mocks for DesignerViewContext hooks.
libs/designer-v2/src/lib/ui/CustomNodes/test/OperationCardNode.spec.tsx Updates mocks for DesignerViewContext hooks.
libs/designer-v2/src/lib/ui/CustomNodes/SubgraphCardNode.tsx Uses DesignerViewContext hooks for context menu + delete modal.
libs/designer-v2/src/lib/ui/CustomNodes/ScopeCardNode.tsx Uses DesignerViewContext hooks for context menu + delete modal.
libs/designer-v2/src/lib/ui/CustomNodes/OperationCardNode.tsx Uses DesignerViewContext hooks for context menu + delete modal.
libs/designer-v2/src/lib/ui/CustomNodes/CollapsedCardNode.tsx Uses DesignerViewContext hook for node context menu.
libs/designer-v2/src/lib/ui/Controls.tsx Uses DesignerViewContext hooks for minimap toggling.
libs/designer-v2/src/lib/core/utils/undoredo.ts Excludes panel/staticResults from compressed state and updates selected node tracking.
libs/designer-v2/src/lib/core/utils/swagger/operation.ts Moves addResultSchema and description initialization into operation metadata.
libs/designer-v2/src/lib/core/utils/test/undoredo.spec.ts Updates tests for new panel selectedNodeIds shape and excluded slices.
libs/designer-v2/src/lib/core/store.ts Removes designerView/staticResults/modal reducers from store.
libs/designer-v2/src/lib/core/state/workflow/workflowSlice.ts Drops setNodeDescription reducer and reuses operationMetadata updateOperationDescription.
libs/designer-v2/src/lib/core/state/workflow/workflowSelectors.ts Sources node description from operations.operationMetadata.
libs/designer-v2/src/lib/core/state/undoRedo/undoRedoTypes.ts Removes panel/staticResults from UndoRedoPartialRootState.
libs/designer-v2/src/lib/core/state/staticresultschema/staticresultsSlice.ts Deletes static results schema slice (moved into operation metadata).
libs/designer-v2/src/lib/core/state/staticresultschema/staitcresultsSelector.ts Deletes static-results selectors (replaced by operation selector hooks).
libs/designer-v2/src/lib/core/state/panel/panelTypes.ts Hoists selectedNodeIds to top-level panel state.
libs/designer-v2/src/lib/core/state/panel/panelSlice.ts Consolidates selected node tracking and removes undo/redo restoration.
libs/designer-v2/src/lib/core/state/panel/panelSelectors.ts Repoints selectors to new top-level selectedNodeIds.
libs/designer-v2/src/lib/core/state/operation/operationSelector.ts Adds static-results schema/property hooks under operation state.
libs/designer-v2/src/lib/core/state/operation/operationMetadataSlice.ts Adds staticResultSchemas/staticResultProperties and related reducers; widens description payload.
libs/designer-v2/src/lib/core/state/modal/modalSlice.ts Deletes Redux modal slice (moved to ModalContext).
libs/designer-v2/src/lib/core/state/modal/modalSelectors.ts Removes selectors that read from deleted modal slice.
libs/designer-v2/src/lib/core/state/modal/ModalContext.tsx Introduces ModalContext + module-level service bridge for thunks.
libs/designer-v2/src/lib/core/state/designerView/designerViewSlice.ts Deletes Redux designerView slice (moved to DesignerViewContext).
libs/designer-v2/src/lib/core/state/designerView/designerViewSelectors.ts Re-exports DesignerViewContext hooks (no longer Redux selectors).
libs/designer-v2/src/lib/core/state/designerView/DesignerViewContext.tsx Introduces DesignerViewContext provider + selector/action-equivalent hooks.
libs/designer-v2/src/lib/core/state/test/staticresultsSlice.spec.ts Updates test to validate undo/redo restore via operation metadata slice.
libs/designer-v2/src/lib/core/state/test/panelSlice.spec.ts Removes undo/redo restore test for panel (no longer in snapshots).
libs/designer-v2/src/lib/core/state/test/modalSlice.spec.ts Replaces modal slice tests with ModalContext tests.
libs/designer-v2/src/lib/core/state/test/designerViewSlice.spec.ts Replaces reducer tests with DesignerViewContext tests.
libs/designer-v2/src/lib/core/parsers/test/combineSequentialInitializeVariables.spec.ts Formatting-only update in test data.
libs/designer-v2/src/lib/core/parsers/ParseReduxAction.ts Uses ModalContext service to await combine-variable decision; moves initStaticResultProperties import.
libs/designer-v2/src/lib/core/index.ts Re-exports DesignerViewContext reset hook + ModalContext exports; stops exporting modal slice.
libs/designer-v2/src/lib/core/actions/bjsworkflow/serializer.ts Serializes description/staticResults from operation metadata state instead of removed slices.
libs/designer-v2/src/lib/core/actions/bjsworkflow/operationdeserializer.ts Initializes operation description and moves addResultSchema import.
libs/designer-v2/src/lib/core/actions/bjsworkflow/delete.ts Moves deinitializeStaticResultProperty import under operation metadata slice.
libs/designer-v2/src/lib/core/actions/bjsworkflow/copypaste.ts Reads/writes description + static results via operation metadata/state.
libs/designer-v2/src/lib/core/actions/bjsworkflow/agent.ts Moves addResultSchema import under operation metadata slice.
libs/designer-v2/src/lib/core/actions/bjsworkflow/add.ts Uses ModalContext service to open kind-change dialog (no Redux modal slice).
libs/designer-v2/src/lib/core/DesignerProvider.tsx Adds DesignerViewProvider + ModalProvider; resets context state on id changes.
libs/designer-v2/src/lib/test/redux-test-helper.tsx Removes deleted reducers from test store.
libs/designer-v2/src/lib/test/mock-root-state.tsx Removes deleted slices from mocked RootState/UndoRedoPartialRootState.
apps/vs-code-react/src/app/designer/DesignerCommandBar/indexV2.tsx Switches from dispatching resetDesignerView to using context reset hook.
apps/Standalone/src/designer/app/AzureLogicAppsDesigner/DesignerCommandBarV2.tsx Switches from dispatching resetDesignerView to using context reset hook.
Comments suppressed due to low confidence (1)

libs/designer-v2/src/lib/core/state/operation/operationSelector.ts:1

  • The reducer callback mutates the acc parameter, but the no-param-reassign suppression was removed. If no-param-reassign is enforced, this will fail lint/CI. Either restore the targeted ESLint suppression for this block or rewrite the reduce to return a new accumulator object without mutating the parameter.
import type { NodeStaticResults } from '../../actions/bjsworkflow/staticresults';

Comment on lines +6 to +7
showMinimap: boolean | undefined;
clampPan: boolean | undefined;
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

showMinimap and clampPan are now typed as boolean | undefined, which is a type regression from the previous selector behavior (always boolean). Since these values are always initialized in the provider, make them plain boolean to preserve the prior contract and avoid downstream consumers needing unnecessary undefined handling.

Suggested change
showMinimap: boolean | undefined;
clampPan: boolean | undefined;
showMinimap: boolean;
clampPan: boolean;

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +29
// Module-level service holder for thunks that can't use React hooks
export interface ModalService {
openCombineVariable: () => Promise<boolean>;
openKindChange: (type: string) => void;
}

let _modalService: ModalService | null = null;
export const getModalService = (): ModalService => {
if (!_modalService) {
throw new Error('ModalService not initialized. Ensure ModalProvider is mounted.');
}
return _modalService;
};
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a module-level singleton service has two concrete pitfalls: (1) it’s not initialized until the first useEffect runs, so getModalService() can throw even after the provider renders (before effects flush), and (2) it implicitly supports only one mounted ModalProvider at a time (last mounted wins), which can break multi-designer scenarios. Consider initializing the service synchronously (e.g., during render via a stable ref / useMemo or useLayoutEffect) and/or avoiding a global singleton by injecting a modal service through thunk extraArgument or a host-provided service container.

Copilot uses AI. Check for mistakes.
Comment on lines +68 to +74
// Set up the module-level service for thunks
useEffect(() => {
_modalService = { openCombineVariable, openKindChange };
return () => {
_modalService = null;
};
}, [openCombineVariable, openKindChange]);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a module-level singleton service has two concrete pitfalls: (1) it’s not initialized until the first useEffect runs, so getModalService() can throw even after the provider renders (before effects flush), and (2) it implicitly supports only one mounted ModalProvider at a time (last mounted wins), which can break multi-designer scenarios. Consider initializing the service synchronously (e.g., during render via a stable ref / useMemo or useLayoutEffect) and/or avoiding a global singleton by injecting a modal service through thunk extraArgument or a host-provided service container.

Copilot uses AI. Check for mistakes.
getModalService,
} from '../modal/ModalContext';

const wrapper = ({ children }: { children: React.ReactNode }) => createElement(ModalProvider, null, children);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test file references React.ReactNode but does not import the React type/namespace, which will fail TypeScript compilation in setups where React isn’t a global namespace. Import type React from 'react' (or use import('react').ReactNode) for the wrapper props type.

Copilot uses AI. Check for mistakes.
export { initializeNotes } from './state/notes/notesSlice';
export { useCanUndo, useCanRedo } from './state/undoRedo/undoRedoSelectors';
export { resetDesignerView } from './state/designerView/designerViewSlice';
export { useResetDesignerView } from './state/designerView/DesignerViewContext';
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The public API now exposes useResetDesignerView (hook) instead of a dispatchable action (resetDesignerView). This makes resetting designer-view state impossible from non-React contexts (similar to why getModalService was introduced). Consider exposing an imperative reset entrypoint (e.g., a DesignerView service getter like the modal service) or a backwards-compatible export name to reduce friction for consumers that previously called this from outside React components.

Suggested change
export { useResetDesignerView } from './state/designerView/DesignerViewContext';
export {
useResetDesignerView,
useResetDesignerView as resetDesignerView,
} from './state/designerView/DesignerViewContext';

Copilot uses AI. Check for mistakes.
Comment on lines 130 to +131
export * from './state/modal/modalSelectors';
export * from './state/modal/modalSlice';
export * from './state/modal/ModalContext';
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The public API now exposes useResetDesignerView (hook) instead of a dispatchable action (resetDesignerView). This makes resetting designer-view state impossible from non-React contexts (similar to why getModalService was introduced). Consider exposing an imperative reset entrypoint (e.g., a DesignerView service getter like the modal service) or a backwards-compatible export name to reduce friction for consumers that previously called this from outside React components.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-validated risk:medium Medium risk change with potential impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants