diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 679ac299a67..6a5595cb5bc 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -126,6 +126,7 @@ module.exports = { 'other-charts/radar', 'other-charts/radar-skip-points', 'other-charts/combo-bar-line', + 'other-charts/stacked-bar-line', ] }, { diff --git a/docs/charts/bar.md b/docs/charts/bar.md index d71b553deca..d2ea8ba45d6 100644 --- a/docs/charts/bar.md +++ b/docs/charts/bar.md @@ -286,7 +286,7 @@ The following dataset properties are specific to stacked bar charts. | Name | Type | Description | ---- | ---- | ----------- -| `stack` | `string` | The ID of the group to which this dataset belongs to (when stacked, each group will be a separate stack). +| `stack` | `string` | The ID of the group to which this dataset belongs to (when stacked, each group will be a separate stack). Defaults to `bar`. ## Horizontal Bar Chart diff --git a/docs/general/data-structures.md b/docs/general/data-structures.md index b66f9e2a597..778aa295ab6 100644 --- a/docs/general/data-structures.md +++ b/docs/general/data-structures.md @@ -67,7 +67,7 @@ In this mode, property name is used for `index` scale and value for `value` scal | `label` | `string` | The label for the dataset which appears in the legend and tooltips. | `clip` | `number`\|`object` | How to clip relative to chartArea. Positive value allows overflow, negative value clips that many pixels inside chartArea. 0 = clip at chartArea. Clipping can also be configured per side: clip: {left: 5, top: false, right: -2, bottom: 0} | `order` | `number` | The drawing order of dataset. Also affects order for stacking, tooltip and legend. -| `stack` | `string` | The ID of the group to which this dataset belongs to (when stacked, each group will be a separate stack). +| `stack` | `string` | The ID of the group to which this dataset belongs to (when stacked, each group will be a separate stack). Defaults to dataset `type`. | `parsing` | `boolean`\|`object` | How to parse the dataset. The parsing can be disabled by specifying parsing: false at chart options or dataset. If parsing is disabled, data must be sorted and in the formats the associated chart type and scales use internally. | `hidden` | `boolean` | Configure the visibility of the dataset. Using `hidden: true` will hide the dataset from being rendered in the Chart. diff --git a/docs/samples/other-charts/stacked-bar-line.md b/docs/samples/other-charts/stacked-bar-line.md new file mode 100644 index 00000000000..1afbbd4aaee --- /dev/null +++ b/docs/samples/other-charts/stacked-bar-line.md @@ -0,0 +1,120 @@ +# Stacked bar/line + +```js chart-editor +// +const actions = [ + { + name: 'Randomize', + handler(chart) { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers({count: chart.data.labels.length, min: 0, max: 100}); + }); + chart.update(); + } + }, + { + name: 'Add Dataset', + handler(chart) { + const data = chart.data; + const dsColor = Utils.namedColor(chart.data.datasets.length); + const newDataset = { + label: 'Dataset ' + (data.datasets.length + 1), + backgroundColor: Utils.transparentize(dsColor, 0.5), + borderColor: dsColor, + borderWidth: 1, + stack: 'combined', + data: Utils.numbers({count: data.labels.length, min: 0, max: 100}), + }; + chart.data.datasets.push(newDataset); + chart.update(); + } + }, + { + name: 'Add Data', + handler(chart) { + const data = chart.data; + if (data.datasets.length > 0) { + data.labels = Utils.months({count: data.labels.length + 1}); + + for (var index = 0; index < data.datasets.length; ++index) { + data.datasets[index].data.push(Utils.rand(0, 100)); + } + + chart.update(); + } + } + }, + { + name: 'Remove Dataset', + handler(chart) { + chart.data.datasets.pop(); + chart.update(); + } + }, + { + name: 'Remove Data', + handler(chart) { + chart.data.labels.splice(-1, 1); // remove the label first + + chart.data.datasets.forEach(dataset => { + dataset.data.pop(); + }); + + chart.update(); + } + } +]; +// + +// +const DATA_COUNT = 7; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const labels = Utils.months({count: 7}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.red, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), + stack: 'combined', + type: 'bar' + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.CHART_COLORS.blue, + backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), + stack: 'combined' + } + ] +}; +// + +// +const config = { + type: 'line', + data: data, + options: { + plugins: { + title: { + display: true, + text: 'Chart.js Stacked Line/Bar Chart' + } + }, + scales: { + y: { + stacked: true + } + } + }, +}; +// + +module.exports = { + actions: actions, + config: config, +}; +``` diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index e1a0f48e912..8d8e9a95ec9 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -111,7 +111,7 @@ function isStacked(scale, meta) { } function getStackKey(indexScale, valueScale, meta) { - return indexScale.id + '.' + valueScale.id + '.' + meta.stack + '.' + meta.type; + return `${indexScale.id}.${valueScale.id}.${meta.stack || meta.type}`; } function getUserBounds(scale) { diff --git a/test/fixtures/mixed/bar+line-stacked.js b/test/fixtures/mixed/bar+line-stacked.js new file mode 100644 index 00000000000..17f28aa0ba1 --- /dev/null +++ b/test/fixtures/mixed/bar+line-stacked.js @@ -0,0 +1,40 @@ +module.exports = { + config: { + data: { + datasets: [ + { + type: 'bar', + stack: 'mixed', + data: [5, 20, 1, 10], + backgroundColor: '#00ff00', + borderColor: '#ff0000' + }, + { + type: 'line', + stack: 'mixed', + data: [6, 16, 3, 19], + borderColor: '#0000ff', + fill: false + }, + ] + }, + options: { + scales: { + x: { + axis: 'y', + labels: ['a', 'b', 'c', 'd'] + }, + y: { + stacked: true + } + } + } + }, + options: { + spriteText: true, + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/mixed/bar+line-stacked.png b/test/fixtures/mixed/bar+line-stacked.png new file mode 100644 index 00000000000..4768675e3da Binary files /dev/null and b/test/fixtures/mixed/bar+line-stacked.png differ diff --git a/test/specs/core.datasetController.tests.js b/test/specs/core.datasetController.tests.js index a549679d849..d9075754ca4 100644 --- a/test/specs/core.datasetController.tests.js +++ b/test/specs/core.datasetController.tests.js @@ -539,11 +539,11 @@ describe('Chart.DatasetController', function() { }); expect(chart._stacks).toEqual({ - 'x.y.1.bar': { + 'x.y.1': { 0: {0: 1, 2: 3}, 1: {0: 10, 2: 30} }, - 'x.y.2.bar': { + 'x.y.2': { 0: {1: 2}, 1: {1: 20} } @@ -553,11 +553,11 @@ describe('Chart.DatasetController', function() { chart.update(); expect(chart._stacks).toEqual({ - 'x.y.1.bar': { + 'x.y.1': { 0: {0: 1}, 1: {0: 10} }, - 'x.y.2.bar': { + 'x.y.2': { 0: {1: 2, 2: 3}, 1: {1: 20, 2: 30} } @@ -583,11 +583,11 @@ describe('Chart.DatasetController', function() { }); expect(chart._stacks).toEqual({ - 'x.y.1.bar': { + 'x.y.1': { 0: {0: 1, 2: 3}, 1: {0: 10, 2: 30} }, - 'x.y.2.bar': { + 'x.y.2': { 0: {1: 2}, 1: {1: 20} } @@ -597,11 +597,11 @@ describe('Chart.DatasetController', function() { chart.update(); expect(chart._stacks).toEqual({ - 'x.y.1.bar': { + 'x.y.1': { 0: {0: 1, 2: 4}, 1: {0: 10} }, - 'x.y.2.bar': { + 'x.y.2': { 0: {1: 2}, 1: {1: 20} }