Skip to content

feat(chart): chart-view redesign — config bar, autoChart, 5 types via Chart.js#20

Open
BorisTyshkevich wants to merge 3 commits into
mainfrom
feat/chart-views
Open

feat(chart): chart-view redesign — config bar, autoChart, 5 types via Chart.js#20
BorisTyshkevich wants to merge 3 commits into
mainfrom
feat/chart-views

Conversation

@BorisTyshkevich

Copy link
Copy Markdown
Collaborator

What

Replaces the single hard-wired bar chart with a real chart surface, matching the Claude Design handoff.

  • Config bar: Type (Bar = horizontal / Column / Line / Area / Pie), X, Y, an "All measures" multi-series toggle, and a group-by Series dropdown.
  • autoChart heuristic — classifies each column from its ClickHouse type (stripping Nullable/LowCardinality): temporal X → line, categorical X → horizontal bar, ordinal X → column. Non-chartable results show a hint instead of a broken axis.
  • Five renderers + multi-series and group-by pivot, themed to the CSS tokens (accent series, mono ticks, humanized K/M + YYYY-MM labels).
  • Chart config persists per query tab (re-derived on schema change, overrides kept otherwise).

Dependency decision

Rendering now uses Chart.js — the one bundled runtime dependency, inlined into dist/sql.html, so the page still makes zero third-party requests. This relaxes CLAUDE.md hard rule #4 (no runtime deps); the change is recorded in CLAUDE.md, README.md, and build/build.mjs. React-only libs (Recharts/visx) were ruled out — the app is framework-free.

Keeping the layers honest

All role/axis/pivot/scale math and the Chart.js config builder are pure in src/core/chart-data.js (100/100/100/100); the new Chart() call is an injected seam (app.Chart, like the fetch/crypto seams), so results.js stays fully tested. Net: coverage never had to drop — only the no-deps rule did. Live Chart instances are destroyed on re-render to avoid canvas/observer leaks.

Verification

  • npm test488 passed, per-file coverage gate green.
  • npm run build313 KB self-contained dist/sql.html (Chart.js inlined).
  • Rendered all 7 chart shapes in a browser against ClickHouse-shaped data — autoChart picks the right default per data shape; every type draws correctly.

🤖 Generated with Claude Code

…s via Chart.js

Replace the single hard-wired bar chart with a real chart surface matching the
Claude Design handoff: a config bar (Type / X / Y / multi-series toggle /
group-by Series), an autoChart heuristic that classifies columns from their
ClickHouse types (temporal → line, categorical → horizontal bar, ordinal →
column), and five renderers (h-bar / column / line / area / pie) with
multi-series and group-by pivot, themed to the CSS tokens.

Rendering now uses Chart.js — the one bundled runtime dependency, inlined into
dist/sql.html so the page still makes zero third-party requests. This relaxes
CLAUDE.md hard rule #4 (recorded there, in the README, and build.mjs). To keep
the coverage model intact, all role/axis/pivot/scale math and the Chart.js
config builder are pure in src/core/chart-data.js (100%), and the `new Chart()`
call is an injected seam (app.Chart) so the DOM wrapper stays fully tested.

Chart config persists per query tab (tab.chartCfg/chartKey), re-derived on
schema change; live Chart instances are destroyed on re-render to avoid leaks.

npm test: 488 passed, per-file gate green. Build: 313 KB self-contained artifact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wtzg34E1iGzKm11TArRpko
BorisTyshkevich and others added 2 commits June 22, 2026 20:59
…t, tighten "All measures"

- Show "first 500 of N rows" in the config bar when a result exceeds the chart's
  CHART_ROW_CAP, so the truncation is never silent (the table still shows all).
- chartRole reuses format.js:isNumericType on the stripped type instead of a
  second copy of the numeric regex.
- "All measures" now targets only true measures and excludes the current X
  column, so it can't plot an ordinal axis (e.g. month) against itself.
- Comment chartNumFmt's deliberate divergence from formatRows.

(Reviewer's sort-on-every-config-click note left as-is: sortRows no-ops without
an active sort, and caching only the chart's sort would diverge from the table.)

npm test: 490 passed, per-file gate green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wtzg34E1iGzKm11TArRpko
`.res-body` is display:block, so `.chart-view { flex: 1 }` was inert: the view
collapsed to the config bar's height and Chart.js sized the canvas to a few
pixels — a config bar with no visible chart. Use `height: 100%` (the block
parent has a definite height) for `.chart-view` and `.chart-empty` so the
canvas wrapper gets real height.

Not caught earlier: happy-dom does no layout, and the manual browser harness
attached the canvas into a pre-sized container, bypassing the real `.res-body`
parent. Verified live against an otel system.query_log result.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wtzg34E1iGzKm11TArRpko
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.

1 participant