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] Add minTickLabelGap to x-axis #16548

Merged
merged 10 commits into from
Feb 20, 2025

Conversation

bernardobelchior
Copy link
Member

@bernardobelchior bernardobelchior commented Feb 12, 2025

Part of #10928.

Changelog

  • Add minTickLabelGap to x-axis, which allows users to define the minimum gap, in pixels, between two tick labels. The default value is 4px. Make sure to check your charts as the spacing between tick labels might have changed.

@mui-bot
Copy link

mui-bot commented Feb 12, 2025

Comment on lines 143 to 147
/**
* The minimum gap in pixels between two tick labels.
* @default 8
*/
minTickLabelGap?: number;
Copy link
Member Author

Choose a reason for hiding this comment

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

Added this only for the x-axis because the y-axis doesn't accept discrete data.

Copy link
Member

Choose a reason for hiding this comment

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

what do you mean that the y-axis doesn't accept discrete data? 🤔

Wouldn't it depend on the chart type and orientation?

Copy link
Member Author

Choose a reason for hiding this comment

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

Wouldn't it depend on the chart type and orientation?

This gap only makes sense for cartesian axes, and I'm assuming that the Y axis represents the second coordinate, regardless of orientation. Is that assumption wrong?

Highcharts does the same. In this example, the x-axis on the left and the y-axis is at the bottom.

Do you think we should not follow this convention? I see some benefits, namely having correct types for the x- and y-axes: since the x-axis is the only one that can accept discrete data, we don't need props related to discrete data in the y-axis.

Let me know your thoughts, maybe I'm missing something or there are some less conventional charts where my assumption is wrong.

Copy link
Member

Choose a reason for hiding this comment

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

Is that assumption wrong?

Yes, and we have a nice counter example for that: the heatmap which is made of two band axes

If you only want to add something to some scale type (point and band in your case) you have the AxisScaleConfig.

Notice this property will only apply on the x-axis because it's the only one to hide some labels.
The reason behind that is not the continuouse/categorical data. It's because x-axis labels are more likely to overflow since they are impacted by labels length

Copy link
Member Author

Choose a reason for hiding this comment

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

Good points, hadn't thought of that.

So, in theory, the minTickLabelGap can also be applied to the y-axis. However, it seems unlikely we'll need it because of the reason you delineated, so I'm going to assume we're only going to implement this for the x-axis for now.

@bernardobelchior bernardobelchior force-pushed the chart-axis-min-gap branch 2 times, most recently from 52b38e3 to c549b8e Compare February 12, 2025 10:52
@bernardobelchior
Copy link
Member Author

@alexfauquette @JCQuintas just realized that we have an issue in our measuring code and it doesn't properly measure text sizes when classes are applied.
I think we might need to change our approach because in AxisSharedComponents we're using CSS selectors to properly style elements, however the measurement span isn't a child of Axis, so even if we apply the tickLabel class, it won't be properly styled, causing the measurement to be off.
I'm thinking that we'll need to actually render the labels (maybe with visibility: hidden) and measure them inside the SVG, otherwise we won't be able to get a correct measurement.
Since we're already rendering all labels in SSR, would it make sense to render them with visibility: hidden, measure them, and then create a ResizeObserver (to listen to style changes)? On style change, we'd need to re-render and re-measure the labels.

What do you think?

@JCQuintas
Copy link
Member

What do you think?

Are you suggesting that we should get the measurements from the actual tick label element instead of a mock element?

then create a ResizeObserver

Is ResizeObserver correct here? You might want a MutationObserver as well/instead

@bernardobelchior
Copy link
Member Author

Are you suggesting that we should get the measurements from the actual tick label element instead of a mock element?

Yes.

Is ResizeObserver correct here? You might want a MutationObserver as well/instead

Yeah, I meant a ResizeObserver because we only care about the size of the element. It's fine if a label changes its color: as long as it doesn't impact it's size, we don't need to know about it.

I don't think a MutationObserver would work here because style changes in a class wouldn't be caught. We could only capture changes to a style attribute, as far as I understand.

@JCQuintas
Copy link
Member

Are you suggesting that we should get the measurements from the actual tick label element instead of a mock element?

Yes.

Is ResizeObserver correct here? You might want a MutationObserver as well/instead

Yeah, I meant a ResizeObserver because we only care about the size of the element. It's fine if a label changes its color: as long as it doesn't impact it's size, we don't need to know about it.

I don't think a MutationObserver would work here because style changes in a class wouldn't be caught. We could only capture changes to a style attribute, as far as I understand.

