Chart.js icon indicating copy to clipboard operation
Chart.js copied to clipboard

Fix: display stacked bar with multiple x-Axis

Open xavierleune opened this issue 7 months ago • 2 comments

Hello everyone,

When you try to use stackedBars with multiple axes, the bars all render at the center of each axis chunk, so they're hidden by whichever bar is rendered last.

You can see a live example on codepen

screenshot

This pull request fixes this behavior. It counts the number of x axes and adjusts size and positions according to that number.

As this is my first pull request on this project, I may have missed something. If that's so, please tell me.

Have a nice day !

xavierleune avatar May 07 '25 13:05 xavierleune

@LeeLenaleee thanks for the approval on the pipeline, do you have an idea why it failed ? It seems to be unrelated to my changes ?

xavierleune avatar May 09 '25 14:05 xavierleune

Sorry I have not had the time to give the tests a good look, I will try to look at it later this week

LeeLenaleee avatar May 13 '25 07:05 LeeLenaleee

@LeeLenaleee Hi there, do you we can retry the pipeline to see if it's still failing ? 🙏

xavierleune avatar Jun 05 '25 12:06 xavierleune

@xavierleune I re-ran the pipeline to see. It looked like an image difference was detected. If it fails again, I'll look a bit more closely

etimberg avatar Jun 05 '25 13:06 etimberg

ok, it failed again. The failing image is https://github.com/chartjs/Chart.js/blob/master/test/fixtures/scale.linear/grace/grace-beginAtZero.png

Not sure why that's failing only on ubuntu, but I wonder if the failure is due to the test having multiple x axes and now the image has changed. I'm a bit hesitant to change the tolerance on that test without knowing why it has changed. Are you able to run the test locally and see?

etimberg avatar Jun 05 '25 13:06 etimberg

thanks for the feedback @etimberg I'll have a look on that test 👍

xavierleune avatar Jun 05 '25 13:06 xavierleune

Ok I see, this test use: indexAxis: 'y' and this pull request focuses on x axis. I'll make the code more generic

xavierleune avatar Jun 05 '25 13:06 xavierleune

This should be correct now, I carefuly reviewed the tests and I think the other failures are only due to fonts mismatch and so... 🤞

xavierleune avatar Jun 05 '25 14:06 xavierleune

@LeeLenaleee, @etimberg This change broke my code. I'm using two x-axes to create an annotation effect with a bar chart: Github Chartjs Issue

The annotation is the red bullet. A real world scenario would be to indicate that a bar is beyond a limit. I'm doing this by having a secondary x-axis on top of the chart area. To create an (optional) annotation I'm styling the tick label using a red bullet "⬤". This approach solved many other issues, e.g. you can easily hide the annotations by hiding just the secondary x-axis and it also frees space on the canvas when it's hidden by just the way Chart.js works.

With this change my bars appear misaligned to the left. The reason is obvious as now two x-axes are counted and that alters the calculation of the bar positions in a bad in IMO unexpected way. I understand the intention of this PR but I'm also conviced the new behavior is a bug. Instead of grouping bars using multiple x-axes one should group using a group of bars instead: https://www.chartjs.org/docs/latest/samples/bar/stacked-groups.html.

I can imagine this change will break other applications, too. For me, I either create another local patch to restore the previous behavior as now I cannot see any other way to implement this aside from creating a (complex) plugin.

Edit: Or maybe we need a flag to indicate whether an axis should be counted. Creating this flag should be easy. But the question remains whether this is the right approach anyways or if one should just use groups of bars.

liondog avatar Jun 20 '25 09:06 liondog

@LeeLenaleee, @etimberg This change broke my code. I'm using two x-axes to create an annotation effect with a bar chart: Github Chartjs Issue

The annotation is the red bullet. A real world scenario would be to indicate that a bar is beyond a limit. I'm doing this by having a secondary x-axis on top of the chart area. To create an (optional) annotation I'm styling the tick label using a red bullet "⬤". This approach solved many other issues, e.g. you can easily hide the annotations by hiding just the secondary x-axis and it also frees space on the canvas when it's hidden by just the way Chart.js works.

