Skip to content

Comments

Fix missing TEXT_SET editors in service creation forms#1533

Merged
kanushka merged 2 commits intowso2:release/bi-1.8.xfrom
kanushka:fix-text-set-editor
Feb 20, 2026
Merged

Fix missing TEXT_SET editors in service creation forms#1533
kanushka merged 2 commits intowso2:release/bi-1.8.xfrom
kanushka:fix-text-set-editor

Conversation

@kanushka
Copy link
Contributor

@kanushka kanushka commented Feb 20, 2026

Summary

This fixes a regression where TEXT_SET fields (for example, Databases and Schemas in Microsoft SQL Server CDC Integration) showed only label/description without input editors in the new BI form flow.

Screen.Recording.2026-02-20.at.20.54.34.mov

Root cause

During side-panel editor cleanup, the dynamic array editor path for TEXT_SET/EXPRESSION_SET was removed from ExpressionField.
As a result, array-backed values resolved to no rendered editor.

Also, type mode selection depended on selected flags, which can be all false in some refactored models.

Fix

  • Reintroduced DynamicArrayBuilder usage for InputMode.ARRAY and InputMode.TEXT_ARRAY in:
    • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
  • Restored the dynamic array editor component:
    • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/DynamicArrayBuilder/DynamicArrayBuilder.tsx
  • Updated type-selection fallback logic in:
    • workspaces/ballerina/ballerina-side-panel/src/components/editors/FieldFactory.tsx
  • New behavior:
    • If types.length === 1, use that type regardless of selected
    • If multiple types and all selected === false, fallback to last type (typically expression mode)

Notes

This aligns with the previous editor behavior before the side-panel cleanup refactor where the same DynamicArrayBuilder path was used for set-like array fields.

Related Task

Summary by CodeRabbit

  • Bug Fixes

    • Fixed a syntax issue in configuration.
  • New Features

    • Added support for enhanced array input modes with automatic validation, minimum item enforcement, and streamlined item editing capabilities.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

📝 Walkthrough

Walkthrough

These changes introduce a new DynamicArrayBuilder component to handle array input modes (ARRAY and TEXT_ARRAY) in the expression editor, integrate it into ExpressionField, refine input type fallback logic in FieldFactory, and fix a trailing semicolon in a utility file.

Changes

Cohort / File(s) Summary
Array Input Handling
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/DynamicArrayBuilder/DynamicArrayBuilder.tsx, workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
New DynamicArrayBuilder component with array initialization, padding, per-item editing via ChipExpressionEditorComponent, add/delete functionality, and form state synchronization. ExpressionField now routes ARRAY and TEXT_ARRAY input modes to this component before default array handling.
Input Type Selection Logic
workspaces/ballerina/ballerina-side-panel/src/components/editors/FieldFactory.tsx
Refined fallback behavior in getInitialSelectedInputType: single-type fields now return immediately; multi-type fields default to the last type (typically EXPRESSION mode) instead of the first type when no selection exists and field.value is falsy.
Code Quality
workspaces/ballerina/ballerina-extension/src/features/ai/utils.ts
Fixed trailing semicolon in BACKEND_URL declaration.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ExpressionField
    participant DynamicArrayBuilder
    participant ChipExpressionEditorComponent
    participant FormContext

    User->>ExpressionField: Interact with array input
    ExpressionField->>ExpressionField: Detect InputMode.ARRAY or TEXT_ARRAY
    ExpressionField->>DynamicArrayBuilder: Render with label, value, onChange
    
    DynamicArrayBuilder->>DynamicArrayBuilder: Initialize array from various value shapes
    DynamicArrayBuilder->>DynamicArrayBuilder: Pad array to meet minItems/defaultItems
    DynamicArrayBuilder->>FormContext: Set initial array value
    
    User->>DynamicArrayBuilder: Edit array item
    DynamicArrayBuilder->>ChipExpressionEditorComponent: Render per-item editor
    ChipExpressionEditorComponent->>User: Display editor UI
    User->>ChipExpressionEditorComponent: Submit item value
    ChipExpressionEditorComponent->>DynamicArrayBuilder: Return edited value
    DynamicArrayBuilder->>FormContext: Update form state with new array
    FormContext->>DynamicArrayBuilder: Validate and update dirty flag
    
    User->>DynamicArrayBuilder: Add/Remove items
    DynamicArrayBuilder->>DynamicArrayBuilder: Enforce minimum item constraints
    DynamicArrayBuilder->>FormContext: Sync updated array
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Arrays dance in dynamic rows,
New components help them grow,
From text modes to expressions flow,
Each item built with careful glow—
A builder's touch, now watch them go! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: reintroducing the DynamicArrayBuilder for TEXT_SET fields in service creation forms.
Description check ✅ Passed The description provides a clear summary, root cause analysis, detailed fix explanation, and links to related issues, though it deviates from the template structure.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests 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

🧹 Nitpick comments (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/DynamicArrayBuilder/DynamicArrayBuilder.tsx (2)

168-199: Editor configuration objects are instantiated per-item on every render.

new StringTemplateEditorConfig() and new ChipExpressionEditorDefaultConfiguration() are constructed inside .map() on every render cycle. If ChipExpressionEditorComponent performs shallow-equality checks on its configuration prop (e.g. via React.memo), every render forces a full re-render of all list items regardless of value changes.

♻️ Proposed fix — hoist to a memo
+    const editorConfig = useMemo(() =>
+        expressionSetType?.fieldType === "TEXT_SET"
+            ? new StringTemplateEditorConfig()
+            : new ChipExpressionEditorDefaultConfiguration(),
+        [expressionSetType?.fieldType]
+    );

     return (
         <S.Container>
             {arrayValues.map((itemValue, index) => (
                 <S.ItemContainer key={`${expressionFieldProps.field.key}-${index}`}>
                     <ChipExpressionEditorComponent
                         ...
-                        configuration={expressionSetType?.fieldType === "TEXT_SET" ? new StringTemplateEditorConfig() : new ChipExpressionEditorDefaultConfiguration()}
+                        configuration={editorConfig}
                     />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/DynamicArrayBuilder/DynamicArrayBuilder.tsx`
around lines 168 - 199, The configuration objects are being re-created inside
arrayValues.map which breaks shallow equality checks for
ChipExpressionEditorComponent; hoist the construction out of the loop by
creating a memoized config (using React.useMemo) before rendering the map and
compute a single instance based on expressionSetType?.fieldType (e.g., memoize
either new StringTemplateEditorConfig() or new
ChipExpressionEditorDefaultConfiguration() with expressionSetType?.fieldType as
the dependency), then pass that memoized configuration into
ChipExpressionEditorComponent instead of instantiating it inline.

29-34: label is declared but never used; onChange is accepted but intentionally bypassed.

Both create a misleading public interface:

  • label is passed from the call site (label={field.label}) but never referenced in the component body — remove it from both the interface and the call site, or wire it to an aria-label / visible heading.
  • onChange is silently ignored in favour of setValue (the comment at line 55 explains why). Consider dropping it from the interface altogether to make the contract explicit, since callers (e.g. ExpressionField.tsx line 161) construct a wrapper that is never invoked.
♻️ Suggested cleanup
 interface DynamicArrayBuilderProps {
-    label: string;
     value: string | any[];
-    onChange?: (value: string) => void;
     expressionFieldProps: ExpressionFieldProps;
 }

And at the call site in ExpressionField.tsx:

 <DynamicArrayBuilder
-    label={field.label}
     value={value}
-    onChange={(val) => onChange(val, val.length)}
     expressionFieldProps={props}
 />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/DynamicArrayBuilder/DynamicArrayBuilder.tsx`
around lines 29 - 34, The DynamicArrayBuilderProps interface exposes unused
props: remove the unused label and the misleading onChange from the public
contract and callers, or wire label into the component (e.g., as
aria-label/heading) if a visible/accessibility label is required; similarly,
either invoke the provided onChange wrapper instead of bypassing it or delete
onChange from DynamicArrayBuilderProps and update call sites (notably where
ExpressionField constructs a wrapper at ExpressionField.tsx line ~161) so
callers no longer pass it; ensure refs to setValue remain internal to
DynamicArrayBuilder and update the prop types and all usages consistently.
🤖 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-side-panel/src/components/editors/MultiModeExpressionEditor/DynamicArrayBuilder/DynamicArrayBuilder.tsx`:
- Around line 186-198: The delete-button disables threshold is inconsistent with
the auto-padding logic: compute a single derived minimum (e.g., const minVisible
= useMemo(() => Math.max(minItems, defaultItems), [minItems, defaultItems]))
inside the DynamicArrayBuilder component and use this minVisible in both the
padding effect that auto-adds items and in the S.DeleteButton disabled prop
(replace arrayValues.length <= minItems with arrayValues.length <= minVisible)
so deletion and re-padding thresholds match; keep handleDelete and existing
padding logic but reference minVisible everywhere instead of
minItems/defaultItems separately.

---

Nitpick comments:
In
`@workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/DynamicArrayBuilder/DynamicArrayBuilder.tsx`:
- Around line 168-199: The configuration objects are being re-created inside
arrayValues.map which breaks shallow equality checks for
ChipExpressionEditorComponent; hoist the construction out of the loop by
creating a memoized config (using React.useMemo) before rendering the map and
compute a single instance based on expressionSetType?.fieldType (e.g., memoize
either new StringTemplateEditorConfig() or new
ChipExpressionEditorDefaultConfiguration() with expressionSetType?.fieldType as
the dependency), then pass that memoized configuration into
ChipExpressionEditorComponent instead of instantiating it inline.
- Around line 29-34: The DynamicArrayBuilderProps interface exposes unused
props: remove the unused label and the misleading onChange from the public
contract and callers, or wire label into the component (e.g., as
aria-label/heading) if a visible/accessibility label is required; similarly,
either invoke the provided onChange wrapper instead of bypassing it or delete
onChange from DynamicArrayBuilderProps and update call sites (notably where
ExpressionField constructs a wrapper at ExpressionField.tsx line ~161) so
callers no longer pass it; ensure refs to setValue remain internal to
DynamicArrayBuilder and update the prop types and all usages consistently.

Comment on lines +186 to +198
//have a switch to show the array editor mode and the expression mode.
//Exception: TEXT_SET uses StringTemplateEditorConfig for TEXT mode
configuration={expressionSetType?.fieldType === "TEXT_SET" ? new StringTemplateEditorConfig() : new ChipExpressionEditorDefaultConfiguration()}
placeholder={expressionFieldProps.field.placeholder}
/>
<S.DeleteButton
appearance="icon"
onClick={() => handleDelete(index)}
disabled={arrayValues.length <= minItems}
>
<Codicon name="trash" />
</S.DeleteButton>
</S.ItemContainer>
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 | 🟡 Minor

Delete-button minimum (minItems) is inconsistent with the padding minimum (Math.max(minItems, defaultItems)).

When defaultItems > minItems the delete button stays enabled until the array hits minItems, but the padding effect at line 136 immediately re-adds items up to Math.max(minItems, defaultItems). The net result is that clicking Delete appears to succeed (the item disappears momentarily) and then the item re-appears — a confusing experience.

The disable threshold should match the padding threshold:

🐛 Proposed fix
-const requiredCount = Math.max(minItems, defaultItems);
+const minVisible = Math.max(minItems, defaultItems);

 ...

-                    disabled={arrayValues.length <= minItems}
+                    disabled={arrayValues.length <= minVisible}

(Declare minVisible with useMemo alongside minItems/defaultItems, then use it in both the padding effect and the disabled prop.)

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

In
`@workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/DynamicArrayBuilder/DynamicArrayBuilder.tsx`
around lines 186 - 198, The delete-button disables threshold is inconsistent
with the auto-padding logic: compute a single derived minimum (e.g., const
minVisible = useMemo(() => Math.max(minItems, defaultItems), [minItems,
defaultItems])) inside the DynamicArrayBuilder component and use this minVisible
in both the padding effect that auto-adds items and in the S.DeleteButton
disabled prop (replace arrayValues.length <= minItems with arrayValues.length <=
minVisible) so deletion and re-padding thresholds match; keep handleDelete and
existing padding logic but reference minVisible everywhere instead of
minItems/defaultItems separately.

@kanushka kanushka merged commit 606f465 into wso2:release/bi-1.8.x Feb 20, 2026
7 checks passed
@kanushka kanushka deleted the fix-text-set-editor branch February 20, 2026 15:40
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.

2 participants