Ok, I don't see an issue with using the elements themselves, but I didn't build that feature, so maybe @alexfauquette has other concerns

@@ -96,6 +100,7 @@ const defaultProps = {
disableLine: false,
disableTicks: false,
tickSize: 6,
tickLabelMinGap: 4,
Copy link
Member Author

Choose a reason for hiding this comment

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

I've reduced this to 4px as it seems enough.

Examples with 4px:

image

image

Examples with 8px:

image

image

@bernardobelchior bernardobelchior added the component: charts This is the name of the generic UI component, not the React module! label Feb 19, 2025
@bernardobelchior
Copy link
Member Author

@alexfauquette @JCQuintas this is a breaking change in the sense that the previous behavior has been altered, but there's no way to get the exact previous behavior back. We can suggest setting tickLabelMinGap to 0, but the previous implementation always added 20% on top of the width of the tick labels.

As such, this isn't codemod-able, so where should I write documentation to inform users of this change? Is it enough to add a changelog entry or should I also write a section in migration-charts-v7.md about this change?

Copy link

codspeed-hq bot commented Feb 19, 2025

CodSpeed Performance Report

Merging #16548 will improve performances by 17.31%

Comparing bernardobelchior:chart-axis-min-gap (e701067) with master (68412f1)

Summary

⚡ 2 improvements
✅ 4 untouched benchmarks

Benchmarks breakdown

Benchmark BASE HEAD Change
BarChart with big data amount 584 ms 497.8 ms +17.31%
BarChartPro with big data amount 868.5 ms 767.4 ms +13.17%

@JCQuintas
Copy link
Member

@alexfauquette @JCQuintas this is a breaking change in the sense that the previous behavior has been altered, but there's no way to get the exact previous behavior back. We can suggest setting tickLabelMinGap to 0, but the previous implementation always added 20% on top of the width of the tick labels.

As such, this isn't codemod-able, so where should I write documentation to inform users of this change? Is it enough to add a changelog entry or should I also write a section in migration-charts-v7.md about this change?

Usually we add it on both. The ## Changelog is for information purposes, the migration-charts-v7.md is the actual doc

Comment on lines 144 to 145
* The minimum gap in pixels between two tick labels.
* @default 8
Copy link
Member

Choose a reason for hiding this comment

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

Probably good to mention that if two tick labels overlap, one of them will be hidden.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done 👍

Copy link
Member

@alexfauquette alexfauquette left a comment

Choose a reason for hiding this comment

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

Comments are about

  • A slightly less verbose default. But the way it's defined is less obvious, so feel free to ignore
  • The update of the @default (will require to run scripts again)
    Other than that it looks ready to merge 👍

@@ -39,10 +39,14 @@ function addLabelDimension(
{
tickLabelStyle: style,
tickLabelInterval,
tickLabelMinGap,
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
tickLabelMinGap,
tickLabelMinGap = 4,

@@ -39,10 +39,14 @@ function addLabelDimension(
{
tickLabelStyle: style,
tickLabelInterval,
tickLabelMinGap,
reverse,
isMounted,
}: Pick<ChartsXAxisProps, 'tickLabelInterval' | 'tickLabelStyle'> &
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
}: Pick<ChartsXAxisProps, 'tickLabelInterval' | 'tickLabelStyle'> &
}: Pick<ChartsXAxisProps, 'tickLabelInterval' | 'tickLabelStyle' | 'tickLabelMinGap'> &

Pick<AxisDefaultized, 'reverse'> & { isMounted: boolean },
Pick<AxisDefaultized<ScaleName, any, ChartsXAxisProps>, 'reverse'> & {
isMounted: boolean;
tickLabelMinGap: NonNullable<ChartsXAxisProps['tickLabelMinGap']>;
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
tickLabelMinGap: NonNullable<ChartsXAxisProps['tickLabelMinGap']>;

@@ -96,6 +101,7 @@ const defaultProps = {
disableLine: false,
disableTicks: false,
tickSize: 6,
tickLabelMinGap: 4,
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
tickLabelMinGap: 4,

@bernardobelchior
Copy link
Member Author

bernardobelchior commented Feb 20, 2025

  • A slightly less verbose default. But the way it's defined is less obvious, so feel free to ignore

I had it this way before, but I changed it to be more in line with other defaults. It probably makes sense to have the defaults in the same place so it's easier to understand where they come from, or do you see any drawbacks with that?

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Feb 20, 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 Feb 20, 2025
@bernardobelchior bernardobelchior merged commit ff10228 into mui:master Feb 20, 2025
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change component: charts This is the name of the generic UI component, not the React module!
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants