• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stacklok / codegate-ui / 12791977239

15 Jan 2025 03:48PM UTC coverage: 69.536%. Remained the same
12791977239

Pull #50

github

web-flow
Merge bb214a870 into 989f4bc41
Pull Request #50: Bump @tailwindcss/typography from 0.5.15 to 0.5.16

209 of 380 branches covered (55.0%)

Branch coverage included in aggregate %.

421 of 526 relevant lines covered (80.04%)

32.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

29.61
/src/components/ui/chart.tsx
1
import * as React from "react";
2
import * as RechartsPrimitive from "recharts";
3

4
import { cn } from "@/lib/utils";
5

6
// Format: { THEME_NAME: CSS_SELECTOR }
7
const THEMES = { light: "", dark: ".dark" } as const;
2✔
8

9
export type ChartConfig = {
10
  [k in string]: {
11
    label?: React.ReactNode;
12
    icon?: React.ComponentType;
13
  } & (
14
    | { color?: string; theme?: never }
15
    | { color?: never; theme: Record<keyof typeof THEMES, string> }
16
  );
17
};
18

19
type ChartContextProps = {
20
  config: ChartConfig;
21
};
22

23
const ChartContext = React.createContext<ChartContextProps | null>(null);
2✔
24

25
function useChart() {
26
  const context = React.useContext(ChartContext);
90✔
27

28
  if (!context) {
90!
29
    throw new Error("useChart must be used within a <ChartContainer />");
×
30
  }
31

32
  return context;
90✔
33
}
34

35
const ChartContainer = React.forwardRef<
2✔
36
  HTMLDivElement,
37
  React.ComponentProps<"div"> & {
38
    config: ChartConfig;
39
    children: React.ComponentProps<
40
      typeof RechartsPrimitive.ResponsiveContainer
41
    >["children"];
42
  }
43
>(({ id, className, children, config, ...props }, ref) => {
44
  const uniqueId = React.useId();
90✔
45
  const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
90✔
46

47
  return (
48
    <ChartContext.Provider value={{ config }}>
49
      <div
50
        data-chart={chartId}
51
        ref={ref}
52
        className={cn(
53
          "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
54
          className,
55
        )}
56
        {...props}
57
      >
58
        <ChartStyle id={chartId} config={config} />
59
        <RechartsPrimitive.ResponsiveContainer>
60
          {children}
61
        </RechartsPrimitive.ResponsiveContainer>
62
      </div>
63
    </ChartContext.Provider>
64
  );
65
});
66
ChartContainer.displayName = "Chart";
2✔
67

68
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
2✔
69
  const colorConfig = Object.entries(config).filter(
90✔
70
    ([, config]) => config.theme || config.color,
90✔
71
  );
72

73
  if (!colorConfig.length) {
90!
74
    return null;
×
75
  }
76

77
  return (
78
    <style
79
      dangerouslySetInnerHTML={{
80
        __html: Object.entries(THEMES)
81
          .map(
82
            ([theme, prefix]) => `
180✔
83
${prefix} [data-chart=${id}] {
84
${colorConfig
85
  .map(([key, itemConfig]) => {
86
    const color =
87
      itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
180✔
88
      itemConfig.color;
89
    return color ? `  --color-${key}: ${color};` : null;
180!
90
  })
91
  .join("\n")}
92
}
93
`,
94
          )
95
          .join("\n"),
96
      }}
97
    />
98
  );
99
};
100

101
const ChartTooltip = RechartsPrimitive.Tooltip;
2✔
102

103
const ChartTooltipContent = React.forwardRef<
2✔
104
  HTMLDivElement,
105
  React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
106
    React.ComponentProps<"div"> & {
107
      hideLabel?: boolean;
108
      hideIndicator?: boolean;
109
      indicator?: "line" | "dot" | "dashed";
110
      nameKey?: string;
111
      labelKey?: string;
112
    }
