Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[charts] Introduce the radar chart #16406

Open
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

alexfauquette
Copy link
Member

@alexfauquette alexfauquette commented Jan 30, 2025

This PR is a rough first version of the Radar chart #7925. Lots of features are missing.

The idea is to validate the data structure, and it's mapping to a rotation/radius scale system. If ok, we can merge this one and add features in distinct PR such that we keep them small enough to be relevant (I don't expect someone to read 200 files with 10 different features and spot any bug or room for improvement)

Main idea

The polar plugin introduce the support of rotatio/radius axes that work similarly to the x/y axes

The radar don't expose directly those axies. It uses an intermediate metrics to make sure the radar has only one rotation axis with scale point, and one radius axis per metric.

Then we have one component per item rendered (grid, area, and marks)

Features

  • Polar coordinate
  • Map series config to rendering
  • Correct margins
  • Correct light/dark colors
  • Axes labels
  • Variants of axes shape (rounded one)
  • Interaction
  • Highlight
  • Tooltip
  • Defining the composition customization (opportunity to experiment with base-ui DX)
  • ARIA attributes?

Preview: https://deploy-preview-16406--material-ui-x.netlify.app/x/react-charts/radar/

@alexfauquette alexfauquette added new feature New feature or request component: charts This is the name of the generic UI component, not the React module! labels Jan 30, 2025
@mui-bot
Copy link

mui-bot commented Jan 30, 2025

Deploy preview: https://deploy-preview-16406--material-ui-x.netlify.app/

Updated pages:

Generated by 🚫 dangerJS against 8cf53e3

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Jan 31, 2025
Copy link

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Jan 31, 2025
@alexfauquette alexfauquette marked this pull request as ready for review January 31, 2025 11:11
Copy link

codspeed-hq bot commented Jan 31, 2025

CodSpeed Performance Report

Merging #16406 will not alter performance

Comparing alexfauquette:radar-chart (8cf53e3) with master (b252b9a)

Summary

✅ 6 untouched benchmarks

@alexfauquette alexfauquette marked this pull request as draft January 31, 2025 15:28
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Feb 4, 2025
Copy link

github-actions bot commented Feb 4, 2025

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Feb 4, 2025
@alexfauquette alexfauquette marked this pull request as ready for review February 4, 2025 10:59
@bernardobelchior
Copy link
Member

Is it just me or are the circles not being affected by the area above them?

For example, I think it's clear the blue area is below the yellow area because its color is affected by the yellow area's. However, this doesn't seem to be the case for the circles in blue, which seem unaffected.

image


The radar chart displays a grid behind the series that can be configured with

- `startAngle` The rotation angle of the entire chart
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to mention the angle should be specified in degrees?

Copy link
Member

Choose a reason for hiding this comment

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

Do you think it should be startAngleDeg?

Copy link
Member

@bernardobelchior bernardobelchior Feb 5, 2025

Choose a reason for hiding this comment

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

Sorry, I should have been more specific. I was suggesting something along the lines of:

- `startAngle` The rotation angle of the entire chart in degrees

The radar chart displays a grid behind the series that can be configured with

- `startAngle` The rotation angle of the entire chart
- `divisionNumber` The nb of division of the grid
Copy link
Member

Choose a reason for hiding this comment

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

Not sure if it would make more sense to write "number" here. It might not be clear "nb" means number.

degreeToRad((axis as RotationConfig).endAngle, 2 * Math.PI),
];
const diff = angles[1] - angles[0];
if (diff > Math.PI * 2 - 0.1) {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe I'm missing some context here. What is the reason for subtracting specifically by 0.1?

Copy link
Member Author

Choose a reason for hiding this comment

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

To take into account the risk of computation errors that could lead to diff === 2*Math.PI being false for less that 0.1

We could move that to 0.001. But I assume polar axes are either displayed on 360° in which case we need to avoid the overlap of first and last point. Or 180° in which case we have nothing to do

params: UseChartPolarAxisParameters;
defaultizedParams: UseChartPolarAxisDefaultizedParameters;
state: UseChartPolarAxisState;
// instance: UseChartPolarAxisInstance;
Copy link
Member

Choose a reason for hiding this comment

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

Just double-checking: is this commented on purpose?

Comment on lines 62 to +71
valueType: DefaultizedPieValueType;
};
radar: {
seriesInput: DefaultizedProps<RadarSeriesType, 'id'> & { color: string };
series: DefaultizedRadarSeriesType;
seriesProp: RadarSeriesType;
itemIdentifier: RadarItemIdentifier;
valueType: number;
polar: true;
};
Copy link
Member

Choose a reason for hiding this comment

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

Just curious: why do we need a polar: true prop here, but not for the pie chart?

As I see it, the pie chart is also polar where the radius is constant and only the rotation axis changes, isn't it?

Copy link
Member

Choose a reason for hiding this comment

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

I assume it is because pie chart doesn't really use polar coordinates, since it can be much simpler.

@JCQuintas
Copy link
Member

Is it just me or are the circles not being affected by the area above them?

I assume this got carried over because of how we handle the Line/Area charts as well. Should we start rendering the series fully one on top of eachother and only then adding "click/markhandlers" without styling on top of the charts? 🤔

This would make the styles more consistent, but might create other issues 🫠

The `metrics` property of `radar` takes an array with one item per corner of the radar.
This item can either be:

- A string used as the axis label. The other properties will be defaulted.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
- A string used as the axis label. The other properties will be defaulted.
- A string used as the axis label. The other properties are populated from the data.

I feel that defaulted usually has a negative connotation, we should probably not use it in documents. Eg: "John defaulted on his loans"

Another option is to fully remove the second phrase as well, it doesn't add much information.

Comment on lines +35 to +53
// @ts-ignore
onAxisClick,
disableAxisListener,
// @ts-ignore
highlightedItem,
// @ts-ignore
onHighlightChange,
sx,
title,
// @ts-ignore
xAxis,
// @ts-ignore
yAxis,
// @ts-ignore
zAxis,
// @ts-ignore
rotationAxis,
// @ts-ignore
radiusAxis,
Copy link
Member

@JCQuintas JCQuintas Feb 5, 2025

Choose a reason for hiding this comment

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

Eh, these are bad 😅

Might as well have "any" as type.

WDYT about moving these type changes to a different PR so we can discuss them in isolation? 🤔

Comment on lines +38 to +39
// The effect do not track any value defined synchronously during the 1st render by hooks called after `useChartPolarAxis`
// As a consequence, the state generated by the 1st run of this useEffect will always be equal to the initialization one
Copy link
Member

Choose a reason for hiding this comment

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

I didn't understand this comment

Comment on lines +17 to +60
testSkipIf(!isJSDOM)('should throw an error when axis have duplicate ids', () => {
const expectedError = [
'MUI X: The following axis ids are duplicated: qwerty.',
'Please make sure that each axis has a unique id.',
].join('\n');

expect(() =>
render(
<BarChart
xAxis={[
{ scaleType: 'band', id: 'qwerty', data: ['a', 'b', 'c'] },
{ scaleType: 'band', id: 'qwerty', data: ['a', 'b', 'c'] },
]}
series={[{ data: [1, 2, 3] }]}
height={100}
width={100}
/>,
),
).toErrorDev(expectedError);
});

// can't catch render errors in the browser for unknown reason
// tried try-catch + error boundary + window onError preventDefault
testSkipIf(!isJSDOM)(
'should throw an error when axis have duplicate ids across different directions (x,y)',
() => {
const expectedError = [
'MUI X: The following axis ids are duplicated: qwerty.',
'Please make sure that each axis has a unique id.',
].join('\n');

expect(() =>
render(
<BarChart
xAxis={[{ scaleType: 'band', id: 'qwerty', data: ['a', 'b', 'c'] }]}
yAxis={[{ id: 'qwerty' }]}
series={[{ data: [1, 2, 3] }]}
height={100}
width={100}
/>,
),
).toErrorDev(expectedError);
},
);
Copy link
Member

Choose a reason for hiding this comment

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

I guess you missed the tests here 😆

Comment on lines +26 to +40
export interface RadarDataProviderProps
extends Omit<
ChartContainerProps<'radar', RadarPluginSignatures>,
'series' | 'plugins' | 'rotationAxis' | 'radiusAxis' | 'dataset'
> {
/**
* The series to display in the bar chart.
* An array of [[RadarSeriesType]] objects.
*/
series: MakeOptional<RadarSeriesType, 'type'>[];
/**
* The configuration of the radar scales.
*/
radar: RadarConfig;
}
Copy link
Member

Choose a reason for hiding this comment

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

I like the API, sounds reasonable. series handles the series, radar is the chart config 👍

@bernardobelchior
Copy link
Member

Is it just me or are the circles not being affected by the area above them?

I assume this got carried over because of how we handle the Line/Area charts as well. Should we start rendering the series fully one on top of eachother and only then adding "click/markhandlers" without styling on top of the charts? 🤔

This would make the styles more consistent, but might create other issues 🫠

Hm, good points. I don't have any suggestions at the moment, but I'll let you know if I can think of something.

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Feb 7, 2025
Copy link

github-actions bot commented Feb 7, 2025

This pull request has conflicts, please resolve those before we can evaluate the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: charts This is the name of the generic UI component, not the React module! new feature New feature or request PR: out-of-date The pull request has merge conflicts and can't be merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants