Charts: Make PieSemiCircleChart responsive by default#47312
Charts: Make PieSemiCircleChart responsive by default#47312adamwoodnz wants to merge 7 commits intotrunkfrom
Conversation
…ments. Update BarChart, LineChart, and PieChart interfaces to include the new prop, and reflect this change in their respective documentation. Remove redundant gap prop definitions from the interfaces.
The chart previously hardcoded a 400px width and derived height by subtracting legend height from the chart area. This caused incorrect sizing when the container had height constraints or the legend position changed. The chart now fills its parent container and measures the available SVG wrapper area to maintain a 2:1 width-to-height ratio, constrained by both available width and height. Explicit width/height props override the responsive behavior. - Replace fixed width default with responsive container measurement - Use @wordpress/ui Stack for gap spacing between chart elements - Add configurable gap prop using design system tokens - Wrap SVG in a flex measurement div for accurate sizing Co-authored-by: Cursor <cursoragent@cursor.com>
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖 Follow this PR Review Process:
If you have questions about anything, reach out in #jetpack-developers for guidance! |
There was a problem hiding this comment.
Pull request overview
This PR makes PieSemiCircleChart responsive by default, so it fills its parent container when no explicit dimensions are provided, while preserving a strict 2:1 width-to-height ratio based on available space. It also standardizes inter-element spacing via a shared gap prop and updates Storybook/docs/tests accordingly.
Changes:
- Implement responsive-by-default sizing for
PieSemiCircleChartusing measured available space and a constrained 2:1 ratio. - Replace the legend layout hack with
@wordpress/uiStack, and introduce a sharedgapprop onBaseChartProps. - Update Storybook stories/docs and adjust unit tests for the new responsive behavior.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| projects/js-packages/charts/src/types.ts | Adds shared gap prop to BaseChartProps so charts can use a consistent spacing API. |
| projects/js-packages/charts/src/charts/pie-semi-circle-chart/test/pie-semi-circle-chart.test.tsx | Updates tests to reflect responsive-by-default behavior and constrained sizing. |
| projects/js-packages/charts/src/charts/pie-semi-circle-chart/stories/index.stories.tsx | Updates default story to be responsive and adds a fixed-dimensions story using the unresponsive export. |
| projects/js-packages/charts/src/charts/pie-semi-circle-chart/stories/index.api.mdx | Updates API docs for responsive width behavior and documents the new gap prop. |
| projects/js-packages/charts/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx | Implements measured sizing, 2:1 constraint logic, and Stack-based layout with configurable gap. |
| projects/js-packages/charts/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss | Adds responsive/container-filling styles and an SVG wrapper that can be measured and flexed. |
| projects/js-packages/charts/src/charts/pie-chart/pie-chart.tsx | Removes duplicated gap typing now that it’s in BaseChartProps. |
| projects/js-packages/charts/src/charts/line-chart/types.ts | Removes duplicated gap typing now that it’s in BaseChartProps. |
| projects/js-packages/charts/src/charts/line-chart/stories/index.api.mdx | Documents gap for LineChart API. |
| projects/js-packages/charts/src/charts/bar-chart/stories/index.api.mdx | Documents gap for BarChart API. |
| projects/js-packages/charts/src/charts/bar-chart/bar-chart.tsx | Removes duplicated gap typing now that it’s in BaseChartProps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
projects/js-packages/charts/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx
Outdated
Show resolved
Hide resolved
projects/js-packages/charts/src/charts/pie-semi-circle-chart/stories/index.api.mdx
Outdated
Show resolved
Hide resolved
…onsive fix Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
858a52d to
3ce083e
Compare
Correct docs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Code Coverage SummaryCoverage changed in 1 file.
|
The invalid-data render path hardcoded width and ignored propHeight, so the error SVG could overflow a height-constrained container. Apply the same 2:1 aspect-ratio constraint used by the valid chart. Co-authored-by: Cursor <cursoragent@cursor.com>
Stories were still using fixed widths and container overrides from the old sizing model. Now that the chart is responsive by default, most stories no longer need explicit width/containerWidth props. - Add height range control to Storybook argTypes - Remove redundant Responsiveness story (all stories are responsive) - Simplify composition and interactive legend examples - Use height prop instead of width where a size constraint is needed Co-authored-by: Cursor <cursoragent@cursor.com>
The flex/column properties are now handled by the Stack component, and margin declarations have no effect on SVG <Text> elements. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Mock useParentSize so the responsive wrapper returns predictable dimensions in tests | ||
| jest.mock( '@visx/responsive', () => ( { | ||
| useParentSize: jest.fn( () => ( { | ||
| parentRef: { current: null }, | ||
| width: 400, | ||
| height: 200, | ||
| } ) ), |
There was a problem hiding this comment.
The mock is mocking useParentSize from @visx/responsive, which is used by the withResponsive HOC that wraps the component. However, the component also uses useElementSize internally (line 190 in the implementation) for additional dimension constraints. In test environments, useElementSize typically returns 0,0 unless ResizeObserver is mocked, so the component falls back to using propWidth/propHeight passed from the HOC. While this may work for the current tests, it doesn't actually test the full responsive behavior including the useElementSize logic. Consider also mocking ResizeObserver or useElementSize to test the complete responsive sizing behavior, including height-constrained scenarios.
| // Mock useParentSize so the responsive wrapper returns predictable dimensions in tests | |
| jest.mock( '@visx/responsive', () => ( { | |
| useParentSize: jest.fn( () => ( { | |
| parentRef: { current: null }, | |
| width: 400, | |
| height: 200, | |
| } ) ), | |
| // Mock responsive hooks so the wrapper and internal sizing logic return predictable dimensions in tests | |
| jest.mock( '@visx/responsive', () => ( { | |
| useParentSize: jest.fn( () => ( { | |
| parentRef: { current: null }, | |
| width: 400, | |
| height: 200, | |
| } ) ), | |
| useElementSize: jest.fn( () => ( { | |
| ref: { current: null }, | |
| width: 300, | |
| height: 100, | |
| } ) ), |
| // Mock useParentSize so the responsive wrapper returns predictable dimensions in tests | ||
| jest.mock( '@visx/responsive', () => ( { | ||
| useParentSize: jest.fn( () => ( { | ||
| parentRef: { current: null }, | ||
| width: 400, | ||
| height: 200, | ||
| } ) ), | ||
| } ) ); | ||
|
|
There was a problem hiding this comment.
The test is mocking useParentSize from @visx/responsive, but the new implementation uses useElementSize from the local hooks (which uses ResizeObserver). This mock is now incorrect and won't actually affect the component behavior. The mock should either be removed, or if predictable dimensions are needed for tests, useElementSize should be mocked instead, or ResizeObserver should be mocked.
| // Mock useParentSize so the responsive wrapper returns predictable dimensions in tests | |
| jest.mock( '@visx/responsive', () => ( { | |
| useParentSize: jest.fn( () => ( { | |
| parentRef: { current: null }, | |
| width: 400, | |
| height: 200, | |
| } ) ), | |
| } ) ); |
| const wrapper = screen.getByTestId( 'responsive-wrapper' ); | ||
| expect( wrapper ).toHaveStyle( { height: '100%' } ); |
There was a problem hiding this comment.
This test is checking for the 'responsive-wrapper' test ID, which comes from the withResponsive HOC wrapper. However, the PR's main change is to make the component internally responsive using useElementSize and the Stack component with responsive styling. To properly test the new responsive behavior, this test should check the Stack element (test ID 'pie-chart-container') for the 'pie-semi-circle-chart--responsive' class and the height: 100% style when no width/height props are provided. The current test is validating the HOC's behavior, not the component's new internal responsive implementation.
| const wrapper = screen.getByTestId( 'responsive-wrapper' ); | |
| expect( wrapper ).toHaveStyle( { height: '100%' } ); | |
| const container = screen.getByTestId( 'pie-chart-container' ); | |
| expect( container ).toHaveClass( 'pie-semi-circle-chart--responsive' ); | |
| expect( container ).toHaveStyle( { height: '100%' } ); |
| const prefersReducedMotion = usePrefersReducedMotion(); | ||
|
|
||
| if ( ! isValid ) { | ||
| const errorWidth = Math.min( propWidth || 400, ( propHeight || ( propWidth || 400 ) / 2 ) * 2 ); |
There was a problem hiding this comment.
The error width calculation has a subtle logic issue. When only propHeight is provided, the expression ( propHeight || ( propWidth || 400 ) / 2 ) evaluates to propHeight, then propHeight * 2 could exceed the default width of 400. For example, if propHeight=400, errorWidth would be min(400, 800) = 400, and errorHeight would be 200, which is smaller than the specified propHeight of 400. This might not match user expectations when they've explicitly set a height constraint. Consider: const errorWidth = Math.min( propWidth || ( propHeight ? propHeight * 2 : 400 ), propHeight ? propHeight * 2 : ( propWidth || 400 ) ); to be more explicit about the intent.
| const errorWidth = Math.min( propWidth || 400, ( propHeight || ( propWidth || 400 ) / 2 ) * 2 ); | |
| const errorWidth = Math.min( | |
| propWidth || ( propHeight ? propHeight * 2 : 400 ), | |
| propHeight ? propHeight * 2 : ( propWidth || 400 ) | |
| ); |
Fixes: https://linear.app/a8c/issue/CHARTS-169
Proposed changes:
PieSemiCircleChartresponsive by default — fills its parent container when no explicitwidth/heightis provideduseElementSizeon the SVG wrapperpropHeightand doesn't overflow short containers@wordpress/uiStackfor layout; add configurablegappropgapprop to sharedBaseChartPropsand remove duplicateGapSizeimports fromBarChart,LineChart, andPieChartResponsivenessstory, drop hardcodedwidth/containerWidthprops, addheightcontrol and fixed-dimensions storyOther information:
Testing instructions:
cd projects/js-packages/charts && pnpm storybookPieSemiCircleChartstoriestopandbottompositionscd projects/js-packages/charts && pnpm testChangelog
See
projects/js-packages/charts/changelog/charts-169-fix-chart-height-and-size-calculation-for-pie-semi-circle-chartDoes this pull request change what data or activity we track or use?
No. This PR only changes how
PieSemiCircleChartcalculates and renders its dimensions — no data collection, tracking, or analytics are added or modified.