Skip to content

fix(rest-dsl): keyboard Enter now correctly sets HTTP method in Add Operation dialog#3516

Open
QuantumBreakz wants to merge 3 commits into
KaotoIO:mainfrom
QuantumBreakz:fix/3474-rest-dsl-enter-key-method-selection
Open

fix(rest-dsl): keyboard Enter now correctly sets HTTP method in Add Operation dialog#3516
QuantumBreakz wants to merge 3 commits into
KaotoIO:mainfrom
QuantumBreakz:fix/3474-rest-dsl-enter-key-method-selection

Conversation

@QuantumBreakz

@QuantumBreakz QuantumBreakz commented Jul 20, 2026

Copy link
Copy Markdown

Fixes #3474

Problem

The HTTP Method field in the Add Operation dialog was rendered as a typeahead select (from @kaoto/forms EnumField). When the user typed a valid method (e.g. post) and pressed Enter, the dropdown visually closed and showed "post" but the underlying onChangeProp callback was never fired. As a result, formModel.method remained 'get'.

Solution

We replaced the typeahead EnumField (via KaotoForm) for the HTTP Method field with a plain Carbon React Select dropdown. Since there are only 6 valid HTTP methods, a simple <Select> is simpler and has correct keyboard behavior by default (no typeahead edge cases).

The path and id fields were replaced with TextInput components to match, removing the KaotoForm / KaotoFormApi dependency from this modal entirely.

Testing

  • Added regression test #3474: selecting a method by keyboard now produces the correct method in the onAddMethod callback.
  • All existing unit tests updated to reflect the new DOM structure.

@jichenggepeter-dev I am very sorry! I know you were assigned to this issue recently. I actually already started working on a fix for this bug locally and had the solution ready. I apologize for the overlap and stepping on your toes here.

Summary by CodeRabbit

  • Bug Fixes
    • Ensured the selected HTTP method is reliably applied when adding an operation.
    • Added required path validation and ensured invalid submissions do not trigger add/close actions.
    • Improved handling of whitespace trimming for path and optional id, including id being omitted when empty.
    • Validation errors now clear immediately as users begin typing a valid path.
  • Improvements
    • Refreshed the “Add Method” modal UI for method, path, and optional id using a more direct form layout.
    • Reset all fields each time the modal is opened for a cleaner editing experience.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

AddMethodModal now uses explicit Carbon controls and local state for REST method creation, with synchronous validation and normalized path/ID payloads. Tests cover supported methods, POST selection, cancellation, validation, trimming, and optional IDs.

Changes

REST method modal

Layer / File(s) Summary
Local REST method form
packages/ui/src/pages/RestDslEditor/components/AddMethodModal.tsx
The modal replaces schema-driven handling with local state, Carbon inputs, REST verb options, required-path validation, and normalized submission payloads.
Modal interaction and validation coverage
packages/ui/src/pages/RestDslEditor/components/AddMethodModal.test.tsx
Tests cover direct rendering, supported methods, POST regression behavior, cancellation, required paths, optional IDs, whitespace trimming, and validation-error clearing.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • KaotoIO/kaoto#3470: Updates the same modal’s props, rendering contract, focus behavior, and tests.

Suggested reviewers: etzel-mes

Poem

I’m a rabbit with a method to choose,
GET or POST in bright input hues.
Paths are trimmed, IDs may hide,
Empty paths cannot slip inside.
Tests hop through every REST trail—
With tidy forms, the right verbs prevail!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix for the REST DSL Add Operation dialog.
Linked Issues check ✅ Passed The code and tests address #3474 by ensuring Enter on HTTP method selection persists post and creates a POST operation.
Out of Scope Changes check ✅ Passed The changes stay focused on the Add Operation dialog refactor and bug fix, with no clearly unrelated code added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (1)
packages/ui/src/pages/RestDslEditor/components/AddMethodModal.tsx (1)

