Vivek Burman
Vivek Burman

Reputation: 11

Stacked Grouped Chart

<!DOCTYPE html>
<html>
<head>
    <title>Task Status Bar Chart</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
    <canvas id="myChart"></canvas>

    <script>
        var ctx = document.getElementById('myChart').getContext('2d');
        var myChart = new Chart(ctx, {
            type: 'bar',
            data: {
                labels: ['ToDo', 'InProgress', 'Done'],
                datasets: [
                    {
                        label: 'Under Review',
                        data: [
                            [10, 5, 0],  // Blocker
                            [20, 10, 5], // High
                            [5, 10, 20], // Low
                            [5, 5, 10],  // Critical
                            [10, 15, 15] // Medium
                        ],
                        backgroundColor: [
                            'rgba(255, 99, 132, 0.5)',
                            'rgba(54, 162, 235, 0.5)',
                            'rgba(255, 206, 86, 0.5)',
                            'rgba(75, 192, 192, 0.5)',
                            'rgba(153, 102, 255, 0.5)'
                        ],
                        borderColor: [
                            'rgba(255, 99, 132, 1)',
                            'rgba(54, 162, 235, 1)',
                            'rgba(255, 206, 86, 1)',
                            'rgba(75, 192, 192, 1)',
                            'rgba(153, 102, 255, 1)'
                        ],
                        borderWidth: 1
                    },
                    {
                        label: 'In Progress',
                        data: [
                            [10, 5, 0],  // Blocker
                            [20, 10, 5], // High
                            [5, 10, 20], // Low
                            [5, 5, 10],  // Critical
                            [10, 15, 15] // Medium
                        ],
                        backgroundColor: [
                            'rgba(255, 99, 132, 0.5)',
                            'rgba(54, 162, 235, 0.5)',
                            'rgba(255, 206, 86, 0.5)',
                            'rgba(75, 192, 192, 0.5)',
                            'rgba(153, 102, 255, 0.5)'
                        ],
                        borderColor: [
                            'rgba(255, 99, 132, 1)',
                            'rgba(54, 162, 235, 1)',
                            'rgba(255, 206, 86, 1)',
                            'rgba(75, 192, 192, 1)',
                            'rgba(153, 102, 255, 1)'
                        ],
                        borderWidth: 1
                    },
                    {
                        label: 'New',
                        data: [
                            [10, 5, 0],  // Blocker
                            [20, 10, 5], // High
                            [5, 10, 20], // Low
                            [5, 5, 10],  // Critical
                            [10, 15, 15] // Medium
                        ],
                        backgroundColor: [
                            'rgba(255, 99, 132, 0.5)',
                            'rgba(54, 162, 235, 0.5)',
                            'rgba(255, 206, 86, 0.5)',
                            'rgba(75, 192, 192, 0.5)',
                            'rgba(153, 102, 255, 0.5)'
                        ],
                        borderColor: [
                            'rgba(255, 99, 132, 1)',
                            'rgba(54, 162, 235, 1)',
                            'rgba(255, 206, 86, 1)',
                            'rgba(75, 192, 192, 1)',
                            'rgba(153, 102, 255, 1)'
                        ],
                        borderWidth: 1
                    },
                    {
                        label: 'Completed',
                        data: [
                            [10, 5, 0],  // Blocker
                            [20, 10, 5], // High
                            [5, 10, 20], // Low
                            [5, 5, 10],  // Critical
                            [10, 15, 15] // Medium
                        ],
                        backgroundColor: [
                            'rgba(255, 99, 132, 0.5)',
                            'rgba(54, 162, 235, 0.5)',
                            'rgba(255, 206, 86, 0.5)',
                            'rgba(75, 192, 192, 0.5)',
                            'rgba(153, 102, 255, 0.5)'
                        ],
                        borderColor: [
                            'rgba(255, 99, 132, 1)',
                            'rgba(54, 162, 235, 1)',
                            'rgba(255, 206, 86, 1)',
                            'rgba(75, 192, 192, 1)',
                            'rgba(153, 102, 255, 1)'
                        ],
                        borderWidth: 1
                    }
                ]
            },
            options: {
                indexAxis: 'y',
                scales: {
                    x: {
                        stacked: false
                    },
                    y: {
                        stacked: false
                    }
                }
            }
        });
    </script>
</body>
</html>

The result showed up like this: Output I was expecting something like this: Expectation I was expecting a Stacked Grouped Chart: For example:

X-axis: ToDo, InProgress, Done

Each x-axis label has 4 stacks: [Under Review, In Progress, New, Completed] 3 - 4

