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

Mixed chart #5134

Closed
wants to merge 18 commits into from
Closed

Mixed chart #5134

wants to merge 18 commits into from

Conversation

loicbourgois
Copy link
Contributor

@loicbourgois loicbourgois commented Jan 11, 2018

As mentioned in #4184, #4587, #4096, the mixed chart functionality is a bit buggy : it only allows line charts to be added to a bar chart.

Original comment

I found a way to display line, bar, scatter on a chart of type line or bar.
I'm afraid it doesn't address the root problem, but it's a net improvement.
You can find a working example here : https://codepen.io/anon/pen/zpWMOj

More mixed chart

I made changes to initConfig in the core controller, to merge options from the child charts in the parent chart.
Supported parents are : line, bar, undefined
Supported childs are : scatter, line, bubble, bar
Here is an example with the changes until commit 042920e : mixed
You can compare it to the current behavior : 2.7.1

package-lock.json Outdated Show resolved Hide resolved
Bar works as parent and child
Line works as parent and child
Scatter works as child only
@@ -37,6 +37,10 @@ defaults._set('scatter', {
module.exports = function(Chart) {

// Scatter charts use line controllers
Chart.controllers.scatter = Chart.controllers.line;
Chart.controllers.scatter = Chart.controllers.line.extend({
Copy link
Member

Choose a reason for hiding this comment

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

What's the reason for overriding and returning false here? Shouldn't that have already happened?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From what i understand, with mixed chart, the default style applied is the style of the 'main' chart.
And for now, only bar or line are valid main types.
So the option showLines is never set to false. You'd have to do it manually in the child chart.
You can try commenting the line n°8187 and n°19040 in this codepen
And you can check the console for Chart.config.data.datasets[0].showLine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But now that I think about it, is being able to show lines in a scatter chart a wanted feature ?
If so, then this overriding would break 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 think lines on a scatter chart is something that should be possible and its currently the default behaviour

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So does that mean a test should be added via another PR to make sure it stays that way in the future ?

Copy link
Member

Choose a reason for hiding this comment

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

I would think adding a test to ensure that a scatter chart can optionally use a line or not is a good idea. @simonbrunel thoughts?

Copy link
Member

Choose a reason for hiding this comment

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

I don't see why scatter charts couldn't display lines, so yes these lineEnabled changes should be reverted. We could add a test indeed, but in a different PR.

return this.getScaleForId(this.getIndexScaleId());
var scale = this.getScaleForId(this.getIndexScaleId());
if (scale.options.categoryPercentage === undefined) {
scale.options.categoryPercentage = defaultBar.scales.xAxes[0].categoryPercentage;
Copy link
Member

Choose a reason for hiding this comment

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

[nit] I would put defaultBar.scales.xAxes[0] into a local variable to save a few bytes

@loicbourgois loicbourgois changed the title Mixed chart (line+bar+scatter) with line or bar as main type Mixed chart Jan 12, 2018
@@ -31,10 +31,31 @@ module.exports = function(Chart) {
data.datasets = data.datasets || [];
data.labels = data.labels || [];


// Merge options for mixed charts
// If there is at least one 'bar' chart, main type is set to 'bar'
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you expand the comment to explain why If there is at least one 'bar' chart, main type is set to 'bar'

One thing I'm wondering about is if there's a more generic way to do this. I'm worried that this won't work if you want to mix a financial chart and line chart for example (https://github.com/chartjs/chartjs-chart-financial)

Copy link
Member

Choose a reason for hiding this comment

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

I share the same concerns: wouldn't be better to add support for bar specific options (scale) at the dataset level and avoid touching the options logic? Actually, I think that can cause breaking changes, especially the logic that merges all dataset defaults on top of each others. I'm not even sure it works great since the last dataset (options) overwrites the previous ones.

For example, with the following types: line > scatter, the value of showLines will be false but scatter > line generates showLines: true (not consistent). Same for line > bubble, the tooltip options will break for the line.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree that we should try to have all options supported at the dataset level

@simonbrunel
Copy link
Member

I don't know exactly what are the "blocking" issues that make mixed charts impossible, but unfortunately, charts are not consistent in their default options and I don't think there is a magic way to generate the "good" defaults for any mixed charts (e.g. can't merge legendCallback, tooltips.callbacks.label, etc.).

IMO, if no global type is given, then only the global defaults should be merged and the dataset.type should become mandatory. The user would have to provide the correct options that works great with his datasets and on our side we should make sure that we can handle mixed dataset types.

@benmccann
Copy link
Contributor

The are two main issues with mixed charts.

The first is that we have scatter and line charts which are the same exact thing, but with different default values of showLines. It's problematic that we try to set this variable both via chart type and via a config option.

The second is that categoryPercentage and barPercentage do not have default values unless the overall chart type is bar. That means that you cannot have a line chart with bar dataset because those values will be undefined. It currently works only if you swap them (i.e. bar chart with line dataset)

@simonbrunel
Copy link
Member

Are these really blocking issues?

With 2.7.1, mixed line + bar + scatter + bubble seems to work (fiddle). Though, it only works if the chart type is line or bar, that would need to be investigated. But it doesn't seem better with the suggested changes?

@benmccann
Copy link
Contributor

I've explicitly defined categoryPercentage and barPercentage in the past when I want to use a mixed chart. However, it took me half an hour of debugging to realized I needed to do this. Hopefully we can make it easier for users. I would suggest that we move the default values for these config options from controller.bar.js to core.scale.js

@benmccann
Copy link
Contributor

benmccann commented Jan 14, 2018

It looks like the fix of providing default categoryPercentage and barPercentage fixes some mixed charts with bars in the sample provided here, but not all. There's an additional issue, which is that the bar chart calls scale.getPixelForValue(null, i, datasetIndex) and each scale has a different signature for getPixelForValue

Bar charts work with time scales and category scales today:

src/scales/scale.time.js:		getPixelForValue: function(value, index, datasetIndex) {
src/scales/scale.logarithmic.js:		getPixelForValue: function(value) {
src/scales/scale.category.js:		getPixelForValue: function(value, index) {
src/scales/scale.linear.js:		getPixelForValue: function(value) {

I think we'd need to finish the implementation of linear and logarithmic charts getPixelForValue functions to make bar charts work with those scales.

@loicbourgois
Copy link
Contributor Author

loicbourgois commented Jan 15, 2018

I updated the merging of options, removed the changes to scatter and line controllers, and added a new test.
The test however doesn't seem to fail. I'm using expect(meta.data[0].draw.calls.count()).toBe(1);
What would be the appropriate way to verify that all the charts are actually displaying ?
With these latest commits, here are fiddles to compare it to master :
Master + parent defined
Mixed + parent defined
Mixed + parent undefined
Combinations that work in mixed and not master

  • bar > scatter ;
  • bubble > horizontalBar ;
  • bubble > bar ;
  • bubble > line ;
  • bubble > scatter ;
  • line > bar ;
  • scatter > bar ;
  • scatter (but the line is showing) > line ;

From a user point of view, I don't think I should be required to set a main type if all my childs have a type. And I shouldn't have to specify some options that should have a default value already.

@benmccann
Copy link
Contributor

benmccann commented Jan 15, 2018

I tend to agree with @simonbrunel that this approach becomes really difficult to understand the code to know which order options get applied, which override which, etc. I think your first approach was closer

I think we should try to move some chart options to be dataset or scale options instead. E.g. see my last two comments for an approach with bar charts. And here's a PR to fix the line / scatter issue: #5151

@benmccann
Copy link
Contributor

I talked to Simon on Slack today about this. He suggested the right long term solution is the one outlined in #5191 and that he will hopefully try to see if it's feasible if he has time

@loicbourgois
Copy link
Contributor Author

It sounds like a better solution indeed.
Will this allow to have an undefined main type ?

@simonbrunel
Copy link
Member

Thanks @loicbourgois! I'm closing this PR because I think #5999 will help handling mixed chart in a better way than the typesByPriotity approach suggested here which doesn't seem a scalable, stable and durable solution.

@nazariyv
Copy link

nazariyv commented Jul 25, 2019

Can the scatter plot take the x-values that are different to the line chart, but are on the same scale as the line chart? i.e. assume you have x-axes range for the line chart to be [0.5, 1.0, 2.0, 2.5, 3.0] and for the scatter chart, one of the points takes an x value of 0.65. As far as I can tell, it gets moved to 0.5...

@benmccann
Copy link
Contributor

@nazariyv let's keep the discussion of that question on #6409. thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants