-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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] Decouple margin
and axis-size
#16418
base: master
Are you sure you want to change the base?
Conversation
Deploy preview: https://deploy-preview-16418--material-ui-x.netlify.app/ |
CodSpeed Performance ReportMerging #16418 will degrade performances by 58.76%Comparing Summary
Benchmarks breakdown
|
86b3461
to
12e239f
Compare
@alexfauquette having the config in either the plugin or in the component cause a re-render which slows up the chart. An alternative could be to get rid of eg: <LineChart
xAxis={[
{
data: ['A', 'B', 'C', 'D'],
scaleType: 'point',
position: 'right',
},
]}
series={[{ data: [40, 30, 20, 10] }]}
height={200}
/> |
Sounds reasonable. Wwe will need to find a hack to let the You would still have the plugin in the following order "dimensions > series > axes" but the dimensions could have a selector that uses xAxis and yAxis config if available. The If later we want to adapt the size according the real space an axis takes we will also need to take into account the zoom and the tick we render. I assume the idea is then to have something like
|
By the way, the removal of leftAxis, ... can be done in a non breaking way. We can deprecate the props and keep default the same |
12e239f
to
7ee2fe4
Compare
I was effectively thinking of this issue #16387 about multiple axis as a nice example of what this new axis config could fix :) I'm good with the API, and adding |
|
||
export interface ChartsAxisProps { | ||
/** | ||
* Indicate which axis to display the top of the charts. | ||
* Can be a string (the id of the axis) or an object `ChartsXAxisProps`. | ||
* @default null | ||
* @deprecated Use `xAxis[].position="top"` instead. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're suggesting using xAxis
, but xAxis
isn't a prop of this component. I'm a bit confused by this, so I suspect a user would be too. Where should I set the xAxis
prop to maintain the behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 true it is not a prop of the ChartsAxis
but it is of the XxxChart
since it reuses the props.
In this use-case
I would assume there is no "single best approach", since the prop is not on this component, but also not on a single external component.
What we can do is provide a url to the migration guide where we can explain with specific examples.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm not sure what's the best way to proceed here. I suppose a URL to the migration guide is helpful, at least, so unless we can think of any better option, that seems good to me 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not super concerned by this point. I guess most of the users are using directly the ChartsXAxis
/ChartsYAxis
and they all now where the xAxis
props goes because with series
they are the most used
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
export const DEFAULT_AXIS_SIZE = 30; | ||
export const DEFAULT_AXIS_SIZES = { | ||
top: DEFAULT_AXIS_SIZE, | ||
bottom: DEFAULT_AXIS_SIZE, | ||
left: DEFAULT_AXIS_SIZE, | ||
right: DEFAULT_AXIS_SIZE, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm dubious about this approach. For me either you have an axis. ANd then this axis can have a default width
/height
properties. But the axis size should not have another default value than 0.
I don't see why it should get 30px space whereas their is no axis displayed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, I was copying the previous sizes, (margin = 50, now margin = 20, axis = 30) but it doesn't make sense indeed. 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to know which axis is actually going to be displayed beforehand? 🤔
My current issue is that the "default" axis, is added to the "available axis array", and counted towards sizing, even when not used. Any declared, but not used axis would count towards the "axisSize" with the current logic.
In the axis Components we have these:
// Skip axis rendering if no data is available
// - The domain is an empty array for band/point scales.
// - The domains contains Infinity for continuous scales.
if ((ordinalAxis && domain.length === 0) || (!ordinalAxis && domain.some(isInfinity))) {
return null;
}
So I assume we can only get the scale domain after processing the data, which means I'll probably have to move this to selectors
I guess 😢
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I assume we can only get the scale domain after processing the data
I don't see the relation between having the scale and displaying the axis. Users could on purpose:
- defines multiple scales but show only one of them because they need a mix of points and linear
- defines one scale and show it multiple times (for example on top and bottom or left and right)
Is there a way to know which axis is actually going to be displayed beforehand? 🤔
For me, it's part of your PR. The axes that are going to be displayed are the ones with
- position defined (left, right, top, or bottom)
- the first one of the defined axes
A simplification could be to say the default axes got by default position set to left/bottom and then only rely on the position to know which one should be displayed.
return { ...acc, top: acc.top + (cur.height || 0) }; | ||
} | ||
return { ...acc, bottom: acc.bottom + (cur.height || 0) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about moving the default here
return { ...acc, top: acc.top + (cur.height || 0) }; | |
} | |
return { ...acc, bottom: acc.bottom + (cur.height || 0) }; | |
return { ...acc, top: acc.top + (cur.height || DEFAULT_AXIS_HEIGHT) }; | |
} | |
return { ...acc, bottom: acc.bottom + (cur.height || DEFAULT_AXIS_HEIGHT) }; |
// TODO: fix dep cycle, should we move selectors to their own folder/file? Eg: | ||
// useChartCartesianAxis/selectors/chartXAxis.ts | ||
import { | ||
selectorChartXAxis, | ||
selectorChartYAxis, | ||
} from '../../featurePlugins/useChartCartesianAxis/useChartCartesianAxis.selectors'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mui/xcharts the current file setup is prone to dep cycles unrelated to the needed selectors. Should we move to more unitary files for selectors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having all selectors in the same file is usually useful because we can chain them.
You have a special. For which I would be in favor of having a dedicated file like useChartCartesianAxisLayout.selectors.ts
or useChartCartesianAxisRendering.selectors.ts
such that it's explicit that it contains selectors about how axes impacts the layout.
Thos selectors could compute the rendered ids and the size required.
(acc, id) => { | ||
const axis = xAxis.axis[id]; | ||
|
||
// TODO: move this to the selectorChartXAxis, axis.isUsed? axis.shouldRender? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mui/xcharts what do you think about moving this logic as a permanent part of the axis definition? This can then be used in the x/y axis renderers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thaugth it's the role of position?: 'left'|'right
with something like
axis.isUsed = !axis.position===undefined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thaugth it's the role of
position?: 'left'|'right
with something likeaxis.isUsed = !axis.position===undefined
Not really, because of
// Skip axis rendering if no data is available
// - The domain is an empty array for band/point scales.
// - The domains contains Infinity for continuous scales.
if ((ordinalAxis && domain.length === 0) || (!ordinalAxis && domain.some(isInfinity))) {
return null;
}
We could remove it, but it would only display a line, and we would have to fix some other issues with having bad values 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those are pathological cases where there is no data to display. The axis should exist and we should reserve some space for it when data gets loaded.
Here is an illustration where I modify the left margin when there is no data for the y-axis
https://codesandbox.io/p/sandbox/pensive-dust-g5jl44?file=%2Fsrc%2FDemo.tsx%3A15%2C51
Capture.video.du.2025-02-11.10-01-44.mp4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, makes sense
const completeBottomAxisProps = ( | ||
bottomAxes.length === 0 && !xAxis[xAxisIds[0]].position ? [xAxis[xAxisIds[0]]] : bottomAxes | ||
).map((axis) => ({ ...axis, ...mergeProps(axis, slots, slotProps) })); | ||
const completeLeftAxisProps = ( | ||
leftAxes.length === 0 && !yAxis[yAxisIds[0]].position ? [yAxis[yAxisIds[0]]] : leftAxes | ||
).map((axis) => ({ ...axis, ...mergeProps(axis, slots, slotProps) })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be simplified if you setup by default axis.position === 'bottom'
for the first item in axis pluggin
// TODO: fix dep cycle, should we move selectors to their own folder/file? Eg: | ||
// useChartCartesianAxis/selectors/chartXAxis.ts | ||
import { | ||
selectorChartXAxis, | ||
selectorChartYAxis, | ||
} from '../../featurePlugins/useChartCartesianAxis/useChartCartesianAxis.selectors'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having all selectors in the same file is usually useful because we can chain them.
You have a special. For which I would be in favor of having a dedicated file like useChartCartesianAxisLayout.selectors.ts
or useChartCartesianAxisRendering.selectors.ts
such that it's explicit that it contains selectors about how axes impacts the layout.
Thos selectors could compute the rendered ids and the size required.
(acc, id) => { | ||
const axis = xAxis.axis[id]; | ||
|
||
// TODO: move this to the selectorChartXAxis, axis.isUsed? axis.shouldRender? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thaugth it's the role of position?: 'left'|'right
with something like
axis.isUsed = !axis.position===undefined
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
@alexfauquette FYI this will still be a breaking change for users who are using custom components. There is no way that the Should we go full BC instead then? Would simplify some stuff, and we can try to provide some codemods |
|
||
// TODO: Add links to the migration docs for each prop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be done in this PR or as a follow-up? Just making sure it doesn't fall through the cracks 😄
input: LayoutConfig['margin'], | ||
defaultMargin?: ChartMargin, | ||
): Partial<ChartMargin> | undefined; | ||
export function defaultizeMargin( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although used across our codebase, "defaultize" doesn't seem to be used. I wonder if we should rename this to defaultMargin
, withDefaultMargin
or withFallbackMargin
. defaultMargin
would make more sense, but "default" it is both a verb and a noun, so it could be confusing.
Not a blocker, just something to think about.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't review too much this PR, as it is still in Draft
😆
Fixes #16387
Fixes #16383