Skip to content

feat(form): multi-step form wizard - #726

Merged
alfredo1996 merged 8 commits into
devfrom
feat/issue-165-form-wizard
May 14, 2026
Merged

feat(form): multi-step form wizard#726
alfredo1996 merged 8 commits into
devfrom
feat/issue-165-form-wizard

Conversation

@alfredo1996

@alfredo1996 alfredo1996 commented May 13, 2026

Copy link
Copy Markdown
Owner

Summary

Add optional multi-step wizard mode to the form widget. Admins assign step numbers to fields, enabling guided data entry with step-by-step navigation, per-step validation, and a summary review before submit.

Single-page mode (default — unchanged)

Forms without step assignments render exactly as before.

Wizard mode (when any field has a step number)

● Basic Info  ○ Permissions  ○ Review
─────────────────────────────────────
  Name:     [________________]
  Email:    [________________]

            [Next →]

What's new

  • FormFieldDef.step — optional step index per field (no DB migration)
  • FormStepIndicator — progress bar component (completed/current/upcoming)
  • useFormWizard — hook managing step state, validation, navigation
  • Summary step — review all entered values before submit
  • Editor UI — step number input per field + step label inputs in advanced tab

Key decisions

  • Wizard state is component-local (resets on re-render, reset on submit)
  • Only current step's fields are mounted (seed queries for off-screen dropdowns don't fire)
  • Free backward navigation (no validation when going Back)
  • Backward compatible — step is optional, forms without it are unchanged

Test plan

  • 10 new unit tests (isWizardForm, groupFieldsByStep, validateStepFields)
  • 1937 app tests pass
  • 1238 component tests pass
  • 199 E2E tests pass (fresh Docker)
  • TypeScript clean

Closes #165

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Multi-step form wizard with Back/Next/Submit navigation, per-step validation, and optional Review summary that formats field values
    • Visual step indicator with clickable completed steps
    • Form editor: assign numeric steps per field and edit step labels in the advanced tab
  • Tests

    • Extensive unit and integration tests for wizard hook, renderer, step grouping/validation, summary rendering, and step-indicator behavior
  • Chores

    • E2E tests refined to use state-based waits; test tooling config updated to ignore build artifacts

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds multi-step wizard support: a step field on form definitions, useFormWizard hook for navigation and per-step validation, FormStepIndicator component, renderer integration with a summary review step and step-aware controls, and editor UI for per-step labels and field-step assignment.

Changes

Multi-Step Wizard Implementation

Layer / File(s) Summary
Data model and validation foundation
app/src/lib/widget/form-field-def.ts, app/src/lib/widget/form-field-validation.ts, app/src/lib/__tests__/widget/form-field-def.test.ts, app/src/lib/__tests__/widget/form-field-validation.test.ts
FormFieldDef gains optional step?: number. New utilities isWizardForm() detects wizard mode, groupFieldsByStep() groups fields by step with gap normalization. validateStepFields() aggregates per-step validation errors by field name. Tests cover detection, grouping, and validation aggregation.
Wizard state management and navigation
app/src/hooks/use-form-wizard.ts, app/src/hooks/__tests__/use-form-wizard.test.ts
useFormWizard hook derives wizard enablement from field steps, computes grouped fields and optional "Review" summary step, tracks current step, and exports navigation callbacks: goNext (validates before advancing and returns errors or null), goBack, goToStep (backward-only), and reset. Tests cover labels, navigation gating, and summary behavior.
Step indicator UI component
component/src/components/composed/form-step-indicator.tsx, component/src/components/composed/index.ts, component/src/components/composed/__tests__/form-step-indicator.test.tsx
FormStepIndicator renders per-step buttons with progress styling (completed/current/upcoming), disables forward steps, marks current step with aria-current, and wires completed-step clicks to optional onStepClick. Tests validate accessibility, badges/checkmarks, disabled rules, and click behavior.
Form renderer integration and summary display
app/src/components/form-widget-renderer.tsx, app/src/components/__tests__/form-widget-renderer-wizard.test.tsx
Renderer initializes useFormWizard, adds formatSummaryValue() helper for summary display, renders FormStepIndicator in wizard mode, shows read-only summary step or editable wizard.currentFields with blur validation, and replaces single submit button with Back/Next/Submit controls; non-wizard mode retains original submit behavior. Calls wizard.reset() on successful submission. Tests verify wizard/non-wizard rendering, navigation, field scoping, and summary rendering.
Wizard configuration UI in editor
app/src/components/widget-editor/form-fields-editor.tsx, app/src/components/widget-editor-modal.tsx
Per-field editor adds optional "Step" numeric input. Advanced modal adds a conditional "Wizard Steps" block to edit chartOptions.stepLabels when fields are assigned steps.
Tests, e2e, and misc adjustments
app/src/components/__tests__/form-widget-renderer-wizard.test.tsx, app/src/hooks/__tests__/use-form-wizard.test.ts, component/src/components/composed/__tests__/form-step-indicator.test.tsx, various test and e2e files
Adds extensive unit tests for hook, renderer, and indicator. Small test timing changes for login/signup userEvent, date-range test consolidation, e2e waits replaced with readiness checks, connection test pruning, and Jest config update to ignore dist.

