feat(widgets): scale chart axes/tooltips with unit + scale options

Consistent graph scaling across every line-chart widget. A new shared
frontend/src/lib/metricFormat.ts picks a decimal prefix (kB/MB/GB, kbps/Mbps,
Gbps, …) from the series magnitude and formats values; LineSeriesChart accepts
unit + scale and formats both the Y-axis ticks and the tooltip with the SAME
prefix (one consistent unit per axis). MetricChartWidget (Prometheus) and
QbittorrentSpeedWidget pass the widget config through; qBit speed defaults to
bytes/sec → MB/s.

WidgetConfigDialog now renders `enum` schema fields as a <Select> dropdown, so
the backend's unit/scale Literal enums become consistent pickers in every graph
widget's config (and any future enum option).

Decimal (x1000) prefixes by default (matches Mbps/MB/s/Grafana).

Tests: 13 new metricFormat tests (auto/fixed scaling, percent, seconds,
nulls, trailing-zero trimming). 179/179 frontend tests pass; tsc + ESLint clean.
This commit is contained in:
Developer
2026-07-12 11:46:07 +00:00
parent 05a9faca3e
commit b7019b33ac
12 changed files with 333 additions and 26 deletions
+14 -6
View File
@@ -4,19 +4,27 @@ dir: frontend/src/lib
index: frontend/src/lib/.pi-map.index.md
## role
Provides shared utility functions for the frontend application, specifically Tailwind CSS class merging and conflict resolution.
Shared utility library providing metric formatting, chart axis scaling, and CSS class manipulation for the frontend application.
## files
- metricFormat.ts | Formats metric values and computes axis scaling using decimal prefixes for chart widgets. | exp: MetricUnit, MetricScale, METRIC_UNIT_LABELS, METRIC_SCALE_LABELS, ScaleInfo, func:metricScaleInfo(maxAbs: number, unit: MetricUnit, scale: MetricScale) → ScaleInfo, call:Math.floor, call:Math.log, call:Math.max, call:Math.min, func:formatScaled(value: number | null | undefined, { divisor, suffix }: ScaleInfo, unit: MetricUnit) → string, call:Number.isNaN, call:trim, call:formatDuration, call:`${trim(value / divisor)} ${suffix}`.trim, func:formatMetricValue(value: number | null | undefined, unit: MetricUnit, scale: MetricScale) → string, call:Number.isNaN, call:metricScaleInfo, call:Math.abs, call:formatScaled
- utils.ts | Utility function that merges Tailwind CSS classes with proper deduplication and conflict resolution | exp: func:cn(...inputs: ClassValue[]), call:twMerge, call:clsx | dep: clsx, tailwind-merge
## arch
Functional utility module pattern using the `clsx` + `tailwind-merge` (cn) convention for deterministic class composition.
Functional utility module pattern exposing pure helper functions, leveraging external libraries (decimal prefixes, tailwind-merge) for stateless transformations.
## tags
merge, tailwind, cn, call:tw, call:clsx, utils, utility, merges
metric, scale, info, format, unit, labels, scaled, call:number.is
## symbols
- metricScaleInfo
- formatScaled
- formatMetricValue
- cn
- call:twMerge
- call:clsx
- MetricUnit
- MetricScale
- METRIC_UNIT_LABELS
- METRIC_SCALE_LABELS
## workflows
- change lib behavior
read: utils.ts
read: metricFormat.ts, utils.ts
- explore lib subdirectories
index: frontend/src/lib/__tests__/.pi-map.index.md
## dirty
-