How to make chartjs Tooltip scrollable?

109 Views Asked by At

I tried creating custom tooltip. The below code provides a tooltip with scrollbar, but unable to scroll through the tooltip. Not sure what I missed.

There is a callback function which provided a list of item that I have to display in the tool tip. But the tooltip does not scroll. It only gives a scrollbar at the right but on trying to scroll thorough, the list stays still.

import React from 'react';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend } from 'chart.js';
import { Bar } from 'react-chartjs-2';

ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);

interface IChartProps {
    xAxis: any[]
    yAxis: any[]
    horizontal: boolean
    height: string
    width: string
    tooltip: any | undefined
}

function BarGraph(props: IChartProps) {

    let options: any = {
        responsive: true,
        maintainAspectRatio: false,
        plugins: {
            legend: {
                position: 'top',
            },
            title: {
                display: false,
                text: ' Bar-Chart',
            },
        },
        scales: {
            x: {
                grid: { display: false },
                ticks: {
                    autoSkip: false,
                },
            },
            y: {
                grid: { display: false },
                ticks: {
                    // callback: function(value: any) {
                    //   return value.substring(0, 5);
                    // },
                    // maxRotation: 0,
                    // minRotation: 0, 
                    autoSkip: false,
                },
            }
        },
    }


    const getOrCreateTooltip = (chart: any) => {
        let tooltipEl = chart.canvas.parentNode.querySelector('div');

        if (!tooltipEl) {
            tooltipEl = document.createElement('div');
            tooltipEl.style.background = 'rgba(0, 0, 0, 0.7)';
            tooltipEl.style.borderRadius = '3px';
            tooltipEl.style.color = 'white';
            tooltipEl.style.opacity = 1;
            tooltipEl.style.pointerEvents = 'none';
            tooltipEl.style.position = 'absolute';
            tooltipEl.style.transform = 'translate(-50%, 0)';
            tooltipEl.style.transition = 'all .1s ease';

            const table = document.createElement('table');
            table.style.margin = '0px';

            tooltipEl.appendChild(table);
            chart.canvas.parentNode.appendChild(tooltipEl);
        }

        return tooltipEl;
    };

    const externalTooltipHandler = (context: any) => {
        // Tooltip Element
        const { chart, tooltip } = context;
        let datasetIndex = tooltip.dataPoints[0].datasetIndex

        const tooltipEl = getOrCreateTooltip(chart);

        // Hide if no tooltip
        if (tooltip.opacity === 0) {
            tooltipEl.style.opacity = 0;
            return;
        }

        // Set Text
        if (tooltip.body) {
            const titleLines = tooltip.title || [];
            const bodyLines = tooltip.body.map((b: any) => b.lines);
            const footerLines = tooltip.footer || [];

            const tableHead = document.createElement('thead');

            titleLines.forEach((title: any) => {
                const tr: any = document.createElement('tr');
                tr.style.borderWidth = 0;

                const th: any = document.createElement('th');
                th.style.borderWidth = 0;
                const text = document.createTextNode(title);

                th.appendChild(text);
                tr.appendChild(th);
                tableHead.appendChild(tr);
            });

            const tableBody = document.createElement('tbody');
            bodyLines.forEach((body: any, i: any) => {
                const colors = tooltip.labelColors[i];

                const span = document.createElement('span');
                span.style.background = colors.backgroundColor;
                span.style.borderColor = colors.borderColor;
                span.style.borderWidth = '2px';
                span.style.marginRight = '10px';
                span.style.height = '10px';
                span.style.width = '10px';
                span.style.display = 'inline-block';

                const tr: any = document.createElement('tr');
                tr.style.backgroundColor = 'inherit';
                tr.style.borderWidth = 0;

                const td: any = document.createElement('td');
                td.style.borderWidth = 0;

                const text = document.createTextNode(body);

                td.appendChild(span);
                td.appendChild(text);
                tr.appendChild(td);
                tableBody.appendChild(tr);
            });

            const tableFooter = document.createElement('tfoot');
            footerLines.forEach((title: any) => {
                const tr: any = document.createElement('tr');
                tr.style.borderWidth = 0;

                const td: any = document.createElement('td');
                td.style.borderWidth = 0;
                const text = document.createTextNode(title);

                td.appendChild(text);
                tr.appendChild(td);
                tableFooter.appendChild(tr);
            });

            const tableRoot = tooltipEl.querySelector('table');

            // Remove old children
            while (tableRoot.firstChild) {
                tableRoot.firstChild.remove();
            }

            // Add new children
            tableRoot.appendChild(tableHead);
            tableRoot.appendChild(tableBody);
            tableRoot.appendChild(tableFooter);
        }

        const { offsetLeft: positionX, offsetTop: positionY } = chart.canvas;

        // Display, position, and set styles for font
        tooltipEl.style.opacity = 1;
        tooltipEl.style.maxHeight = "400px";
        tooltipEl.style.position = 'absolute';
        tooltipEl.style.fontSize = '0.7rem';
        tooltipEl.style.left = positionX + tooltip.caretX + 'px';
        tooltipEl.style.font = tooltip.options.bodyFont.string;
        tooltipEl.style.padding = tooltip.options.padding + 'px ' + tooltip.options.padding + 'px';
        if (datasetIndex === 0) {
            tooltipEl.style.top = positionY + 'px';
            tooltipEl.style.overflowY = "auto";
            tooltipEl.style.scrollBehavior = "smooth";   
        }
        else {
            tooltipEl.style.top = positionY + tooltip.caretY + 'px';
        }
    };

    if (props.tooltip !== undefined) {
        if (props.tooltip['enabled'] === false) {
            props.tooltip['external'] = externalTooltipHandler
        }
        options['plugins']['tooltip'] = props.tooltip

    }
    if (props.horizontal === true) {
        options['indexAxis'] = 'y'
    }



    const labels = props.xAxis

    const data = {
        labels,
        datasets: props.yAxis
    };
    return (<Bar options={options} data={data} height={props.height} width={props.width} />);
}

export default BarGraph;

1

There are 1 best solutions below

0
Nitesh On

The chart options are set with the “options” variable, where the “plugins” option is set to enable the tooltip and make it scrollable.

tooltip: {
      enabled: false,
      intersect: false,
      callbacks: {
        label: function (context) {
          let label = data.labels[context.datasetIndex];
          return (
            label +
            ",  " +
            context.parsed.x +
            ",  " +
            context.parsed.y
          );
        }
      },