'How to show a "total" sum label on the top of stacked bars
I'd like each stacked bar to have their "total" label position just above the entire bar.
I've managed to get the sum, but I'm having trouble positioning the label at the top.
If you notice, the labels are positioned at the bottom:
I've tried playing around with the anchor and offset properties, but I was unable to achieve the desired result.
Code:
import * as React from "react";
import { Bar as RCBar } from "react-chartjs-2";
import { CategoryScale } from "chart.js";
import Chart from "chart.js/auto";
import events from "./events";
import chartOptions from "./chartOptions";
import ChartDataLabels from "chartjs-plugin-datalabels";
Chart.register(CategoryScale, ChartDataLabels);
const Graph = React.memo(() => {
const canvasRef = React.useRef(null);
const chartContainerRef = React.useRef(null);
const formatter = (value, ctx) => {
const datasets = ctx.chart.data.datasets.filter(
(ds) => !ds._meta?.[0].hidden
);
const foundDatasetIndex = datasets.indexOf(ctx.dataset);
if (foundDatasetIndex === datasets.length - 1) {
let sum = 0;
datasets.forEach((dataset) => {
sum += dataset.data[ctx.dataIndex];
});
return sum;
} else {
return "";
}
};
const generateChartData = React.useCallback(() => {
const labels = [];
const dataNew = [];
const dataSuccess = [];
const dataOnHold = [];
const dataCanceled = [];
events.forEach((event) => {
labels.push(event.trigger);
event.detailStatus.forEach((detailStatus) => {
if (detailStatus.code === 0) {
dataNew.push({
label: event.trigger,
sum: detailStatus.sum
});
}
if (detailStatus.code === 1) {
dataSuccess.push({
label: event.trigger,
sum: detailStatus.sum
});
}
if (detailStatus.code === 10) {
dataOnHold.push({
label: event.trigger,
sum: detailStatus.sum
});
}
if (detailStatus.code === 11) {
dataCanceled.push({
label: event.trigger,
sum: detailStatus.sum
});
}
});
});
return {
labels,
datasets: [
{
label: "New",
data: labels.map(
(label) =>
dataNew.find(({ label: labelInData }) => labelInData === label)
?.sum ?? 0
),
backgroundColor: "rgba(0, 120, 153, 1)",
borderColor: "rgba(255, 255, 255, 1)",
borderWidth: 2,
borderRadius: 5,
borderSkipped: "bottom",
barThickness: 15
},
{
label: "Finished",
data: labels.map(
(label) =>
dataSuccess.find(
({ label: labelInData }) => labelInData === label
)?.sum ?? 0
),
backgroundColor: "rgba(4, 198, 201, 1)",
borderColor: "rgba(255, 255, 255, 1)",
borderWidth: 2,
borderRadius: 5,
borderSkipped: "bottom",
barThickness: 15
},
{
label: "On hold",
data: labels.map(
(label) =>
dataOnHold.find(({ label: labelInData }) => labelInData === label)
?.sum ?? 0
),
backgroundColor: "rgba(211, 156, 247, 1)",
borderColor: "rgba(255, 255, 255, 1)",
borderWidth: 2,
borderRadius: 5,
borderSkipped: "bottom",
barThickness: 15
},
{
label: "Cancelled",
data: labels.map(
(label) =>
dataCanceled.find(
({ label: labelInData }) => labelInData === label
)?.sum ?? 0
),
backgroundColor: "rgba(168, 180, 189, 1)",
borderColor: "rgba(255, 255, 255, 1)",
borderWidth: 2,
borderRadius: 5,
borderSkipped: "bottom",
barThickness: 15
}
]
};
}, []);
return (
<div ref={chartContainerRef}>
<RCBar
options={{
...chartOptions,
plugins: {
...chartOptions.plugins,
datalabels: {
...chartOptions.plugins.datalabels,
formatter
}
}
}}
data={generateChartData()}
ref={canvasRef}
/>
</div>
);
});
export default Graph;
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|

