Skip to content

[charts] Custom tooltip for line chart #20721

@achoud4444

Description

@achoud4444

Summary

I have a feature where I display multiple vendors on a line chart based on which vendors are checked. Additionally, the user can select a specific vendor. When a vendor is selected, I want the tooltip on the line chart to display only the details for that selected vendor, instead of showing all the vendors.

Here is my code.
Here BMW is selected, I want all vendor lines to remain visible on the chart, but the tooltip should display information only for the BMW vendor. Additionally, when I hover over any vendor line, that line should be highlighted, while all other lines should appear dimmed.

import React, { useMemo } from 'react';
import { LineChart } from '@mui/x-charts/LineChart';

const baseColors = [
	'#393b79',
	'#1f77b4',
	'#ff7f0e',
	'#d62728',
	'#9467bd',
	'#8c564b',
	'#e377c2',
	'#7f7f7f',
	'#bcbd22',
	'#17becf',
	'#637939',
	'#8c6d31',
	'#843c39',
	'#7b4173',
	'#3182bd',
	'#2ca02c',
	'#31a354',
	'#756bb1',
	'#636363',
	'#e6550d',
];

const getMonthName = (dateStr: string) => {
	const [year, month] = dateStr.split('-');
	const monthIndex = parseInt(month, 10) - 1;
	const monthsShort = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
	return `${monthsShort[monthIndex]} ${year}`;
};

const getLastXMonths = (latestMonth: string, count: number): string[] => {
	const [year, month] = latestMonth.split('-').map(Number);
	const months: string[] = [];

	for (let i = count - 1; i >= 0; i--) {
		const d = new Date(year, month - 1 - i, 1);
		months.push(`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`);
	}
	return months;
};
interface VendorLineChartProps {
	setHoveredIndex: React.Dispatch<React.SetStateAction<number | null>>;
	hoveredIndex: number | null;
}

