How do I format data arrays in the correct way React ChartJS

Viewed 62

I'm running into some trouble trying to visualize data using the react version of ChartJS. I believe I'm aware of where the problem lies but after not being able to find anything online, I have no idea how to fix it. Basically, I am trying to create a doughnut chart which visualizes the amount of tasks in a specific stage (todo, in progress and completed). Here's my code:

const STATUS_BUCKETS = {
  "ToDo": "ToDo",
  "In Progress": "Progress",
  "Completed": "Completed"
}

function DashboardCard06(props) {

  const chartData = {labels: [], datasets: []};
  for (const bucket in STATUS_BUCKETS) {
    const filteredStatusTasks = props.task.reduce((prev, current) => {
      if (current.status === STATUS_BUCKETS[bucket]) {
      return prev + 1; }
      else {
        return prev
      }
    }, 0);
    chartData.labels.push(bucket);
    chartData.datasets.push({
          data: [filteredStatusTasks],
          backgroundColor: [
            tailwindConfig().theme.colors.indigo[500],
            tailwindConfig().theme.colors.blue[400],
            tailwindConfig().theme.colors.indigo[800],
          ],
          hoverBackgroundColor: [
            tailwindConfig().theme.colors.indigo[600],
            tailwindConfig().theme.colors.blue[500],
            tailwindConfig().theme.colors.indigo[900],
          ],
          hoverBorderColor: tailwindConfig().theme.colors.white,
    })
  }



  return (
    <div className="flex flex-col col-span-full sm:col-span-6 xl:col-span-4 bg-white shadow-lg rounded-sm border border-slate-200">
      <header className="px-5 py-4 border-b border-slate-100">
        <h2 className="font-semibold text-slate-800">Stage</h2>
      </header>
      {/* Chart built with Chart.js 3 */}
      {/* Change the height attribute to adjust the chart height */}
      <DoughnutChart data={chartData} width={389} height={260} />
    </div>
  );
}

The labels that I provide to ChartJS don't seem to be the problem but there is something with the datasets. For some reason, the code I wrote delivers 3 different arrays with seperated data points inside them (see screenshot).

enter image description here

Now, if I were to replace the data with some manually entered numbers like this:

 chartData.labels.push(bucket);
    chartData.datasets.push({
          data: [5, 8, 13],
          backgroundColor: [
            tailwindConfig().theme.colors.indigo[500],
            tailwindConfig().theme.colors.blue[400],
            tailwindConfig().theme.colors.indigo[800],
          ],
          hoverBackgroundColor: [
            tailwindConfig().theme.colors.indigo[600],
            tailwindConfig().theme.colors.blue[500],
            tailwindConfig().theme.colors.indigo[900],
          ],
          hoverBorderColor: tailwindConfig().theme.colors.white,
    })

My datasets would look like this: enter image description here

Even though I now have 3 copies of the same line which is not the idea, apparently this is the way ChartJS wants the datasets to be formated, yet I have no idea how to achieve that with my own code. Is there anyone that knows how I can transform my own data into 1 array structured the way ChartJS expects it?

Thank you very much for your time and effort in advance. Looking forward to getting this fixed!

Kind regards,

Bram

EDIT: Chart configuration

import React, { useRef, useEffect } from 'react';

import {
  Chart, DoughnutController, ArcElement, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';

// Import utilities
import { tailwindConfig } from '../utils/Utils';

Chart.register(DoughnutController, ArcElement, TimeScale, Tooltip);

function DoughnutChart({
  data,
  width,
  height
}) {

  const canvas = useRef(null);
  const legend = useRef(null);

  useEffect(() => {
    const ctx = canvas.current;
    // eslint-disable-next-line no-unused-vars
    const chart = new Chart(ctx, {
      type: 'doughnut',
      data: data,
      options: {
        cutout: '80%',
        layout: {
          padding: 24,
        },
        plugins: {
          legend: {
            display: false,
          },
        },
        interaction: {
          intersect: false,
          mode: 'nearest',
        },
        animation: {
          duration: 500,
        },
        maintainAspectRatio: false,
        resizeDelay: 200,
      },
      plugins: [{
        id: 'htmlLegend',
        afterUpdate(c, args, options) {
          const ul = legend.current;
          if (!ul) return;
          // Remove old legend items
          while (ul.firstChild) {
            ul.firstChild.remove();
          }
          // Reuse the built-in legendItems generator
          const items = c.options.plugins.legend.labels.generateLabels(c);
          items.forEach((item) => {
            const li = document.createElement('li');
            li.style.margin = tailwindConfig().theme.margin[1];
            // Button element
            const button = document.createElement('button');
            button.classList.add('btn-xs');
            button.style.backgroundColor = tailwindConfig().theme.colors.white;
            button.style.borderWidth = tailwindConfig().theme.borderWidth[1];
            button.style.borderColor = tailwindConfig().theme.colors.slate[200];
            button.style.color = tailwindConfig().theme.colors.slate[500];
            button.style.boxShadow = tailwindConfig().theme.boxShadow.md;
            button.style.opacity = item.hidden ? '.3' : '';
            button.onclick = () => {
              c.toggleDataVisibility(item.index, !item.index);
              c.update();
            };
            // Color box
            const box = document.createElement('span');
            box.style.display = 'block';
            box.style.width = tailwindConfig().theme.width[2];
            box.style.height = tailwindConfig().theme.height[2];
            box.style.backgroundColor = item.fillStyle;
            box.style.borderRadius = tailwindConfig().theme.borderRadius.sm;
            box.style.marginRight = tailwindConfig().theme.margin[1];
            box.style.pointerEvents = 'none';
            // Label
            const label = document.createElement('span');
            label.style.display = 'flex';
            label.style.alignItems = 'center';
            const labelText = document.createTextNode(item.text);
            label.appendChild(labelText);
            li.appendChild(button);
            button.appendChild(box);
            button.appendChild(label);
            ul.appendChild(li);
          });
        },
      }],
    });
    return () => chart.destroy();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <div className="grow flex flex-col justify-center">
      <div>
        <canvas ref={canvas} width={width} height={height}></canvas>
      </div>
      <div className="px-5 pt-2 pb-6">
        <ul ref={legend} className="flex flex-wrap justify-center -m-1"></ul>
      </div>
    </div>
  );
}

export default DoughnutChart;

enter image description here

0 Answers
Related