113
>(
114
  (
115
    {
116
      active,
117
      payload,
118
      className,
119
      indicator = "dot",
41✔
120
      hideLabel = false,
49✔
121
      hideIndicator = false,
90✔
122
      label,
123
      labelFormatter,
124
      labelClassName,
125
      formatter,
126
      color,
127
      nameKey,
128
      labelKey,
129
    },
130
    ref,
131
  ) => {
132
    const { config } = useChart();
90✔
133

134
    const tooltipLabel = React.useMemo(() => {
90✔
135
      if (hideLabel || !payload?.length) {
90!
136
        return null;
90✔
137
      }
138

139
      const [item] = payload;
×
140
      const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
×
141
      const itemConfig = getPayloadConfigFromPayload(config, item, key);
×
142
      const value =
143
        !labelKey && typeof label === "string"
×
144
          ? config[label as keyof typeof config]?.label || label
×
145
          : itemConfig?.label;
146

147
      if (labelFormatter) {
×
148
        return (
149
          <div className={cn("font-medium", labelClassName)}>
150
            {labelFormatter(value, payload)}
151
          </div>
152
        );
153
      }
154

155
      if (!value) {
×
156
        return null;
×
157
      }
158

159
      return <div className={cn("font-medium", labelClassName)}>{value}</div>;
160
    }, [
161
      label,
162
      labelFormatter,
163
      payload,
164
      hideLabel,
165
      labelClassName,
166
      config,
167
      labelKey,
168
    ]);
169

170
    if (!active || !payload?.length) {
90!
171
      return null;
90✔
172
    }
173

174
    const nestLabel = payload.length === 1 && indicator !== "dot";
×
175

176
    return (
177
      <div
178
        ref={ref}
179
        className={cn(
180
          "grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
181
          className,
182
        )}
183
      >
184
        {!nestLabel ? tooltipLabel : null}
×
185
        <div className="grid gap-1.5">
186
          {payload.map((item, index) => {
187
            const key = `${nameKey || item.name || item.dataKey || "value"}`;
×
188
            const itemConfig = getPayloadConfigFromPayload(config, item, key);
×
189
            const indicatorColor = color || item.payload.fill || item.color;
×
190

191
            return (
192
              <div
193
                key={item.dataKey}
194
                className={cn(
195
                  "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
196
                  indicator === "dot" && "items-center",
×
197
                )}
198
              >
199
                {formatter && item?.value !== undefined && item.name ? (
×
200
                  formatter(item.value, item.name, item, index, item.payload)
×
201
                ) : (
202
                  <>
203
                    {itemConfig?.icon ? (
204
                      <itemConfig.icon />
×
205
                    ) : (
206
                      !hideIndicator && (
×
207
                        <div
208
                          className={cn(
209
                            "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
210
                            {
211
                              "h-2.5 w-2.5": indicator === "dot",
212
                              "w-1": indicator === "line",
213
                              "w-0 border-[1.5px] border-dashed bg-transparent":
214
                                indicator === "dashed",
215
                              "my-0.5": nestLabel && indicator === "dashed",
×
216
                            },
217
                          )}
218
                          style={
219
                            {
220
                              "--color-bg": indicatorColor,
221
                              "--color-border": indicatorColor,
222
                            } as React.CSSProperties
223
                          }
224
                        />
225
                      )
226
                    )}
227
                    <div
228
                      className={cn(
229
                        "flex flex-1 justify-between leading-none",
230
                        nestLabel ? "items-end" : "items-center",
×
231
                      )}
232
                    >
233
                      <div className="grid gap-1.5">
234
                        {nestLabel ? tooltipLabel : null}
×
235
                        <span className="text-muted-foreground">
236
                          {itemConfig?.label || item.name}
×
237
                        </span>
238
                      </div>
239
                      {item.value && (
×
240
                        <span className="font-mono font-medium tabular-nums text-foreground">
241
                          {item.value.toLocaleString()}
242
                        </span>
243
                      )}
244
                    </div>
245
                  </>
246
                )}
247
              </div>
248
            );
249
          })}
250
        </div>
251
      </div>
252
    );
253
  },
254
);
255
ChartTooltipContent.displayName = "ChartTooltip";
2✔
256

257
const ChartLegend = RechartsPrimitive.Legend;
2✔
258

259
const ChartLegendContent = React.forwardRef<
2✔
260
  HTMLDivElement,
261
  React.ComponentProps<"div"> &
262
    Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
263
      hideIcon?: boolean;
264
      nameKey?: string;
265
    }
266
>(
267
  (
268
    { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
×
269
    ref,
270
  ) => {
271
    const { config } = useChart();
×
272

273
    if (!payload?.length) {
×
274
      return null;
×
275
    }
276

277
    return (
278
      <div
279
        ref={ref}
280
        className={cn(
281
          "flex items-center justify-center gap-4",
282
          verticalAlign === "top" ? "pb-3" : "pt-3",
×
283
          className,
284
        )}
285
      >
286
        {payload.map((item) => {
287
          const key = `${nameKey || item.dataKey || "value"}`;
×
288
          const itemConfig = getPayloadConfigFromPayload(config, item, key);
×
289

290
          return (
291
            <div
292
              key={item.value}
293
              className={cn(
294
                "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground",
295
              )}
296
            >
297
              {itemConfig?.icon && !hideIcon ? (
×
298
                <itemConfig.icon />
×
299
              ) : (
300
                <div
301
                  className="h-2 w-2 shrink-0 rounded-[2px]"
302
                  style={{
303
                    backgroundColor: item.color,
304
                  }}
305
                />
306
              )}
307
              {itemConfig?.label}
308
            </div>
309
          );
310
        })}
311
      </div>
312
    );
313
  },
314
);
315
ChartLegendContent.displayName = "ChartLegend";
2✔
316

317
// Helper to extract item config from a payload.
318
function getPayloadConfigFromPayload(
319
  config: ChartConfig,
320
  payload: unknown,
321
  key: string,
322
) {
323
  if (typeof payload !== "object" || payload === null) {
×
324
    return undefined;
×
325
  }
326

327
  const payloadPayload =
328
    "payload" in payload &&
×
329
    typeof payload.payload === "object" &&
330
    payload.payload !== null
331
      ? payload.payload
332
      : undefined;
333

334
  let configLabelKey: string = key;
×
335

336
  if (
×
337
    key in payload &&
×
338
    typeof payload[key as keyof typeof payload] === "string"
339
  ) {
340
    configLabelKey = payload[key as keyof typeof payload] as string;
×
341
  } else if (
×
342
    payloadPayload &&
×
343
    key in payloadPayload &&
344
    typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
345
  ) {
346
    configLabelKey = payloadPayload[
×
347
      key as keyof typeof payloadPayload
348
    ] as string;
349
  }
350

351
  return configLabelKey in config
×
352
    ? config[configLabelKey]
353
    : config[key as keyof typeof config];
354
}
355

356
export {
357
  ChartContainer,
358
  ChartTooltip,
359
  ChartTooltipContent,
360
  ChartLegend,
361
  ChartLegendContent,
362
  ChartStyle,
363
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc