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

Group stacked bar charts (#2643) #3563

Merged
merged 6 commits into from
Jan 1, 2017
Merged

Group stacked bar charts (#2643) #3563

merged 6 commits into from
Jan 1, 2017

Conversation

potatopeelings
Copy link
Contributor

Code changes, documentation update and sample for #2643.

JSFiddle: https://jsfiddle.net/Lvj2qnrp/1/
(same as samples/bar/bar-stacked-group.html)

@@ -49,6 +49,7 @@ borderSkipped | `String or Array<String>` | Which edge to skip drawing the borde
hoverBackgroundColor | `Color or Array<Color>` | Bar background color when hovered
hoverBorderColor | `Color or Array<Color>` | Bar border color when hovered
hoverBorderWidth | `Number or Array<Number>` | Border width of bar when hovered
stack | `String` | For grouping stacked bars
Copy link
Member

Choose a reason for hiding this comment

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

Is String the best type for this? Would a number make more sense?

Copy link
Contributor Author

@potatopeelings potatopeelings Nov 8, 2016

Choose a reason for hiding this comment

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

I think String is the best, because it serves as the name of the stack. For instance, here's chadcodes' image from the related issue

fff8fe0e-270e-11e6-8675-f2dfb0594676

Also a number would imply an order (confusing if Dataset1 and 2 are in stack 2 and Dataset 3 is in stack 1)

On a slightly related note - do you think stack is a generic enough property name, or would something like group (or cluster) be better?

Copy link
Member

Choose a reason for hiding this comment

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

I think that's a good call then. You're right that the numbers could imply an order to the stacks.

I think stack is ok. Maybe @simonbrunel has some thoughts on this

Copy link
Member

Choose a reason for hiding this comment

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

I was thinking group first, but finally stack might be better. Can we explicitly document this dataset.stack as an ID and not a name to avoid any confusion with label/text/title?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was thinking group first, but finally stack might be better.

After I chose stack, I was worried that it would be very bar specific (just in case we want to use the same property for grouping datasets for other chart types) + we are calling it grouped stacked bars. But I'll go with whatever you suggest (I've being going back and forth between them that they both seem logical - I'll have good things to say about stack once we've changed to group :-))

Can we explicitly document this dataset.stack as an ID and not a name to avoid any confusion with label/text/title?

You mean call it stackID (like we do with axisIDs) and point to options (in future) for name, styling? Or do you mean just change For grouping stacked bars to ID for grouping stacked bars? Should be simple enough either way.

Copy link
Member

Choose a reason for hiding this comment

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

I mean simply change the documentation: identifier to group bars in stack(s) (or similar) but keep the property name simple: stack. About the stack vs group, group would make sense if this concept could be implemented for every types of chart, which I'm not sure it's possible, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok done!

About the stack vs group , group would make sense if this concept could be implemented for every types of chart, which I'm not sure it's possible, right?

I was thinking about folks who extend Chart.js to add other chart types (outside of the core library). If we call this stack, they may be tempted to add / use a new property that's better suited to their chart type.

A convoluted example would be a grouped scatter plot (with pie charts instead of dots). stack feels out of place, group might be ok.

},

// Get the number of datasets that display bars. We use this to correctly calculate the bar width
// Correctly calculate the bar width accounting for stacks and the fact that not all bars are visible
getBarCount: function() {
Copy link
Member

Choose a reason for hiding this comment

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

Might be a good idea to have a unit test that tests this new functionality by creating a chart with multiple stacks

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree. I haven't worked with karma yet. Will read up a bit and add tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added unit test. Cheers!

if (xScale.options.barThickness) {
return xScale.options.barThickness;
}
return xScale.options.stacked ? ruler.categoryWidth : ruler.barWidth;
return (xScale.options.stacked && meta.stack === undefined) ? ruler.categoryWidth : ruler.barWidth;
Copy link
Member

Choose a reason for hiding this comment

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

might be a good idea to always return bar width here and then have it correctly calculate for the stacks in getRuler

Copy link
Contributor Author

@potatopeelings potatopeelings Nov 8, 2016

Choose a reason for hiding this comment

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

The && meta.stack === undefined retains the existing behaviour for (non-grouped) stacked bar widths - which is currently independent of barPercentage (and depends only on categoryPercentage).

I think it would be consistent to have non grouped stacked bar widths proportional to categoryPercentage AND barPercentage. If it's ok to change to make this change, I can remove meta.stack === undefined from getBarCount and remove the entire (xScale.options.stacked && meta.stack === undefined) ? ruler.categoryWidth : part from here (i.e. just use ruler.barWidth which I believe is already correctly calculated)

Copy link
Member

Choose a reason for hiding this comment

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

I think the ungrouped stacked bar (or stacked with a single group) should use the bar width. I think it's safe to go ahead and make those changes. This will likely cause some test changes as well if we are testing the bar width at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done and changed unit tests.

I also changed the controller method names - getBarCount to getStackCount and getBarIndex to getStackIndex. The ruler property barCount is now stackCount. Related unit tests changed too.

@@ -33,16 +33,16 @@ module.exports = function(Chart) {

helpers.each(datasets, function(dataset, datasetIndex) {
var meta = chart.getDatasetMeta(datasetIndex);
if (valuesPerType[meta.type] === undefined) {
valuesPerType[meta.type] = {
if (valuesPerType[meta.type + meta.stack] === undefined) {
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if there is a way to not have the stack leak into here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm... its somewhat similar to meta.type in that it's just another property to group datasets. Maybe a common meta property that factors in both type and (an optional stack) would be better? Might be too big a change though.

Can't think of anything nicer :-(

@erick2014
Copy link

hey guys, when will be this branch merged?

@simonbrunel
Copy link
Member

simonbrunel commented Nov 9, 2016

I didn't get time to review the whole code (will do asap) but I feel that the stack option is not at the right place and should live only under scales. It could be confusing since people would expect that two datasets with the same stack id would be automatically stacked (so implicitly set scale.stacked: true), whatever scale is bound to each datasets. Also, the current approach doesn't give room for future stack customization (name, style, etc.).

A rough idea would be to make the stacked option to accept a config object:

(deprecated code hidden)

stacked: true would mean to stack all datasets using that scale.

@potatopeelings
Copy link
Contributor Author

@simonbrunel - the stack name feels a lot similar to the xAxisID and yAxisID properties on the dataset. And it would probably be a bit easier to manipulate the datasets in one place vs. changing them in both the data property and within the options property.

IMO stack (i.e. the group name) is more data than option (data being something you can't omit without losing information, and option being something you can omit if you're ok with the defaults).

@simonbrunel
Copy link
Member

@potatopeelings, after a second thought, I agree with you that it's more convenient to keep the stack property under datasets. Then, what about to implicitly turn stacking on if scale.stacked is undefined (and stack only datasets with an explicit stack ID)? I'm sure it would save some questions/issues.

@potatopeelings
Copy link
Contributor Author

@simonbrunel - sounds good! I'll add in something in scale initialize to set stacked based on the datasets and a stack === undefined checks in the bar controller. Cheers!

@simonbrunel
Copy link
Member

simonbrunel commented Nov 11, 2016

@etimberg what do you think about this scale.stacked behavior when undefined?

Actually, there are many cases to think about:

(a) datasets: [{}, {}, {}, {}]
(b) datasets: [{ stack: 'foo' }, { stack: 'foo' }, {}, {}]
(c) datasets: [{ stack: 'foo' }, { stack: 'foo' }, { stack: 'bar' }, { stack: 'bar' }]

scale.stacked: undefined
  (a) 4 stacks: [0][1][2][3]
  (b) 3 stacks: [0,1][2][3]
  (c) 2 stacks: [0,1][2,3]

scale.stacked: true
  (a) 1 stack: [0,1,2,3]
  (b,c) 2 stacks: [0,1][2,3]

scale.stacked: false
  (a,b,c) 4 stacks: [0][1][2][3]

Does it make sense? if so, would be great to have unit tests for all of these cases.

@etimberg
Copy link
Member

@simonbrunel I think I agree with that behaviour. I think it's pretty unlikely that scale.stacked would be undefined since I think the default config has it as false.

@potatopeelings
Copy link
Contributor Author

potatopeelings commented Nov 12, 2016

@simonbrunel - Done!

@etimberg - The default config doesn't define stacked.

BTW, there seems to be an existing (i.e. before groups) problem with stacks and logarithmic scales. I have raised an issue for it (#3585) . Just thought I'd mention it in case you notice something funny there when reviewing this PR.

@simonbrunel
Copy link
Member

What's the final decision on the option name: stack or group? If we choose group, isn't there any risk to face a case where we want both at the same time but with different meaning? For example, stack for stacking (obviously) and group for tooltips or highlight.

I like group because it can apply to different chart types, but I think I'm more confident with stack regarding API evolution.

@potatopeelings
Copy link
Contributor Author

I prefer group because of the same reason (applying to different chart types).

If we want to group for tooltips or highlighting, I think think something more specific would be better (tooltipGroups) or something where it's clear from the object structure like

       ...
       tooltips: {
            groups: [
                 [ "Dataset 1", "Dataset 2" ], 
                 [ "Dataset 3" ], 
            ]
       }
       ...

That said, I don't mind stack either if everyone is for that.

@etimberg
Copy link
Member

etimberg commented Dec 1, 2016

@simonbrunel is this good to go?

@simonbrunel
Copy link
Member

If everyone agree to use stack as config name, then I guess it's ready.

@potatopeelings
Copy link
Contributor Author

I'm good with stack.

@etimberg
Copy link
Member

etimberg commented Dec 3, 2016

@potatopeelings can you fix the merge conflict? then I'll merge this

@potatopeelings
Copy link
Contributor Author

@etimberg - sure

@murphomatic
Copy link

Excited for this merge ... waiting on bated breath here. :)

@potatopeelings
Copy link
Contributor Author

@etimberg - I will need a few more days to get this sorted out (nothing to do with the code - my monitor just gave out :-(). Sorry! I should have done it straightaway instead of putting it off for the weekend.

@etimberg
Copy link
Member

@potatopeelings thanks for letting me know

@nhaberl
Copy link

nhaberl commented Dec 22, 2016

Excited for this merge :-)

@potatopeelings
Copy link
Contributor Author

All done!

The (new and existing) codeclimate issues are because of the similarity in code between bar and horizontalBar (only the axes differ in these functions). These can be fixed by parameterizing the axes (but anyway, it would be cleaner to do that in a separate PR)

Monitor is still wonky though - it gives out every 15 minutes instead of 1 now, so 2017 is better than 2016 :-)

@etimberg etimberg merged commit eebaa84 into chartjs:master Jan 1, 2017
@etimberg
Copy link
Member

etimberg commented Jan 1, 2017

Thanks @potatopeelings

@ryanwinchester
Copy link

@potatopeelings is it possible to add labels to stacks like in this fiddle (uses v2.1 and I think was a previous fiddle of yours)?

http://jsfiddle.net/ryanwinchester/vw1wc464/1/

I tried editing it to use 2.5 but that didn't work for me.

(@etimberg )

@vaseker
Copy link

vaseker commented Mar 24, 2017

After update chart.js 2.4.0 –> 2.5.0 my chart looks this
2017-03-24 17 33 55
instead of this
2017-03-24 17 34 57

Can anybody suggest which configuration should I set to see the old picture?

Example page
Source code

Thanks.

@simonbrunel
Copy link
Member

@vaseker That's a bug which should be fixed by #4044

@vaseker
Copy link

vaseker commented Mar 24, 2017

@simonbrunel understood. Thanks!

@JohnCoding94
Copy link

@simonbrunel i still have this bug with the current version, do we know why?

@simonbrunel
Copy link
Member

@JohnCoding94 can you build a jsfiddle that demonstrates your issue with 2.7.1?

@JohnCoding94
Copy link

@simonbrunel i am using react-chartjs-2, i'll try to post that, thanks.

@simonbrunel
Copy link
Member

Sounds good, also have a look to that comment to make sure you use the correct stacked options.

@JohnCoding94
Copy link

Here is my options JSON:

{
      maintainAspectRatio: false,
      legend: {
        display: false,
        position: 'left'
      },
      title: {
        display: true,
        text: '2018',
      },
      scales: {
        xAxes: [{
          stacked: true,
        }],
        yAxes: [{
          stacked: true
        }]
      }
};

Do you see something wrong with that syntax?
It may comes from the react-chartjs-2 wrapper, i'll post a jsfiddle ASAP.

@simonbrunel
Copy link
Member

It looks ok and should stack in both directions, what result do you get then?

@JohnCoding94
Copy link

The same as @vaseker when he upgraded from the 2.4.0 to the 2.5.0.

@ghost
Copy link

ghost commented Jun 16, 2018

Any update on this one? https://jsfiddle.net/e8n4xd4z/9385/
read here also #5557

Apparently 2.4.0 was the latest version this thing worked. See above comment by @vaseker

exwm pushed a commit to exwm/Chart.js that referenced this pull request Apr 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants