diff --git a/resources/js/components/ui/stacked-bar-chart.test.tsx b/resources/js/components/ui/stacked-bar-chart.test.tsx
new file mode 100644
index 00000000..2d4e7d2a
--- /dev/null
+++ b/resources/js/components/ui/stacked-bar-chart.test.tsx
@@ -0,0 +1,51 @@
+import { render } from '@testing-library/react';
+import { describe, expect, it } from 'vitest';
+
+import { StackedBarShape } from './stacked-bar-chart';
+
+describe('StackedBarShape', () => {
+ it('uses card background for the rounded edge stroke', () => {
+ const { container } = render(
+ ,
+ );
+
+ const path = container.querySelector('path');
+
+ expect(path?.getAttribute('stroke')).toBe('var(--card)');
+ expect(path?.getAttribute('stroke-width')).toBe('1');
+ expect(path?.getAttribute('stroke-linejoin')).toBe('round');
+ });
+
+ it('clamps radius for very short bar segments', () => {
+ const { container } = render(
+ ,
+ );
+
+ const path = container.querySelector('path');
+
+ expect(path?.getAttribute('d')).toContain('M 1 0');
+ expect(path?.getAttribute('d')).toContain('H 19');
+ });
+});
diff --git a/resources/js/components/ui/stacked-bar-chart.tsx b/resources/js/components/ui/stacked-bar-chart.tsx
index b9ab20e6..d51583ba 100644
--- a/resources/js/components/ui/stacked-bar-chart.tsx
+++ b/resources/js/components/ui/stacked-bar-chart.tsx
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef } from 'react';
-import { Bar, BarChart, Rectangle, XAxis } from 'recharts';
+import { Bar, BarChart, Rectangle, XAxis, type BarShapeProps } from 'recharts';
import {
ChartConfig,
@@ -31,13 +31,13 @@ interface StackedBarShapeProps {
y: number;
width: number;
height: number;
- fill: string;
- payload: Record;
+ fill?: string;
+ payload?: Record;
dataKey: string;
dataKeys: string[];
}
-function StackedBarShape({
+export function StackedBarShape({
x,
y,
width,
@@ -49,8 +49,10 @@ function StackedBarShape({
}: StackedBarShapeProps) {
if (height <= 0) return null;
+ const segmentPayload = payload ?? {};
+ const radius = Math.min(BORDER_RADIUS, width / 2, height / 2);
const visibleKeys = dataKeys.filter((key) => {
- const value = payload[key];
+ const value = segmentPayload[key];
return typeof value === 'number' && value > 0;
});
@@ -61,36 +63,36 @@ function StackedBarShape({
if (isFirstVisible && isLastVisible) {
path = `
- M ${x + BORDER_RADIUS} ${y}
- H ${x + width - BORDER_RADIUS}
- Q ${x + width} ${y} ${x + width} ${y + BORDER_RADIUS}
- V ${y + height - BORDER_RADIUS}
- Q ${x + width} ${y + height} ${x + width - BORDER_RADIUS} ${y + height}
- H ${x + BORDER_RADIUS}
- Q ${x} ${y + height} ${x} ${y + height - BORDER_RADIUS}
- V ${y + BORDER_RADIUS}
- Q ${x} ${y} ${x + BORDER_RADIUS} ${y}
+ M ${x + radius} ${y}
+ H ${x + width - radius}
+ Q ${x + width} ${y} ${x + width} ${y + radius}
+ V ${y + height - radius}
+ Q ${x + width} ${y + height} ${x + width - radius} ${y + height}
+ H ${x + radius}
+ Q ${x} ${y + height} ${x} ${y + height - radius}
+ V ${y + radius}
+ Q ${x} ${y} ${x + radius} ${y}
Z
`;
} else if (isLastVisible) {
path = `
- M ${x + BORDER_RADIUS} ${y}
- H ${x + width - BORDER_RADIUS}
- Q ${x + width} ${y} ${x + width} ${y + BORDER_RADIUS}
+ M ${x + radius} ${y}
+ H ${x + width - radius}
+ Q ${x + width} ${y} ${x + width} ${y + radius}
V ${y + height}
H ${x}
- V ${y + BORDER_RADIUS}
- Q ${x} ${y} ${x + BORDER_RADIUS} ${y}
+ V ${y + radius}
+ Q ${x} ${y} ${x + radius} ${y}
Z
`;
} else if (isFirstVisible) {
path = `
M ${x} ${y}
H ${x + width}
- V ${y + height - BORDER_RADIUS}
- Q ${x + width} ${y + height} ${x + width - BORDER_RADIUS} ${y + height}
- H ${x + BORDER_RADIUS}
- Q ${x} ${y + height} ${x} ${y + height - BORDER_RADIUS}
+ V ${y + height - radius}
+ Q ${x + width} ${y + height} ${x + width - radius} ${y + height}
+ H ${x + radius}
+ Q ${x} ${y + height} ${x} ${y + height - radius}
V ${y}
Z
`;
@@ -105,15 +107,19 @@ function StackedBarShape({
`;
}
- return ;
+ return (
+
+ );
}
-const CustomCursor = (props) => (
-
+const CustomCursor = (props: React.ComponentProps) => (
+
);
export interface StackedBarChartProps> {
@@ -169,16 +175,20 @@ export function StackedBarChart>({
const shapeRenderers = useMemo(() => {
return dataKeys.reduce(
(acc, key) => {
- acc[key] = (props: Record) => (
+ acc[key] = (props: BarShapeProps) => (
)}
+ {...props}
dataKey={key}
dataKeys={dataKeys}
/>
);
+
return acc;
},
- {} as Record) => React.ReactNode>,
+ {} as Record<
+ string,
+ (props: BarShapeProps) => React.ReactElement | null
+ >,
);
}, [dataKeys]);
@@ -201,7 +211,7 @@ export function StackedBarChart>({
tickFormatter={xAxisFormatter}
/>
}
+ cursor={}
content={