Skip to content

feat(editor): expose number-range + cascading-select (#861) - #868

Merged
alfredo1996 merged 2 commits into
release/1.0from
fix/861-expose-all-param-types
May 19, 2026
Merged

feat(editor): expose number-range + cascading-select (#861)#868
alfredo1996 merged 2 commits into
release/1.0from
fix/861-expose-all-param-types

Conversation

@alfredo1996

@alfredo1996 alfredo1996 commented May 19, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #861 — the parameter editor was capped at 3 of the 8 runtime types.

Adds two top-level entries to the editor's `ParamUIType`:

  1. `number-range` — min / max / step inputs map to `rangeMin` / `rangeMax` / `rangeStep` chart-options. Live preview renders `NumberRangeSlider` with a guard against `max <= min` while the user is typing.
  2. `cascading` — a `parentParameterName` input plus the existing seed-query block. Maps to/from the runtime `cascading-select`. Preview renders `CascadingSelector` with the "depends on" hint.

The connection picker and save-button gating in `widget-editor-modal` / `modal-footer` also learn about `cascading` since it needs a DB connection for its seed query.

Drive-by

`SeedQueryInput`'s `setDraft(value)` inside `useEffect` was tripping `react-hooks/set-state-in-effect`; rewrote as the React "adjust state in render" pattern (same fix as #859's `useSeedQueryOptions`).

Test plan

  • Round-trip mapping tests for `number-range` <-> `cascading-select` in `parameter-config-section.test.ts`
  • UI rendering tests for both new sections in `parameter-config-section-ui.test.ts`
  • All 304 widget-editor unit tests pass
  • `parameters.spec.ts` + `parameter-types.spec.ts` E2E pass (31 passed, 2 unrelated flakes on date tests)
  • Full sharded E2E green on CI

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Added number-range parameter type with configurable minimum, maximum, and step values
    • Added cascading parameter type for dependent parameter selections
    • Extended parameter configuration UI to support both new parameter types with appropriate input controls

Review Change Stack

…tor (#861)

ParameterConfigSection was capped at 3 of the 8 supported parameter types
(date / freetext / select). Number-range shipped in PR #853 and
cascading-select went into the param-substitute/seed-query work, but
neither was reachable through the main editor — they could only be
configured by editing dashboard JSON or via FormFieldsEditor.

Adds two new top-level entries to the editor's ParamUIType:

- "number-range" — exposes min / max / step inputs that map to the
  existing rangeMin / rangeMax / rangeStep chart-options. The preview
  renders a live NumberRangeSlider using those bounds (with a guard so a
  partially-typed max <= min still renders without crashing).

- "cascading" — exposes a parentParameterName input plus the same seed
  query block used by select. Maps to/from the runtime cascading-select
  type. The preview renders a live CascadingSelector showing the
  "depends on" label.

Round-trip mappings for both types are covered in
parameter-config-section.test.ts so the local copy of ParamUIType in
widget-editor-store stays in sync. UI rendering of each new section is
covered in parameter-config-section-ui.test.ts.

Also widens the connection-required check in modal-footer / -modal so
the save button and connection picker include cascading (which needs
a DB for its seed query).

Drive-by: SeedQueryInput's sync-prop-to-draft effect was tripping the
react-hooks/set-state-in-effect rule; rewrote it as adjust-state-in-
render per the React docs.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@alfredo1996 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 41 minutes and 46 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 052fd185-4eea-42be-9fab-fe4cf3825829

📥 Commits

Reviewing files that changed from the base of the PR and between 09e4f7c and 99ae1bb.

📒 Files selected for processing (2)
  • app/src/components/widget-editor/__tests__/parameter-preview.test.tsx
  • app/src/stores/__tests__/widget-editor-store.test.ts

Walkthrough

This PR extends the widget editor parameter taxonomy from 3 UI types to 5, adding number-range and cascading. All editor configuration paths, preview rendering, save workflows, and modal logic are updated to support the new types alongside corresponding test coverage.

Changes

Parameter Type System and UI Expansion

Layer / File(s) Summary
Type system and core mappings
app/src/stores/widget-editor-store.ts, app/src/components/widget-editor/parameter-config-section.tsx
ParamUIType expands to include "number-range" and "cascading". resolveInternalParamType and reverseParamTypeMapping functions updated to map these new types to/from internal parameter types (number-range, cascading-select).
Parameter editor UI and configuration
app/src/components/widget-editor/parameter-config-section.tsx
New configuration sections for number-range (min/max/step bound inputs) and cascading (parent parameter name input). SeedQueryInput refactored to resync draft state on prop changes. Seed query section extended to render for both select and cascading. Type dropdown metadata and reference hints updated.
Parameter preview rendering
app/src/components/widget-editor/parameter-preview.tsx
Preview adds rendering branches for number-range (derives min/max/step from chartOptions, renders NumberRangeSlider) and cascading (renders CascadingSelector with parent parameter name).
Modal and save integration
app/src/components/widget-editor-modal.tsx, app/src/components/widget-editor/modal-footer.tsx, app/src/components/widget-editor/use-widget-save.ts
WidgetEditorModal shows connection selector for cascading. ModalFooter applies required-field checks for both select and cascading. useBuildWidgetForSave treats cascading as option-backed type for seedQuery and preserves connectionId for both types.
Test coverage for type mapping and UI
app/src/components/widget-editor/__tests__/parameter-config-section.test.tsx, app/src/components/widget-editor/__tests__/parameter-config-section-ui.test.tsx
Unit tests verify number-range and cascading round-trip through type mapping functions. UI tests assert correct rendering of range bound inputs, parent parameter name input, seed query visibility, and reference hint variables for each type.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • alfredo1996/neoboard#627: Refactors widget save and modal footer logic that overlaps with this PR's updates to use-widget-save.ts and modal-footer.tsx.
  • alfredo1996/neoboard#498: Adds E2E tests for number-range and cascading-select widget runtime behavior.
  • alfredo1996/neoboard#203: Introduces the foundational store-backed widget editor parameter/UI plumbing that this PR builds upon.

Suggested labels

enhancement, pkg:app, area:widgets

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 directly reflects the main feature addition: exposing two new parameter UI types (number-range and cascading-select) in the editor, matching the core changeset.
Linked Issues check ✅ Passed The PR implements all four acceptance criteria from #861: expanded ParamUIType to include number-range and cascading, editor renders sub-forms for both types, preview renders examples, and UI tests added for both.
Out of Scope Changes check ✅ Passed All changes are scoped to #861 objectives. The SeedQueryInput refactor (useEffect to render-time state) is a necessary drive-by fix for correctness, not unrelated scope creep.

✏️ 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 fix/861-expose-all-param-types

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: 1

🧹 Nitpick comments (1)
app/src/components/widget-editor/__tests__/parameter-config-section-ui.test.tsx (1)

375-390: ⚡ Quick win

Assert the rangeMax value, not only call occurrence.

This test currently passes even if the wrong field is updated. Validate that the setChartOptions argument actually sets rangeMax to 250 (for both object and functional-updater forms).

Proposed assertion hardening
   fireEvent.change(screen.getByLabelText("Range maximum"), {
     target: { value: "250" },
   });
-  // Either a direct object or a functional updater is acceptable —
-  // we only need to confirm chartOptions was updated for the max field.
-  expect(mockSetChartOptions).toHaveBeenCalled();
+  expect(mockSetChartOptions).toHaveBeenCalled();
+  const arg = mockSetChartOptions.mock.calls.at(-1)?.[0];
+  if (typeof arg === "function") {
+    const next = arg({ rangeMin: 0, rangeMax: 100, rangeStep: 1 });
+    expect(next).toMatchObject({ rangeMax: 250 });
+  } else {
+    expect(arg).toMatchObject({ rangeMax: 250 });
+  }

As per coding guidelines, app/**/*.test.tsx: Use Vitest with jsdom environment for React component render tests in app/ package with branch coverage.

🤖 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/widget-editor/__tests__/parameter-config-section-ui.test.tsx`
around lines 375 - 390, Update the test that fires a change on the "Range
maximum" input (in the ParameterConfigSection test where mockSetChartOptions is
used) to assert the actual rangeMax was set to 250: after firing the event, grab
the argument passed to mockSetChartOptions (first call), detect whether it is an
object or a function, and in the object case assert arg.rangeMax === 250, and in
the function case call the function with a representative previous chartOptions
(e.g. {rangeMin:0, rangeMax:100, rangeStep:1}) and assert the returned object's
rangeMax === 250; keep using the existing mockSetChartOptions and
ParameterConfigSection references so the test verifies the updated value instead
of only call occurrence.
🤖 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/widget-editor/parameter-config-section.tsx`:
- Around line 245-278: The onChange handlers for the numeric inputs (ids
"range-min", "range-max", "range-step") currently use Number(e.target.value)
which can produce NaN during typing and allows an invalid 0 for rangeStep;
update the handlers in onChartOptionsChange to: parse the input (parseFloat or
Number), check Number.isFinite(value) and only set rangeMin/rangeMax when finite
(otherwise leave previous value), and for rangeStep accept only a finite value >
0 (or fallback to a sensible default like 1) to prevent storing 0 or NaN; apply
these guards in the onChange callbacks referenced by onChartOptionsChange and
validate against chartOptions.rangeStep when computing defaults.

---

Nitpick comments:
In
`@app/src/components/widget-editor/__tests__/parameter-config-section-ui.test.tsx`:
- Around line 375-390: Update the test that fires a change on the "Range
maximum" input (in the ParameterConfigSection test where mockSetChartOptions is
used) to assert the actual rangeMax was set to 250: after firing the event, grab
the argument passed to mockSetChartOptions (first call), detect whether it is an
object or a function, and in the object case assert arg.rangeMax === 250, and in
the function case call the function with a representative previous chartOptions
(e.g. {rangeMin:0, rangeMax:100, rangeStep:1}) and assert the returned object's
rangeMax === 250; keep using the existing mockSetChartOptions and
ParameterConfigSection references so the test verifies the updated value instead
of only call occurrence.
🪄 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: 79aa0510-d016-4423-a32f-10cc2e0568f5

📥 Commits

Reviewing files that changed from the base of the PR and between 93f65a9 and 09e4f7c.

📒 Files selected for processing (8)
  • app/src/components/widget-editor-modal.tsx
  • app/src/components/widget-editor/__tests__/parameter-config-section-ui.test.tsx
  • app/src/components/widget-editor/__tests__/parameter-config-section.test.tsx
  • app/src/components/widget-editor/modal-footer.tsx
  • app/src/components/widget-editor/parameter-config-section.tsx
  • app/src/components/widget-editor/parameter-preview.tsx
  • app/src/components/widget-editor/use-widget-save.ts
  • app/src/stores/widget-editor-store.ts

Comment on lines +245 to +278
onChange={(e) =>
onChartOptionsChange((prev) => ({
...prev,
rangeMin: Number(e.target.value),
}))
}
className="w-24"
/>
<span className="text-xs text-muted-foreground">to</span>
<Input
id="range-max"
type="number"
aria-label="Range maximum"
value={(chartOptions.rangeMax as number | undefined) ?? 100}
onChange={(e) =>
onChartOptionsChange((prev) => ({
...prev,
rangeMax: Number(e.target.value),
}))
}
className="w-24"
/>
<span className="text-xs text-muted-foreground">step</span>
<Input
id="range-step"
type="number"
aria-label="Range step"
min={0}
value={(chartOptions.rangeStep as number | undefined) ?? 1}
onChange={(e) =>
onChartOptionsChange((prev) => ({
...prev,
rangeStep: Number(e.target.value),
}))

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

Prevent invalid numeric values from being stored for range bounds/step.

Using Number(e.target.value) here can write NaN during normal typing states, and Line 272 allows 0 step. That can propagate invalid range config and break number-range behavior.

Proposed fix
             <Input
               id="range-min"
               type="number"
               aria-label="Range minimum"
               value={(chartOptions.rangeMin as number | undefined) ?? 0}
-              onChange={(e) =>
-                onChartOptionsChange((prev) => ({
-                  ...prev,
-                  rangeMin: Number(e.target.value),
-                }))
-              }
+              onChange={(e) => {
+                const n = e.target.valueAsNumber;
+                onChartOptionsChange((prev) => ({
+                  ...prev,
+                  rangeMin: Number.isFinite(n) ? n : undefined,
+                }));
+              }}
               className="w-24"
             />
@@
             <Input
               id="range-max"
               type="number"
               aria-label="Range maximum"
               value={(chartOptions.rangeMax as number | undefined) ?? 100}
-              onChange={(e) =>
-                onChartOptionsChange((prev) => ({
-                  ...prev,
-                  rangeMax: Number(e.target.value),
-                }))
-              }
+              onChange={(e) => {
+                const n = e.target.valueAsNumber;
+                onChartOptionsChange((prev) => ({
+                  ...prev,
+                  rangeMax: Number.isFinite(n) ? n : undefined,
+                }));
+              }}
               className="w-24"
             />
@@
             <Input
               id="range-step"
               type="number"
               aria-label="Range step"
-              min={0}
+              min={0.000001}
               value={(chartOptions.rangeStep as number | undefined) ?? 1}
-              onChange={(e) =>
-                onChartOptionsChange((prev) => ({
-                  ...prev,
-                  rangeStep: Number(e.target.value),
-                }))
-              }
+              onChange={(e) => {
+                const n = e.target.valueAsNumber;
+                onChartOptionsChange((prev) => ({
+                  ...prev,
+                  rangeStep: Number.isFinite(n) && n > 0 ? n : undefined,
+                }));
+              }}
               className="w-20"
             />
🤖 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/widget-editor/parameter-config-section.tsx` around lines
245 - 278, The onChange handlers for the numeric inputs (ids "range-min",
"range-max", "range-step") currently use Number(e.target.value) which can
produce NaN during typing and allows an invalid 0 for rangeStep; update the
handlers in onChartOptionsChange to: parse the input (parseFloat or Number),
check Number.isFinite(value) and only set rangeMin/rangeMax when finite
(otherwise leave previous value), and for rangeStep accept only a finite value >
0 (or fallback to a sensible default like 1) to prevent storing 0 or NaN; apply
these guards in the onChange callbacks referenced by onChartOptionsChange and
validate against chartOptions.rangeStep when computing defaults.

Brings Sonar new-code coverage from 52.5% to >=80% so the quality gate
passes. No production changes:

- parameter-preview.test.tsx: new file covering all 8 preview branches
  including number-range (min/max guards, NaN handling) and cascading
  (parent param, placeholder propagation)
- widget-editor-store.test.ts: round-trip tests for the new
  reverseParamTypeMapping cases (number-range, cascading-select)

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@alfredo1996
alfredo1996 merged commit d742197 into release/1.0 May 19, 2026
14 checks passed
@alfredo1996
alfredo1996 deleted the fix/861-expose-all-param-types branch July 3, 2026 12:42
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