Skip to content

Commit f5428fc

Browse files
committed
Fix Vercel tests and chart markers
1 parent f8cf846 commit f5428fc

3 files changed

Lines changed: 150 additions & 32 deletions

File tree

apps/www/vitest.config.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,29 @@ import config from "@repo/testing";
44
import { mergeConfig } from "vitest/config";
55

66
const __dirname = path.dirname(fileURLToPath(import.meta.url));
7+
const nodeTestFiles = [
8+
"app/api/**/*.test.ts",
9+
"app/mcp/**/*.test.ts",
10+
"lib/llms/**/*.test.ts",
11+
"lib/sitemap/**/*.test.ts",
12+
"lib/utils/seo/**/*.test.ts",
13+
"scripts/**/*.test.ts",
14+
];
15+
const jsdomTestFiles = [
16+
"__tests__/**/*.test.ts",
17+
"components/**/*.test.ts",
18+
"lib/auth/**/*.test.ts",
19+
"lib/store/**/*.test.ts",
20+
"lib/utils/__tests__/**/*.test.ts",
21+
];
722

823
/**
924
* Keep this config aligned with the shared frontend Vitest baseline.
10-
* Do not weaken coverage or add app-local execution overrides here.
25+
* Vitest 4 removed environmentMatchGlobs; projects are the documented
26+
* way to keep Node-only tests out of jsdom's browser-compatible module graph.
27+
*
28+
* @see https://vitest.dev/guide/projects
29+
* @see https://vite.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility
1130
*/
1231
export default mergeConfig(config, {
1332
resolve: {
@@ -19,6 +38,25 @@ export default mergeConfig(config, {
1938
test: {
2039
/** Prepare the React test environment before each suite loads. */
2140
setupFiles: ["./vitest.setup.ts"],
41+
projects: [
42+
{
43+
extends: true,
44+
test: {
45+
environment: "node",
46+
include: nodeTestFiles,
47+
name: "node",
48+
setupFiles: [],
49+
},
50+
},
51+
{
52+
extends: true,
53+
test: {
54+
environment: "jsdom",
55+
include: jsdomTestFiles,
56+
name: "jsdom",
57+
},
58+
},
59+
],
2260
coverage: {
2361
thresholds: {
2462
100: true,

packages/design-system/components/contents/mathematics/scatter-diagram.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ export function ScatterDiagram({
136136
/>
137137
<ChartTooltip
138138
content={<ChartTooltipContent hideLabel={true} />}
139-
shared={false}
140139
wrapperStyle={{ visibility: "hidden" }}
141140
/>
142141
{datasets.map((dataset) => (
@@ -180,7 +179,9 @@ export function ScatterDiagram({
180179
);
181180
})
182181
)}
183-
<ChartLegend content={<ChartLegendContent className="mt-6" />} />
182+
<ChartLegend
183+
content={<ChartLegendContent className="mt-6" variant="circle" />}
184+
/>
184185
</ComposedChart>
185186
</ChartContainer>
186187
</CardContent>

packages/design-system/components/ui/chart.tsx

Lines changed: 108 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ type ChartColors = {
3838
type LegendPayloadEntry = NonNullable<
3939
DefaultLegendContentProps["payload"]
4040
>[number];
41+
type ChartLegendVariant =
42+
| "circle"
43+
| "circle-outline"
44+
| "horizontal-bar"
45+
| "rounded-square"
46+
| "rounded-square-outline"
47+
| "square"
48+
| "vertical-bar";
4149
type PayloadEntry = LegendPayloadEntry | TooltipPayloadEntry;
4250

4351
export type ChartConfig = Record<
@@ -329,10 +337,12 @@ function ChartLegendContent({
329337
payload,
330338
verticalAlign = "bottom",
331339
nameKey,
340+
variant = "rounded-square",
332341
}: ComponentProps<"div"> &
333342
DefaultLegendContentProps & {
334343
hideIcon?: boolean;
335344
nameKey?: string;
345+
variant?: ChartLegendVariant;
336346
}) {
337347
const { config } = useChart();
338348

@@ -348,39 +358,78 @@ function ChartLegendContent({
348358
className
349359
)}
350360
>
351-
{payload.map((item) => {
352-
const key = `${nameKey || item.value || item.dataKey || "value"}`;
353-
const configKey = getPayloadConfigKey(config, item, key);
354-
const itemConfig = configKey ? config[configKey] : undefined;
355-
const indicatorStyle = getLegendIndicatorStyle(
356-
item,
357-
configKey || key,
358-
itemConfig
359-
);
360-
361-
return (
362-
<div
363-
className={cn(
364-
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
365-
)}
366-
key={key}
367-
>
368-
{itemConfig?.icon && !hideIcon ? (
369-
<itemConfig.icon />
370-
) : (
371-
<div
372-
className="h-2 w-2 shrink-0 rounded-[2px]"
373-
style={indicatorStyle}
374-
/>
375-
)}
376-
{itemConfig?.label || item.value}
377-
</div>
378-
);
379-
})}
361+
{payload
362+
.filter((item) => item.type !== "none")
363+
.map((item) => {
364+
const key = `${nameKey || item.value || item.dataKey || "value"}`;
365+
const configKey = getPayloadConfigKey(config, item, key);
366+
const itemConfig = configKey ? config[configKey] : undefined;
367+
const dataKey = configKey || key;
368+
let indicatorStyle = getLegendIndicatorStyle(
369+
item,
370+
dataKey,
371+
itemConfig
372+
);
373+
374+
if (
375+
variant === "circle-outline" ||
376+
variant === "rounded-square-outline"
377+
) {
378+
indicatorStyle = getLegendOutlineStyle(item, dataKey, itemConfig);
379+
}
380+
381+
return (
382+
<div
383+
className={cn(
384+
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
385+
)}
386+
key={key}
387+
>
388+
{itemConfig?.icon && !hideIcon ? (
389+
<itemConfig.icon />
390+
) : (
391+
<div
392+
className={getLegendIndicatorClassName(variant)}
393+
style={indicatorStyle}
394+
/>
395+
)}
396+
{itemConfig?.label || item.value}
397+
</div>
398+
);
399+
})}
380400
</div>
381401
);
382402
}
383403

404+
/** Matches EvilCharts legend variants while keeping Recharts payload support. */
405+
function getLegendIndicatorClassName(variant: ChartLegendVariant) {
406+
if (variant === "circle") {
407+
return "h-2 w-2 shrink-0 rounded-full";
408+
}
409+
410+
if (variant === "circle-outline") {
411+
return "h-2.5 w-2.5 shrink-0 rounded-full p-[1.5px]";
412+
}
413+
414+
if (variant === "horizontal-bar") {
415+
return "h-1 w-3 shrink-0 rounded-[2px]";
416+
}
417+
418+
if (variant === "square") {
419+
return "h-2 w-2 shrink-0 rounded-none";
420+
}
421+
422+
if (variant === "vertical-bar") {
423+
return "h-3 w-1 shrink-0 rounded-[2px]";
424+
}
425+
426+
if (variant === "rounded-square-outline") {
427+
return "h-2.5 w-2.5 shrink-0 rounded-[3px] p-[1.5px]";
428+
}
429+
430+
return "h-2 w-2 shrink-0 rounded-[2px]";
431+
}
432+
384433
/** Finds the chart config entry that matches a Recharts payload item. */
385434
function getPayloadConfigFromPayload(
386435
config: ChartConfig,
@@ -557,6 +606,36 @@ function getLegendIndicatorStyle(
557606
};
558607
}
559608

609+
/** Uses the EvilCharts mask approach so outline legend variants can show gradients. */
610+
function getLegendOutlineStyle(
611+
payload: LegendPayloadEntry,
612+
dataKey: string,
613+
config: ChartConfig[string] | undefined
614+
) {
615+
const maskStyle = {
616+
WebkitMask:
617+
"linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)",
618+
WebkitMaskComposite: "xor",
619+
mask: "linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)",
620+
maskComposite: "exclude",
621+
};
622+
623+
if (config?.colors) {
624+
return {
625+
...getIndicatorStyle(dataKey, getColorsCount(config)),
626+
...maskStyle,
627+
};
628+
}
629+
630+
const color = getPayloadColor(payload);
631+
632+
return {
633+
background: color,
634+
borderColor: color,
635+
...maskStyle,
636+
};
637+
}
638+
560639
/** Uses config color slots when available, otherwise Recharts payload color. */
561640
function getTooltipIndicatorStyle(
562641
payload: TooltipPayloadEntry,

0 commit comments

Comments
 (0)