React Recharts: Design custom tooltip - javascript

I am trying to replace recharts default tooltip with custom tooltip.
The existing tooltip looks like this.
I wanted to replace the above default tooltip as
I made sandbox for the code.
Thanks

Very quick solution. You can alter it as you want
Create a custom tooltip class
export const CustomTooltip = ({ active, payload, label }) => {
if (active && payload && payload.length) {
return (
<div className="custom-tooltip">
<p className="label">{`${label} : ${payload[0].value}`}</p>
<div>
{payload.map((pld) => (
<div style={{ display: "inline-block", padding: 10 }}>
<div style={{ color: pld.fill }}>{pld.value}</div>
<div>{pld.dataKey}</div>
</div>
))}
</div>
</div>
);
}
return null;
};
And use it like this
<Tooltip content={<CustomTooltip />} cursor={{ fill: "transparent" }} />
demo

Related

React Recharts tooltip not showing proper data

I have two lines plotted on a recharts one is plotting everyday and the other is plotting once every quarter. The recharts tooltip only shows up for the pink line when it is directly hovered over the plotted point and says 0 when it is not over the directly plotted point. I want the tooltip to always show the data during that time. I tried adding in the "filterNull={false}" into the tooltip but it did not work. Any Ideas?
<Tooltip content={renderTooltipContentGeneric} filterNull={false} />
export const renderTooltipContentGeneric = o => {
const { payload, label } = o
const total = payload?.reduce((result, entry) => result + entry.value, 0)
return (
<div className="customized-tooltip-content-core">
<div className="list">
{payload?.map((entry, index) => (
<div
className="listItem"
key={`item-${index}`}
style={{
color: entry.color,
display: 'flex',
flexDirection: 'row',
gap: '4px',
}}
>
<div style={{ color: 'var(--white)' }}>{`${moment(label).format(
'lll'
)}: `}</div>
{`${numeral(entry.value).format('0.000 a')} `}
</div>
))}
</div>
</div>
)
}

Conditional styling in react map

I only want to show the display block on the hovered item. but when I hover it shows on every item in a map function. what I'm doing wrong.
basically, I just want to show hovered movie item's title. for now, it shows every movie when I hover.
MovieList.js
const [popular, setPopular] = useState([]);
const [hover, setHover] = useState(false);
return (
<>
<div className="movie-list">
<h2 style={{ fontSize: "49px", marginLeft: "60px" }}>What's Popular</h2>
<div className="popular">
{popular.map((pop, index) => (
<>
<div className="movie" key={index}>
<div className="tot" onMouseEnter={() => setHover(true)}>
<h4
id="pop-title"
style={{ display: hover ? "block" : "none" }}
key={index}
>
{pop.title}
</h4>
</div>
<img
src={"https://image.tmdb.org/t/p/w500" + pop.poster_path}
id="movie-img"
/>
</div>
</>
))}
</div>
</div>
</>
);
};
export default MovieList;
The same hover state variable is used for all your movies, so when it becomes true, all the movies are affected by the change. You could use something like an object instead of just a boolean to store one hover state per movie.
Another problem with your code that isn't helping:
You are missing a unique key prop on each item of the map (it must be on the direct child).
Solution 1: Remove the Fragment
Everything is already under one div so you don't need the React Fragment (<>) in that case. Also, you might wanna use something unique to the current map item other than the index in the array.
{popular.map((pop) => (
<div className="movie" key={pop.somethingUnique}>
<div className="tot" onMouseEnter={() => setHover(true)}>
<h4 id="pop-title" style={{ display: hover ? "block" : "none" }}>
{pop.title}
</h4>
</div>
<img
src={"https://image.tmdb.org/t/p/w500" + pop.poster_path}
id="movie-img"
/>
</div>
))}
Solution 2: Set the key on the Fragment
{popular.map((pop) => (
<Fragment key={pop.somethingUnique}>
<div className="movie">
<div className="tot" onMouseEnter={() => setHover(true)}>
<h4 id="pop-title" style={{ display: hover ? "block" : "none" }}>
{pop.title}
</h4>
</div>
<img
src={"https://image.tmdb.org/t/p/w500" + pop.poster_path}
id="movie-img"
/>
</div>
</Fragment>
))}

Is there a way to perform a click event only on a parent element?

In my app I would like to be able to click an item (background color, text, etc), have a modal pop up with a color picker, then change the color of the item.
The issue I'm having is that I made an onClick handler for the parent element to update a background color, but it's also activating when anything within the parent element is clicked.
I have an example in Codesandbox and you can see that whether you click the background or the buttons, the color picker comes up when I only want it activated for the background.
If anyone is familiar with Chakra-ui, this is my code:
const Navbar = () => {
const [display, setDisplay] = useState('none');
const [color, setColor] = useState('#1A202C');
const [showColorPicker, setShowColorPicker] = useState(false);
const { isOpen, onOpen, onClose } = useDisclosure();
/*
On click, showColorPicker becomes true and isOpen also becomes true
in order to display the modal with a color picker
*/
const handleModalClick = () => {
onOpen();
if (!showColorPicker) {
setShowColorPicker((showColorPicker) => !showColorPicker);
}
};
return (
<div>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent
bg='gray.600'
style={{ boxShadow: '2px 2px 2px 1px rgba(0, 0, 0, 0.2)' }}>
<ModalCloseButton color='gray.200' />
<ModalBody style={{ borderRadius: '10px' }}>
<Center>
{showColorPicker && (
<ChromePicker
color={color}
onChange={(updatedColor) => setColor(updatedColor.hex)}
/>
)}
</Center>
</ModalBody>
</ModalContent>
</Modal>
// Flex === a div with display flex
<Flex
bg={color}
color='gray.200'
style={{
textTransform: 'uppercase',
fontWeight: 'bold',
}}
onClick={handleModalClick}>
<Link p='5' _hover={{ color: 'cyan.400' }}>
<Text fontSize='xl'>Color Selector</Text>
</Link>
<Spacer />
<Flex
display={['none', 'none', 'flex', 'flex']}
fontSize='md'
align='center'>
<Link p='5' _hover={{ color: 'cyan.400' }}>
About
</Link>
<Link p='5' _hover={{ color: 'cyan.400' }}>
Portfolio
</Link>
<Link p='5' _hover={{ color: 'cyan.400' }}>
Contact
</Link>
</Flex>
</Flex>
...
</div>
);
};
Is there a way to show the color picker only when the background is clicked?
The app is also deployed on Netlify if you want to see the real example or all of the code on GitHub.
The event object has a target property, which holds the exact element that the user interacted with to trigger the event. So, you can just check if the target element is the parent element to know if they interacted with the parent directly or one of their children.
Here's one way of doing it:
if (e.target.classList.contains('navbar') && !showColorPicker) {
setShowColorPicker((showColorPicker) => !showColorPicker);
}
A more robust way of doing it would be to store the parent in a React ref, and make sure that e.target is exactly the same as that ref. (This is one of the places where it's ok to use a ref).
Here's a complete example that uses a ref. (in won't run in StackOverflow, because I didn't properly load up the libraries, but it'll work).
import "./styles.css";
import React, { useState, useRef } from "react";
import { ChromePicker } from "react-color";
export default function App() {
const [display, setDisplay] = useState("none");
const [color, setColor] = useState("#1A202C");
const [showColorPicker, setShowColorPicker] = useState(false);
const navBarRef = useRef();
const handleModalClick = e => {
if (e.target === navBarRef.current && !showColorPicker) {
setShowColorPicker((showColorPicker) => !showColorPicker);
}
};
return (
<>
<div
className="navbar"
ref={navBarRef}
style={{ backgroundColor: `${color}`, color: "white" }}
onClick={handleModalClick}
>
<button style={{ padding: "10px 15px 10px 15px", margin: "20px" }}>
Left
</button>
<button style={{ padding: "10px 15px 10px 15px", margin: "20px" }}>
Right
</button>
</div>
{showColorPicker && (
<ChromePicker
color={color}
onChange={(updatedColor) => setColor(updatedColor.hex)}
/>
)}
</>
);
}
Whats happening is called "Event Bubbling" and it is the intended behavior (you can read more about it here). Eventually, you'll find that it is very useful.
If you want to only handle events that are triggered from the same element where the handler is attached, you can do something like this:
const parent = document.getElementById('parent');
const handler = (e) => {
if (e.target !== e.currentTarget) {
return;
}
console.log('PARENT CLICKED!');
};
parent.addEventListener('click', handler);
#parent {
background-color: #123ff0;
}
.box {
display: inline-block;
height: 50px;
width: 50px;
background-color: #000000;
}
p {
color: #ffffff;
background-color: #000000;
}
<div id='parent'>
<span class='box'></span>
<span class='box'></span>
<p>This is a paragraph.</p>
<span class='box'></span>
<span class='box'></span>
</div>
The event object provided gives you some default preventions that you can use.
Example:
const handleClick = (event) => {
event.stopPropagation();
}
Should be added on clickable elements that are part of the parent and that should not trigger the event.
This will prevent your event to be propagated to the parent element.
I've forked your codesanbox and added my solution in.
https://codesandbox.io/s/infallible-hellman-66sln?file=/src/App.js
I think the above solutions are also correct and everything will depend on the situation.
More info here: https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation

Changing the order of Components in React depending on a variable?

I have a React Component which is rendered by this map function:
<div className="links-container">
{links.map((link, i) => (
<Links
key={link.text}
icon={link.icon}
text={link.text}
isRight={i % 2 === 0 ? true : false}
/>
))}
</div>
import React, { Component } from "react";
export default class Links extends Component {
render() {
const { icon, text, isRight } = this.props;
return (
<div style={{ alignSelf: isRight ? "flex-end" : "" }}>
<div className="link">
<img
className="link-img"
src={icon}
alt="link"
style={{ borderColor: isRight ? "#1689FC" : "#FD003A" }}
/>
<div className="link-text">{text}</div>
</div>
</div>
);
}
}
And what I want to do is, if the isRight is true, I want to render the text first and then the img, if isRight is false, I want to render the image and then the text. Now, I am aware that I could wrap this thing in a big if statement like this:
isRight ? <div><text/><img/></div> : <div><img/><text/></div>
But I am wondering if there's a better way to do this because my approach uses repetitive code, which is the reason why I have this Links Component in the first place.
You can use display:flex and flex-direction property on <div className="link">
flex-direction: row-reverse or flex-direction: column-reverse depending on your layout.
Try using this single line of code: flex-direction: row-reverse

Use material-ui tooltip on <i> component

I want to use the material-ui react tooltip on the following component:
const Icon = ({
name,
color,
size,
}) => {
return (
<i
aria-hidden='true'
style={{
fontSize: size,
color,
}}>
{name}
</i>
)
};
export default Icon;
The Icon is created through an Iconfont.
Implementing the tooltip like this does not work - no tooltip is showing
...
icons.map(icon => {
<Tooltip
title='Foo'
placement='right'>
<Icon
size={icon.size}
color={icon.color}
name={icon.name} />
</Tooltip>
}
...
But, replacing the <Icon /> through a simple <i>, works.
...
icons.map(icon => {
<Tooltip
title='Foo'
placement='right'>
<i>
{'Bar'}
</i>
</Tooltip>
}
...
How can I use the material-ui react tooltip with th <Icon /> component?

Categories

Resources