Related
I'm having rounding issues with the tooltip in my Line chart for ChartJs. When I hover over my datapoints on the chart, the tooltip displays a rounded version of my data (usually 3 decimal points, or less if there's trailing 0s). Is there a way to stop the auto rounding in the tooltip and show the full number?
Here's my code and a screenshot of the issue.
const LineChart = () => {
const [dataForChart, setDataForChart]=useState<any[]>([1.0023, 1.0231, 1.0347412, 1.03541, 1.0434, 1.04001, 1.0459])
const [labelsForChart, setlabelsForChart]=useState<any[]>(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July'])
const options = {
responsive: true,
plugins: {
legend: {
display:false
},
title: {
display: true,
text: 'Price',
},
},
};
const data = {
labels: labelsForChart,
datasets: [
{
label: 'Price',
data: dataForChart,
borderColor: 'green',
backgroundColor: 'green',
},
],
};
return (
<div>
<Line options={options} data={data} />
</div>
);
};
export default LineChart;
Screenshot of the tooltip
try to use label callback it can modify your tooltip text
change your options to
const options = {
responsive: true,
plugins: {
legend: {
display:false
},
title: {
display: true,
text: 'Price',
},
tooltip: {
callbacks: {
label: function(context) {
return context.dataset.label;
}
}
}
},
};
I am building an app for gym-goers to record and track their progress. For each exercise they input, it should render a chart showing their previous track record. This is working fine.
Users can then add another entry to this track record, but it does not update the chart unless you refresh the page. I can't work out why or how to fix it.
There are a number of different components involved - a parent Exercise.js one, then an ExerciseFooter.js one, which contains the buttons to adjust the target or add a new entry to the exercise, and then AddHistory.js and SetTarget.js components which contain modals and the logic to update the exercise via Redux and MongoDB.
A minimal version of the Exercise.js page is here (I've collapsed the stuff that's mainly styling into single lines as much as possible):
import React, { useState, useEffect } from "react";
import { ExerciseFooter } from "./ExerciseFooter";
import { Line } from "react-chartjs-2";
import { useLocation } from "react-router-dom";
import { useSelector } from "react-redux";
export const Exercise = (props) => {
const location = useLocation();
const users = useSelector((state) => state.auth);
const localUser = JSON.parse(localStorage.getItem("profile"));
const [user, setUser] = useState("");
const [exerciseProp, setExerciseProp] = useState({
history: [""],
target: 0,
});
useEffect(() => {
localUser &&
localUser?.result &&
users.length > 0 &&
setUser(
users.filter(
(filteredUser) => filteredUser._id == props.match.params.userId
)[0]
);
if (!localUser) setUser("");
setExerciseProp(
user?.exercises?.filter(
(exercise) => exercise._id == props.match.params.exerciseId
)[0]
);
}, [users, location]);
//styling for chart
const [barData, setBarData] = useState({
labels: [""],
datasets: [
{ label: "Target", fill: false, radius: 0, data: [""], borderColor: ["rgba(35, 53, 89)"], borderWidth: [3], },
{ label: "You did", data: [""], tension: 0.3, borderColor: ["white"], backgroundColor: ["white"], borderWidth: 3, },
],
});
//updating chart data
var weightArr = [];
var dateArr = [];
var targetArr = [];
if (exerciseProp) {
exerciseProp.history.map((hist) =>
weightArr.push(parseInt(hist.weight) || 0)
);
exerciseProp.history.map((hist) => dateArr.push(hist.date));
for (let i = 0; i < exerciseProp.history.length; i++) {
targetArr.push(exerciseProp.target);
}
}
useEffect(() => {
if (exerciseProp) {
setBarData({
labels: dateArr,
datasets: [
{
label: "Target",
fill: false,
radius: 0,
data: targetArr,
borderColor: ["rgba(35, 53, 89)"], borderWidth: [3],
},
{
label: "Weight",
data: weightArr,
tension: 0.3, borderColor: ["white"], backgroundColor: ["white"], borderWidth: 3,
},
],
});
}
}, [users]);
//render chart ones exerciseProp is populated
if (exerciseProp) {
return (
<div style={{ marginTop: "200px" }}>
<Line
data={barData}
options={{ plugins: { title: { display: false, }, legend: { display: false, }, },
scales: { x: { grid: { color: "white", font: { family: "Dongle", size: 20, }, }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, y: { grid: { color: "white", }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, }, }}
/>
{exerciseProp && <ExerciseFooter user={user} exercise={exerciseProp} />}
</div>
);
} else {
return <>Loading...</>;
}
};
I've tried doing a few different things but nothing has worked. I tried adding an 'update' state variable which was updated by a function passed down to the the various dispatches, and then added it to the dependencies of the useEffects, but that didn't seem to make any difference.
Any help much appreciated! As I say, if I just force a refresh then it works fine but know that's bad practice so trying to work out why it isn't re-rendering correctly.
Thanks!
You just have to enable redraw prop
like this
<Line
redraw={true}
data={barData}
options={{ plugins: { title: { display: false, }, legend: { display: false, }, },
scales: { x: { grid: { color: "white", font: { family: "Dongle", size: 20, }, }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, y: { grid: { color: "white", }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, }, }}/>
this all you have to do
redraw={true}
I am trying to implement nested Doughnut using React-ChartJS-2 but the label seem not to appear correctly in the Outer Doughnut.
Here is what I tried after checking out many examples online.
The version I use for chart.js is ^3.7.1 and ^4.1.0 for react-chartjs-2
import React from "react";
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
import { Doughnut } from "react-chartjs-2";
ChartJS.register(ArcElement, Tooltip, Legend);
const DoughnutChart = ({ xLabels, yData, titleText }) => {
console.log(xLabels);
console.log(yData);
const options = {
responsive: true,
rotation: 270,
circumference: 180,
maintainAspectRatio: true,
animation: false,
legend: {
display: false,
},
plugins: {
tooltips: {
callbacks: {
label: function (tooltipItem, data) {
console.log(tooltipItem.datasetIndex);
var dataset = data.datasets[tooltipItem.datasetIndex];
var index = tooltipItem.index;
return dataset.labels[index] + ": " + dataset.data[index];
},
},
},
},
};
const data = {
labels: ["Time elapsed", "Time remaining"],
datasets: [
{
labels: ["Total Time"],
label: "D1",
data: [5],
backgroundColor: ["rgb(80, 170, 80)", "#ccc"],
},
{
label: "D2",
labels: ["Time elapsed", "Time remaining"],
data: [3.2, 1.3],
backgroundColor: ["goldenrod", "#cccff"],
},
],
};
return (
<div style={{ position: "relative", margin: "auto", width: "80%" }}>
<Doughnut data={data} options={options} />
</div>
);
};
export default DoughnutChart;
The outer doughnut "1" label should be Time elapsed: 5 (does not show correct label)
The inner doughnut "2" label should be Time elapsed: 3.2 and "3" should be Time remaining: 1.3 (correct label is shown)
I expect the outer doughnut label to show Total Time: 5 but I see Time elapsed: 5. How do I fix it or what am I doing wrong ?
By the way, I try to use the call back to update the label but it seems not to work.
For those facing similar issue, to name individual doughnut chart based on separate data within the dataset, I updated the callbacks for the tooltip like so:
plugins: {
tooltip: {
enabled: true,
callbacks: {
label: (tooltipItem) => {
var dataIndex = tooltipItem.dataIndex;
return (tooltipItem.dataset.labels[dataIndex] + ": " + tooltipItem.dataset.data[dataIndex]
);
}
}
}
}
Note: For Chart.js 3, "context" parameter can also be used instead of tooltipItem.
I am using react-chart-2.
When I hover the line graph, the tooltip is displayed, but I want to hide the tooltip when I hover the line graph.
I also want to hide the numbers 0,0.1,0.2 to 1 on the left (y-axis) of the line graph.
How can I implement this to hide the y-axis of the line graph?
Also, how do I hide the tooltip in the line graph?
code
import React from "react";
import { Bar } from "react-chartjs-2";
const data = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "Sales",
type: "line",
data: [51, 300, 40, 49, 60, 37, 40],
fill: false,
borderColor: "#555555",
backgroundColor: "#555555",
pointBorderColor: "#000000",
pointBackgroundColor: "#EC932F",
pointHoverBackgroundColor: "#EC932F",
pointHoverBorderColor: "#EC932F",
yAxisID: "y-axis-1"
},
{
type: "bar",
label: "Visitor",
data: [200, 185, 590, 621, 250, 400, 95],
fill: false,
backgroundColor: "#F7C520",
borderColor: "#F7C520",
hoverBackgroundColor: "#E6B71E",
hoverBorderColor: "#E6B71E",
yAxisID: "y-axis-1"
}
]
};
const options = {
responsive: true,
tooltips: {
mode: "label"
},
elements: {
line: {
fill: false
}
},
scales: {
xAxes: [
{
display: true,
gridLines: {
display: true
}
}
],
yAxes: [
{
type: "linear",
display: true,
position: "left",
id: "y-axis-1",
gridLines: {
display: true
}
},
{
type: "linear",
display: true,
position: "right",
id: "y-axis-2",
gridLines: {
display: false
}
}
]
}
};
class MixExample extends React.Component {
render() {
return (
<div>
<h2>Mixed data Example</h2>
<Bar data={data} options={options} />
</div>
);
}
}
export default MixExample;
react-chartjs-2 has 2 major versions relevant to this issue: v2, which has support for chart.js 2.9.4 and below, and v3, which significantly changes a lot of options and configurations, and which supports chart.js 3.0.0 and above.
The original fork of your codesandbox link uses chart.js: 2.9.4 and react-chartjs-2: 2.1.1, which differs from the sandbox link you provided, which uses chart.js: 3.5.1 and react-chartjs-2: 3.0.4. Notably, instead of structuring your options object like
scales: {
x-axes: [ /* array of axis options... */],
y-axes: [ /* array of axis options... */],
}
where each axis has an axisID property, you structure them with the axisID as the key and the rest of the original options object is the value, such as:
scales: {
"x-axis-1": {
display: true,
gridLines: {
display: false
}
},
"y-axis-1": {
type: "linear",
display: false,
position: "left"
}
}
Furthermore, the tooltip can be disabled by moving the tooltip into plugins, as the chart.js docs say that tooltip is a part of plugins:
global options for the chart tooltips is defined in Chart.defaults.plugins.tooltip
Therefore, to disable the tooltip entirely:
plugins: {
tooltip: {
enabled: false
}
}
Since you want to only disable the tooltip for a single dataset, the solutions found here may be of some use, but I haven't tried either for myself. It may also be possible to set the global default for Chart.JS tooltips to false, then enable it on a dataset-by-dataset basis, as displayed here, accessing it via react-chartjs-2's chart ref.
For that level of customization, I would highly suggest starting from the beginning with the most up-to-date version of react-chartjs-2 and the examples therein, rather than your current v2-configured starting point.
I'm using echarts and i'm trying to emphasis the "current picked bar" by adding background color.
I want to be able to click on one of the bars, and by doing so the month will change and the background color will be applied on the back of that month.
Makes sense?
Here's a codesandbox that emphasis my idea:
https://codesandbox.io/s/flamboyant-cloud-zy44j?file=/src/App.js
But going over and documentation it seems as though there is no option to add backgroundColor to just one category / bar. I tried using another series but that did not work.
I'm also attaching pictures to explain what should be.
And also attaching the code.
import React, { useState } from "react";
import "./styles.css";
import ReactEcharts from "echarts-for-react";
import moment from "moment";
export default function App() {
const [month, setMonth] = useState(0);
const onChartClick = (params) => {
const monthClicked = moment().month(params.name).month();
setMonth(monthClicked);
};
console.log(month);
const renderChart = () => {
const _onEvents = {
click: onChartClick
};
const option = {
maintainAspectRatio: false,
tooltip: {
trigger: "item"
},
grid: {
left: "0px",
right: "0px",
bottom: "0px",
top: "0px",
containLabel: false,
show: false
},
xAxis: {
position: "top",
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
inside: true,
color: "#74818f",
fontFamily: "SegoePro-Regular",
fontSize: 12
},
splitNumber: 1,
splitLine: {
show: true,
lineStyle: {
color: [
"#ffffff",
"#eaeaea",
"#eaeaea",
"#eaeaea",
"#eaeaea",
"#eaeaea",
"#eaeaea",
"#eaeaea",
"#eaeaea",
"#eaeaea",
"#eaeaea",
"#eaeaea",
"#ffffff",
"#ffffff",
"#ffffff"
]
}
},
type: "category",
data: [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
]
},
yAxis: {
scale: true,
type: "value",
axisLabel: {
show: false
},
axisTick: {
show: false
},
axisLine: {
show: false,
onZero: false
},
splitLine: {
show: false
}
},
series: [
{
name: "Monthly Income",
type: "bar",
barWidth: "30%",
barGap: "-20%",
label: {
show: true,
position: "top",
fontSize: 12,
fontFamily: "SegoePro-Bold",
color: "#3d70ff"
},
itemStyle: {
opacity: 0.7,
color: "#3d70ff"
},
emphasis: {
itemStyle: {
opacity: 1
}
},
tooltip: {},
data: [
8700,
8700,
10400,
8699,
8699,
8699,
8699,
8699,
11643.46,
0,
0,
0
],
markArea: {
silent: true,
data: [
[
{
coord: [0, 0]
},
{
coord: [100, 100]
}
]
],
itemStyle: {
color: "#f5f7fa"
},
label: {
show: false
}
}
}
]
};
const chartStyle = 280;
return (
<div id="chart_div">
<div className="chart-wrapper">
<ReactEcharts
onEvents={_onEvents}
option={option}
style={{ height: chartStyle }}
/>
</div>
</div>
);
};
return renderChart();
}
the requirement is unachievable with this library. you can make x-axis clickable by triggerEvent and do something with it; but adding a background or any other style on a "clicked" bar ( or axis ) needs a dedicated state; clicked state; which this library doesn't have; it listens to click events, but doesn't keep it on individual elements; so you can't style it based on a state that doesn't exist;
I was thinking about a workaround to make it happen by some applying css styles on clicked/hovered bars to fake the effect somehow but all the content are rendered inside a canvas, so no option remains; either use another library or write a chart drawer component yourself or forget this specific styling based on click;