70-108: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider enabling "Enter to submit" functionality.

Currently, pressing Enter inside the text inputs will not submit the modal because there is no <form> wrapping them. To improve keyboard accessibility, consider wrapping the modal body content in a <form> and associating it with the Add button.

⌨️ Proposed refactor for standard form submission
-        <Stack gap={5}>
+        <form id="add-method-form" onSubmit={(e) => { e.preventDefault(); handleAdd(); }}>
+          <Stack gap={5}>
           <Select
             id="add-method-http-method"
...
             data-testid="add-method-id"
           />
-        </Stack>
+          </Stack>
+        </form>

Note: You would also need to update the Add button in the <ModalFooter> to act as the submit trigger for this form:

<Button kind="primary" type="submit" form="add-method-form" data-testid="add-method-modal-add-btn">
  Add
</Button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/pages/RestDslEditor/components/AddMethodModal.tsx` around
lines 70 - 108, Wrap the AddMethodModal input content in a form with a stable
identifier such as add-method-form, and connect submission to the existing Add
action handler. Update the ModalFooter Add button to use type="submit" and
reference that form so pressing Enter in the HTTP method, path, or ID fields
triggers the same submission behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ui/src/pages/RestDslEditor/components/AddMethodModal.tsx`:
- Around line 44-47: Reset the AddMethodModal form state whenever it transitions
to open, including method, path, id, and pathError, so reopening starts with
default values. Add the necessary useEffect import and anchor the reset logic to
the component’s open prop while preserving the existing initial defaults.

---

Nitpick comments:
In `@packages/ui/src/pages/RestDslEditor/components/AddMethodModal.tsx`:
- Around line 70-108: Wrap the AddMethodModal input content in a form with a
stable identifier such as add-method-form, and connect submission to the
existing Add action handler. Update the ModalFooter Add button to use
type="submit" and reference that form so pressing Enter in the HTTP method,
path, or ID fields triggers the same submission behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6f528a27-f6ea-414a-b903-51f76014efe9

📥 Commits

Reviewing files that changed from the base of the PR and between 12a4c4b and 9daee24.

📒 Files selected for processing (2)
  • packages/ui/src/pages/RestDslEditor/components/AddMethodModal.test.tsx
  • packages/ui/src/pages/RestDslEditor/components/AddMethodModal.tsx