With this change my bars appear misaligned to the left. The reason is obvious as now two x-axes are counted and that alters the calculation of the bar positions in a bad in IMO unexpected way. I understand the intention of this PR but I'm also conviced the new behavior is a bug. Instead of grouping bars using multiple x-axes one should group using a group of bars instead: https://www.chartjs.org/docs/latest/samples/bar/stacked-groups.html.

I can imagine this change will break other applications, too. For me, I either create another local patch to restore the previous behavior as now I cannot see any other way to implement this aside from creating a (complex) plugin.

Edit: Or maybe we need a flag to indicate whether an axis should be counted. Creating this flag should be easy. But the question remains whether this is the right approach anyways or if one should just use groups of bars.

We are experiencing the exact same problem

AminFazlMondo avatar Jun 23 '25 04:06 AminFazlMondo

@etimberg maby best of we revert this change for now.

What do you think?

LeeLenaleee avatar Jun 23 '25 06:06 LeeLenaleee

@liondog the stacked group has a different purpose, having different x-axis allows to display side by side different temporal data (i.e: comparison between 2024 and 2025, month by month) ; with stacked groups you won't have different labels (that why multiple axis are made for). It seems to me that this bugfix is still justified and you used a bug as a feature ^^ Fixing a bug is always a kind of BC Break and I don't think that reverting this bugfix to use axis as annotations a good idea.

You probably may achieve the same behavior by:

  • Adding a new dataset on the same axis for the red bullet
  • Using the annotation plugin
  • Listening onClick on the legend to programmatically disable the annotation if your secondary dataset is hidden

This implementation would be much more solid in my opinion

xavierleune avatar Jun 23 '25 09:06 xavierleune

@liondog the stacked group has a different purpose, having different x-axis allows to display side by side different temporal data (i.e: comparison between 2024 and 2025, month by month) ; with stacked groups you won't have different labels (that why multiple axis are made for). It seems to me that this bugfix is still justified and you used a bug as a feature ^^ Fixing a bug is always a kind of BC Break and I don't think that reverting this bugfix to use axis as annotations a good idea.

Thank you for giving more insights into your rationale for using two stacked timescales :-). It's a nice way of some sort of "line wrap" on one axis.

I wouldn't say that the previous behavior was a bug. That was, after all, the unchanged behavior of Chart.js for many years. I think both approaches are correct in their way. Perhaps your behavior could be extended to only count axes on the same border? Or on the same configurable "stack" (meaning a type of configuration that logically groups scales together, as implemented by this bug fix). Otherwise, what is the point of a second x-axis aligned to the top? Imagine another example with months being plotted on the bottom x-axis and quarters on another x-axis at the top of the chart to get a different visual grouping for the same bar dataset. This won't work anymore.

I will need more time to work out another possible solution for my use case, but for now I am fine with my patch. Your suggestions are not easily implementable as they introduce other problems.

To summarize: In my humble opinion, these two different approaches should be configurable.

liondog avatar Jun 25 '25 22:06 liondog

@LeeLenaleee inagree, we should revert it. We can reintroduce it behind a config option if necessary

etimberg avatar Jun 25 '25 22:06 etimberg

hi there :)

I am experiencing an issue related to this bug fix. I utilized stacked bar charts across different axes to overlay a background chart aligned with a time-based axis for visualizing weekend days. When rendering my bar charts with the grouped = false option, the visualization behaves as expected for all stacked bars but when their not stacked all bars are centered on their respective grid lines. However, when setting grouped = true, all axis are aligned on the same grid. This behavior appears to be linked to the grouped configuration for bar charts.

Would it be possible to implement a dataset.group = 'name' property analogous to the dataset.stack option? The intent is to prevent the irregular alignment observed when manipulating both the grouped and stacked options across multiple axes.

chatondearu avatar Jul 17 '25 11:07 chatondearu