Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: changed

Charts: improve pie semi circle chart composition
Comment on lines +1 to +4
Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the label and note props is a breaking API change. Please mark this as a breaking change in the changelog (e.g., Significance: major and/or Type: breaking-change) and include a brief migration note showing how to render labels via children (Group/Text or PieSemiCircleChart.SVG).

Suggested change
Significance: minor
Type: changed
Charts: improve pie semi circle chart composition
Significance: major
Type: breaking-change
Charts: improve pie semi circle chart composition
Breaking change: The `label` and `note` props have been removed from PieSemiCircleChart. To render labels, use children components such as `<Group><Text /></Group>` or render SVG elements directly via `PieSemiCircleChart.SVG`.
Migration note:
Before:
<PieSemiCircleChart label="My Label" note="Some note" ... />
After:
<PieSemiCircleChart ...>
<Group>
<Text>My Label</Text>
<Text>Some note</Text>
</Group>
</PieSemiCircleChart>
// Or use PieSemiCircleChart.SVG for custom SVG rendering.

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the semi-circle chart label and note props are currently being used by any consumers, so I don't think it needs to be a significant/major change in the changelog. I'm open to having my mind changed on this, but at this time I don't think this feedback is necessary.

Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { localPoint } from '@visx/event';
import { Group } from '@visx/group';
import { Pie } from '@visx/shape';
import { Text } from '@visx/text';
import { useTooltip, useTooltipInPortal } from '@visx/tooltip';
import clsx from 'clsx';
import { useCallback, useContext, useMemo } from 'react';
Expand Down Expand Up @@ -46,18 +45,9 @@ export interface PieSemiCircleChartProps extends BaseChartProps< DataPointPercen
*/
clockwise?: boolean;

/**
* Label text to display above the chart
*/
label?: string;

/**
* Note text to display below the label
*/
note?: string;

/**
* Use the children prop to render additional elements on the chart.
* Supports composition API with PieSemiCircleChart.SVG and PieSemiCircleChart.HTML components.
Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Since label/note props were removed, consider adding a short usage example in this docblock showing how to render a title/note via children (Group/Text) and via the SVG compound component to guide consumers migrating to the new API.

Suggested change
* Supports composition API with PieSemiCircleChart.SVG and PieSemiCircleChart.HTML components.
* Supports composition API with PieSemiCircleChart.SVG and PieSemiCircleChart.HTML components.
*
* Usage examples:
* 1. Render a title/note via children (Group/Text):
* <PieSemiCircleChart ...>
* <Group>
* <Text x={width / 2} y={height / 2} textAnchor="middle">
* Chart Title
* </Text>
* </Group>
* </PieSemiCircleChart>
*
* 2. Render a title/note via the SVG compound component:
* <PieSemiCircleChart ...>
* <PieSemiCircleChart.SVG>
* <Group>
* <Text x={width / 2} y={height / 2} textAnchor="middle">
* Chart Title
* </Text>
* </Group>
* </PieSemiCircleChart.SVG>
* </PieSemiCircleChart>

Copilot uses AI. Check for mistakes.

*/
children?: ReactNode;

Expand Down Expand Up @@ -133,8 +123,6 @@ const PieSemiCircleChartInternal: FC< PieSemiCircleChartProps > = ( {
legendItemClassName,
legendShape = 'circle',
legendValueDisplay = 'percentage',
label,
note,
className,
children,
tooltipOffsetX = 0,
Expand Down Expand Up @@ -316,26 +304,6 @@ const PieSemiCircleChartInternal: FC< PieSemiCircleChartProps > = ( {
} }
</Pie>

{ /* Label and note text */ }
<Group>
<Text
textAnchor="middle"
verticalAnchor="start"
y={ -40 } // Position above the chart with space for note
className={ styles.label }
>
{ label }
</Text>
<Text
textAnchor="middle"
verticalAnchor="start"
y={ -20 } // Position between label and chart
className={ styles.note }
>
{ note }
</Text>
</Group>

{ /* Render SVG children from composition API */ }
{ svgChildren }
</Group>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,23 @@ export const Default: Story = {
resize: 'none',
thickness: 0.4,
data,
label: 'OS',
note: 'Windows +10%',
clockwise: true,
children: (
<Group>
<Text
textAnchor="middle"
verticalAnchor="start"
y={ -40 }
fontSize={ 16 }
fontWeight={ 600 }
>
OS
</Text>
<Text textAnchor="middle" verticalAnchor="start" y={ -20 } fontSize={ 14 }>
Windows +10%
</Text>
</Group>
),
},
};

Expand Down Expand Up @@ -95,24 +109,46 @@ export const WithCompositionLegend: Story = {
<PieSemiCircleChart
width={ 400 }
data={ args.data }
label="Performance Metrics"
note="Q4 2023 Results"
showLegend={ true }
legendPosition={ args.legendPosition || 'bottom' }
legendOrientation={ args.legendOrientation || 'horizontal' }
legendAlignment={ args.legendAlignment || 'center' }
legendMaxWidth={ args.legendMaxWidth }
legendTextOverflow={ args.legendTextOverflow || 'wrap' }
/>
>
<Group>
<Text
textAnchor="middle"
verticalAnchor="start"
y={ -40 }
fontSize={ 16 }
fontWeight={ 600 }
>
Performance Metrics
</Text>
<Text textAnchor="middle" verticalAnchor="start" y={ -20 } fontSize={ 14 }>
Q4 2023 Results
</Text>
</Group>
</PieSemiCircleChart>
</div>
<div>
<h3>Composition API with Legend Component</h3>
<PieSemiCircleChart
width={ 400 }
data={ args.data }
label="Performance Metrics"
note="Q4 2023 Results"
>
<PieSemiCircleChart width={ 400 } data={ args.data }>
<Group>
<Text
textAnchor="middle"
verticalAnchor="start"
y={ -40 }
fontSize={ 16 }
fontWeight={ 600 }
>
Performance Metrics
</Text>
<Text textAnchor="middle" verticalAnchor="start" y={ -20 } fontSize={ 14 }>
Q4 2023 Results
</Text>
</Group>
<PieSemiCircleChart.Legend
position={ args.legendPosition || 'bottom' }
orientation={ args.legendOrientation || 'horizontal' }
Expand Down Expand Up @@ -163,14 +199,28 @@ export const CustomLegendPositioning: Story = {
percentage: 48,
},
],
label: 'OS',
note: 'Windows +10%',
withTooltips: true,
showLegend: true,
legendOrientation: 'vertical',
legendAlignment: 'end',
legendPosition: 'top',
legendShape: 'circle',
children: (
<Group>
<Text
textAnchor="middle"
verticalAnchor="start"
y={ -40 }
fontSize={ 16 }
fontWeight={ 600 }
>
OS
</Text>
<Text textAnchor="middle" verticalAnchor="start" y={ -20 } fontSize={ 14 }>
Windows +10%
</Text>
</Group>
),
},
parameters: {
docs: {
Expand Down Expand Up @@ -261,15 +311,15 @@ export const CompositionAPI: Story = {
>
<div>
<h3>With Custom SVG Elements</h3>
<PieSemiCircleChart
width={ 400 }
data={ args.data }
label="OS Usage"
note="Q4 2023"
withTooltips={ true }
>
<PieSemiCircleChart width={ 400 } data={ args.data } withTooltips={ true }>
<PieSemiCircleChart.SVG>
<Group>
<Text textAnchor="middle" y={ -40 } fontSize={ 16 } fontWeight={ 600 }>
OS Usage
</Text>
<Text textAnchor="middle" y={ -20 } fontSize={ 14 }>
Q4 2023
</Text>
<Text
x={ 0 }
y={ -80 }
Expand Down Expand Up @@ -300,12 +350,17 @@ export const CompositionAPI: Story = {

<div>
<h3>With Custom Legend and HTML Content</h3>
<PieSemiCircleChart
width={ 400 }
data={ args.data }
label="Performance"
note="Latest Results"
>
<PieSemiCircleChart width={ 400 } data={ args.data }>
<PieSemiCircleChart.SVG>
<Group>
<Text textAnchor="middle" y={ -40 } fontSize={ 16 } fontWeight={ 600 }>
Performance
</Text>
<Text textAnchor="middle" y={ -20 } fontSize={ 14 }>
Latest Results
</Text>
</Group>
</PieSemiCircleChart.SVG>
<PieSemiCircleChart.HTML>
<div
style={ {
Expand Down Expand Up @@ -350,19 +405,20 @@ export const CompositionAPI: Story = {
</div>

<div style={ { marginTop: '3rem' } }>
<h3>Legacy Support - Direct Group Components</h3>
<h3>Direct Group Components</h3>
<p style={ { fontSize: '14px', color: '#666', marginBottom: '1rem' } }>
For backward compatibility, Group components are still supported directly:
Group components are supported directly for simpler use cases:
</p>
<PieSemiCircleChart
width={ 400 }
data={ args.data }
label="Legacy Mode"
note="Still works!"
>
<PieSemiCircleChart width={ 400 } data={ args.data }>
<Group>
<Text textAnchor="middle" y={ -40 } fontSize={ 16 } fontWeight={ 600 }>
Direct Usage
</Text>
<Text textAnchor="middle" y={ -20 } fontSize={ 14 }>
Simple and clean!
</Text>
<Text x={ 0 } y={ -70 } textAnchor="middle" fontSize={ 12 } fill="#999">
Direct Group usage
Additional annotation
</Text>
<rect x={ -30 } y={ -85 } width={ 60 } height={ 2 } fill="#ddd" />
</Group>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Group } from '@visx/group';
import { Text } from '@visx/text';
import { act } from 'react';
import { GlobalChartsProvider } from '../../../providers';
import PieSemiCircleChart from '../pie-semi-circle-chart';
Expand Down Expand Up @@ -35,13 +37,23 @@ describe( 'PieSemiCircleChart', () => {
expect( segments ).toHaveLength( 2 );
} );

it( 'renders label and note when provided', () => {
const label = 'Chart Title';
const note = 'Additional Info';
renderPieChart( { data: mockData, label, note } );
it( 'renders custom children content', () => {
renderPieChart( {
data: mockData,
children: (
<Group>
<Text textAnchor="middle" y={ -40 }>
Chart Title
</Text>
<Text textAnchor="middle" y={ -20 }>
Additional Info
</Text>
</Group>
),
} );

expect( screen.getByText( label ) ).toBeInTheDocument();
expect( screen.getByText( note ) ).toBeInTheDocument();
expect( screen.getByText( 'Chart Title' ) ).toBeInTheDocument();
expect( screen.getByText( 'Additional Info' ) ).toBeInTheDocument();
} );

it( 'shows legend when showLegend is true', () => {
Expand Down Expand Up @@ -178,4 +190,89 @@ describe( 'PieSemiCircleChart', () => {
).toBeInTheDocument();
} );
} );

describe( 'Composition API', () => {
it( 'renders children with SVG content', () => {
renderPieChart( {
data: mockData,
children: (
<Group>
<Text textAnchor="middle" y={ -40 }>
Custom Title
</Text>
</Group>
),
} );

expect( screen.getByText( 'Custom Title' ) ).toBeInTheDocument();
} );

it( 'renders PieSemiCircleChart.SVG compound component', () => {
render(
<GlobalChartsProvider>
<PieSemiCircleChart data={ mockData }>
<PieSemiCircleChart.SVG>
<Group>
<Text textAnchor="middle" y={ -50 }>
SVG Component Title
</Text>
</Group>
</PieSemiCircleChart.SVG>
</PieSemiCircleChart>
</GlobalChartsProvider>
);
Comment on lines +210 to +223
Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] For consistency and to reduce duplication, consider using the existing renderPieChart helper in this test (and the similar cases below) instead of directly calling render with GlobalChartsProvider.

Copilot uses AI. Check for mistakes.


expect( screen.getByText( 'SVG Component Title' ) ).toBeInTheDocument();
} );

it( 'renders PieSemiCircleChart.HTML compound component', () => {
render(
<GlobalChartsProvider>
<PieSemiCircleChart data={ mockData }>
<PieSemiCircleChart.HTML>
<div data-testid="html-content">HTML Content</div>
</PieSemiCircleChart.HTML>
</PieSemiCircleChart>
</GlobalChartsProvider>
);

expect( screen.getByTestId( 'html-content' ) ).toBeInTheDocument();
expect( screen.getByText( 'HTML Content' ) ).toBeInTheDocument();
} );

it( 'renders mixed SVG and HTML compound components', () => {
render(
<GlobalChartsProvider>
<PieSemiCircleChart data={ mockData }>
<PieSemiCircleChart.SVG>
<Group>
<Text textAnchor="middle" y={ -50 }>
SVG Title
</Text>
</Group>
</PieSemiCircleChart.SVG>
<PieSemiCircleChart.HTML>
<div data-testid="footer">Chart Footer</div>
</PieSemiCircleChart.HTML>
</PieSemiCircleChart>
</GlobalChartsProvider>
);

expect( screen.getByText( 'SVG Title' ) ).toBeInTheDocument();
expect( screen.getByTestId( 'footer' ) ).toBeInTheDocument();
} );

it( 'renders Legend as compound component', () => {
render(
<GlobalChartsProvider>
<PieSemiCircleChart data={ mockData }>
<PieSemiCircleChart.Legend orientation="horizontal" />
</PieSemiCircleChart>
</GlobalChartsProvider>
);

expect( screen.getByText( 'Category A' ) ).toBeInTheDocument();
expect( screen.getByText( 'Category B' ) ).toBeInTheDocument();
} );
} );
} );
Loading
Loading