Comment thread packages/ui/src/pages/RestDslEditor/components/AddMethodModal.tsx
…peration dialog (KaotoIO#3474)

Problem
The HTTP Method field in the Add Operation dialog was rendered as a typeahead select (from @kaoto/forms EnumField). When the user typed a valid method (e.g. post) and pressed Enter, the dropdown visually closed and showed 'post' but the underlying onChangeProp callback was never fired. As a result, formModel.method remained 'get'.

Solution
We replaced the typeahead EnumField (via KaotoForm) for the HTTP Method field with a plain Carbon React Select dropdown. Since there are only 6 valid HTTP methods, a simple <Select> is simpler and has correct keyboard behavior by default (no typeahead edge cases).

The path and id fields were replaced with TextInput components to match, removing the KaotoForm / KaotoFormApi dependency from this modal entirely. Modal state is reset on open.

Fixes KaotoIO#3474
@QuantumBreakz
QuantumBreakz force-pushed the fix/3474-rest-dsl-enter-key-method-selection branch from 3d92095 to e31cd77 Compare July 21, 2026 15:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
packages/ui/src/pages/RestDslEditor/components/AddMethodModal.test.tsx (3)

163-164: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify undefined property assertion.

Using not.toHaveProperty('id', expect.anything()) is technically correct but a bit complex to parse mentally. Asserting that the property is directly undefined expresses the intent more clearly.

♻️ Proposed refactor
       // id should be undefined, not included in the call
-      expect(mockOnAddMethod.mock.calls[0][0]).not.toHaveProperty('id', expect.anything());
+      expect(mockOnAddMethod.mock.calls[0][0].id).toBeUndefined();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/pages/RestDslEditor/components/AddMethodModal.test.tsx`
around lines 163 - 164, In the AddMethodModal test, simplify the assertion on
the first argument passed to mockOnAddMethod so it directly verifies the id
property is undefined, replacing the negated toHaveProperty/expect.anything()
expression while preserving the existing intent.

193-197: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove redundant act wrapper.

fireEvent operations are automatically wrapped in act by @testing-library/react. The explicit await act(async () => { ... }) wrapper is unnecessary, especially since fireEvent.change is fully synchronous.
(Note: You can also remove the act import from @testing-library/react at the top of the file as it will become unused if you apply this.)

♻️ Proposed refactor
-    // Start typing in path — error should clear
-    await act(async () => {
-      fireEvent.change(getPathInput(), { target: { value: '/' } });
-    });
-    expect(screen.queryByText('Path is required')).not.toBeInTheDocument();
+    // Start typing in path — error should clear
+    fireEvent.change(getPathInput(), { target: { value: '/' } });
+    expect(screen.queryByText('Path is required')).not.toBeInTheDocument();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/pages/RestDslEditor/components/AddMethodModal.test.tsx`
around lines 193 - 197, Remove the explicit act wrapper around fireEvent.change
in the AddMethodModal test, calling the synchronous event directly. Then remove
the act import from the test file if it is no longer used, while preserving the
existing assertion that the path error clears.

100-122: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer it.each for parameterized test cases.

Instead of iterating through methods inside a single test case, using it.each runs each HTTP method as a separate, isolated test case. This improves test reporting (each method will have its own pass/fail status) and properly hooks into the beforeEach lifecycle to automatically clear mocks, eliminating the need for manual mock clearing and explicit unmount() calls.

♻️ Proposed refactor
-  it('should update formModel correctly for all supported HTTP methods', async () => {
-    const methods = ['get', 'post', 'put', 'delete', 'patch', 'head'] as const;
-
-    for (const m of methods) {
-      mockOnAddMethod.mockClear();
-      mockOnClose.mockClear();
-
-      const { unmount } = render(<AddMethodModal open onClose={mockOnClose} onAddMethod={mockOnAddMethod} />);
-      const select = screen.getByLabelText('HTTP Method') as HTMLSelectElement;
-      const pathInput = screen.getByLabelText('Path') as HTMLInputElement;
-      const addBtn = screen.getByRole('button', { name: 'Add' });
-
-      fireEvent.change(select, { target: { value: m } });
-      fireEvent.change(pathInput, { target: { value: '/test' } });
-      fireEvent.click(addBtn);
-
-      await waitFor(() => {
-        expect(mockOnAddMethod).toHaveBeenCalledWith(expect.objectContaining({ method: m }));
-      });
-
-      unmount();
-    }
-  });
+  it.each(['get', 'post', 'put', 'delete', 'patch', 'head'] as const)(
+    'should update formModel correctly for HTTP method %s',
+    async (m) => {
+      const { getMethodSelect, getPathInput, getAddButton } = setupModal();
+
+      fireEvent.change(getMethodSelect(), { target: { value: m } });
+      fireEvent.change(getPathInput(), { target: { value: '/test' } });
+      fireEvent.click(getAddButton());
+
+      await waitFor(() => {
+        expect(mockOnAddMethod).toHaveBeenCalledWith(expect.objectContaining({ method: m }));
+      });
+    },
+  );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/pages/RestDslEditor/components/AddMethodModal.test.tsx`
around lines 100 - 122, Refactor the “should update formModel correctly for all
supported HTTP methods” test to use it.each with the methods list, making each
HTTP method an isolated test case. Remove the per-iteration mockClear calls and
explicit unmount, relying on the existing beforeEach/test lifecycle while
preserving the current render, form input, submission, and method assertion
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/ui/src/pages/RestDslEditor/components/AddMethodModal.test.tsx`:
- Around line 163-164: In the AddMethodModal test, simplify the assertion on the
first argument passed to mockOnAddMethod so it directly verifies the id property
is undefined, replacing the negated toHaveProperty/expect.anything() expression
while preserving the existing intent.
- Around line 193-197: Remove the explicit act wrapper around fireEvent.change
in the AddMethodModal test, calling the synchronous event directly. Then remove
the act import from the test file if it is no longer used, while preserving the
existing assertion that the path error clears.
- Around line 100-122: Refactor the “should update formModel correctly for all
supported HTTP methods” test to use it.each with the methods list, making each
HTTP method an isolated test case. Remove the per-iteration mockClear calls and
explicit unmount, relying on the existing beforeEach/test lifecycle while
preserving the current render, form input, submission, and method assertion
behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6aec56df-08f9-4c89-b593-3318e6068216

📥 Commits

Reviewing files that changed from the base of the PR and between 9daee24 and e31cd77.

📒 Files selected for processing (2)
  • packages/ui/src/pages/RestDslEditor/components/AddMethodModal.test.tsx
  • packages/ui/src/pages/RestDslEditor/components/AddMethodModal.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ui/src/pages/RestDslEditor/components/AddMethodModal.tsx

@sonarqubecloud

Copy link
Copy Markdown

@PVinaches

Copy link
Copy Markdown
Member

Hello! Thanks for checking this issue. Do you think you could possibly tackled it with the current forms library without the need to implement the carbon form directly here? We are already planning migration but it will go through the forms library in a later stage.

@QuantumBreakz

Copy link
Copy Markdown
Author

Hi @PVinaches,

Good catch, and yes, we can stay within @kaoto/forms. The root cause isn't in this modal at all: it's in Typeahead.js's onInputKeyDown, which only fires onChange when the typed value is a custom (not-in-list) match:

if (!exactMatch && inputValue !== selectedItem?.name) {
    onChange?.(customItem);
}

So typing "post", an exact enum match , and hitting Enter skips onChange entirely, and formModel.method stays 'get'. EnumField always renders via Typeahead, and AddMethodModal just inherits that bug through the schema's enum.

Rather than dropping to Carbon's , I'd suggest using the same customFieldsFactory extension point we already use for MediaTypeField and UriField: Add a SelectEnumField that uses useFieldValue from @kaoto/forms for model wiring, but renders a PatternFly (native select) instead of Typeahead, sidesteps the broken keydown handler entirely since there's no typeahead/combobox logic involved. Register it in a local addMethodFieldFactory, scoped to small closed enums (e.g. schema.enum.length <= 8), so it only applies here. Pass that factory to in AddMethodModal — everything else (KaotoFormApi, validation, onChangeProp) stays exactly as-is. That keeps this PR consistent with the forms-library direction and doesn't introduce a second form system. I'll file the Typeahead.js bug as a separate issue against @kaoto/forms since it likely affects any EnumField consumer, not just this modal , worth fixing upstream once the migration reaches that package.

@PVinaches

Copy link
Copy Markdown
Member

Hello again! 

Thanks for the suggestion. In Kaoto with use the forms library as the main source to handle everything that happens and is related to forms. There are a very few cases that are very particular and only applies to specific circumstances. Those are the ones handled through customFieldsFactory. The workaround you are suggesting will be for the general case, so we think that it will be more appropriate to solve it in forms.
I think you can add that part of the information (the solving proposal in forms) directly in the issue you are trying and we will move it to forms repo issues after that.
Please check out the specific file here. It’s slightly different than the code you pointed out before. If you want, we can discuss further how to fix the issue as we will need to consider which situations exactly this change is needed. Or if you prefer I could point you to an issue that could be kaoto repo directly. Let me know what you prefer

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.

REST DSL editor keeps GET when POST is typed and confirmed with Enter

2 participants