const VendorLineChart: React.FC<VendorLineChartProps> = ({ setHoveredIndex, hoveredIndex }) => {
	const vendorChartDataList = [
		{
			id: '7ed8633d-16bf-44e7-8419-10352cb672a0',
			vendorName: 'BMW',
			graphData: [
				{
					month: '2024-12',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-01',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-02',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-03',
					count: 6,
					amount: 0,
				},
				{
					month: '2025-04',
					count: 167,
					amount: 0,
				},
				{
					month: '2025-05',
					count: 16897,
					amount: 0,
				},
				{
					month: '2025-06',
					count: 161,
					amount: 0,
				},
				{
					month: '2025-07',
					count: 257,
					amount: 0,
				},
				{
					month: '2025-08',
					count: 272,
					amount: 0,
				},
				{
					month: '2025-09',
					count: 262,
					amount: 0,
				},
				{
					month: '2025-10',
					count: 12,
					amount: 0,
				},
				{
					month: '2025-11',
					count: 60,
					amount: 0,
				},
				{
					month: '2025-12',
					count: 24,
					amount: 0,
				},
			],
		},
		{
			id: 'c90b92db-6ba1-408a-a7fe-2d3a900acff8',
			vendorName: 'Volkswagon',
			graphData: [
				{
					month: '2024-12',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-01',
					count: 1,
					amount: 0,
				},
				{
					month: '2025-02',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-03',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-04',
					count: 130925,
					amount: 0,
				},
				{
					month: '2025-05',
					count: 1,
					amount: 0,
				},
				{
					month: '2025-06',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-07',
					count: 12,
					amount: 0,
				},
				{
					month: '2025-08',
					count: 24,
					amount: 0,
				},
				{
					month: '2025-09',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-10',
					count: 1022,
					amount: 0,
				},
				{
					month: '2025-11',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-12',
					count: 12,
					amount: 0,
				},
			],
		},
		{
			id: 'b5c7572c-2f87-45c4-af29-b21862cb2bda',
			vendorName: 'Suzuki',
			graphData: [
				{
					month: '2024-12',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-01',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-02',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-03',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-04',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-05',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-06',
					count: 12,
					amount: 0,
				},
				{
					month: '2025-07',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-08',
					count: 12,
					amount: 0,
				},
				{
					month: '2025-09',
					count: 12,
					amount: 0,
				},
				{
					month: '2025-10',
					count: 24,
					amount: 0,
				},
				{
					month: '2025-11',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-12',
					count: 0,
					amount: 0,
				},
			],
		},
		{
			id: 'c453d2db-1542-4a5e-9297-9419243a6136',
			vendorName: 'Tata',
			graphData: [
				{
					month: '2024-12',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-01',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-02',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-03',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-04',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-05',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-06',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-07',
					count: 24,
					amount: 0,
				},
				{
					month: '2025-08',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-09',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-10',
					count: 12,
					amount: 0,
				},
				{
					month: '2025-11',
					count: 24,
					amount: 0,
				},
				{
					month: '2025-12',
					count: 24,
					amount: 0,
				},
			],
		},
		{
			id: '25f38a99-e46c-4d93-95d3-25a5df209068',
			vendorName: 'Audi',
			graphData: [
				{
					month: '2024-12',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-01',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-02',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-03',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-04',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-05',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-06',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-07',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-08',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-09',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-10',
					count: 2,
					amount: 0,
				},
				{
					month: '2025-11',
					count: 0,
					amount: 0,
				},
				{
					month: '2025-12',
					count: 0,
					amount: 0,
				},
			],
		},
	];
	const checkedVendors = [
		'7ed8633d-16bf-44e7-8419-10352cb672a0',
		'c90b92db-6ba1-408a-a7fe-2d3a900acff8',
		'b5c7572c-2f87-45c4-af29-b21862cb2bda',
		'c453d2db-1542-4a5e-9297-9419243a6136',
		'25f38a99-e46c-4d93-95d3-25a5df209068',
	];
	const selectedVendorId = '7ed8633d-16bf-44e7-8419-10352cb672a0';

	const chartData = useMemo(() => {
		if (!vendorChartDataList?.length) {
			return { labels: [], series: [] };
		}

		let latestMonth = '1900-01';
		vendorChartDataList.forEach((v) =>
			v.graphData.forEach((g) => {
				if (g.month > latestMonth) latestMonth = g.month;
			})
		);

		const allMonths = getLastXMonths(latestMonth, 13);
		const labels = allMonths.map(getMonthName);

		// 🔑 PRIORITY LOGIC
		const vendorsToShow =
			checkedVendors.length > 0
				? vendorChartDataList.filter((v) => checkedVendors.includes(v.id))
				: vendorChartDataList;

		const series = vendorsToShow.map((v) => ({
			id: v.id,
			label: v.vendorName,
			data: allMonths.map((m) => v.graphData.find((g) => g.month === m)?.count ?? 0),
		}));

		return { labels, series };
	}, [vendorChartDataList, checkedVendors, selectedVendorId]);

	return (
		<>
				<LineChart
					xAxis={[
						{
							id: 'months',
							data: chartData.labels,
							tickLabelStyle: { fontSize: '0.6rem', fontWeight: 600 },
							scaleType: 'point',
						},
					]}
					series={chartData.series.map((s, i) => {
						const isSelected = selectedVendorId === s.id;
						const isHovered = hoveredIndex === i;

						const shouldHighlight = selectedVendorId ? isSelected : hoveredIndex === null || isHovered;

						return {
							id: s.id,
							label: s.label,
							data: s.data,
							color: shouldHighlight ? baseColors[i % baseColors.length] : '#ccc',
							lineWidth: shouldHighlight ? 3 : 1.5,
							opacity: shouldHighlight ? 1 : 0.4,
						};
					})}
					yAxis={[
						{
							tickLabelStyle: { fontSize: '0.6rem', fontWeight: 600 },
							label: '<-- Orders -->',
							width: 50,
						},
					]}
					slots={{
						legend: () => null,
					}}
					margin={{ left: 0, right: 0, bottom: 0 }}
					sx={{
						width: '100%',
						'& .MuiLineElement-root': { strokeWidth: 2 },
						'& .MuiMarkElement-root': { display: 'none' },
					}}
				/>
		</>
	);
};

export default VendorLineChart;

Examples

No response

Motivation

No response

Search keywords: Custom tooltip for line chart

Order ID: 86305

Metadata

Metadata

Assignees

No one assigned

    Labels

    scope: chartsChanges related to the charts.status: waiting for maintainerThese issues haven't been looked at yet by a maintainer.support: premium standardSupport request from a Premium standard plan user. https://mui.com/legal/technical-support-sla.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions