Keep the theme redraw from wiping a chart's x scale
Reaching Home from another page builds its charts in the same commit their theme effect first fires in, with the readings already cached. uPlot ranges its scales in a microtask, so `redraw()` re-set the x scale from the chart's own — still empty — bounds before that ran, and the pending range taken from the data was lost: axes without ticks, no lines, and no way back but the range control, which rebuilds the chart. Redrawing without the paths is all a colour swap needs and leaves the scales alone. The cards also carried "Nothing has run yet." while the first readings were still on their way; they now carry the skeleton the rest of Home uses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SKL7sUgNWhukDEz95vSMQv
This commit is contained in:
@@ -4,6 +4,7 @@ import "uplot/dist/uPlot.min.css"
|
||||
|
||||
import type { HistoryPoint } from "@/client"
|
||||
import { useTheme } from "@/components/theme-provider"
|
||||
import { Skeleton } from "@/components/ui/skeleton"
|
||||
import { si } from "@/lib/utils"
|
||||
|
||||
/**
|
||||
@@ -74,6 +75,7 @@ export function UplotChart({
|
||||
labels,
|
||||
plots,
|
||||
empty = "Nothing has come through yet.",
|
||||
pending = false,
|
||||
unit,
|
||||
yRange,
|
||||
yLabel,
|
||||
@@ -85,6 +87,9 @@ export function UplotChart({
|
||||
/** The points of each series, in the same order as `labels`. */
|
||||
plots: HistoryPoint[][]
|
||||
empty?: string
|
||||
/** No readings yet because none have arrived: a chart with nothing in it
|
||||
* says "nothing has run", which is a different thing to say. */
|
||||
pending?: boolean
|
||||
/** Written after every reading, on the axis and in the legend. */
|
||||
unit?: string
|
||||
/** A y axis fixed to these bounds; unset lets it follow the data. */
|
||||
@@ -240,10 +245,16 @@ export function UplotChart({
|
||||
chart.current.setData(table(plots))
|
||||
}, [points, key])
|
||||
|
||||
// The canvas cannot follow a CSS variable, so a theme swap is a redraw.
|
||||
// The canvas cannot follow a CSS variable, so a theme swap is a redraw. The
|
||||
// paths are geometry and stay as they are — and leaving them alone is what
|
||||
// keeps this safe on the mount it also fires on: a full redraw re-sets the x
|
||||
// scale from the chart's own bounds, which are still empty when the chart was
|
||||
// built in this same commit (uPlot ranges its scales in a microtask). That
|
||||
// overwrites the range it was about to take from the data, and the chart is
|
||||
// left with no x axis and no lines until something sets its data again.
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: the theme is the signal, not something the effect reads.
|
||||
useEffect(() => {
|
||||
chart.current?.redraw()
|
||||
chart.current?.redraw(false)
|
||||
}, [resolvedTheme])
|
||||
|
||||
return (
|
||||
@@ -251,9 +262,13 @@ export function UplotChart({
|
||||
<div className="relative min-h-0 flex-1">
|
||||
<div ref={host} className="absolute inset-0" />
|
||||
{points === 0 ? (
|
||||
<p className="absolute inset-0 flex items-center justify-center text-sm text-muted-foreground">
|
||||
{empty}
|
||||
</p>
|
||||
pending ? (
|
||||
<Skeleton className="absolute inset-0" />
|
||||
) : (
|
||||
<p className="absolute inset-0 flex items-center justify-center text-sm text-muted-foreground">
|
||||
{empty}
|
||||
</p>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
{/* Kept at the legend's resting height, so the plot does not resize
|
||||
|
||||
@@ -100,11 +100,13 @@ function Chart({
|
||||
title,
|
||||
labels,
|
||||
plots,
|
||||
pending,
|
||||
moment,
|
||||
}: {
|
||||
title: string
|
||||
labels: string[]
|
||||
plots: HistoryPoint[][]
|
||||
pending: boolean
|
||||
moment: Moment
|
||||
}) {
|
||||
return (
|
||||
@@ -127,6 +129,7 @@ function Chart({
|
||||
labels={labels}
|
||||
plots={plots}
|
||||
empty="Nothing has run yet."
|
||||
pending={pending}
|
||||
onCursor={moment.onCursor}
|
||||
onSelect={moment.onSelect}
|
||||
/>
|
||||
@@ -205,7 +208,9 @@ export function HealthActivity({ range }: { range: Range }) {
|
||||
const runsAt = useMoment()
|
||||
const failuresAt = useMoment()
|
||||
|
||||
const { data: series } = useQuery(timeseriesQueryOptions(range))
|
||||
const { data: series, isPending: seriesPending } = useQuery(
|
||||
timeseriesQueryOptions(range),
|
||||
)
|
||||
const { data: runs } = useQuery(runsQueryOptions(range))
|
||||
const { data: pinnedRuns } = useQuery(minuteRunsQueryOptions(runsAt.pinned))
|
||||
const { data: failures } = useQuery(failuresQueryOptions(range))
|
||||
@@ -267,6 +272,7 @@ export function HealthActivity({ range }: { range: Range }) {
|
||||
at((point) => point.messages),
|
||||
at((point) => point.executions),
|
||||
]}
|
||||
pending={seriesPending}
|
||||
moment={runsAt}
|
||||
/>
|
||||
<Chart
|
||||
@@ -277,6 +283,7 @@ export function HealthActivity({ range }: { range: Range }) {
|
||||
at((point) => point.avg_ms),
|
||||
at((point) => point.avg_lag_ms),
|
||||
]}
|
||||
pending={seriesPending}
|
||||
moment={failuresAt}
|
||||
/>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user