chore: remove chartjs (#18016)

- Remove ChartJS in favor of Recharts
- Migrate ActiveUserChart to use the new chart design

<img width="1624" alt="Screenshot 2025-05-23 at 15 00 03"
src="https://github.com/user-attachments/assets/5f451a88-f2ef-4139-a888-c0358eb8cf17"
/>
This commit is contained in:
Bruno Quaresma
2025-05-24 11:33:16 -03:00
committed by GitHub
parent 196eccb457
commit 70edc2403a
6 changed files with 119 additions and 157 deletions
+1 -3
View File
@@ -40,9 +40,7 @@ module.exports = {
// can see many act warnings that may can help us to find the issue.
"/usePaginatedQuery.test.ts",
],
transformIgnorePatterns: [
"<rootDir>/node_modules/@chartjs-adapter-date-fns",
],
transformIgnorePatterns: [],
moduleDirectories: ["node_modules", "<rootDir>/src"],
moduleNameMapper: {
"\\.css$": "<rootDir>/src/testHelpers/styleMock.ts",
-3
View File
@@ -78,8 +78,6 @@
"@xterm/xterm": "5.5.0",
"ansi-to-html": "0.7.2",
"axios": "1.8.2",
"chart.js": "4.4.0",
"chartjs-adapter-date-fns": "3.0.0",
"chroma-js": "2.4.2",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
@@ -99,7 +97,6 @@
"monaco-editor": "0.52.0",
"pretty-bytes": "6.1.1",
"react": "18.3.1",
"react-chartjs-2": "5.3.0",
"react-color": "2.19.3",
"react-confetti": "6.2.2",
"react-date-range": "1.4.0",
-44
View File
@@ -145,12 +145,6 @@ importers:
axios:
specifier: 1.8.2
version: 1.8.2
chart.js:
specifier: 4.4.0
version: 4.4.0
chartjs-adapter-date-fns:
specifier: 3.0.0
version: 3.0.0(chart.js@4.4.0)(date-fns@2.30.0)
chroma-js:
specifier: 2.4.2
version: 2.4.2
@@ -208,9 +202,6 @@ importers:
react:
specifier: 18.3.1
version: 18.3.1
react-chartjs-2:
specifier: 5.3.0
version: 5.3.0(chart.js@4.4.0)(react@18.3.1)
react-color:
specifier: 2.19.3
version: 2.19.3(react@18.3.1)
@@ -1253,9 +1244,6 @@ packages:
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==, tarball: https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz}
'@kurkle/color@0.3.2':
resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==, tarball: https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz}
'@leeoniya/ufuzzy@1.0.10':
resolution: {integrity: sha512-OR1yiyN8cKBn5UiHjKHUl0LcrTQt4vZPUpIf96qIIZVLxgd4xyASuRvTZ3tjbWvuyQAMgvKsq61Nwu131YyHnA==, tarball: https://registry.npmjs.org/@leeoniya/ufuzzy/-/ufuzzy-1.0.10.tgz}
@@ -3122,16 +3110,6 @@ packages:
character-reference-invalid@2.0.1:
resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==, tarball: https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz}
chart.js@4.4.0:
resolution: {integrity: sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==, tarball: https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz}
engines: {pnpm: '>=7'}
chartjs-adapter-date-fns@3.0.0:
resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==, tarball: https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz}
peerDependencies:
chart.js: '>=2.8.0'
date-fns: '>=2.0.0'
check-error@2.1.1:
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==, tarball: https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz}
engines: {node: '>= 16'}
@@ -5306,12 +5284,6 @@ packages:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==, tarball: https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz}
engines: {node: '>= 0.8'}
react-chartjs-2@5.3.0:
resolution: {integrity: sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==, tarball: https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz}
peerDependencies:
chart.js: ^4.1.1
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-color@2.19.3:
resolution: {integrity: sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==, tarball: https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz}
peerDependencies:
@@ -7362,8 +7334,6 @@ snapshots:
'@jridgewell/sourcemap-codec': 1.5.0
optional: true
'@kurkle/color@0.3.2': {}
'@leeoniya/ufuzzy@1.0.10': {}
'@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1)':
@@ -9356,15 +9326,6 @@ snapshots:
character-reference-invalid@2.0.1: {}
chart.js@4.4.0:
dependencies:
'@kurkle/color': 0.3.2
chartjs-adapter-date-fns@3.0.0(chart.js@4.4.0)(date-fns@2.30.0):
dependencies:
chart.js: 4.4.0
date-fns: 2.30.0
check-error@2.1.1: {}
chokidar@3.6.0:
@@ -12148,11 +12109,6 @@ snapshots:
iconv-lite: 0.4.24
unpipe: 1.0.0
react-chartjs-2@5.3.0(chart.js@4.4.0)(react@18.3.1):
dependencies:
chart.js: 4.4.0
react: 18.3.1
react-color@2.19.3(react@18.3.1):
dependencies:
'@icons/material': 0.2.4(react@18.3.1)
@@ -6,19 +6,37 @@ const meta: Meta<typeof ActiveUserChart> = {
component: ActiveUserChart,
args: {
data: [
{ date: "1/1/2024", amount: 5 },
{ date: "1/2/2024", amount: 6 },
{ date: "1/3/2024", amount: 7 },
{ date: "1/4/2024", amount: 8 },
{ date: "1/5/2024", amount: 9 },
{ date: "1/6/2024", amount: 10 },
{ date: "1/7/2024", amount: 11 },
{ date: "2024-01-01", amount: 5 },
{ date: "2024-01-02", amount: 6 },
{ date: "2024-01-03", amount: 7 },
{ date: "2024-01-04", amount: 8 },
{ date: "2024-01-05", amount: 9 },
{ date: "2024-01-06", amount: 10 },
{ date: "2024-01-07", amount: 11 },
],
interval: "day",
},
decorators: [
(Story) => (
<div style={{ height: "400px" }}>
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof ActiveUserChart>;
export const Example: Story = {};
export const ManyDataPoints: Story = {
args: {
data: Array.from({ length: 30 }).map((_, i) => {
const date = new Date(2024, 0, i + 1);
return {
date: date.toISOString().split("T")[0],
amount: 5 + Math.floor(Math.random() * 15),
};
}),
},
};
@@ -1,19 +1,9 @@
import "chartjs-adapter-date-fns";
import { useTheme } from "@emotion/react";
import {
CategoryScale,
Chart as ChartJS,
type ChartOptions,
Filler,
Legend,
LineElement,
LinearScale,
PointElement,
TimeScale,
Title,
Tooltip,
defaults,
} from "chart.js";
type ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "components/Chart/Chart";
import {
HelpTooltip,
HelpTooltipContent,
@@ -21,95 +11,99 @@ import {
HelpTooltipTitle,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import dayjs from "dayjs";
import type { FC } from "react";
import { Line } from "react-chartjs-2";
ChartJS.register(
CategoryScale,
LinearScale,
TimeScale,
LineElement,
PointElement,
Filler,
Title,
Tooltip,
Legend,
);
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
const chartConfig = {
amount: {
label: "Active Users",
color: "hsl(var(--highlight-purple))",
},
} satisfies ChartConfig;
export interface ActiveUserChartProps {
data: readonly { date: string; amount: number }[];
interval: "day" | "week";
data: { date: string; amount: number }[];
}
export const ActiveUserChart: FC<ActiveUserChartProps> = ({
data,
interval,
}) => {
const theme = useTheme();
const labels = data.map((val) => dayjs(val.date).format("YYYY-MM-DD"));
const chartData = data.map((val) => val.amount);
defaults.font.family = theme.typography.fontFamily as string;
defaults.color = theme.palette.text.secondary;
const options: ChartOptions<"line"> = {
responsive: true,
animation: false,
plugins: {
legend: {
display: false,
},
tooltip: {
displayColors: false,
callbacks: {
title: (context) => {
const date = new Date(context[0].parsed.x);
return date.toLocaleDateString();
},
},
},
},
scales: {
y: {
grid: { color: theme.palette.divider },
suggestedMin: 0,
ticks: {
precision: 0,
},
},
x: {
grid: { color: theme.palette.divider },
ticks: {
stepSize: data.length > 10 ? 2 : undefined,
},
type: "time",
time: {
unit: interval,
},
},
},
maintainAspectRatio: false,
};
export const ActiveUserChart: FC<ActiveUserChartProps> = ({ data }) => {
return (
<Line
data-chromatic="ignore"
data={{
labels: labels,
datasets: [
{
label: `${interval === "day" ? "Daily" : "Weekly"} Active Users`,
data: chartData,
pointBackgroundColor: theme.roles.active.outline,
pointBorderColor: theme.roles.active.outline,
borderColor: theme.roles.active.outline,
},
],
}}
options={options}
/>
<ChartContainer config={chartConfig} className="aspect-auto h-full">
<AreaChart
accessibilityLayer
data={data}
margin={{
top: 10,
left: 0,
right: 0,
}}
>
<CartesianGrid vertical={false} />
<XAxis
dataKey="date"
tickLine={false}
tickMargin={12}
minTickGap={24}
tickFormatter={(value: string) =>
new Date(value).toLocaleDateString(undefined, {
month: "short",
day: "numeric",
})
}
/>
<YAxis
dataKey="amount"
tickLine={false}
axisLine={false}
tickMargin={12}
tickFormatter={(value: number) => {
return value === 0 ? "" : value.toLocaleString();
}}
/>
<ChartTooltip
cursor={false}
content={
<ChartTooltipContent
className="font-medium text-content-secondary"
labelClassName="text-content-primary"
labelFormatter={(_, p) => {
const item = p[0];
return `${item.value} active users`;
}}
formatter={(v, n, item) => {
const date = new Date(item.payload.date);
return date.toLocaleString(undefined, {
month: "long",
day: "2-digit",
});
}}
/>
}
/>
<defs>
<linearGradient id="fillAmount" x1="0" y1="0" x2="0" y2="1">
<stop
offset="5%"
stopColor="var(--color-amount)"
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor="var(--color-amount)"
stopOpacity={0.1}
/>
</linearGradient>
</defs>
<Area
isAnimationActive={false}
dataKey="amount"
type="linear"
fill="url(#fillAmount)"
fillOpacity={0.4}
stroke="var(--color-amount)"
stackId="a"
/>
</AreaChart>
</ChartContainer>
);
};
@@ -257,7 +257,6 @@ const ActiveUsersPanel: FC<ActiveUsersPanelProps> = ({
{data && data.length === 0 && <NoDataAvailable />}
{data && data.length > 0 && (
<ActiveUserChart
interval={interval}
data={data.map((d) => ({
amount: d.active_users,
date: d.start_time,