I am using v4 of react chart js. The chart type is horizontal bar and I am using a custom tooltip to generate the tooltip but the tooltip is not displaying for bars which have 100% bar length. Initially, I was using
interaction: 'y' as const
but then changed it to
interaction: { mode: 'index' as const, axis: 'y' as const },
The reason for this was, we changed to display only 1 dataset and so sometimes the bar would not be 100% in length. In these cases if I hovered over the unused chart area corresponding to a bar, then the tooltip would just crash.
The following is the complete code for setting the bar options
export const options = (
xAxisTitle: string,
yAxisTitle: string,
spendLabel: string,
balanceLabel: string,
langIetf: string
) => ({
animation: false as const,
indexAxis: 'y' as const,
responsive: true,
aspectRatio: 2,
scales: {
x: {
stacked: true,
display: true,
title: {
display: true,
text: xAxisTitle,
font: {
weight: 'bold' as const
}
},
ticks: {
stepSize: 20,
callback(value: any) {
return `${value}%`;
}
},
min: 0,
max: 100
},
y: {
stacked: true,
display: true,
title: {
display: true,
text: yAxisTitle,
font: {
weight: 'bold' as const
}
},
ticks: {
// For a category axis, the val is the index so the lookup via getLabelForValue is needed
callback(value: any): string {
const newThis = this as any;
if (newThis.getLabelForValue(value).length > 10) {
return `${newThis.getLabelForValue(value).substring(0, 10)}...`;
}
return newThis.getLabelForValue(value);
}
},
grid: {
drawOnChartArea: false,
drawTicks: false
}
}
},
interaction: {
mode: 'index' as const,
axis: 'xy' as const
},
plugins: {
legend: {
display: false
},
tooltip: {
enabled: false,
external(context: any) {
const dir = document.querySelector('html')?.getAttribute('dir');
const { tooltipEl, tooltipCaret } = generateTooltipElement();
const { tooltip, chart } = context;
// Hide if no tooltip
if (tooltip.opacity === 0) {
hideTooltip(tooltipEl, tooltipCaret);
return;
}
const labelArr: string[] = [spendLabel, balanceLabel, xAxisTitle];
// Set HTML & Data
if (tooltip.body) {
const innerHtml = setTooltipBody(tooltip, chart, labelArr, langIetf, dir!);
tooltipEl!.querySelector('table')!.innerHTML = innerHtml;
}
if (tooltipEl !== null && tooltipCaret !== null) {
setTooltipStyles(tooltip, tooltipEl, tooltipCaret, chart);
}
}
}
}
});
Any ideas on the above? Is this the correct way to go about it or is there a better way?
My first obstacle was to not let the tooltip crash when the user hovered over an empty bar area
I tried to work with the configurable options. The interaction
solved the above issue but has introduced a new issue where the tooltip just won't display for bars with 100% length so in the attached image the tooltip doesn't show for the 1st two bars.
Figured out what was wrong - it was how I was passing the data to generate the charts. I needed to display only 1 dataset on the chart but needed to show more data on the tooltip. I was doing that by passing in all the data to the chart object but keeping the other data as transparent. I solved this issue by passing in additional data which was required to be displayed in the custom tooltip as follows
const chartData = {
labels: dataLabels,
datasets: [
/* passing in additional datasets for spend and balance as below so that they are not plotted on the chart
but can be used to display the required information in the tooltip */
{
label: usageLabel,
data: currentUsageDataset,
backgroundColor: humanity60,
hoverBackgroundColor: humanity60,
barThickness: 24,
[spendLabel]: spendDataset,
[balanceLabel]: actualBalanceDataset
}
]
};
The
spendDataset
and
actualBalanceDataset
is the additional data. After that I changed the interaction back to
interaction: 'y' as const
and it worked perfectly.