mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
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:
+1
-3
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Generated
-44
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user