-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LineChart: add custom tooltip just above a point #488
Comments
Have same problem with bar chart when trying place tooltip on the top of bar. It's possible to change tooltip's position on bar mouse enter, but not on cursor render. |
The same problem, how set tooltip to top of bar. |
My workaround with area chart: import React from 'react';
import {
AreaChart,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
Area,
} from 'recharts';
export default class UIAreaChart extends React.PureComponent {
static propTypes = {
data: React.PropTypes.array.isRequired
};
constructor(props) {
super(props);
this.area = null;
this.tooltip = null;
this.point = null;
this.onChartMouseMove = this.onChartMouseMove.bind(this);
this.onChartMouseLeave = this.onChartMouseLeave.bind(this);
}
onChartMouseMove(chart) {
if (chart.isTooltipActive) {
let point = this.area.props.points[chart.activeTooltipIndex];
if (point != this.point) {
this.point = point;
this.updateTooltip();
}
}
}
onChartMouseLeave() {
this.point = null;
this.updateTooltip();
}
updateTooltip() {
if (this.point) {
let x = Math.round(this.point.x);
let y = Math.round(this.point.y);
this.tooltip.style.opacity = '1';
this.tooltip.style.transform = `translate(${x}px, ${y}px)`;
this.tooltip.childNodes[0].innerHTML = this.point.payload['value'];
}
else {
this.tooltip.style.opacity = '0';
}
}
render() {
return (
<div className="ui-chart">
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={this.props.data}
onMouseMove={this.onChartMouseMove}
onMouseLeave={this.onChartMouseLeave}>
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Area ref={ref => this.area = ref} type="monotone" dataKey="value"/>
</AreaChart>
</ResponsiveContainer>
<div className="ui-chart-tooltip" ref={ref => this.tooltip = ref}>
<div className="ui-chart-tooltip-content"></div>
</div>
</div>
);
}
} |
Since this is a very common case, shouldn't this be added to the library? <Tooltip position="top" /> |
Thx a lot @Odrin. Could you maybe show what your css (ui-chart, ui-chart-tooltip) looks like? |
@angelozehr nothing special:
|
Any updates on position="top"? I am still confused regarding how to show tooltip on top of a data point? |
@Odrin daysaver, thx m8. adopted the solution for |
+1 for positioning tooltips at the top of bars in bar chart 😊 |
Any updates on this topic? |
+1 |
1 similar comment
+1 |
+1 need update |
+1 |
2 similar comments
+1 |
+1 |
+1 Seems like there is no points-key in area-props. Am I missing something? |
@jesperlandmer Perhaps something has changed in api over the past year, I didn't test it on new versions. |
+1 |
3 similar comments
+1 |
+1 |
+1 |
Why is this still not a feature? |
Using below logic you can achieve individual tool-tip for each dot. Demo Link: Line chart with custom Tooltip
|
@rajatkanthaliya12 Will this work for other charts also like barcharts ? |
Thanks @Odrin ! Your workaround works very well 👍 |
I think there are may workaround now. |
THIS IS THE BEST WAY TO DO IT:
The trick here is to render a custom tooltip, and display data pertaining to the hovered line on the tooltip.
To make it like:
Add your custom tooltip:
The custom tooltip will display data of the nth line: where n starts from 0 (index number of line). |
+1 |
1 similar comment
+1 |
Here is a solution I came up with for getting the tooltip on top of each bar whenever you are in the cursor area:
More info here https://stackoverflow.com/questions/55310451/how-to-position-tooltip-on-the-top-of-a-barchart-in-rechart/69581293#69581293 |
+1 |
9 similar comments
+1 |
+1 |
+1 |
+1 |
+1 |
+1 |
+1 |
+1 |
+1 |
Here is a solution for a CustomTooltip over the active Dot.
And here the CustomTooltipElement with styled components:
In this solution there is a fixed Tooltip at a fixed position (defaultPosition) when not hovering over the Chart as default. If you're not using it just remove 'defaultPosition, setDefaultTooltipPosition, useEffect' |
Such functionality should be core. Maybe we should reopen this issue? |
Why this issue has been closed when there is still no core functionality available for this? |
Ridiculous that this isn't a core feature. There are only a handful of "solutions" that don't even work. This entire system needs to be refactored. Adding small features shouldn't be this messy. |
Still a thing |
then on the tooltip |
In my case below method worked fine for me. define customActiveDot
Apply like this in
|
While the method mentioned above using the activeDot component does work, it is bound to the chart's container, so points at the edges of the chart will be cut off. |
@wopian that is because the overflow property of the outside svg is set to "hidden", you can fix this by setting it to "visible" in your css code. I change the code from @sankaSanjeeva a little bit, in case someone still wants to use the tooltip provided by recharts We can create a state to synchronize the position of activeDot to the tooltip. But for this way, we need to specify which dataKey we want to follow, otherwise the state will keep updating. here is the link of an example and my code:
But still, please we really need this functionality 😢 |
To communicate transparently, adding new features is not high on our TODO list. We would rather first finish migrating the documentation to storybook, fixing bugs, and refactoring the code to functional components. All that work will enable us to finally focus on adding features again, but for now we need to make the code base easier to work with. That being said, if you would like to give it a shoot, I would love to review your PR :) We are always looking for contributors. |
@ashhher thank you for the great solution! One small tweak to your code. The quoted code should be |
It took me some time to come up with a solution for one of our charts. There's probably a much better approach we can take but without looking through the recharts codebase I am not clear on what that would look like. Would be curious what others have been able to do. Here is a gist for the code: https://gist.github.com/alettieri/e5bfa8982e0cd2c38ebc6b3bf94a0462 In short, I used the Here is the code that really matters: const getLinePointItems = (
projectedPoints?: Array<LinePointItem>,
actualPoints?: Array<LinePointItem>
) => {
if (projectedPoints && actualPoints) {
// Merge the actual and projected points together
// We want to keep the indexes aligned so we can use the index to find the correct point
return actualPoints.map((point, index) => {
if (point.x && point.y) {
return point;
}
return projectedPoints[index];
});
}
return [];
}; const PlanItemChart = ({
planItem,
projectedRule,
group,
planId,
}: {
planItem: PlanItem;
projectedRule: IPlanItemProjectedRule;
group: PlanItemGroup;
planId: Plan['id'];
}) => {
const [toolTipIndex, updateToolTipIndex] = React.useState(0);
const theme = useTheme();
const actualRef = React.useRef<null | (Line & SVGPathElement)>(null);
const projectedRef = React.useRef<null | (Line & SVGPathElement)>(null);
//... cut out some code for brevity
const handleMouseMove = React.useCallback<OnMouseMoveHandler>(
(coords) => {
if (
coords.isTooltipActive &&
coords.activeTooltipIndex !== undefined
) {
updateToolTipIndex(coords.activeTooltipIndex);
}
},
[updateToolTipIndex]
);
const toolTipPosition = React.useMemo<
TooltipProps<number, string>['position']
>(() => {
const linePointItems = getLinePointItems(
projectedRef.current?.props?.points,
actualRef.current?.props?.points
);
if (linePointItems[toolTipIndex]) {
// Grab the current tooltip position based off the point index list
const point = linePointItems[toolTipIndex];
return { x: point.x, y: point.y };
}
return undefined;
}, [toolTipIndex]);
// ... left out some code for brevity
return (
<LineChart
data={createPlanVersionPreview.data?.[0]}
margin={{ right: 8, left: 8 }}
onMouseMove={handleMouseMove}
>
<Tooltip
content={<PlanItemChartTooltip />}
position={toolTipPosition}
/>
<Line
type="monotone"
dataKey="p"
stroke={theme.palette.grey[500]}
activeDot={{ r: 4 }}
dot={{ r: 2 }}
ref={projectedRef}
/>
<Line
type="monotone"
dataKey="a"
stroke={theme.palette.primary.main}
activeDot={{ r: 4 }}
dot={{ r: 2 }}
ref={actualRef}
/>
<Legend
verticalAlign="bottom"
wrapperStyle={{
bottom: 0,
fontSize: '10px',
}}
content={<PlanItemChartLegend />}
/>
</LineChart>
);
}; The refs are read here: <Line
type="monotone"
dataKey="p"
stroke={theme.palette.grey[500]}
activeDot={{ r: 4 }}
dot={{ r: 2 }}
ref={projectedRef}
/>
<Line
type="monotone"
dataKey="a"
stroke={theme.palette.primary.main}
activeDot={{ r: 4 }}
dot={{ r: 2 }}
ref={actualRef}
/>
); Adding the custom Tooltip content component so it's easier to reference. import React from 'react';
import { Typography, Box, SxProps, Theme, alpha } from '@mui/material';
import { TooltipProps } from 'recharts';
import { IPlanVersionPreviewResponse } from '../../shared/types/plan';
import { convertNumberToCurrency } from '../../utils/currency-utils';
const styles: SxProps<Theme> = {
'--_height': '48px',
'--_width': '68px',
'--_offsetX': '-50%',
'--_offsetY': 'calc(-1 * (var(--_height) + 18px))',
'--_tooltip-bg': (theme) => alpha(theme.palette.grey[700], 0.9),
display: 'flex',
flexDirection: 'column',
gap: 1,
textAlign: 'center',
minWidth: 'var(--_width)',
height: 'var(--_height)',
p: '10px',
backgroundColor: 'var(--_tooltip-bg)',
color: 'common.white',
fontSize: 'tooltip.fontSize',
fontWeight: 'tooltip.fontWeight',
transform: `translate(var(--_offsetX), var(--_offsetY))`,
borderRadius: 1,
position: 'relative',
overflow: 'clip-content',
'& .PlanItemChartTooltip-arrow': {
position: 'absolute',
bottom: '-7px',
left: '50%',
width: 0,
height: 0,
borderLeft: '8px solid transparent',
borderRight: '8px solid transparent',
borderTop: '8px solid var(--_tooltip-bg)',
ml: '-8px',
},
};
export const PlanItemChartTooltip = (props: TooltipProps<number, string>) => {
const toolTipContent = React.useMemo<null | {
value: string;
date: string;
}>(() => {
if (props.payload.length > 0) {
const payload = props.payload[0];
const dataKey = payload?.dataKey as 'p' | 'a';
const dataPoint =
payload.payload as IPlanVersionPreviewResponse[0][0];
const value = Reflect.get(dataPoint, dataKey);
const date = dataPoint.d;
return { value: convertNumberToCurrency(value), date };
}
return null;
}, [props.payload]);
if (toolTipContent !== null) {
return (
<Box className="PlanItemChartTooltip-root" sx={styles}>
<Typography variant="inherit">{toolTipContent.date}</Typography>
<Typography variant="inherit">
{toolTipContent.value}
</Typography>
<Box className="PlanItemChartTooltip-arrow" />
</Box>
);
}
return null;
}; |
I've tried it like this. And it worked in my use case |
Hi everyone,
in my project I'm trying to achieve something like in the image below, where the tooltip is rendered just above the point it refers to.
Looking at the props the custom tooltip receives, coordinates follow the mouse cursor. Is there a way to have coordinates fixed to the point in the graph?
The text was updated successfully, but these errors were encountered: