From 5d907b0449c7d5df940ae691885318a5b6f8a84c Mon Sep 17 00:00:00 2001 From: imagineer99 Date: Thu, 5 Mar 2026 21:54:27 +0000 Subject: [PATCH] fix: guard recharts ResponsiveContainer behind measured container dimensions --- studio/frontend/src/components/ui/chart.tsx | 116 ++++++++++++------ .../studio/sections/training-section.tsx | 2 +- 2 files changed, 81 insertions(+), 37 deletions(-) diff --git a/studio/frontend/src/components/ui/chart.tsx b/studio/frontend/src/components/ui/chart.tsx index 071148982f..341f0b8fc1 100644 --- a/studio/frontend/src/components/ui/chart.tsx +++ b/studio/frontend/src/components/ui/chart.tsx @@ -46,22 +46,66 @@ function ChartContainer({ }) { const uniqueId = React.useId(); const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`; + const containerRef = React.useRef(null); + const [containerSize, setContainerSize] = React.useState<{ + width: number; + height: number; + } | null>(null); + + React.useEffect(() => { + const element = containerRef.current; + if (!element) return; + + const updateSizeState = () => { + const { width, height } = element.getBoundingClientRect(); + if (width > 0 && height > 0) { + setContainerSize({ + width: Math.round(width), + height: Math.round(height), + }); + return; + } + setContainerSize(null); + }; + + updateSizeState(); + + if (typeof ResizeObserver === "undefined") { + return; + } + + const observer = new ResizeObserver(() => { + updateSizeState(); + }); + observer.observe(element); + + return () => observer.disconnect(); + }, []); return (
- - {children} - + {containerSize ? ( + + {children} + + ) : null}
); @@ -100,30 +144,30 @@ ${colorConfig ); }; -const ChartTooltip = RechartsPrimitive.Tooltip; - -function ChartTooltipContent({ - active, - payload, - className, +const ChartTooltip = RechartsPrimitive.Tooltip; + +function ChartTooltipContent({ + active, + payload, + className, indicator = "dot", hideLabel = false, hideIndicator = false, label, labelFormatter, labelClassName, - formatter, - color, - nameKey, - labelKey, -}: Partial> & - React.ComponentProps<"div"> & { - hideLabel?: boolean; - hideIndicator?: boolean; - indicator?: "line" | "dot" | "dashed"; - nameKey?: string; - labelKey?: string; - }) { + formatter, + color, + nameKey, + labelKey, +}: Partial> & + React.ComponentProps<"div"> & { + hideLabel?: boolean; + hideIndicator?: boolean; + indicator?: "line" | "dot" | "dashed"; + nameKey?: string; + labelKey?: string; + }) { const { config } = useChart(); const tooltipLabel = React.useMemo(() => { @@ -248,20 +292,20 @@ function ChartTooltipContent({ ); } -const ChartLegend = RechartsPrimitive.Legend; - -function ChartLegendContent({ - className, - hideIcon = false, - payload, - verticalAlign = "bottom", - nameKey, -}: React.ComponentProps<"div"> & - Pick & { - hideIcon?: boolean; - nameKey?: string; - }) { - const { config } = useChart(); +const ChartLegend = RechartsPrimitive.Legend; + +function ChartLegendContent({ + className, + hideIcon = false, + payload, + verticalAlign = "bottom", + nameKey, +}: React.ComponentProps<"div"> & + Pick & { + hideIcon?: boolean; + nameKey?: string; + }) { + const { config } = useChart(); if (!payload?.length) { return null; diff --git a/studio/frontend/src/features/studio/sections/training-section.tsx b/studio/frontend/src/features/studio/sections/training-section.tsx index af6a16fc44..2f99bea2b7 100644 --- a/studio/frontend/src/features/studio/sections/training-section.tsx +++ b/studio/frontend/src/features/studio/sections/training-section.tsx @@ -105,7 +105,7 @@ export function TrainingSection() {