feat: add ThemedChart wrapper component
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { render } from '@testing-library/react'
|
||||
import { ThemedChart } from './ThemedChart'
|
||||
import { Line } from 'recharts'
|
||||
|
||||
// Recharts uses ResizeObserver internally
|
||||
class ResizeObserverMock {
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
||||
globalThis.ResizeObserver = ResizeObserverMock as any
|
||||
|
||||
describe('ThemedChart', () => {
|
||||
it('renders nothing when data is empty', () => {
|
||||
const { container } = render(
|
||||
<ThemedChart data={[]}>
|
||||
<Line dataKey="value" />
|
||||
</ThemedChart>,
|
||||
)
|
||||
expect(container.innerHTML).toBe('')
|
||||
})
|
||||
|
||||
it('renders a chart container when data is provided', () => {
|
||||
const data = [
|
||||
{ time: '10:00', value: 10 },
|
||||
{ time: '10:01', value: 20 },
|
||||
]
|
||||
const { container } = render(
|
||||
<ThemedChart data={data} height={160}>
|
||||
<Line dataKey="value" />
|
||||
</ThemedChart>,
|
||||
)
|
||||
expect(container.querySelector('.recharts-responsive-container')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('applies custom className', () => {
|
||||
const data = [{ time: '10:00', value: 5 }]
|
||||
const { container } = render(
|
||||
<ThemedChart data={data} className="my-chart">
|
||||
<Line dataKey="value" />
|
||||
</ThemedChart>,
|
||||
)
|
||||
expect(container.querySelector('.my-chart')).toBeTruthy()
|
||||
})
|
||||
})
|
||||
66
src/design-system/composites/ThemedChart/ThemedChart.tsx
Normal file
66
src/design-system/composites/ThemedChart/ThemedChart.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
ResponsiveContainer,
|
||||
ComposedChart,
|
||||
CartesianGrid,
|
||||
XAxis,
|
||||
YAxis,
|
||||
Tooltip,
|
||||
} from 'recharts'
|
||||
import { rechartsTheme } from '../../utils/rechartsTheme'
|
||||
import { ChartTooltip } from './ChartTooltip'
|
||||
|
||||
interface ThemedChartProps {
|
||||
data: Record<string, any>[]
|
||||
height?: number
|
||||
xDataKey?: string
|
||||
xType?: 'number' | 'category'
|
||||
xTickFormatter?: (value: any) => string
|
||||
yTickFormatter?: (value: any) => string
|
||||
yLabel?: string
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function ThemedChart({
|
||||
data,
|
||||
height = 200,
|
||||
xDataKey = 'time',
|
||||
xType = 'category',
|
||||
xTickFormatter,
|
||||
yTickFormatter,
|
||||
yLabel,
|
||||
children,
|
||||
className,
|
||||
}: ThemedChartProps) {
|
||||
if (!data.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className} style={{ width: '100%', height }}>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<ComposedChart data={data} margin={{ top: 4, right: 8, bottom: 0, left: 0 }}>
|
||||
<CartesianGrid {...rechartsTheme.cartesianGrid} />
|
||||
<XAxis
|
||||
dataKey={xDataKey}
|
||||
type={xType}
|
||||
{...rechartsTheme.xAxis}
|
||||
tickFormatter={xTickFormatter}
|
||||
/>
|
||||
<YAxis
|
||||
{...rechartsTheme.yAxis}
|
||||
tickFormatter={yTickFormatter}
|
||||
label={yLabel ? {
|
||||
value: yLabel,
|
||||
angle: -90,
|
||||
position: 'insideLeft',
|
||||
style: { fontSize: 11, fill: 'var(--text-muted)' },
|
||||
} : undefined}
|
||||
/>
|
||||
<Tooltip content={<ChartTooltip />} cursor={rechartsTheme.tooltip.cursor} />
|
||||
{children}
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user