Now each stack has 5 data points: [Blocked, High, Critical, Low, Medium] 4 - 5

Upvotes: 0

Views: 44

Answers (1)

M69k65y
M69k65y

Reputation: 647

You could use a Stacked Bar Chart with Groups, which allows you to have a stacked bar graph with control over how the data is grouped.

With this approach, you will need to have distinct data entries for each data point in each stack. This means that you will need to define "Blocked" data for the Under Review, In Progress, New and Completed stacks.

You will also need to use a plugin called chartjs-plugin-datalabels, which allows you to label the different groups/stacks.

Two known issues with the included code:

  1. Clicking on the legend does not strike out the legend text when the text is hidden.
  2. The stack label disappears when the first dataset element is hidden.

The code is as follows:

// Register the plugin (chartjs-plugin-datalabels).
// Needed for the stack labels to work.
Chart.register(ChartDataLabels);
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ['ToDo', 'InProgress', 'Done'],
    datasets: [{
        label: 'Blocker',
        data: [10, 5, 0],
        borderWidth: 1,
        backgroundColor: 'rgba(255, 99, 132, 0.5)',
        borderColor: 'rgba(255, 99, 132, 1)',
        stack: 'Under Review',
      },
      {
        label: 'High',
        data: [20, 10, 5],
        borderWidth: 1,
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
        borderColor: 'rgba(54, 162, 235, 1)',
        stack: 'Under Review',
      },
      {
        label: 'Low',
        data: [5, 10, 20],
        borderWidth: 1,
        backgroundColor: 'rgba(75, 192, 192, 0.5)',
        borderColor: 'rgba(75, 192, 192, 1)',
        stack: 'Under Review',
      },
      {
        label: 'Critical',
        data: [5, 5, 10],
        borderWidth: 1,
        backgroundColor: 'rgba(255, 206, 86, 0.5)',
        borderColor: 'rgba(255, 206, 86, 1)',
        stack: 'Under Review',
      },
      {
        label: 'Medium',
        data: [10, 15, 15],
        borderWidth: 1,
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
        borderColor: 'rgba(153, 102, 255, 1)',
        stack: 'Under Review',
      },
      {
        label: 'Blocker',
        data: [0, 25, 10],
        borderWidth: 1,
        backgroundColor: 'rgba(255, 99, 132, 0.5)',
        borderColor: 'rgba(255, 99, 132, 1)',
        stack: 'In Progress',
      },
      {
        label: 'High',
        data: [2, 8, 6],
        borderWidth: 1,
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
        borderColor: 'rgba(54, 162, 235, 1)',
        stack: 'In Progress',
      },
      {
        label: 'Low',
        data: [15, 0, 9],
        borderWidth: 1,
        backgroundColor: 'rgba(75, 192, 192, 0.5)',
        borderColor: 'rgba(75, 192, 192, 1)',
        stack: 'In Progress',
      },
      {
        label: 'Critical',
        data: [12, 6, 18],
        borderWidth: 1,
        backgroundColor: 'rgba(255, 206, 86, 0.5)',
        borderColor: 'rgba(255, 206, 86, 1)',
        stack: 'In Progress',
      },
      {
        label: 'Medium',
        data: [3, 4, 7],
        borderWidth: 1,
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
        borderColor: 'rgba(153, 102, 255, 1)',
        stack: 'In Progress',
      },
      {
        label: 'Blocker',
        data: [10, 5, 0],
        borderWidth: 1,
        backgroundColor: 'rgba(255, 99, 132, 0.5)',
        borderColor: 'rgba(255, 99, 132, 1)',
        stack: 'New',
      },
      {
        label: 'High',
        data: [20, 10, 5],
        borderWidth: 1,
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
        borderColor: 'rgba(54, 162, 235, 1)',
        stack: 'New',
      },
      {
        label: 'Low',
        data: [5, 10, 20],
        borderWidth: 1,
        backgroundColor: 'rgba(75, 192, 192, 0.5)',
        borderColor: 'rgba(75, 192, 192, 1)',
        stack: 'New',
      },
      {
        label: 'Critical',
        data: [5, 5, 10],
        borderWidth: 1,
        backgroundColor: 'rgba(255, 206, 86, 0.5)',
        borderColor: 'rgba(255, 206, 86, 1)',
        stack: 'New',
      },
      {
        label: 'Medium',
        data: [10, 15, 15],
        borderWidth: 1,
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
        borderColor: 'rgba(153, 102, 255, 1)',
        stack: 'New',
      },
      {
        label: 'Blocker',
        data: [0, 25, 10],
        borderWidth: 1,
        backgroundColor: 'rgba(255, 99, 132, 0.5)',
        borderColor: 'rgba(255, 99, 132, 1)',
        stack: 'Completed',
      },
      {
        label: 'High',
        data: [2, 8, 6],
        borderWidth: 1,
        backgroundColor: 'rgba(54, 162, 235, 0.5)',
        borderColor: 'rgba(54, 162, 235, 1)',
        stack: 'Completed',
      },
      {
        label: 'Low',
        data: [15, 0, 9],
        borderWidth: 1,
        backgroundColor: 'rgba(75, 192, 192, 0.5)',
        borderColor: 'rgba(75, 192, 192, 1)',
        stack: 'Completed',
      },
      {
        label: 'Critical',
        data: [12, 6, 18],
        borderWidth: 1,
        backgroundColor: 'rgba(255, 206, 86, 0.5)',
        borderColor: 'rgba(255, 206, 86, 1)',
        stack: 'Completed',
      },
      {
        label: 'Medium',
        data: [3, 4, 7],
        borderWidth: 1,
        backgroundColor: 'rgba(153, 102, 255, 0.5)',
        borderColor: 'rgba(153, 102, 255, 1)',
        stack: 'Completed',
      },
    ],
  },
  interaction: {
    intersect: false,
  },
  options: {
    plugins: {
      // Configuration of the datalabels plugin
      datalabels: {
        align: "start",
        anchor: "start",
        color: "black",
        formatter: function(value, context) {
          let ds = context.chart.data.datasets;
          // Check if it's the first dataset
          if (ds[context.datasetIndex - 1]) {
            // Check if the dataset is in the same stack as the dataset before
            if (
              ds[context.datasetIndex - 1].stack ===
              ds[context.datasetIndex].stack
            ) {
              return "";
            } else {
              return ds[context.datasetIndex].stack;
            }
          } else {
            return ds[context.datasetIndex].stack;
          }
        }
      },
      legend: {
        // Since the labels defined below point only to single datasets,
        // we use this to show/hide all datasets with the same label.
        onClick: function(e, legendItem, legend) {
          const ci = legend.chart;
          const numberOfIndeces = ci.data.datasets.length;
          let initIndex = legendItem.datasetIndex;
          // Loop over all datasets corresponding to the label that has
          // been clicked on.
          while (initIndex < numberOfIndeces) {
            let index = initIndex;
            if (ci.isDatasetVisible(index)) {
              ci.hide(index);
              legendItem.hidden = true;
            } else {
              ci.show(index);
              legendItem.hidden = false;
            }
            // 5 matches the number of different categories we have
            // i.e., Blocker, High, Low, Critical, Medium.
            initIndex += 5;
          }
        },
        labels: {
          // Manually override the labels returned.
          // Failure to do this will lead to duplicated labels.
          generateLabels: function(chart) {
            return [{
                text: 'Blocker',
                fillStyle: 'rgba(255, 99, 132, 0.5)',
                strokeStyle: 'rgba(255, 99, 132, 1)',
                datasetIndex: 0,
                lineWidth: 1,
                hidden: false,

              },
              {
                text: 'High',
                fillStyle: 'rgba(54, 162, 235, 0.5)',
                strokeStyle: 'rgba(54, 162, 235, 1)',
                datasetIndex: 1,
                lineWidth: 1,
                hidden: false,

              },
              {
                text: 'Low',
                fillStyle: 'rgba(75, 192, 192, 0.5)',
                strokeStyle: 'rgba(75, 192, 192, 1)',
                datasetIndex: 2,
                lineWidth: 1,
                hidden: false,

              },
              {
                text: 'Critical',
                fillStyle: 'rgba(255, 206, 86, 0.5)',
                strokeStyle: 'rgba(255, 206, 86, 1)',
                datasetIndex: 3,
                lineWidth: 1,
                hidden: false,

              },
              {
                text: 'Medium',
                fillStyle: 'rgba(153, 102, 255, 0.5)',
                strokeStyle: 'rgba(153, 102, 255, 1)',
                datasetIndex: 4,
                lineWidth: 1,
                hidden: false,

              },
            ]
          }
        }
      }
    },
    scales: {
      x: {
        stacked: true,
        ticks: {
          callback: function(value, index, ticks) {
            // Hide the default label
            return ''
          }
        }
      },
      // Display the main label beneath the stack labels
      x2: {
        border: {
          display: false
        },
        grid: {
          display: false
        }
      },
      y: {
        stacked: true
      }
    }
  }
});
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2"></script>

<canvas id="myChart"></canvas>

You will need to get rid of indexAxis: 'y', in your code for the proper display of the axes.

Upvotes: 1

Related Questions