Skip to content

fix: bracket wrapping in code editor + radar chart scale - #179

Closed
alfredo1996 wants to merge 10 commits into
devfrom
fix/editor-brackets-radar-scale
Closed

fix: bracket wrapping in code editor + radar chart scale#179
alfredo1996 wants to merge 10 commits into
devfrom
fix/editor-brackets-radar-scale

Conversation

@alfredo1996

@alfredo1996 alfredo1996 commented Mar 23, 2026

Copy link
Copy Markdown
Owner

Summary

Two bug fixes:

1. Parenthesis wrapping in query editor

Selecting text and typing ( now wraps it as (text) instead of replacing it. Added closeBrackets() extension and closeBracketsKeymap from @codemirror/autocomplete.

2. Radar chart uniform shape

The radar chart was showing a near-uniform pentagon because each indicator had its own independent max. Now uses a single global max so ACTED_IN=172 vs REVIEWED=9 shows actual relative differences.

Test plan

  • App: 72 suites, all pass (4 new/updated radar tests)
  • Component: 67 suites, all pass (mock updated)
  • Build + lint clean

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a seeded "Chart Catalog" dashboard with multi-page examples and pre-configured widget variants.
  • Improvements

    • Radar charts now use a single shared maximum across indicators when no per-indicator max is provided; explicit maxima are preserved.
    • Query editor now includes close-brackets behavior for smoother editing.
    • Remove-control rendering updated to avoid nested buttons and support Enter/Space activation.
  • Tests

    • Expanded radar transform tests to cover global-max behavior and explicit max preservation; extended editor mocks.

Bug 1 — CodeMirror bracket wrapping:
- Add closeBrackets() extension and closeBracketsKeymap to query editor
- Selecting text and typing ( now wraps as (text) instead of replacing
- Both Cypher and SQL bracket facets are now activated

Bug 2 — Radar chart uniform shape:
- Use single global max across all indicators instead of per-indicator max
- Values like 172 vs 9 now show actual relative differences
- Explicit max column values preserved when provided
- 4 new/updated tests for global max behavior

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

coderabbitai Bot commented Mar 23, 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

Replace per-indicator radar auto-scaling with a single global max (observedMax × 1.1, fallback 100) unless explicit per-indicator max is present; add CodeMirror close-brackets support to the query editor and tests; add a script to seed a multi-page Chart Catalog dashboard; adjust cross-filter tag remove control to avoid nested buttons and add keyboard handling.

Changes

Cohort / File(s) Summary
Radar chart scaling
app/src/lib/chart-registry.ts, app/src/lib/__tests__/chart-registry.test.ts
Switch per-indicator auto-scaling to a single shared global max when no explicit max is provided; update/add tests for labeled single-indicator case, global-max behavior (long and wide formats), and preservation of explicit per-record max.
Query editor (CodeMirror close-brackets)
component/src/components/composed/query-editor.tsx, component/src/components/composed/__tests__/query-editor.test.tsx
Include closeBrackets and closeBracketsKeymap in dynamic import/extension and keymap composition; extend test mocks to export these items.
Demo dashboard seeding
scripts/seed-demo.mjs
Add buildChartCatalog(neo4jId) and call it from main() to upsert a versioned, multi-page Chart Catalog dashboard with many chart pages, palettes, click-action helpers, and detail/behavior pages.
Cross-filter tag rendering
component/src/components/composed/cross-filter-tag.tsx
Replace inline remove-button with removeControl that renders a non-button interactive element when onClick is provided to avoid nested buttons; stop event propagation on clicks and add keyboard activation handling.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

pkg:app, pkg:component, area:widgets

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the two main fixes: bracket wrapping in the code editor and radar chart scaling behavior changes.

✏️ 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/editor-brackets-radar-scale

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/lib/chart-registry.ts`:
- Around line 505-513: The code treats missing/invalid explicit max values as an
explicit 100 which then prevents using the computed globalMax; change the logic
so indicatorExplicitMax only contains entries when an explicit/valid numeric max
was actually provided (e.g., check isFinite/max !== null/undefined before set),
and keep missing/invalid entries out (or store undefined) so the indicators
mapping (the indicators array construction using indicatorExplicitMax, maxKey,
globalMax) will fall back to globalMax; update the place that populates
indicatorExplicitMax (not the indicators mapping itself) to perform this
validation so references to indicatorExplicitMax.has(name) truly mean the user
supplied an explicit max.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 28893d48-64e5-4690-80bf-0018713af111

📥 Commits

Reviewing files that changed from the base of the PR and between 59e78c2 and d6305d8.

📒 Files selected for processing (4)
  • app/src/lib/__tests__/chart-registry.test.ts
  • app/src/lib/chart-registry.ts
  • component/src/components/composed/__tests__/query-editor.test.tsx
  • component/src/components/composed/query-editor.tsx

Comment thread app/src/lib/chart-registry.ts Outdated
New "Chart Catalog" dashboard with 12 pages, one per chart type:
- Bar: vertical, horizontal, stacked, show values, styling, click, colorblind + 6 palettes
- Line: default, smooth+area, stepped, show points, colorblind + 6 palettes
- Pie: default, donut, rose, labels inside, click, colorblind + 6 palettes
- Single Value: prefix/suffix, comma, compact, styling, trend
- Table: default, sorting+filters, selection, click
- Gauge: default, no pointer, half gauge, styling + 6 palettes
- Radar: default, circle, filled+values, colorblind + 6 palettes
- Sankey: horizontal, vertical + 6 palettes
- Treemap: default, with values + 6 palettes
- Sunburst: default, no labels + 6 palettes
- Content: markdown (with tables), JSON viewer, iframe
- Detail: click action target page

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 (1)
scripts/seed-demo.mjs (1)

2018-2024: Use targetPageId for navigation rules to match existing contract.

Line 2023 uses navigateToPageId, but navigate actions elsewhere in this file use targetPageId (Line 1312, Line 1530). Aligning this avoids latent breakage if this helper is used.

Suggested fix
 const clickNavPage = (triggerCol, pageId) => ({
   type: "navigate-to-page",
   rules: [{
     id: uuid(), type: "navigate-to-page",
     triggerColumn: triggerCol,
-    navigateToPageId: pageId,
+    targetPageId: pageId,
   }],
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/seed-demo.mjs` around lines 2018 - 2024, The helper clickNavPage is
creating a navigation rule using the property navigateToPageId which is
inconsistent with the rest of the codebase; update the rule object inside
clickNavPage (type "navigate-to-page", rules array) to use targetPageId instead
of navigateToPageId so it matches other navigate actions and the expected
contract (keep triggerColumn, id, type unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/seed-demo.mjs`:
- Around line 2028-2035: paletteRow is adding the palette under
settings.chartOptions.colorPalette but existing seed rows expect
settings.colorPalette; update the paletteRow function (symbol: paletteRow) to
set colorPalette at settings.colorPalette (merge baseSettings, set title and
colorPalette on the top-level settings) and keep chartOptions merged from
baseSettings.chartOptions so palette demos use the intended color palette shape.

---

Nitpick comments:
In `@scripts/seed-demo.mjs`:
- Around line 2018-2024: The helper clickNavPage is creating a navigation rule
using the property navigateToPageId which is inconsistent with the rest of the
codebase; update the rule object inside clickNavPage (type "navigate-to-page",
rules array) to use targetPageId instead of navigateToPageId so it matches other
navigate actions and the expected contract (keep triggerColumn, id, type
unchanged).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ea9596a6-afff-4940-9a8d-1b75299934dd

📥 Commits

Reviewing files that changed from the base of the PR and between d6305d8 and 5c166f6.

📒 Files selected for processing (1)
  • scripts/seed-demo.mjs

Comment thread scripts/seed-demo.mjs
When onClick is set, CrossFilterTag renders as a <button>. The remove
control inside was also a <button>, creating invalid nested buttons
that cause React hydration errors.

Fix: use <span role="button"> with keyboard handlers when the outer
element is a button. Native <button> is preserved when outer is a div.

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.

🧹 Nitpick comments (1)
component/src/components/composed/cross-filter-tag.tsx (1)

40-40: Consider splitting the long onKeyDown handler for readability.

The inline handler works but is dense. Extracting to a named handler or splitting across lines would improve scannability.

♻️ Optional: extract handler
+  const handleRemoveKeyDown = (e: React.KeyboardEvent) => {
+    if (e.key === "Enter" || e.key === " ") {
+      e.stopPropagation();
+      e.preventDefault();
+      onRemove?.();
+    }
+  };
+
   const removeControl = onRemove && (
     onClick ? (
       <span
         role="button"
         tabIndex={0}
         onClick={(e) => { e.stopPropagation(); onRemove(); }}
-        onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.stopPropagation(); e.preventDefault(); onRemove(); } }}
+        onKeyDown={handleRemoveKeyDown}
         className="ml-1 rounded-full p-0.5 hover:bg-muted cursor-pointer"
       >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@component/src/components/composed/cross-filter-tag.tsx` at line 40, The
inline onKeyDown handler in the CrossFilterTag component is too dense; extract
it into a named function (e.g., handleKeyDown) near the component body and
attach it as onKeyDown={handleKeyDown}; the handler should check e.key for
"Enter" or " " (space), call e.stopPropagation() and e.preventDefault(), then
call the existing onRemove() prop; optionally wrap the handler with useCallback
to avoid unnecessary re-renders and reference onKeyDown, onRemove, and the
CrossFilterTag component when locating where to change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@component/src/components/composed/cross-filter-tag.tsx`:
- Line 40: The inline onKeyDown handler in the CrossFilterTag component is too
dense; extract it into a named function (e.g., handleKeyDown) near the component
body and attach it as onKeyDown={handleKeyDown}; the handler should check e.key
for "Enter" or " " (space), call e.stopPropagation() and e.preventDefault(),
then call the existing onRemove() prop; optionally wrap the handler with
useCallback to avoid unnecessary re-renders and reference onKeyDown, onRemove,
and the CrossFilterTag component when locating where to change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 160b4585-7964-4556-a975-fc3b0a3c4b3a

📥 Commits

Reviewing files that changed from the base of the PR and between 5c166f6 and 6dd50c9.

📒 Files selected for processing (1)
  • component/src/components/composed/cross-filter-tag.tsx

Add 6 new pages (18 total) to the Chart Catalog seed dashboard:

- Page 12: Graph — force/circular/hierarchical, node sizes, labels, physics
- Page 13: Parameter Widgets — select (searchable/not), text, date, date-range, relative-date
- Page 14: Form Widget — default form, custom button + no-reset
- Page 15: Behavior — showRefreshButton, manualRun, cacheMode (forever/TTL)
- Page 16: Axis & Grid — axis labels, grid lines off, bar width/gap, legend off, sorted slices, no percentages
- Page 17: Advanced — table pagination/pageSize, gauge min/max/progress/detail, radar legend, sankey nodeWidth/gap, sunburst sort/highlight, treemap breadcrumb/saturation, JSON fontSize/theme/copyButton

Every chart option from chart-options-schema.ts now has at least one
widget demonstrating it.

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.

♻️ Duplicate comments (1)
scripts/seed-demo.mjs (1)

2034-2041: ⚠️ Potential issue | 🟠 Major

Move colorPalette back to top-level settings.

This helper writes the palette to settings.chartOptions.colorPalette, but the rest of this file seeds palettes at settings.colorPalette (for example Line 411). That means the catalog palette variants will likely fall back to the default palette.

Suggested fix
 function paletteRow(chartType, query, baseSettings = {}) {
   return P.map((p) => ({
     id: uuid(),
     chartType,
     connectionId: neo4jId,
     query,
-    settings: { ...baseSettings, title: p, chartOptions: { ...baseSettings.chartOptions, colorPalette: p } },
+    settings: {
+      ...baseSettings,
+      title: p,
+      colorPalette: p,
+      chartOptions: { ...baseSettings.chartOptions },
+    },
   }));
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/seed-demo.mjs` around lines 2034 - 2041, The paletteRow helper is
currently writing the palette into settings.chartOptions.colorPalette which is
inconsistent with other seed data that uses settings.colorPalette; update
paletteRow (the function named paletteRow) to set colorPalette at the top-level
of settings instead of under chartOptions—i.e., keep title and existing
baseSettings.chartOptions intact but add colorPalette: p directly on the
settings object (and remove any chartOptions.colorPalette assignment).
🧹 Nitpick comments (1)
scripts/seed-demo.mjs (1)

2024-2030: Use targetPageId in the navigation helper.

Every working navigate rule in this file uses targetPageId (for example Line 1312 and Line 1510). This helper emits navigateToPageId, so any future caller will serialize the wrong shape.

Suggested fix
 const clickNavPage = (triggerCol, pageId) => ({
   type: "navigate-to-page",
   rules: [{
     id: uuid(), type: "navigate-to-page",
     triggerColumn: triggerCol,
-    navigateToPageId: pageId,
+    targetPageId: pageId,
   }],
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/seed-demo.mjs` around lines 2024 - 2030, The navigation helper
clickNavPage is emitting the wrong property name — it currently serializes
navigateToPageId for the rule of type "navigate-to-page"; change the emitted
property to targetPageId so the rule shape matches other navigate rules (e.g.,
those at lines referenced) and callers consuming clickNavPage will produce the
correct serialized shape; update the object returned by clickNavPage (and any
references to navigateToPageId) to use targetPageId while keeping the rule id,
type ("navigate-to-page"), triggerColumn, and uuid() usage unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@scripts/seed-demo.mjs`:
- Around line 2034-2041: The paletteRow helper is currently writing the palette
into settings.chartOptions.colorPalette which is inconsistent with other seed
data that uses settings.colorPalette; update paletteRow (the function named
paletteRow) to set colorPalette at the top-level of settings instead of under
chartOptions—i.e., keep title and existing baseSettings.chartOptions intact but
add colorPalette: p directly on the settings object (and remove any
chartOptions.colorPalette assignment).

---

Nitpick comments:
In `@scripts/seed-demo.mjs`:
- Around line 2024-2030: The navigation helper clickNavPage is emitting the
wrong property name — it currently serializes navigateToPageId for the rule of
type "navigate-to-page"; change the emitted property to targetPageId so the rule
shape matches other navigate rules (e.g., those at lines referenced) and callers
consuming clickNavPage will produce the correct serialized shape; update the
object returned by clickNavPage (and any references to navigateToPageId) to use
targetPageId while keeping the rule id, type ("navigate-to-page"),
triggerColumn, and uuid() usage unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7afe3ff6-d8fa-4067-9eee-6153cadfd606

📥 Commits

Reviewing files that changed from the base of the PR and between 6dd50c9 and f31270f.

📒 Files selected for processing (1)
  • scripts/seed-demo.mjs

alfredorubin96 and others added 6 commits March 23, 2026 12:11
…ces)

Add FILMED_IN and BORN_IN relationships to Neo4j seed connecting Movies
and Persons to City nodes (which already had lat/lng coordinates).

New Map Chart page in Chart Catalog (6 widgets):
- Cities by population (OSM)
- Filming locations (Carto Light)
- Birthplaces (Carto Dark)
- Clustered markers
- Custom zoom / no popup
- Large markers with click action

All map options covered: tileLayer (osm/carto-light/carto-dark),
autoFitBounds, markerSize, showPopup, clusterMarkers, zoom, minZoom, maxZoom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Markdown: use actual \n newlines instead of escaped \\n literals
- iFrame: replace echarts.apache.org (blocks framing via X-Frame-Options)
  with Wikipedia which allows embedding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add GFM table parsing to markdown-widget.tsx on this branch
  (was only on feat/issue-143-markdown-tables branch)
- Reduce graph widgets from 6 to 4, use smaller queries (LIMIT 10)
  to prevent NVL physics engine overload with simultaneous renders
- Fix graphSmall query: bind relationship variable properly
- Type annotations for parseCells in markdown parser

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Gate NVL canvas visibility with a layoutReady state that flips true only
when onLayoutDone fires. Reset is synchronous during render (not useEffect)
to avoid a race where the effect runs after onLayoutDone on the main thread.
Replace the arbitrary 100ms autoFit timer with a deterministic layoutReady
guard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Radar chart: only store explicit max when finite and > 0, so
  invalid/null/NaN values fall back to globalMax instead of being
  treated as explicit 100.
- Seed: move colorPalette to settings.colorPalette (matches
  chart-renderer.tsx), fix navigateToPageId -> targetPageId.
- Markdown: replace ReDoS-vulnerable table alignment regex with
  linear split-and-check function (isTableAlignmentRow).
- Add tests for radar invalid max fallback and GFM table rendering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The typeInEditor fixture was retrying until timeout when CM6's
internal view.state.readOnly was true. Now falls through to the
keyboard fallback strategy instead of throwing.

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

Copy link
Copy Markdown

@alfredo1996

Copy link
Copy Markdown
Owner Author

Superseded by consolidated PR (release/a11y-and-fixes)

@alfredo1996
alfredo1996 deleted the fix/editor-brackets-radar-scale branch March 29, 2026 22:22
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