Skip to content

Commit c6c26b5

Browse files
authored
Label viewbox in polar charts (#6180)
1 parent 6c4acb7 commit c6c26b5

File tree

3 files changed

+124
-26
lines changed

3 files changed

+124
-26
lines changed

src/component/Label.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,16 @@ export function Label({ offset = 5, ...restProps }: Props) {
404404
} = props;
405405

406406
const polarViewBox = useAppSelector(selectPolarViewBox);
407-
const viewBoxFromContext = useViewBox();
407+
const cartesianViewBox = useViewBox();
408408

409-
const viewBox = viewBoxFromProps || polarViewBox || viewBoxFromContext;
409+
/*
410+
* I am not proud about this solution but it's a quick fix for https://github.com/recharts/recharts/issues/6030#issuecomment-3155352460.
411+
* What we should really do is split Label into two components: CartesianLabel and PolarLabel and then handle their respective viewBoxes separately.
412+
* Also other components should set its own viewBox in a context so that we can fix https://github.com/recharts/recharts/issues/6156
413+
*/
414+
const resolvedViewBox = position === 'center' ? cartesianViewBox : (polarViewBox ?? cartesianViewBox);
415+
416+
const viewBox = viewBoxFromProps || resolvedViewBox;
410417

411418
if (
412419
!viewBox ||
@@ -415,14 +422,19 @@ export function Label({ offset = 5, ...restProps }: Props) {
415422
return null;
416423
}
417424

425+
const propsWithViewBox = {
426+
...props,
427+
viewBox,
428+
};
429+
418430
if (isValidElement(content)) {
419-
const { labelRef: _, ...propsWithoutLabelRef } = props;
431+
const { labelRef: _, ...propsWithoutLabelRef } = propsWithViewBox;
420432
return cloneElement(content, propsWithoutLabelRef);
421433
}
422434

423435
let label: ReactNode;
424436
if (typeof content === 'function') {
425-
label = createElement(content as any, props);
437+
label = createElement(content as any, propsWithViewBox);
426438

427439
if (isValidElement(label)) {
428440
return label;

storybook/stories/API/chart/PieChart.stories.tsx

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState } from 'react';
22
import { Label, Legend, Pie, PieChart, ResponsiveContainer, Tooltip } from '../../../../src';
3-
import { pageData } from '../../data';
3+
import { pageDataWithFillColor } from '../../data';
44
import { CategoricalChartProps } from '../props/ChartProps';
55
import { ActiveShapeProps } from '../props/ActiveShapeProps';
66
import { getStoryArgsFromArgsTypesObject } from '../props/utils';
@@ -34,7 +34,7 @@ export const Simple = {
3434
},
3535
args: {
3636
...getStoryArgsFromArgsTypesObject(CategoricalChartProps),
37-
data: pageData,
37+
data: pageDataWithFillColor,
3838
activeShape: { fill: 'red' },
3939
margin: {
4040
top: 0,
@@ -47,29 +47,29 @@ export const Simple = {
4747

4848
export const Donut = {
4949
render: (args: Record<string, any>, context: RechartsStoryContext) => {
50-
const { data } = args;
5150
return (
52-
<ResponsiveContainer width="100%" height={400}>
53-
<PieChart {...args}>
54-
<Pie data={data} dataKey="uv" nameKey="name" innerRadius={50} outerRadius={80}>
55-
<Label position="center" fill="#000" fontSize={12} fontWeight="bold" dy={-7}>
56-
Donut
57-
</Label>
58-
<Label position="center" fontSize={12} fontWeight="bold" dy={8}>
59-
Chart
60-
</Label>
61-
</Pie>
62-
<RechartsHookInspector
63-
position={context.rechartsInspectorPosition}
64-
setPosition={context.rechartsSetInspectorPosition}
65-
/>
66-
</PieChart>
67-
</ResponsiveContainer>
51+
<PieChart {...args}>
52+
<Pie data={args.data} dataKey="uv" nameKey="name" innerRadius={50} outerRadius={80} cornerRadius={8}>
53+
<Label position="center" fill="#000" fontSize={12} fontWeight="bold" dy={-7}>
54+
Donut
55+
</Label>
56+
<Label position="center" fontSize={12} fontWeight="bold" dy={8}>
57+
Chart
58+
</Label>
59+
<Legend align="right" verticalAlign="middle" layout="vertical" />
60+
</Pie>
61+
<RechartsHookInspector
62+
position={context.rechartsInspectorPosition}
63+
setPosition={context.rechartsSetInspectorPosition}
64+
/>
65+
</PieChart>
6866
);
6967
},
7068
args: {
7169
...getStoryArgsFromArgsTypesObject(CategoricalChartProps),
72-
data: pageData,
70+
width: 500,
71+
height: 300,
72+
data: pageDataWithFillColor,
7373
},
7474
};
7575

test/component/Label.spec.tsx

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { render, screen } from '@testing-library/react';
22
import React from 'react';
3-
4-
import { Label, Line, LineChart, ReferenceLine, Surface } from '../../src';
3+
import { describe, it, expect, vi } from 'vitest';
4+
import { Label, Line, LineChart, PieChart, ReferenceLine, Surface } from '../../src';
55
import { cleanupMockAnimation, mockAnimation } from '../helper/animation-frame-helper';
66

77
const data = [
@@ -156,4 +156,90 @@ describe('<Label />', () => {
156156

157157
cleanupMockAnimation();
158158
});
159+
160+
describe('in PieChart', () => {
161+
describe('with custom content function', () => {
162+
it('should pass the correct props to the content function when position=center', () => {
163+
const contentFn = vi.fn();
164+
render(
165+
<PieChart height={100} width={200}>
166+
<Label value="text" position="center" content={contentFn} />
167+
</PieChart>,
168+
);
169+
170+
expect(contentFn).toHaveBeenLastCalledWith(
171+
{
172+
viewBox: {
173+
height: 90,
174+
width: 190,
175+
x: 5,
176+
y: 5,
177+
},
178+
value: 'text',
179+
content: contentFn,
180+
position: 'center',
181+
offset: 5,
182+
},
183+
{},
184+
);
185+
});
186+
187+
it('should pass the correct props to the content function when position=insideEnd', () => {
188+
const contentFn = vi.fn();
189+
render(
190+
<PieChart height={100} width={200}>
191+
<Label value="text" position="insideEnd" content={contentFn} />
192+
</PieChart>,
193+
);
194+
195+
expect(contentFn).toHaveBeenLastCalledWith(
196+
{
197+
viewBox: {
198+
clockWise: false,
199+
cx: 100,
200+
cy: 50,
201+
endAngle: 360,
202+
innerRadius: 0,
203+
outerRadius: 36,
204+
startAngle: 0,
205+
},
206+
value: 'text',
207+
content: contentFn,
208+
position: 'insideEnd',
209+
offset: 5,
210+
},
211+
{},
212+
);
213+
});
214+
});
215+
});
216+
217+
describe('in LineChart', () => {
218+
describe('with custom content function', () => {
219+
it('should pass the correct props to the content function', () => {
220+
const contentFn = vi.fn();
221+
render(
222+
<LineChart width={400} height={400} data={data}>
223+
<Label value="text" position="center" content={contentFn} />
224+
</LineChart>,
225+
);
226+
227+
expect(contentFn).toHaveBeenLastCalledWith(
228+
{
229+
viewBox: {
230+
height: 390,
231+
width: 390,
232+
x: 5,
233+
y: 5,
234+
},
235+
value: 'text',
236+
content: contentFn,
237+
position: 'center',
238+
offset: 5,
239+
},
240+
{},
241+
);
242+
});
243+
});
244+
});
159245
});

0 commit comments

Comments
 (0)