Problem: I have created a bar chart with a custom tooltip. Now What I need is to position tooltip on top of the bar not within the area of the chart. Like in this picture.
This is how It looks like now.
Here I am providing how I organize my code.
import React, { Component } from "react";
import {
BarChart,
Tooltip,
Bar,
Legend,
ResponsiveContainer,
Cell
} from "recharts";
import { Card, CardTitle, CardBody } from "reactstrap";
import "./SessionDuration.css";
const colors = ["#26a0a7", "#79d69f", "#f9ec86", "#ec983d"];
const data = [
{
name: "Page A",
pv: 2400,
amt: 2400
},
{
name: "Page B",
pv: 1398,
amt: 2210
},
{
name: "Page C",
pv: 9800,
amt: 2290
},
{
name: "Page D",
pv: 3908,
amt: 2000
}
];
const getTitleOfTooltip = (label) =>{
if (label === 0) {
return "<=5 min";
}
if (label === 1) {
return "5-30 min";
}
if (label === 2) {
return "30-60 min";
}
if (label === 3) {
return ">60";
}
}
const getIntroOfPage = label => {
if (label === 0) {
return "Page A is about men's clothing";
}
if (label === 1) {
return "Page B is about women's dress";
}
if (label === 2) {
return "Page C is about women's bag";
}
if (label === 3) {
return "Page D is about household goods";
}
};
class SessionDuration extends Component {
render() {
return (
<Card className="session-duration-card">
<CardTitle className="session-duration-card-header">
Session Duration
</CardTitle>
<CardBody>
<ResponsiveContainer width="100%" height="100%" aspect={4.0 / 5.5}>
<BarChart
data={data}
margin={{
top: 3,
right: 5,
left: 5,
bottom: 13
}}
barGap={10}
>
<Tooltip
coordinate={{ x: 0, y: 150 }}
content={<CustomTooltip />}
/>
<Bar dataKey="pv" fill="#8884d8">
{data.map((entry, index) => (
<Cell key={`cell-${index + 1}`} fill={colors[index]} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</CardBody>
</Card>
);
}
}
export default SessionDuration;
const CustomTooltip = ({ active, payload, label }) => {
if (active) {
return (
<div className="session-duration-tooltip">
<p className="session-duration-tooltip-label">{getTitleOfTooltip(label)}</p>
<p className="session-duration-tooltip-intro">
{getIntroOfPage(label)}
</p>
<p className="session-duration-tooltip-desc">
Anything you want can be displayed here.
</p>
</div>
);
}
return null;
};
Here I am providing my CSS file.
@media only screen and (min-width: 1024px) {
.session-duration-card {
margin-top: 14.5%;
margin-left: -44%;
width: 190% !important;
border-radius: none !important;
height: 86%
}
.session-duration-card-header {
font-weight: 700;
text-align: left;
padding-left: 6%
}
.session-duration-tooltip {
width: 210%;
}
.session-duration-tooltip-label {
font-weight: 700;
font-size: 11px;
}
}
@media only screen and (min-width: 308px) and (max-width: 1024px) {
.session-duration-card {
margin-top: 5%;
margin-left: 3.2%;
width: 94.5%;
border-radius: none !important;
}
.session-duration-card-header {
font-weight: 700;
text-align: center;
}
}
@media only screen and (min-width: 1666px) {
.session-duration-card {
margin-top: 11%;
margin-left: -13%;
width: 190% !important;
height: 97%;
border-radius: none !important;
}
.session-duration-card-header {
font-weight: 700;
text-align: center;
}
}
I tried a lot to find a solution for my problem but I was unable to get it done can someone help me by modifying my code to get this work done. Thank you very much.
To solve this problem, will need to get the height and width of each bar and tooltip.
useState
to get and keep the height and width to the state, also place extra boolean to the state, to handle when the mouse is outside of the Bar. (So, to function we add object with two paramets data and boolean ).useEffect
, to find the .recharts-tooltip-wrapper
class, to define the height and width of the DOM element..recharts-tooltip-wrapper
. (Rechart. js in .recharts-tooltip-wrapper
has its own generated style, we need to keep some static style of the .recharts-tooltip-wrapper
and add our).import React, { useState, useEffect } from 'react';
import { BarChart, Tooltip, Bar, ResponsiveContainer, Cell } from 'recharts';
import { Card, CardTitle, CardBody } from 'reactstrap';
import './SessionDuration.css';
const colors = ['#26a0a7', '#79d69f', '#f9ec86', '#ec983d'];
const data = [
{
name: 'Page A',
pv: 2400,
amt: 2400,
},
{
name: 'Page B',
pv: 1398,
amt: 2210,
},
{
name: 'Page C',
pv: 9800,
amt: 2290,
},
{
name: 'Page D',
pv: 3908,
amt: 2000,
},
];
const getTitleOfTooltip = label => {
if (label === 0) {
return '<=5 min';
}
if (label === 1) {
return '5-30 min';
}
if (label === 2) {
return '30-60 min';
}
if (label === 3) {
return '>60';
}
};
const getIntroOfPage = label => {
if (label === 0) {
return "Page A is about men's clothing";
}
if (label === 1) {
return "Page B is about women's dress";
}
if (label === 2) {
return "Page C is about women's bag";
}
if (label === 3) {
return 'Page D is about household goods';
}
};
export const SessionDuration = () => {
const [position, setPosition] = useState(null);
useEffect(() => {
const tooltip = document.querySelector('.recharts-tooltip-wrapper');
if (!tooltip) return;
// Init tooltip values
const tooltipHeight = tooltip.getBoundingClientRect().height;
const tooltipWidth = tooltip.getBoundingClientRect().width;
const spaceForLittleTriangle = 10;
// Rewrite tooltip styles
tooltip.style = `
transform: translate(${position?.data.x}px, ${position?.data.y}px);
pointer-events: none; position: absolute;
top: -${tooltipHeight + spaceForLittleTriangle}px;
left: -${tooltipWidth / 2 - position?.data.width / 2}px;
opacity: ${position?.show ? '1' : 0};
transition: all 400ms ease 0s;
`;
}, [position]);
return (
<>
<Card className="session-duration-card">
<CardTitle className="session-duration-card-header">
Session Duration
</CardTitle>
<CardBody>
<ResponsiveContainer width="100%" height="100%" aspect={4.0 / 5.5}>
<BarChart
data={data}
margin={{
top: 100, // for tooltip visibility
right: 5,
left: 5,
bottom: 13,
}}
barGap={10}
>
<Tooltip
cursor={false} // hide hover effect 'grey rectangle'
position={{
// Static position
x: position?.data.x ?? 0,
y: position?.data.y ?? 0,
}}
content={<CustomTooltip />}
/>
<Bar
dataKey="pv"
fill="#8884d8"
// Handlers
onMouseMove={data => setPosition({ data: data, show: true })}
onMouseOut={data => setPosition({ data: data, show: false })}
>
{data.map((entry, index) => (
<Cell key={`cell-${index + 1}`} fill={colors[index]} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</CardBody>
</Card>
</>
);
};
export default SessionDuration;
const CustomTooltip = ({ active, payload, label }) => {
if (active) {
return (
<div className="session-duration-tooltip">
<p className="session-duration-tooltip-label">
{getTitleOfTooltip(label)}
</p>
<p className="session-duration-tooltip-intro">
{getIntroOfPage(label)}
</p>
<p className="session-duration-tooltip-desc">
Anything you want can be displayed here.
</p>
</div>
);
}
return null;
};
:root {
--bg-color: hsl(270 3% 29% / 0.9);
}
.session-duration-tooltip {
max-width: 250px;
position: relative;
padding: 0.5em;
border-radius: 5px;
background-color: var(--bg-color);
color: aliceblue;
}
.session-duration-tooltip::after {
content: '';
height: 0;
width: 0;
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
border-style: solid;
border-width: 10px 10px 0 10px;
border-color: var(--bg-color) transparent transparent transparent;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With