Skip to content

Fix fork form#1458

Open
senithkay wants to merge 2 commits intowso2:mainfrom
senithkay:fix-fork-form
Open

Fix fork form#1458
senithkay wants to merge 2 commits intowso2:mainfrom
senithkay:fix-fork-form

Conversation

@senithkay
Copy link
Contributor

@senithkay senithkay commented Feb 17, 2026

Purpose

Fix issues in the Fork form where key UI interactions were not working correctly:

  • The Add button in the Fork form was not functioning, preventing users from adding new worker entries.
  • The Type field for the Worker was not appearing, blocking proper worker configuration.

These issues negatively impacted the Fork configuration workflow and usability.

Partially Resolves: wso2/product-ballerina-integrator#2419


Goals

  • Restore the functionality of the Add button in the Fork form.
  • Ensure the Worker Type field is visible and accessible.
  • Allow users to successfully add and configure Fork workers.
  • Maintain existing behavior and validations without regressions.

Approach

  • Investigated the Fork form component and its event handling logic.
  • Fixed the issue preventing the Add button from triggering the expected state updates.
  • Corrected rendering logic to ensure the Worker Type field is displayed.

Summary by CodeRabbit

  • New Features

    • Added type field configuration support for branches
    • Enhanced record editor with improved editing context
  • Improvements

    • Strengthened form validation and error handling
    • Enhanced visual styling and form structure
    • Improved expression editor diagnostics integration
    • Better state management when adding or removing branch workers

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

Walkthrough

The ForkForm component is refactored to adopt a Provider-based architecture with enhanced form management and type field support for branches. FormGenerator is updated to integrate stack-based type creation modals with breadcrumb navigation, wiring a new openRecordEditor public API that enables inline type editing during fork form composition.

Changes

Cohort / File(s) Summary
ForkForm Core Restructuring
workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/ForkForm/index.tsx
Replaced direct rendering with Provider-based composition; added FieldGroup styling; extended ForkFormProps with openRecordEditor callback; integrated form control via register/unregister and formState exposure; implemented branch type field support (branch-${index}-type) with initialization and persistence; added TypeEditor and EditorFactory components for each worker; integrated diagnostics proxy (handleGetExpressionDiagnostics) and form value consolidation; updated save button logic and worker removal affordances.
FormGenerator Type Creation Flow
workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGenerator/index.tsx
Added NodeProperties import; extended handleOpenTypeEditor signature with optional newType parameter; introduced EditorContext wrapper around ForkForm; added stack-based DynamicModal instances for type creation with breadcrumb navigation and FormTypeEditor component; wired new form props and type-creation modal flow to ForkForm.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ForkForm
    participant Provider
    participant TypeEditor
    participant FormGenerator
    participant DynamicModal
    
    User->>ForkForm: Interact with branch type field
    ForkForm->>Provider: Request form context
    Provider->>ForkForm: Provide form control & diagnostics
    ForkForm->>TypeEditor: Click edit type
    TypeEditor->>ForkForm: Trigger openRecordEditor callback
    ForkForm->>FormGenerator: Call openRecordEditor(isOpen, formValues, editingField)
    FormGenerator->>DynamicModal: Render type creation modal (depth 1)
    User->>DynamicModal: Edit/create type
    DynamicModal->>FormGenerator: Return newType value
    FormGenerator->>ForkForm: Pass newType to form state
    ForkForm->>Provider: Update branch-${index}-type value
    Provider->>User: Display updated type in form
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 whiskers twitching with delight
Forms now dance with Provider's might,
Branch types bloom in fields of light,
Modal stacks reach new heights,
The fork flows smooth—a rabbit's dream come true! 🌿✨

🚥 Pre-merge checks | ✅ 3 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (147 files):

