I'm facing an issue with the layout of my Chart.js stacked bar chart. I'm trying to display a single bar for "前年電力量" (Previous Year's Power Consumption) alongside a stacked bar for "予想電力量" (Current Year's Power Consumption).
The issue is with the ordering and stack. if I comment the dataset "Previous year's total power consumption" in the testdata and the order and stack, the layout works fine, there is an example of data with the correct layout but only for one month, trying to achieve for all 12.
here is the code example in the codesandbox: https://codesandbox.io/p/sandbox/dv2wl3
Trying to achieve the same is if you use data instead of testdata in .
const testdata = {
labels: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
datasets: [
// Unique bar for 前年電力量 (Previous Year's Power Consumption)
{
label: "前年電力量", // Previous year's total power consumption
data: [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100],
backgroundColor: "rgb(114, 225, 164)", // Green color for previous year
// stack: "previousYear", // Separate stack for previous year
barPercentage: 0.5,
// grouped: false,
// order: 3,
},
// Baseline bar (container) for 今年の電力消費量 (Current Year Estimate)
{
label: "予想電力量", // Estimated power consumption for the current year
data: [150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150], // Baseline data for each month
backgroundColor: "rgba(237, 239, 255, 1)", // Light color to act as background container
borderColor: "rgba(106, 122, 242, 1)",
borderWidth: 1,
borderDash: [5, 5],
stack: "currentYear", // Stack for current year estimate
grouped: false,
order: 1, // Render this first to act as a background layer
barPercentage: 0.5,
},
// Stacked categories within 今年の電力消費量 (Current Year Power Consumption)
{
label: "空調 (今年)", // Air Conditioning - Current Year
data: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
backgroundColor: "rgba(88, 181, 247, 1)", // Blue color for air conditioning
stack: "currentYear", // Stack for current year
grouped: false,
order: 1,
barPercentage: 0.5,
},
{
label: "照明 (今年)", // Illumination - Current Year
data: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
backgroundColor: "rgb(71, 68, 222)", // Color for illumination
stack: "currentYear", // Stack for current year
grouped: false,
order: 1,
barPercentage: 0.5,
},
{
label: "冷ケース (今年)", // Cooling Case - Current Year
data: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
backgroundColor: "rgb(157, 201, 210)", // Color for cooling case
stack: "currentYear", // Stack for current year
grouped: false,
order: 1,
barPercentage: 0.5,
},
{
label: "その他 (今年)", // Others - Current Year
data: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
backgroundColor: "rgb(182, 193, 200)", // Color for others
stack: "currentYear", // Stack for current year
grouped: true,
order: 1,
barPercentage: 0.5,
},
],
};
Thanks!
After some trial and error, it seems like showing the baseline bar "Estimated power consumption for the current year" as background and working with multiple bars: single bar "前年電力量 (Previous Year's Power Consumption)" and stacked bar "Stacked categories within 今年の電力消費量 (Current Year Power Consumption)" tricky and challenging.
However, thinking of a trick to combine the baseline bar and stacked bar as the stacked bar. For the value of the baseline bar, it should deduct the values for categories
150 (Estimated power consumption for the current year) - 40 (Sum of Stacked categories within 今年の電力消費量 (Current Year Power Consumption)) = 110 (Value of baseline chart stacked)
so that the baseline bar looks like the background bar.
This is achieved by manipulating the data, resulting in formattedData
.
When hovering over the baseline bar and the tooltip, the original value should be shown instead of the formatted value. This can be achieved via the callback
event in the tooltip
settings.
import _ from "lodash";
const testdata = {
labels: [
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月",
],
datasets: [
// Unique bar for 前年電力量 (Previous Year's Power Consumption)
{
label: "前年電力量", // Previous year's total power consumption
data: [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100],
backgroundColor: "rgb(114, 225, 164)", // Green color for previous year
stack: "previousYear", // Separate stack for previous year
barPercentage: 0.5,
grouped: true,
// order: 3,
},
// Baseline bar (container) for 今年の電力消費量 (Current Year Estimate)
{
label: "予想電力量", // Estimated power consumption for the current year
data: [150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150], // Baseline data for each month
backgroundColor: "rgba(237, 239, 255, 1)", // Light color to act as background container
borderColor: "rgba(106, 122, 242, 1)",
borderWidth: 1,
borderDash: [5, 5],
stack: "currentYear", // Stack for current year estimate
grouped: true,
order: 2, // Render this first to act as a background layer
barPercentage: 0.5,
},
// Stacked categories within 今年の電力消費量 (Current Year Power Consumption)
{
label: "空調 (今年)", // Air Conditioning - Current Year
data: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
backgroundColor: "rgba(88, 181, 247, 1)", // Blue color for air conditioning
stack: "currentYear", // Stack for current year
grouped: true,
order: 1,
barPercentage: 0.5,
},
{
label: "照明 (今年)", // Illumination - Current Year
data: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
backgroundColor: "rgb(71, 68, 222)", // Color for illumination
stack: "currentYear", // Stack for current year
grouped: true,
order: 1,
barPercentage: 0.5,
},
{
label: "冷ケース (今年)", // Cooling Case - Current Year
data: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
backgroundColor: "rgb(157, 201, 210)", // Color for cooling case
stack: "currentYear", // Stack for current year
grouped: true,
order: 1,
barPercentage: 0.5,
},
{
label: "その他 (今年)", // Others - Current Year
data: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
backgroundColor: "rgb(182, 193, 200)", // Color for others
stack: "currentYear", // Stack for current year
grouped: true,
order: 1,
barPercentage: 0.5,
},
],
};
let formattedData = _.cloneDeep(testdata);
let currentYearCategoriesDataset = testdata.datasets
.filter((x) => x.stack === "currentYear")
.filter((x, i) => i > 0);
// Display Baseline bar with stacked bar by total minus categories
formattedData.datasets = [
...formattedData.datasets.filter((x) => x.stack !== "currentYear"),
...formattedData.datasets
.filter((x) => x.stack === "currentYear")
.map((dataset, i) => {
if (i == 0) {
dataset.data = dataset.data.map(
(data, j) =>
data -
currentYearCategoriesDataset.reduce(
(sum, cur) => sum + cur.data[j],
0
)
);
}
return dataset;
}),
];
<GraphContainer>
<GraphWrapper>
<Bar options={options} data={formattedData} />
</GraphWrapper>
</GraphContainer>
const options = {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
stacked: true,
},
y: {
stacked: true,
beginAtZero: true,
max: 250,
ticks: {
stepSize: 50,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
label: function (tooltipItem) {
const dataset = tooltipItem.dataset;
const label = dataset.label;
const value = tooltipItem.raw;
// Get the original value for 予想電力量
if (dataset.stack === "currentYear") {
// Or
//if (label === "予想電力量") {
let totalValue = testdata.datasets
.filter((x) => x.stack === "currentYear")
.find((x, i) => i === 0).data[tooltipItem.dataIndex];
console.log(totalValue);
return `${label}: ${totalValue}`;
}
return `${label}: ${value}`;
},
},
},
annotation: {
annotations: {
predictionLine: {
type: "line",
yMin: 180, // prediction value
yMax: 180,
borderColor: "rgba(255, 99, 132, 0.8)",
borderWidth: 2,
borderDash: [6, 6],
label: {
enabled: true,
content: "予想値", // Prediction label
position: "end",
backgroundColor: "rgba(255, 99, 132, 0.8)",
color: "white",
padding: 4,
},
},
},
},
},
};