Sequence Diagram(s)

sequenceDiagram
  participant FormWidgetRenderer
  participant useFormWizard
  participant validateStepFields
  participant FormStepIndicator
  FormWidgetRenderer->>useFormWizard: initialize(fields, chartOptions)
  FormWidgetRenderer->>FormStepIndicator: render(stepLabels, currentStep)
  FormWidgetRenderer->>useFormWizard: goNext()
  useFormWizard->>validateStepFields: validateStepFields(currentStepFields, localValues)
  alt valid
    useFormWizard->>FormWidgetRenderer: advance step (no errors)
  else invalid
    useFormWizard-->>FormWidgetRenderer: return errors
  end
  FormWidgetRenderer->>useFormWizard: reset() on successful submit
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

enhancement, app, pkg:component, testing

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 feature being added: multi-step form wizard support.
Linked Issues check ✅ Passed All objectives from #165 are met: step navigation (goNext/goBack), progress bar (FormStepIndicator), per-step validation (validateStepFields), and summary step (isSummaryStep).
Out of Scope Changes check ✅ Passed Minor test infrastructure updates (userEvent delay changes, waitForTimeout→enabled assertion replacements) are supporting maintenance for test reliability.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-165-form-wizard

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
app/src/hooks/use-form-wizard.ts (1)

50-58: ⚡ Quick win

Optimize stepLabels memoization by moving configuredLabels extraction inside useMemo.

Currently, configuredLabels creates a new empty array [] on every render when chartOptions.stepLabels is undefined. This new reference causes the stepLabels useMemo to recalculate unnecessarily even when nothing has changed, defeating the optimization.

♻️ Proposed fix
-  const configuredLabels =
-    (chartOptions.stepLabels as string[] | undefined) ?? [];
   const stepLabels = useMemo(() => {
+    const configuredLabels =
+      (chartOptions.stepLabels as string[] | undefined) ?? [];
     const labels = stepGroups.map(
       (_, i) => configuredLabels[i] || `Step ${i + 1}`,
     );
     if (enableSummary) labels.push("Review");
     return labels;
-  }, [stepGroups, configuredLabels, enableSummary]);
+  }, [stepGroups, chartOptions.stepLabels, enableSummary]);
🤖 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 `@app/src/hooks/use-form-wizard.ts` around lines 50 - 58, The current
extraction of configuredLabels creates a new [] on every render which
invalidates the stepLabels useMemo; move the configuredLabels computation inside
the useMemo that defines stepLabels so it only creates the fallback array when
memo runs, and update the dependency list to reference chartOptions.stepLabels
(or chartOptions) and enableSummary and stepGroups instead of the external
configuredLabels; locate the code around the useMemo for stepLabels and the
chartOptions.stepLabels usage to apply this change.
🤖 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 `@app/src/components/form-widget-renderer.tsx`:
- Around line 314-324: The generic Array.isArray(value) early-return prevents
the number-range formatting in form-widget-renderer.tsx from ever running;
change the logic so number-range arrays are handled first (or special-case them
inside the array branch): check field.parameterType === "number-range" when
value is an array and return `${value[0]} – ${value[1]}` (use the existing
number-range formatting) before falling back to value.join(", ") || "—"; update
the branches around the Array.isArray(value) and field.parameterType checks to
ensure the number-range path is reachable.

In `@app/src/components/widget-editor/form-fields-editor.tsx`:
- Around line 187-193: The step input handler currently uses
parseInt(e.target.value, 10) without NaN checks, which allows NaN to be stored
and later dropped by groupFieldsByStep; update the onChange in
form-fields-editor.tsx so that after parseInt you validate Number.isFinite or
!Number.isNaN and only pass a numeric step to onUpdate (otherwise pass
undefined/null), referencing the onChange callback that calls onUpdate(field.id,
{ step: ... }) and the parseInt usage; also ensure groupFieldsByStep in
form-field-def.ts treats non-numeric f.step the same as undefined (coerce NaN to
undefined or use Number.isFinite before comparing) so fields with invalid steps
are not lost.

In `@app/src/hooks/use-form-wizard.ts`:
- Around line 81-86: goToStep currently only allows stepping backward but lacks
bounds checks; update the goToStep callback to validate 0 <= step < max (e.g.,
use the wizard's totalSteps or steps.length) before calling setCurrentStep and
preserve the existing backward-only behavior (allow change only if step <
currentStep and within bounds). Reference the goToStep function and the state
setters currentStep and setCurrentStep; also update the useCallback dependencies
to include the totalSteps/steps length you use for the upper bound.

In `@component/src/components/composed/form-step-indicator.tsx`:
- Around line 37-42: The connector before the current step is using isCompleted
(computed as idx < currentStep) so the link for idx === currentStep renders as
upcoming; inside the FormStepIndicator component update the connector logic to
treat the connector as completed when idx <= currentStep (e.g., compute a
separate connectorCompleted = idx <= currentStep or adjust the condition used in
className for the connector) so the connector immediately before the active step
is rendered as completed.

---

Nitpick comments:
In `@app/src/hooks/use-form-wizard.ts`:
- Around line 50-58: The current extraction of configuredLabels creates a new []
on every render which invalidates the stepLabels useMemo; move the
configuredLabels computation inside the useMemo that defines stepLabels so it
only creates the fallback array when memo runs, and update the dependency list
to reference chartOptions.stepLabels (or chartOptions) and enableSummary and
stepGroups instead of the external configuredLabels; locate the code around the
useMemo for stepLabels and the chartOptions.stepLabels usage to apply this
change.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 559d81df-f592-4a2f-bfb3-e9056a9b535a

📥 Commits

Reviewing files that changed from the base of the PR and between bba40e7 and 6289f09.

📒 Files selected for processing (10)
  • app/src/components/form-widget-renderer.tsx
  • app/src/components/widget-editor-modal.tsx
  • app/src/components/widget-editor/form-fields-editor.tsx
  • app/src/hooks/use-form-wizard.ts
  • app/src/lib/__tests__/widget/form-field-def.test.ts
  • app/src/lib/__tests__/widget/form-field-validation.test.ts
  • app/src/lib/widget/form-field-def.ts
  • app/src/lib/widget/form-field-validation.ts
  • component/src/components/composed/form-step-indicator.tsx
  • component/src/components/composed/index.ts

Comment thread app/src/components/form-widget-renderer.tsx Outdated
Comment thread app/src/components/widget-editor/form-fields-editor.tsx Outdated
Comment thread app/src/hooks/use-form-wizard.ts
Comment on lines +37 to +42
{idx > 0 && (
<div
className={cn(
"h-px flex-1 min-w-2",
isCompleted ? "bg-primary" : "bg-border",
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Connector state is off by one for the current step.

At Line 41, the connector before the current step renders as upcoming because it reuses isCompleted (idx < currentStep). For idx === currentStep, that link should already be completed.

Proposed fix
       {stepLabels.map((label, idx) => {
         const isCompleted = idx < currentStep;
         const isCurrent = idx === currentStep;
+        const isConnectorCompleted = idx <= currentStep;

         return (
           <React.Fragment key={idx}>
             {idx > 0 && (
               <div
                 className={cn(
                   "h-px flex-1 min-w-2",
-                  isCompleted ? "bg-primary" : "bg-border",
+                  isConnectorCompleted ? "bg-primary" : "bg-border",
                 )}
               />
             )}
📝 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
{idx > 0 && (
<div
className={cn(
"h-px flex-1 min-w-2",
isCompleted ? "bg-primary" : "bg-border",
)}
{stepLabels.map((label, idx) => {
const isCompleted = idx < currentStep;
const isCurrent = idx === currentStep;
const isConnectorCompleted = idx <= currentStep;
return (
<React.Fragment key={idx}>
{idx > 0 && (
<div
className={cn(
"h-px flex-1 min-w-2",
isConnectorCompleted ? "bg-primary" : "bg-border",
)}
/>
)}
🤖 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 `@component/src/components/composed/form-step-indicator.tsx` around lines 37 -
42, The connector before the current step is using isCompleted (computed as idx
< currentStep) so the link for idx === currentStep renders as upcoming; inside
the FormStepIndicator component update the connector logic to treat the
connector as completed when idx <= currentStep (e.g., compute a separate
connectorCompleted = idx <= currentStep or adjust the condition used in
className for the connector) so the connector immediately before the active step
is rendered as completed.

Add optional multi-step wizard mode to the form widget. Fields can be
assigned to numbered steps, enabling guided data entry workflows.

Data model:
- FormFieldDef gains optional `step?: number` field (no DB migration)
- chartOptions.stepLabels: string[] for custom step names
- chartOptions.enableSummary: boolean (default true when steps exist)

New components:
- FormStepIndicator (component/) — progress bar with completed/current states
- useFormWizard hook — manages step navigation, per-step validation, reset

Renderer changes:
- Wizard mode: shows step indicator, renders only current step's fields
- Next button validates current step before advancing
- Back button navigates freely (no validation)
- Summary step shows all values for review before submit
- Non-wizard forms render unchanged (backward compatible)

Editor changes:
- Step number input per field in form-fields-editor
- Step label inputs in advanced tab (shown when steps are assigned)

Closes #165

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@alfredo1996
alfredo1996 force-pushed the feat/issue-165-form-wizard branch from 6289f09 to 95ac404 Compare May 13, 2026 13:30
Skip per-keystroke delays in login page tests that timed out in CI.
Add unit tests for useFormWizard hook, FormStepIndicator component,
and FormWidgetRenderer wizard mode to satisfy SonarCloud 80% new
code coverage gate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (3)
app/src/components/__tests__/form-widget-renderer-wizard.test.tsx (2)

193-195: 💤 Low value

Redundant assertion: toBeDefined() after getByTestId.

screen.getByTestId() throws if the element is not found, so the .toBeDefined() assertion on lines 193-195 (and similar patterns throughout the file) is redundant. Consider removing .toBeDefined() or using .toBeInTheDocument() from @testing-library/jest-dom matchers for clarity.

🤖 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 `@app/src/components/__tests__/form-widget-renderer-wizard.test.tsx` around
lines 193 - 195, Remove the redundant .toBeDefined() assertions that follow
calls like screen.getByTestId("form-step-indicator") and screen.getByText("Step
1"/"Step 2"); either drop the .toBeDefined() completely (since getBy* throws if
missing) or replace them with the clearer matcher .toBeInTheDocument() from
`@testing-library/jest-dom` for explicit presence checks in the tests referencing
these selectors.

1-1: 💤 Low value

Consider adding explicit jsdom environment directive.

The test file app/src/hooks/__tests__/use-form-wizard.test.ts includes // @vitest-environment jsdom on line 1, but this file does not. For consistency and explicit environment declaration, consider adding the directive here as well.

📝 Proposed addition
+// `@vitest-environment` jsdom
 import { describe, it, expect, vi, beforeEach } from "vitest";
🤖 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 `@app/src/components/__tests__/form-widget-renderer-wizard.test.tsx` at line 1,
Add an explicit Vitest jsdom environment directive at the top of the test file
by inserting the comment "// `@vitest-environment` jsdom" as the very first line
(above the existing import line that starts with `import { describe, it, expect,
vi, beforeEach } from "vitest";`) so the test explicitly runs under jsdom like
the other hook test.
component/src/components/composed/__tests__/form-step-indicator.test.tsx (1)

156-189: ⚖️ Poor tradeoff

Consider using data attributes for connector queries.

The tests query connectors using CSS class selectors (.h-px.flex-1), which couples tests to Tailwind implementation details. If styling classes change, tests will break even though behavior is correct.

Consider adding data-testid attributes to connector elements for more resilient querying, or document that these tests intentionally verify styling classes.

🤖 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 `@component/src/components/composed/__tests__/form-step-indicator.test.tsx`
around lines 156 - 189, The tests are brittle because they select connectors by
Tailwind classes; update the FormStepIndicator implementation to add a stable
data attribute (e.g. data-testid="connector") to each connector element, then
change the tests in form-step-indicator.test.tsx to query connectors by that
attribute (e.g. container.querySelectorAll('[data-testid="connector"]') or
getAllByTestId) and assert classes or state as before; ensure the attribute is
added where connectors are rendered in the FormStepIndicator component so all
three tests (render count, completed vs border color checks, upcoming checks)
use the new selector.
🤖 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 `@app/src/components/__tests__/form-widget-renderer-wizard.test.tsx`:
- Around line 450-492: The test claims to check formatSummaryValue but only
asserts the field label; either (A) change the test to actually assert the
formatted array by having mockUseFormWizard return localValues (or whatever
prop/state FormWidgetRenderer reads) with parameterName "tags" mapped to an
array (e.g., ["a","b"]) and then render FormWidgetRenderer and expect the
summary cell to contain the comma-separated string "a, b" (verifying
formatSummaryValue output), or (B) rename the test and its description from
"formats array values as comma-separated" to "renders multi-select field label
on summary step" to reflect that it only verifies the label rendering; update
the test title and keep the existing assertion (screen.getByText("Tags")).
Ensure you modify the mock returned by mockUseFormWizard and target the
parameterName "tags" or adjust the expectation accordingly if choosing option A.

---

Nitpick comments:
In `@app/src/components/__tests__/form-widget-renderer-wizard.test.tsx`:
- Around line 193-195: Remove the redundant .toBeDefined() assertions that
follow calls like screen.getByTestId("form-step-indicator") and
screen.getByText("Step 1"/"Step 2"); either drop the .toBeDefined() completely
(since getBy* throws if missing) or replace them with the clearer matcher
.toBeInTheDocument() from `@testing-library/jest-dom` for explicit presence checks
in the tests referencing these selectors.
- Line 1: Add an explicit Vitest jsdom environment directive at the top of the
test file by inserting the comment "// `@vitest-environment` jsdom" as the very
first line (above the existing import line that starts with `import { describe,
it, expect, vi, beforeEach } from "vitest";`) so the test explicitly runs under
jsdom like the other hook test.

In `@component/src/components/composed/__tests__/form-step-indicator.test.tsx`:
- Around line 156-189: The tests are brittle because they select connectors by
Tailwind classes; update the FormStepIndicator implementation to add a stable
data attribute (e.g. data-testid="connector") to each connector element, then
change the tests in form-step-indicator.test.tsx to query connectors by that
attribute (e.g. container.querySelectorAll('[data-testid="connector"]') or
getAllByTestId) and assert classes or state as before; ensure the attribute is
added where connectors are rendered in the FormStepIndicator component so all
three tests (render count, completed vs border color checks, upcoming checks)
use the new selector.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 29e96416-30f7-4438-b713-7efd739b20b7

📥 Commits

Reviewing files that changed from the base of the PR and between 95ac404 and 94c0ba8.

📒 Files selected for processing (4)
  • app/src/app/(auth)/login/__tests__/page.test.tsx
  • app/src/components/__tests__/form-widget-renderer-wizard.test.tsx
  • app/src/hooks/__tests__/use-form-wizard.test.ts
  • component/src/components/composed/__tests__/form-step-indicator.test.tsx

Comment thread app/src/components/__tests__/form-widget-renderer-wizard.test.tsx
- Fix unreachable number-range branch in formatSummaryValue by moving
  the check before the generic array early-return
- Add NaN guard to step number input in form-fields-editor
- Add bounds validation (step >= 0) to goToStep in useFormWizard
- Remove unused import in form-step-indicator test
- Rename misleading test description

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@app/src/hooks/use-form-wizard.ts`:
- Around line 66-75: The goNext callback can increment currentStep past the last
step causing an invalid wizard state; modify goNext (the function using
setCurrentStep) to clamp advancement by computing the max index from
stepGroups.length - 1 and calling setCurrentStep(s => Math.min(s + 1, maxIndex))
(or only increment when s < maxIndex), ensuring currentStep never exceeds the
terminal step; keep the existing validation via validateStepFields and return
semantics intact.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c2898755-2f25-4ce8-84c8-d2d572293d2d

📥 Commits

Reviewing files that changed from the base of the PR and between 94c0ba8 and 82647f8.

📒 Files selected for processing (6)
  • app/src/components/__tests__/form-widget-renderer-wizard.test.tsx
  • app/src/components/form-widget-renderer.tsx
  • app/src/components/widget-editor/form-fields-editor.tsx
  • app/src/hooks/__tests__/use-form-wizard.test.ts
  • app/src/hooks/use-form-wizard.ts
  • component/src/components/composed/__tests__/form-step-indicator.test.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • app/src/components/widget-editor/form-fields-editor.tsx
  • app/src/components/tests/form-widget-renderer-wizard.test.tsx
  • app/src/components/form-widget-renderer.tsx
  • component/src/components/composed/tests/form-step-indicator.test.tsx
  • app/src/hooks/tests/use-form-wizard.test.ts

Comment on lines +66 to +75
const goNext = useCallback(
(localValues: Record<string, unknown>) => {
const stepFields = stepGroups[currentStep] ?? [];
const errors = validateStepFields(stepFields, localValues);
if (Object.keys(errors).length > 0) return errors;
setCurrentStep((s) => s + 1);
return null;
},
[currentStep, stepGroups],
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clamp goNext to the terminal step to avoid invalid wizard state.

At Line 71, setCurrentStep((s) => s + 1) can advance past the last valid step if goNext is triggered while already at the terminal step. Add a max-step guard and clamp the increment.

Suggested fix
   const goNext = useCallback(
     (localValues: Record<string, unknown>) => {
+      const maxStep = enableSummary ? totalSteps : totalSteps - 1;
+      if (currentStep >= maxStep) return null;
+
       const stepFields = stepGroups[currentStep] ?? [];
       const errors = validateStepFields(stepFields, localValues);
       if (Object.keys(errors).length > 0) return errors;
-      setCurrentStep((s) => s + 1);
+      setCurrentStep((s) => Math.min(s + 1, maxStep));
       return null;
     },
-    [currentStep, stepGroups],
+    [currentStep, stepGroups, enableSummary, totalSteps],
   );
📝 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
const goNext = useCallback(
(localValues: Record<string, unknown>) => {
const stepFields = stepGroups[currentStep] ?? [];
const errors = validateStepFields(stepFields, localValues);
if (Object.keys(errors).length > 0) return errors;
setCurrentStep((s) => s + 1);
return null;
},
[currentStep, stepGroups],
);
const goNext = useCallback(
(localValues: Record<string, unknown>) => {
const maxStep = enableSummary ? totalSteps : totalSteps - 1;
if (currentStep >= maxStep) return null;
const stepFields = stepGroups[currentStep] ?? [];
const errors = validateStepFields(stepFields, localValues);
if (Object.keys(errors).length > 0) return errors;
setCurrentStep((s) => Math.min(s + 1, maxStep));
return null;
},
[currentStep, stepGroups, enableSummary, totalSteps],
);
🤖 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 `@app/src/hooks/use-form-wizard.ts` around lines 66 - 75, The goNext callback
can increment currentStep past the last step causing an invalid wizard state;
modify goNext (the function using setCurrentStep) to clamp advancement by
computing the max index from stepGroups.length - 1 and calling setCurrentStep(s
=> Math.min(s + 1, maxIndex)) (or only increment when s < maxIndex), ensuring
currentStep never exceeds the terminal step; keep the existing validation via
validateStepFields and return semantics intact.

alfredorubin96 and others added 5 commits May 13, 2026 22:53
Skip per-keystroke delays in signup page tests that timed out in CI.
Fix TS spread error in wizard test mock. Export and add direct unit
tests for formatSummaryValue covering all branches (number-range,
multi-select, date-range, scalars, empty values).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Revert the export and drop the 15-test file that only existed to
compensate for SonarCloud recounting lines after the source edit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove 4 TypeScript type-checking tests from advanced-connection-options
that only verified compilation, not runtime behavior. Consolidate 5
repetitive date-range-picker preset tests into a single test.each,
adding the missing 'Last 30 days' case in the process.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- connection: exclude dist/ from Jest testPathIgnorePatterns (fixes 2
  spurious suite failures from compiled output)
- app: rename chart-error-boundary.test.tsx → chart-renderer.test.tsx
  to match the component it actually tests
- e2e/transforms: remove 1s sleep after connection select (Run button
  enabled check already gates); replace 1s post-save sleep with
  Save button re-enabled assertion
- e2e/form-widget: remove duplicate 400ms debounce wait
- e2e/widget-lab: replace 1s post-save sleep with Save button
  re-enabled assertion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…zard

# Conflicts:
#	app/src/components/form-widget-renderer.tsx
#	app/src/components/widget-editor-modal.tsx
@alfredo1996
alfredo1996 merged commit aa10b11 into dev May 14, 2026
8 of 9 checks passed
@sonarqubecloud

Copy link
Copy Markdown

@alfredo1996
alfredo1996 deleted the feat/issue-165-form-wizard branch May 16, 2026 17:47
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.

feat(component): multi-step form wizard

2 participants