⚔️ common/config/rush/.pnpmfile.cjs (content)
⚔️ common/config/rush/pnpm-lock.yaml (content)
⚔️ workspaces/ballerina/ballerina-core/src/interfaces/bi.ts (content)
⚔️ workspaces/ballerina/ballerina-core/src/interfaces/extended-lang-client.ts (content)
⚔️ workspaces/ballerina/ballerina-core/src/rpc-types/ai-panel/index.ts (content)
⚔️ workspaces/ballerina/ballerina-core/src/rpc-types/ai-panel/interfaces.ts (content)
⚔️ workspaces/ballerina/ballerina-core/src/rpc-types/ai-panel/rpc-type.ts (content)
⚔️ workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/index.ts (content)
⚔️ workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/interfaces.ts (content)
⚔️ workspaces/ballerina/ballerina-core/src/rpc-types/bi-diagram/rpc-type.ts (content)
⚔️ workspaces/ballerina/ballerina-core/src/state-machine-types.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/CHANGELOG.md (content)
⚔️ workspaces/ballerina/ballerina-extension/package.json (content)
⚔️ workspaces/ballerina/ballerina-extension/src/core/extension.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/ai/agent/np/prompts.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/ai/agent/prompts.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/ai/agent/tool-registry.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/ai/state/ApprovalManager.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/ai/state/ApprovalViewManager.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/ai/utils/ai-utils.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/ai/utils/events.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/project/cmds/cmd-runner.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/test-explorer/activator.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/test-explorer/commands.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/test-explorer/discover.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/test-explorer/evalset-tree-view.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/test-explorer/runner.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/tracing/trace-converter.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/tracing/trace-details-webview.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/features/tryit/activator.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-handler.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/rpc-managers/ai-panel/rpc-manager.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/rpc-managers/bi-diagram/rpc-handler.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/rpc-managers/bi-diagram/rpc-manager.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/rpc-managers/test-manager/rpc-manager.ts (content)
⚔️ workspaces/ballerina/ballerina-extension/src/utils/bi.ts (content)
⚔️ workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/ai-panel/rpc-client.ts (content)
⚔️ workspaces/ballerina/ballerina-rpc-client/src/rpc-clients/bi-diagram/rpc-client.ts (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/Form/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/Panel/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/ParamManager/ParamManager.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/ActionTypeEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/ArrayEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/AutoCompleteEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/CustomDropdownEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/DropdownChoiceForm.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/DropdownEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/EditorFactory.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/ExpandedEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/types.ts (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/FileSelect.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/FormMapEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/IdentifierEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/IdentifierField.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/BooleanEditor/BooleanEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionDefaultConfig.ts (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/EnumEditor/EnumEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiSelectEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/PathEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/TextAreaEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/TextEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/TypeEditor.tsx (content)
⚔️ workspaces/ballerina/ballerina-side-panel/src/components/editors/utils.ts (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/MainPanel.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/PopupPanel.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/components/Skeletons/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/segment.ts (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/AgentChatPanel/Components/ChatInterface.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/DiagramWrapper/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/ForkForm/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGenerator/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGeneratorNew/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/FunctionForm/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/RecordConfigModal.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ImportIntegration/ConfigureProjectForm.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ImportIntegration/ImportIntegrationForm.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ImportIntegration/components/MultiProjectFormFields.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ImportIntegration/utils.ts (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectForm.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/AddProjectFormFields.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/ProjectFormFields.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ProjectForm/utils.ts (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/Forms/ResourceForm/ResourceResponse/ResponseItem.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/ServiceConfigureView.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/components/ResourceAccordion.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/components/ResourceAccordionV2.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceFunctionForm/utils.ts (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/TestFunctionForm/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/BI/TypeEditor/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/EvalsetViewer/ConfirmationModal.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/EvalsetViewer/EvalThreadViewer.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/EvalsetViewer/EvalsetViewer.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/EvalsetViewer/ToolEditorModal.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/EvalsetViewer/utils/traceAdapters.ts (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/GraphQLDiagram/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/ReviewMode/ReadonlyComponentDiagram.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/ReviewMode/ReadonlyFlowDiagram.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/ReviewMode/ReadonlyTypeDiagram.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/ReviewMode/ReviewNavigation.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/ReviewMode/index.tsx (content)
⚔️ workspaces/ballerina/ballerina-visualizer/src/views/TypeDiagram/index.tsx (content)
⚔️ workspaces/ballerina/bi-diagram/package.json (content)
⚔️ workspaces/ballerina/overview-view/package.json (content)
⚔️ workspaces/ballerina/persist-layer-diagram/package.json (content)
⚔️ workspaces/ballerina/sequence-diagram/package.json (content)
⚔️ workspaces/ballerina/statement-editor/package.json (content)
⚔️ workspaces/ballerina/type-diagram/package.json (content)
⚔️ workspaces/ballerina/type-editor/src/Context/index.tsx (content)
⚔️ workspaces/ballerina/type-editor/src/TypeEditor/ArrayEditor.tsx (content)
⚔️ workspaces/ballerina/type-editor/src/TypeEditor/ClassEditor.tsx (content)
⚔️ workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/ContextTypeCreator.tsx (content)
⚔️ workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/ContextTypeEditor.tsx (content)
⚔️ workspaces/ballerina/type-editor/src/TypeEditor/ContextBasedTypeEditor/SchemaRecordEditor.tsx (content)
⚔️ workspaces/ballerina/type-editor/src/TypeEditor/Contexts/TypeEditorContext.ts (content)
⚔️ workspaces/ballerina/type-editor/src/TypeEditor/FieldEditor.tsx (content)
⚔️ workspaces/ballerina/type-editor/src/TypeEditor/RecordEditor.tsx (content)
⚔️ workspaces/ballerina/type-editor/src/TypeEditor/TypeEditor.tsx (content)
⚔️ workspaces/ballerina/type-editor/src/TypeEditor/TypeField.tsx (content)
⚔️ workspaces/ballerina/type-editor/src/TypeEditor/UnionEditor.tsx (content)
⚔️ workspaces/bi/bi-extension/.eslintrc.json (content)
⚔️ workspaces/bi/bi-extension/.vscodeignore (content)
⚔️ workspaces/bi/bi-extension/CHANGELOG.md (content)
⚔️ workspaces/bi/bi-extension/README.md (content)
⚔️ workspaces/bi/bi-extension/package.json (content)
⚔️ workspaces/bi/bi-extension/src/project-explorer/project-explorer-provider.ts (content)
⚔️ workspaces/bi/bi-extension/src/test/e2e-playwright-tests/utils/helpers/setup.ts (content)
⚔️ workspaces/common-libs/playwright-vscode-tester/src/components/Form.ts (content)
⚔️ workspaces/common-libs/rpc-generator/package.json (content)
⚔️ workspaces/common-libs/ui-toolkit/package.json (content)
⚔️ workspaces/common-libs/ui-toolkit/src/components/LocationSelector/LocationSelector.tsx (content)
⚔️ workspaces/common-libs/ui-toolkit/src/index.ts (content)
⚔️ workspaces/mi/mi-diagram/src/components/sidePanel/dataServices/input-mapping.tsx (content)
⚔️ workspaces/mi/mi-diagram/src/components/sidePanel/dataServices/output-mapping.tsx (content)
⚔️ workspaces/mi/mi-extension/package.json (content)
⚔️ workspaces/mi/mi-extension/src/constants/index.ts (content)
⚔️ workspaces/mi/mi-extension/src/debugger/debugAdapter.ts (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
Title check ❓ Inconclusive The title 'Fix fork form' is vague and generic, lacking specificity about which issues were fixed or what improvements were made. Use a more descriptive title that clarifies the specific fixes, such as 'Fix Add button and Worker Type field in Fork form' or 'Restore Fork form functionality and type field rendering'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description covers Purpose (with issue link), Goals, and Approach sections from the template with adequate detail about the fork form issues and fixes implemented.
Linked Issues check ✅ Passed The changes implement using node templates for form rendering as required by issue #2419, fixing the Fork form to render Worker Type fields and Add button functionality based on node template structure.
Out of Scope Changes check ✅ Passed All changes are focused on fixing the Fork form component and its integration with FormGenerator, directly addressing the linked issue's requirement to use node templates for field rendering.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch fix-fork-form
  • Post resolved changes as copyable diffs in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/ForkForm/index.tsx (1)

249-264: ⚠️ Potential issue | 🟡 Minor

removeWorker does not clean up stale form values for the last branch index.

After filtering out the removed branch and shifting values upward, the form values for the old last index (branch-${branches.length - 1} and branch-${branches.length - 1}-type) remain registered and populated. This could cause the save handler to pick up ghost data from the removed branch.

Proposed fix — unregister or clear the old last index
         for (let i = index + 1; i < branches.length; i++) {
             const value = getValues(`branch-${i}`);
             setValue(`branch-${i - 1}`, value);
             const typeValue = getValues(`branch-${i}-type`);
             setValue(`branch-${i - 1}-type`, typeValue);
         }
+        // Clean up stale values for the removed last branch index
+        const lastIndex = branches.length - 1;
+        setValue(`branch-${lastIndex}`, undefined);
+        setValue(`branch-${lastIndex}-type`, undefined);
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/ForkForm/index.tsx`
around lines 249 - 264, The removeWorker function leaves stale form entries for
the old last branch index after shifting values; after calling setBranches and
shifting values with getValues/setValue (in removeWorker), clear or unregister
the leftover fields for the previous last index (e.g., call unregister or
setValue('', undefined) for `branch-${branches.length - 1}` and
`branch-${branches.length - 1}-type`) so the removed branch's data is not
retained by the form; ensure you reference and update the same form API used
elsewhere (getValues, setValue, unregister) within removeWorker.
workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGenerator/index.tsx (1)

533-545: ⚠️ Potential issue | 🟡 Minor

Use the correct pattern for extracting type from NodeProperties objects.

When newType is a NodeProperties object, the ternary on line 543 incorrectly falls through to f[editingField?.key], discarding the object. Compare this with the same logic in FormGeneratorNew/index.tsx:821, which properly extracts the type property from NodeProperties:

Corrected pattern
// Current (incorrect):
const newTypeValue = typeof newType === 'string' ? newType : f[editingField?.key];

// Should be:
const newTypeValue = newType
    ? (typeof newType === 'string' ? newType : (newType as NodeProperties)?.type || newType)
    : f[editingField?.key];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGenerator/index.tsx`
around lines 533 - 545, In handleOpenTypeEditor update the newTypeValue logic to
handle NodeProperties correctly: when newType is provided and is a string use
it; when newType is an object (NodeProperties) extract its .type (or fall back
to the object if .type is missing); only when newType is not provided fall back
to f[editingField?.key]; update the computation of newTypeValue before calling
setTypeEditorState (symbols: handleOpenTypeEditor, newTypeValue, NodeProperties,
setTypeEditorState, fields, setBaseFields).
🧹 Nitpick comments (3)
workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/ForkForm/index.tsx (2)

48-60: Import statement placed after non-import code.

The import on line 60 is placed after the FieldGroup styled component definition (lines 49–59). ES module imports are hoisted so this won't cause a runtime error, but it hurts readability and violates the convention of grouping all imports at the top of the file.

Move the import on line 60 above the FieldGroup declaration (i.e., after line 46).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/ForkForm/index.tsx`
around lines 48 - 60, Move the import for convertNodePropertyToFormField and
removeDuplicateDiagnostics so all imports are grouped at the top of the file
above the styled component declaration; specifically, place the line importing
convertNodePropertyToFormField and removeDuplicateDiagnostics before the
FieldGroup styled component declaration (which is declared as const FieldGroup =
styled.div`...`) to restore conventional import ordering and improve
readability.

352-359: getFormValues redundantly re-reads values already in getValues().

Since all branch values are set via setValue, they are already in the form state. The spread { ...getValues() } already contains every branch-${index} and branch-${index}-type key, so the forEach loop overwrites them with identical values. This is harmless but unnecessary.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/ForkForm/index.tsx`
around lines 352 - 359, getFormValues redundantly re-reads branch fields already
present in the object created by {...getValues()}; remove the unnecessary
branches.forEach loop and simply return the form values obtained from getValues
(or spread it once into a FormValues object) so branch-${index} and
branch-${index}-type aren’t re-read; update the getFormValues function (which
references getValues, branches, FormValues) to return the form state directly.
workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGenerator/index.tsx (1)

1337-1391: DynamicModal + breadcrumb + FormTypeEditor stack is duplicated three times.

The Fork form block (lines 1353–1389), the Variable form block (lines 1479–1513), and the default form block (lines 1605–1639) contain near-identical DynamicModal rendering logic with breadcrumbs and FormTypeEditor. Consider extracting a shared TypeCreationModalStack component to reduce duplication and ensure consistency.

Note: the Fork/Variable sections show breadcrumbs when stack.slice(0, i + 1).length > 1, while the default form uses > 2. Verify this divergence is intentional.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGenerator/index.tsx`
around lines 1337 - 1391, Duplicate DynamicModal + breadcrumb + FormTypeEditor
rendering appears across the Fork/Variable/default blocks; extract a reusable
component (e.g., TypeCreationModalStack) that accepts props: stack,
typeEditorState, handleTypeEditorStateChange, refetchStates, isGraphql,
getNewTypeCreateForm, onTypeChange, onSaveType and peekTypeStack to render the
modal list, breadcrumb (using
BreadcrumbContainer/BreadcrumbSeparator/BreadcrumbItem), and FormTypeEditor;
replace the three duplicated blocks with this new component and pass the
appropriate refetchStates[i] and other handlers; also reconcile the breadcrumb
condition (currently stack.slice(0, i + 1).length > 1 in Fork/Variable vs > 2 in
default) — decide which is correct and use the same condition in the new
component.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/ForkForm/index.tsx`:
- Around line 391-430: The map callback for branches.map can return undefined
when branch.properties?.variable is falsy and also accesses
branch.properties.type without a guard; change the logic to first filter
branches for those with branch.properties?.variable (e.g., branches.filter(b =>
b.properties?.variable).map(...)) so the callback always returns a React node,
and before calling convertNodePropertyToFormField for the type, guard or provide
a safe fallback for branch.properties.type (e.g., check if
branch.properties?.type exists and only create/type the typeField when present,
or pass an empty/default NodeProperties) — update the code around branches.map,
convertNodePropertyToFormField, variableField and typeField creation and the
removeWorker/openRecordEditor usages accordingly.

---

Outside diff comments:
In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/ForkForm/index.tsx`:
- Around line 249-264: The removeWorker function leaves stale form entries for
the old last branch index after shifting values; after calling setBranches and
shifting values with getValues/setValue (in removeWorker), clear or unregister
the leftover fields for the previous last index (e.g., call unregister or
setValue('', undefined) for `branch-${branches.length - 1}` and
`branch-${branches.length - 1}-type`) so the removed branch's data is not
retained by the form; ensure you reference and update the same form API used
elsewhere (getValues, setValue, unregister) within removeWorker.

In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGenerator/index.tsx`:
- Around line 533-545: In handleOpenTypeEditor update the newTypeValue logic to
handle NodeProperties correctly: when newType is provided and is a string use
it; when newType is an object (NodeProperties) extract its .type (or fall back
to the object if .type is missing); only when newType is not provided fall back
to f[editingField?.key]; update the computation of newTypeValue before calling
setTypeEditorState (symbols: handleOpenTypeEditor, newTypeValue, NodeProperties,
setTypeEditorState, fields, setBaseFields).

---

Nitpick comments:
In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/ForkForm/index.tsx`:
- Around line 48-60: Move the import for convertNodePropertyToFormField and
removeDuplicateDiagnostics so all imports are grouped at the top of the file
above the styled component declaration; specifically, place the line importing
convertNodePropertyToFormField and removeDuplicateDiagnostics before the
FieldGroup styled component declaration (which is declared as const FieldGroup =
styled.div`...`) to restore conventional import ordering and improve
readability.
- Around line 352-359: getFormValues redundantly re-reads branch fields already
present in the object created by {...getValues()}; remove the unnecessary
branches.forEach loop and simply return the form values obtained from getValues
(or spread it once into a FormValues object) so branch-${index} and
branch-${index}-type aren’t re-read; update the getFormValues function (which
references getValues, branches, FormValues) to return the form state directly.

In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGenerator/index.tsx`:
- Around line 1337-1391: Duplicate DynamicModal + breadcrumb + FormTypeEditor
rendering appears across the Fork/Variable/default blocks; extract a reusable
component (e.g., TypeCreationModalStack) that accepts props: stack,
typeEditorState, handleTypeEditorStateChange, refetchStates, isGraphql,
getNewTypeCreateForm, onTypeChange, onSaveType and peekTypeStack to render the
modal list, breadcrumb (using
BreadcrumbContainer/BreadcrumbSeparator/BreadcrumbItem), and FormTypeEditor;
replace the three duplicated blocks with this new component and pass the
appropriate refetchStates[i] and other handlers; also reconcile the breadcrumb
condition (currently stack.slice(0, i + 1).length > 1 in Fork/Variable vs > 2 in
default) — decide which is correct and use the same condition in the new
component.

Comment on lines +391 to +430
{branches.map((branch, index) => {
if (branch.properties?.variable) {
const variableField = convertNodePropertyToFormField(`branch-${index}`, branch.properties.variable);
variableField.types = [{ fieldType: "IDENTIFIER", selected: false }]
const typeField = convertNodePropertyToFormField(`branch-${index}-type`, branch.properties.type);
typeField.types = [{ fieldType: "TYPE", selected: false }]
variableField.label = "Worker " + (index + 1);
return (
<FormStyles.Row key={variableField.key}>
<FieldGroup>
<div>
<EditorFactory field={variableField} />
</div>
<div>
<TypeEditor
field={typeField}
openRecordEditor={(open: boolean, newType?: string | NodeProperties) =>
openRecordEditor && openRecordEditor(open, getFormValues(), typeField, newType)
}
/>
</div>

<div style={{ position: 'absolute', top: 8, right: 8 }}>
{branches.length > 2 && (
<Codicon
name="close"
sx={{ cursor: 'pointer', opacity: 0.6, '&:hover': { opacity: 1 } }}
onClick={() => {
if (branches.length > 2) {
removeWorker(index);
}
}}
/>
)}
</div>
</FieldGroup>
</FormStyles.Row>
);
}
})}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Map callback does not return on all paths — will produce undefined entries.

When branch.properties?.variable is falsy, the .map() callback returns undefined implicitly. Biome flagged this as well. Use .filter().map() or add an explicit return null for the else case.

Additionally, line 395 accesses branch.properties.type without a guard. If a pre-existing branch lacks a type property, convertNodePropertyToFormField receives undefined, likely causing a runtime error.

Proposed fix
-            {branches.map((branch, index) => {
-                if (branch.properties?.variable) {
-                    const variableField = convertNodePropertyToFormField(`branch-${index}`, branch.properties.variable);
-                    variableField.types = [{ fieldType: "IDENTIFIER", selected: false }]
-                    const typeField = convertNodePropertyToFormField(`branch-${index}-type`, branch.properties.type);
+            {branches.map((branch, index) => {
+                if (branch.properties?.variable) {
+                    const variableField = convertNodePropertyToFormField(`branch-${index}`, branch.properties.variable);
+                    variableField.types = [{ fieldType: "IDENTIFIER", selected: false }]
+                    if (!branch.properties.type) {
+                        return null;
+                    }
+                    const typeField = convertNodePropertyToFormField(`branch-${index}-type`, branch.properties.type);
                     typeField.types = [{ fieldType: "TYPE", selected: false }]
                     variableField.label = "Worker " + (index + 1);
                     return (
                         ...
                     );
                 }
+                return null;
             })}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{branches.map((branch, index) => {
if (branch.properties?.variable) {
const variableField = convertNodePropertyToFormField(`branch-${index}`, branch.properties.variable);
variableField.types = [{ fieldType: "IDENTIFIER", selected: false }]
const typeField = convertNodePropertyToFormField(`branch-${index}-type`, branch.properties.type);
typeField.types = [{ fieldType: "TYPE", selected: false }]
variableField.label = "Worker " + (index + 1);
return (
<FormStyles.Row key={variableField.key}>
<FieldGroup>
<div>
<EditorFactory field={variableField} />
</div>
<div>
<TypeEditor
field={typeField}
openRecordEditor={(open: boolean, newType?: string | NodeProperties) =>
openRecordEditor && openRecordEditor(open, getFormValues(), typeField, newType)
}
/>
</div>
<div style={{ position: 'absolute', top: 8, right: 8 }}>
{branches.length > 2 && (
<Codicon
name="close"
sx={{ cursor: 'pointer', opacity: 0.6, '&:hover': { opacity: 1 } }}
onClick={() => {
if (branches.length > 2) {
removeWorker(index);
}
}}
/>
)}
</div>
</FieldGroup>
</FormStyles.Row>
);
}
})}
{branches.map((branch, index) => {
if (branch.properties?.variable) {
const variableField = convertNodePropertyToFormField(`branch-${index}`, branch.properties.variable);
variableField.types = [{ fieldType: "IDENTIFIER", selected: false }]
if (!branch.properties.type) {
return null;
}
const typeField = convertNodePropertyToFormField(`branch-${index}-type`, branch.properties.type);
typeField.types = [{ fieldType: "TYPE", selected: false }]
variableField.label = "Worker " + (index + 1);
return (
<FormStyles.Row key={variableField.key}>
<FieldGroup>
<div>
<EditorFactory field={variableField} />
</div>
<div>
<TypeEditor
field={typeField}
openRecordEditor={(open: boolean, newType?: string | NodeProperties) =>
openRecordEditor && openRecordEditor(open, getFormValues(), typeField, newType)
}
/>
</div>
<div style={{ position: 'absolute', top: 8, right: 8 }}>
{branches.length > 2 && (
<Codicon
name="close"
sx={{ cursor: 'pointer', opacity: 0.6, '&:hover': { opacity: 1 } }}
onClick={() => {
if (branches.length > 2) {
removeWorker(index);
}
}}
/>
)}
</div>
</FieldGroup>
</FormStyles.Row>
);
}
return null;
})}
🧰 Tools
🪛 Biome (2.3.14)

[error] 391-391: This callback passed to map() iterable method should always return a value.

Add missing return statements so that this callback returns a value on all execution paths.

(lint/suspicious/useIterableCallbackReturn)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/ForkForm/index.tsx`
around lines 391 - 430, The map callback for branches.map can return undefined
when branch.properties?.variable is falsy and also accesses
branch.properties.type without a guard; change the logic to first filter
branches for those with branch.properties?.variable (e.g., branches.filter(b =>
b.properties?.variable).map(...)) so the callback always returns a React node,
and before calling convertNodePropertyToFormField for the type, guard or provide
a safe fallback for branch.properties.type (e.g., check if
branch.properties?.type exists and only create/type the typeField when present,
or pass an empty/default NodeProperties) — update the code around branches.map,
convertNodePropertyToFormField, variableField and typeField creation and the
removeWorker/openRecordEditor usages accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement concurrency nodes to use the node template

1 participant