-
Analytics: show every provider label on the category charts (#890) (#959)
发布于
2026-08-22 00:41:05 +00:00 - Analytics: show every provider label on the category charts (#890)
recharts' default interval is 'preserveStartEnd': when more providers than
fit in the width, it silently drops the middle ticks, so only a few provider
names render, the rest are hover-only, and a bar no longer lines up with the
label the user can see (a bar with no label reads as a different provider).Force every tick (interval=0) and rotate + truncate the labels so they fit,
via a small pure helper (lib/chart-axis.ts) applied to the five category
charts: requests by provider/agent, latency by provider, TTFT by provider and
errors by provider. The timeline charts are time series and are untouched.- client/src/lib/chart-axis.ts: categoryAxisProps() + truncateAxisLabel
- client/src/pages/AnalyticsPage.tsx: spread the props on the 5 category X-axes
- client/src/lib/chart-axis.test.ts: regression tests for the helper
- Analytics: cover the errors-by-category axis and stop the tilted labels colliding (#890)
Follow-ups to the first pass at #890:
-
"Errors by category" is a layout="vertical" chart, so its category axis is
the YAxis. It was left on recharts' default interval and dropped labels the
same way the vertical charts did. Added verticalCategoryAxisProps() — every
tick, plus the 128px gutter and a truncation cap sized to it. -
categoryAxisProps() was a fixed -30 degrees at height 56, which forced all
the ticks on and then let them overlap on a crowded or narrow (single-column
mobile) chart, and under-reserved the strip an 18-char label needs. It now
takes the category count and picks the shallowest tilt that still seats them
on a 280px plot (-30, -45, -90), with the height derived from the tilt and
the truncation cap instead of hard-coded. -
Callers pass their data.length.
-
Added a render test that mounts a real recharts BarChart under jsdom and
counts the tick elements, so the props are proven to reach recharts
rather than merely to have the right shape. Stubs getBoundingClientRect,
which recharts uses to measure labels and jsdom answers with zeroes. -
The comment claimed recharts defaults to 'preserveStartEnd'; both
implicitXAxis and implicitYAxis default to 'preserveEnd'.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
- Fix the client test job on Node 20: pin jsdom to 27 and declare it at the root
The render test added in the previous commit brought in jsdom@30, which broke
"Test & build (Node 20)" — 177 tests passed but vitest exited nonzero on an
unhandled module-load error:TypeError: webidl.util.markAsUncloneable is not a function at new CacheStorage node_modules/undici/lib/web/cache/cachestorage.js:20Two separate problems, both fixed here.
-
jsdom 28+ depends on undici, and jsdom 30 wants undici ^8.9.0. undici 8
declaresengines: >=22.19.0and doesconst { markAsUncloneable } = require('node:worker_threads') webidl.util.markAsUncloneable = markAsUncloneablewith no guard. worker_threads.markAsUncloneable landed in Node 22.10, so on
Node 20 that assigns undefined and the first call throws. (undici 6, which
the server pins, writesmarkAsUncloneable || (() => {}); undici 7
feature-detects. Only 8 assumes it.) jsdom 30's own engines field says
^22.22.2 || ^24.15.0 || >=26.0.0— it never supported the Node 20 this
repo declares inenginesand tests in CI; nothing enforced that.jsdom is now pinned to ^27, the last major with no undici dependency at
all, so the failure mode cannot come back through a transitive bump.
undiciis now absent from the root tree entirely — the only copy left is
the server's own direct 6.26.0. -
jsdom was declared in client/package.json, and npm nested it at
client/node_modules/jsdom rather than hoisting it. vitest hoists to the
root node_modules and resolves an environment package relative to itself,
so it never looks in client/node_modules — the run died with "Cannot find
package 'jsdom'", i.e. the same 177-pass/nonzero-exit symptom for a
different reason. Whether npm hoists or nests depends on how the rest of
the tree deduplicates, which is why this differed between Node versions.Declaring it in the root package.json puts it beside vitest deterministically.
Verified every package in the installed tree now accepts Node 20.19/20.20;
npm cireproduces the tree from the lockfile; fullnpm testis green
(server 2499, cli 95, client 182).Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Co-authored-by: tashdroid 319142293+tashdroid@users.noreply.github.com
Co-authored-by: Tash tash@MacBook-Pro.local
Co-authored-by: Claude Fable 5 noreply@anthropic.com下载附件