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).
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: 
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;

