From 5da6b40eae48e54d3bba7975100fd7114b9dfd75 Mon Sep 17 00:00:00 2001 From: MickLesk Date: Tue, 16 Dec 2025 20:19:52 +0100 Subject: [PATCH] fix: Add missing TypeScript props to ChartTooltipContent component --- frontend/src/components/ui/chart.tsx | 309 +++++++++++---------------- 1 file changed, 127 insertions(+), 182 deletions(-) diff --git a/frontend/src/components/ui/chart.tsx b/frontend/src/components/ui/chart.tsx index 02ce32ccf..5ca77a1ad 100644 --- a/frontend/src/components/ui/chart.tsx +++ b/frontend/src/components/ui/chart.tsx @@ -1,50 +1,45 @@ -"use client" +"use client"; -import * as React from "react" -import * as RechartsPrimitive from "recharts" +import * as React from "react"; +import * as RechartsPrimitive from "recharts"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; // Format: { THEME_NAME: CSS_SELECTOR } -const THEMES = { light: "", dark: ".dark" } as const +const THEMES = { light: "", dark: ".dark" } as const; export type ChartConfig = { [k in string]: { - label?: React.ReactNode - icon?: React.ComponentType - } & ( - | { color?: string; theme?: never } - | { color?: never; theme: Record } - ) -} + label?: React.ReactNode; + icon?: React.ComponentType; + } & ({ color?: string; theme?: never } | { color?: never; theme: Record }); +}; type ChartContextProps = { - config: ChartConfig -} + config: ChartConfig; +}; -const ChartContext = React.createContext(null) +const ChartContext = React.createContext(null); function useChart() { - const context = React.useContext(ChartContext) + const context = React.useContext(ChartContext); if (!context) { - throw new Error("useChart must be used within a ") + throw new Error("useChart must be used within a "); } - return context + return context; } const ChartContainer = React.forwardRef< HTMLDivElement, React.ComponentProps<"div"> & { - config: ChartConfig - children: React.ComponentProps< - typeof RechartsPrimitive.ResponsiveContainer - >["children"] + config: ChartConfig; + children: React.ComponentProps["children"]; } >(({ id, className, children, config, ...props }, ref) => { - const uniqueId = React.useId() - const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` + const uniqueId = React.useId(); + const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`; return ( @@ -58,22 +53,18 @@ const ChartContainer = React.forwardRef< {...props} > - - {children} - + {children} - ) -}) -ChartContainer.displayName = "Chart" + ); +}); +ChartContainer.displayName = "Chart"; const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { - const colorConfig = Object.entries(config).filter( - ([, config]) => config.theme || config.color - ) + const colorConfig = Object.entries(config).filter(([, config]) => config.theme || config.color); if (!colorConfig.length) { - return null + return null; } return ( @@ -85,10 +76,8 @@ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { ${prefix} [data-chart=${id}] { ${colorConfig .map(([key, itemConfig]) => { - const color = - itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || - itemConfig.color - return color ? ` --color-${key}: ${color};` : null + const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color; + return color ? ` --color-${key}: ${color};` : null; }) .join("\n")} } @@ -97,31 +86,36 @@ ${colorConfig .join("\n"), }} /> - ) -} + ); +}; -const ChartTooltip = RechartsPrimitive.Tooltip +const ChartTooltip = RechartsPrimitive.Tooltip; type TooltipPayloadItem = { - value?: string | number - name?: string - dataKey?: string | number - payload?: Record - color?: string - fill?: string -} + value?: string | number; + name?: string; + dataKey?: string | number; + payload?: Record; + color?: string; + fill?: string; +}; const ChartTooltipContent = React.forwardRef< HTMLDivElement, Omit, "content"> & React.ComponentProps<"div"> & { - active?: boolean - payload?: TooltipPayloadItem[] - hideLabel?: boolean - hideIndicator?: boolean - indicator?: "line" | "dot" | "dashed" - nameKey?: string - labelKey?: string + active?: boolean; + payload?: TooltipPayloadItem[]; + hideLabel?: boolean; + hideIndicator?: boolean; + indicator?: "line" | "dot" | "dashed"; + nameKey?: string; + labelKey?: string; + label?: string; + labelFormatter?: (value: any, payload: TooltipPayloadItem[]) => React.ReactNode; + labelClassName?: string; + formatter?: (value: any, name: string, item: TooltipPayloadItem, index: number, payload: any) => React.ReactNode; + color?: string; } >( ( @@ -142,49 +136,37 @@ const ChartTooltipContent = React.forwardRef< }, ref ) => { - const { config } = useChart() + const { config } = useChart(); const tooltipLabel = React.useMemo(() => { if (hideLabel || !payload?.length) { - return null + return null; } - const [item] = payload - const key = `${labelKey || item?.dataKey || item?.name || "value"}` - const itemConfig = getPayloadConfigFromPayload(config, item, key) + const [item] = payload; + const key = `${labelKey || item?.dataKey || item?.name || "value"}`; + const itemConfig = getPayloadConfigFromPayload(config, item, key); const value = !labelKey && typeof label === "string" ? config[label as keyof typeof config]?.label || label - : itemConfig?.label + : itemConfig?.label; if (labelFormatter) { - return ( -
- {labelFormatter(value, payload)} -
- ) + return
{labelFormatter(value, payload)}
; } if (!value) { - return null + return null; } - return
{value}
- }, [ - label, - labelFormatter, - payload, - hideLabel, - labelClassName, - config, - labelKey, - ]) + return
{value}
; + }, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]); if (!active || !payload?.length) { - return null + return null; } - const nestLabel = payload.length === 1 && indicator !== "dot" + const nestLabel = payload.length === 1 && indicator !== "dot"; return (
item.type !== "none") .map((item, index) => { - const key = `${nameKey || item.name || item.dataKey || "value"}` - const itemConfig = getPayloadConfigFromPayload(config, item, key) - const indicatorColor = color || item.payload.fill || item.color + const key = `${nameKey || item.name || item.dataKey || "value"}`; + const itemConfig = getPayloadConfigFromPayload(config, item, key); + const indicatorColor = color || item.payload.fill || item.color; return (
{nestLabel ? tooltipLabel : null} - - {itemConfig?.label || item.name} - + {itemConfig?.label || item.name}
{item.value && ( @@ -260,121 +236,90 @@ const ChartTooltipContent = React.forwardRef< )}
- ) + ); })}
- ) + ); } -) -ChartTooltipContent.displayName = "ChartTooltip" +); +ChartTooltipContent.displayName = "ChartTooltip"; -const ChartLegend = RechartsPrimitive.Legend +const ChartLegend = RechartsPrimitive.Legend; const ChartLegendContent = React.forwardRef< HTMLDivElement, React.ComponentProps<"div"> & Pick & { - hideIcon?: boolean - nameKey?: string + hideIcon?: boolean; + nameKey?: string; } ->( - ( - { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, - ref - ) => { - const { config } = useChart() +>(({ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, ref) => { + const { config } = useChart(); - if (!payload?.length) { - return null - } - - return ( -
- {payload - .filter((item) => item.type !== "none") - .map((item) => { - const key = `${nameKey || item.dataKey || "value"}` - const itemConfig = getPayloadConfigFromPayload(config, item, key) - - return ( -
svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground" - )} - > - {itemConfig?.icon && !hideIcon ? ( - - ) : ( -
- )} - {itemConfig?.label} -
- ) - })} -
- ) + if (!payload?.length) { + return null; } -) -ChartLegendContent.displayName = "ChartLegend" + + return ( +
+ {payload + .filter((item) => item.type !== "none") + .map((item) => { + const key = `${nameKey || item.dataKey || "value"}`; + const itemConfig = getPayloadConfigFromPayload(config, item, key); + + return ( +
svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground")} + > + {itemConfig?.icon && !hideIcon ? ( + + ) : ( +
+ )} + {itemConfig?.label} +
+ ); + })} +
+ ); +}); +ChartLegendContent.displayName = "ChartLegend"; // Helper to extract item config from a payload. -function getPayloadConfigFromPayload( - config: ChartConfig, - payload: unknown, - key: string -) { +function getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) { if (typeof payload !== "object" || payload === null) { - return undefined + return undefined; } const payloadPayload = - "payload" in payload && - typeof payload.payload === "object" && - payload.payload !== null + "payload" in payload && typeof payload.payload === "object" && payload.payload !== null ? payload.payload - : undefined + : undefined; - let configLabelKey: string = key + let configLabelKey: string = key; - if ( - key in payload && - typeof payload[key as keyof typeof payload] === "string" - ) { - configLabelKey = payload[key as keyof typeof payload] as string + if (key in payload && typeof payload[key as keyof typeof payload] === "string") { + configLabelKey = payload[key as keyof typeof payload] as string; } else if ( payloadPayload && key in payloadPayload && typeof payloadPayload[key as keyof typeof payloadPayload] === "string" ) { - configLabelKey = payloadPayload[ - key as keyof typeof payloadPayload - ] as string + configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string; } - return configLabelKey in config - ? config[configLabelKey] - : config[key as keyof typeof config] + return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config]; } -export { - ChartContainer, - ChartTooltip, - ChartTooltipContent, - ChartLegend, - ChartLegendContent, - ChartStyle, -} +export { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle };