272 lines
6.5 KiB
TypeScript
272 lines
6.5 KiB
TypeScript
import type { Meta, StoryObj } from '@storybook/react';
|
|
import { useMemo } from 'react';
|
|
import {
|
|
Area,
|
|
AreaChart,
|
|
Bar,
|
|
BarChart,
|
|
CartesianGrid,
|
|
Label,
|
|
Line,
|
|
LineChart,
|
|
Pie,
|
|
PieChart,
|
|
XAxis,
|
|
} from 'recharts';
|
|
|
|
import {
|
|
type ChartConfig,
|
|
ChartContainer,
|
|
ChartTooltip,
|
|
ChartTooltipContent,
|
|
} from '@konobangu/design-system/components/ui/chart';
|
|
|
|
const multiSeriesData = [
|
|
{ month: 'January', desktop: 186, mobile: 80 },
|
|
{ month: 'February', desktop: 305, mobile: 200 },
|
|
{ month: 'March', desktop: 237, mobile: 120 },
|
|
{ month: 'April', desktop: 73, mobile: 190 },
|
|
{ month: 'May', desktop: 209, mobile: 130 },
|
|
{ month: 'June', desktop: 214, mobile: 140 },
|
|
];
|
|
|
|
const multiSeriesConfig = {
|
|
desktop: {
|
|
label: 'Desktop',
|
|
color: 'hsl(var(--chart-1))',
|
|
},
|
|
mobile: {
|
|
label: 'Mobile',
|
|
color: 'hsl(var(--chart-2))',
|
|
},
|
|
} satisfies ChartConfig;
|
|
|
|
const singleSeriesData = [
|
|
{ browser: 'chrome', visitors: 275, fill: 'var(--color-chrome)' },
|
|
{ browser: 'safari', visitors: 200, fill: 'var(--color-safari)' },
|
|
{ browser: 'other', visitors: 190, fill: 'var(--color-other)' },
|
|
];
|
|
|
|
const singleSeriesConfig = {
|
|
visitors: {
|
|
label: 'Visitors',
|
|
},
|
|
chrome: {
|
|
label: 'Chrome',
|
|
color: 'hsl(var(--chart-1))',
|
|
},
|
|
safari: {
|
|
label: 'Safari',
|
|
color: 'hsl(var(--chart-2))',
|
|
},
|
|
other: {
|
|
label: 'Other',
|
|
color: 'hsl(var(--chart-5))',
|
|
},
|
|
} satisfies ChartConfig;
|
|
|
|
/**
|
|
* Beautiful charts. Built using Recharts. Copy and paste into your apps.
|
|
*/
|
|
const meta = {
|
|
title: 'ui/Chart',
|
|
component: ChartContainer,
|
|
tags: ['autodocs'],
|
|
argTypes: {},
|
|
args: {
|
|
children: <div />,
|
|
},
|
|
} satisfies Meta<typeof ChartContainer>;
|
|
|
|
export default meta;
|
|
|
|
type Story = StoryObj<typeof meta>;
|
|
|
|
/**
|
|
* Combine multiple Area components to create a stacked area chart.
|
|
*/
|
|
export const StackedAreaChart: Story = {
|
|
args: {
|
|
config: multiSeriesConfig,
|
|
},
|
|
render: (args) => (
|
|
<ChartContainer {...args}>
|
|
<AreaChart
|
|
accessibilityLayer
|
|
data={multiSeriesData}
|
|
margin={{
|
|
left: 12,
|
|
right: 12,
|
|
}}
|
|
>
|
|
<CartesianGrid vertical={false} />
|
|
<XAxis
|
|
dataKey="month"
|
|
tickLine={false}
|
|
axisLine={false}
|
|
tickMargin={8}
|
|
tickFormatter={(value) => value.slice(0, 3)}
|
|
/>
|
|
<ChartTooltip
|
|
cursor={false}
|
|
content={<ChartTooltipContent indicator="dot" />}
|
|
/>
|
|
<Area
|
|
dataKey="mobile"
|
|
type="natural"
|
|
fill="var(--color-mobile)"
|
|
fillOpacity={0.4}
|
|
stroke="var(--color-mobile)"
|
|
stackId="a"
|
|
/>
|
|
<Area
|
|
dataKey="desktop"
|
|
type="natural"
|
|
fill="var(--color-desktop)"
|
|
fillOpacity={0.4}
|
|
stroke="var(--color-desktop)"
|
|
stackId="a"
|
|
/>
|
|
</AreaChart>
|
|
</ChartContainer>
|
|
),
|
|
};
|
|
|
|
/**
|
|
* Combine multiple Bar components to create a stacked bar chart.
|
|
*/
|
|
export const StackedBarChart: Story = {
|
|
args: {
|
|
config: multiSeriesConfig,
|
|
},
|
|
render: (args) => (
|
|
<ChartContainer {...args}>
|
|
<BarChart accessibilityLayer data={multiSeriesData}>
|
|
<CartesianGrid vertical={false} />
|
|
<XAxis
|
|
dataKey="month"
|
|
tickLine={false}
|
|
tickMargin={10}
|
|
axisLine={false}
|
|
tickFormatter={(value) => value.slice(0, 3)}
|
|
/>
|
|
<ChartTooltip
|
|
cursor={false}
|
|
content={<ChartTooltipContent indicator="dashed" />}
|
|
/>
|
|
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
|
|
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
|
|
</BarChart>
|
|
</ChartContainer>
|
|
),
|
|
};
|
|
|
|
/**
|
|
* Combine multiple Line components to create a single line chart.
|
|
*/
|
|
export const MultiLineChart: Story = {
|
|
args: {
|
|
config: multiSeriesConfig,
|
|
},
|
|
render: (args) => (
|
|
<ChartContainer {...args}>
|
|
<LineChart
|
|
accessibilityLayer
|
|
data={multiSeriesData}
|
|
margin={{
|
|
left: 12,
|
|
right: 12,
|
|
}}
|
|
>
|
|
<CartesianGrid vertical={false} />
|
|
<XAxis
|
|
dataKey="month"
|
|
tickLine={false}
|
|
axisLine={false}
|
|
tickMargin={8}
|
|
tickFormatter={(value) => value.slice(0, 3)}
|
|
/>
|
|
<ChartTooltip
|
|
cursor={false}
|
|
content={<ChartTooltipContent hideLabel />}
|
|
/>
|
|
<Line
|
|
dataKey="desktop"
|
|
type="natural"
|
|
stroke="var(--color-desktop)"
|
|
strokeWidth={2}
|
|
dot={false}
|
|
/>
|
|
<Line
|
|
dataKey="mobile"
|
|
type="natural"
|
|
stroke="var(--color-mobile)"
|
|
strokeWidth={2}
|
|
dot={false}
|
|
/>
|
|
</LineChart>
|
|
</ChartContainer>
|
|
),
|
|
};
|
|
|
|
/**
|
|
* Combine Pie and Label components to create a doughnut chart.
|
|
*/
|
|
export const DoughnutChart: Story = {
|
|
args: {
|
|
config: singleSeriesConfig,
|
|
},
|
|
render: (args) => {
|
|
const totalVisitors = useMemo(() => {
|
|
return singleSeriesData.reduce((acc, curr) => acc + curr.visitors, 0);
|
|
}, []);
|
|
return (
|
|
<ChartContainer {...args}>
|
|
<PieChart>
|
|
<ChartTooltip
|
|
cursor={false}
|
|
content={<ChartTooltipContent hideLabel />}
|
|
/>
|
|
<Pie
|
|
data={singleSeriesData}
|
|
dataKey="visitors"
|
|
nameKey="browser"
|
|
innerRadius={48}
|
|
strokeWidth={5}
|
|
>
|
|
<Label
|
|
content={({ viewBox }) => {
|
|
if (viewBox && 'cx' in viewBox && 'cy' in viewBox) {
|
|
return (
|
|
<text
|
|
x={viewBox.cx}
|
|
y={viewBox.cy}
|
|
textAnchor="middle"
|
|
dominantBaseline="middle"
|
|
>
|
|
<tspan
|
|
x={viewBox.cx}
|
|
y={viewBox.cy}
|
|
className="fill-foreground font-bold text-3xl"
|
|
>
|
|
{totalVisitors.toLocaleString()}
|
|
</tspan>
|
|
<tspan
|
|
x={viewBox.cx}
|
|
y={(viewBox.cy || 0) + 24}
|
|
className="fill-muted-foreground"
|
|
>
|
|
Visitors
|
|
</tspan>
|
|
</text>
|
|
);
|
|
}
|
|
}}
|
|
/>
|
|
</Pie>
|
|
</PieChart>
|
|
</ChartContainer>
|
|
);
|
|
},
|
|
};
|