Calling function in component class from Highcharts component in React - reactjs

Trying to set a click listener on the Pie Chart made using HighCharts wrapper for react. I would like to manipulate state when a user clicks on a portion of the Pie Chart, but I can't seem to think of a way to set the listener to the click event.
Setting click event to this.clickHandler wont work because this does not refer to the App Class. How can I set the function in the App component to be triggered on click
class App extends Component{
state = {
config:{
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: 'Analysis'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
},
accessibility: {
point: {
valueSuffix: '%'
}
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
events: {
click: this.clickHandler
}
},
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.percentage:.1f} % ({point.n}) '
}
}
},
series: [{
name: 'Market Share in Percentage',
size: 200,
center: [150, 100],
colorByPoint: true,
data: [{
name: 'Common Devices',
y: 40,
n: 243,
sliced: true,
selected: true
}, {
name: 'Unique First Devices',
y: 50,
n: 300,
}, {
name: 'Unique Second Devices',
y: 10,
n: 30
}]
}]
},
test:false
}
clickHandler = (event) => {
console.log(event)
if (event.point.name==="Common Devices"){
this.setState({test:true})
}
}
render(){
return <div>
<HighchartsReact options = {this.state.config}></HighchartsReact>
</div>
}
}

As you mentioned,this does not refer to the App Class, so, change click: this.clickHandler to click: this.clickHandler.bind(this) and you should move state into the class constructor
constructor(props) {
super(props);
this.state ={
// Your state
}
Sample can be found here.

Related

React Highcharts piechart show tooltip on click outside of highcharts container

I am having a "Donutchart" using highcharts and a selfmade legend for that chart. When i click on an element of the legend, i want to trigger the "mouseOver" or "click" event of that container, so that the chart shows the respective area, belonging to the element of the legend. Basically i want the tooltip to show up.
The Piechart Code:
let chartOptions = {
chart: {
plotBackgroundColor: null,
plotBorderWidth: 0,
plotShadow: false,
},
title: {
text: `Title`,
},
plotOptions: {
pie: {
dataLabels: {
enabled: false,
distance: -125,
y: -5,
format: "{y}%",
style: {
fontWeight: "bold",
color: "black",
fontSize: "12px",
},
},
borderWidth: 3,
},
series: {
animation: false,
},
},
tooltip: {
shared: true,
},
series: [
{
type: "pie",
name: "",
tooltip: {
valueDecimals: 2,
valueSuffix: " USD",
},
innerSize: "60%",
data: data_array,
},
],
};
Example button:
<div className='mb_3px mt_3px cursor_pointer color_blue'
onClick={() => {
handleSimulateMouseOver(
item.index
);
}}
>
{truncate(
object[
item.index
].name,
39
)}
</div>
In older posts i found a function called setState('hover'), which i can't bring to work in react, that looks like the following:
let handleSimulateMouseOver= (index) => {
chartOptions.series[0].data[index].setState('hover');
chartOptions.tooltip.refresh(chartOptions.series[0].data[index]);
};
There is an event to trigger callback after legend item click - API: https://api.highcharts.com/highcharts/series.line.events.legendItemClick
Have you tried to use it?

React-chartjs-2 line chart with x and y data not working

I've been trying to figure out how to create a line chart that plots data in this
format {x:time,y:data}.
Basically exactly like this post.
How can I create a time series line graph in chart.js?
Problem is it that i can't manage to get it to work using the react-chartjs2 library.
Below is my code and a link to a image of my graph.
class Grafer extends Component {
constructor(props) {
super(props);
this.state = {};
}
data = {
datasets: [
{
label: 'time-data',
data: [
{ x: '2021-08-21 18:37:39', y: 2 },
{ x: '2021-08-22 18:39:39', y: 3 },
{ x: '2021-09-25 18:44:39', y: 1 },
],
borderColor: 'rgba(255,99,132,0.8)',
tension: 0.3,
},
],
};
options = {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Chart.js Line Chart',
},
scales: {
xAxes: [
{
type: 'time',
distribution: 'linear',
},
],
title: {
display: false,
},
},
},
};
render() {
return (
<div>
<Line data={this.data} options={this.options} />
</div>
);
}
}
export default Grafer;
image to see my line-graph

How can I display the current value from a chart?

I am new to React and I work on a small project basically, I have a chart and I just want to display the current value from the chart.
For example, I have a chart with 4 random values:[5,2,5,1], so I want to have displayed the current value below the chart like first is 5, second is 2 and so on.
Here is my part of code:
class App extends React.Component {
addPoint = (point) => {
currentData = this.state.options.series[0].data
this.setState({
options: {
series: [
{ data: [...currentData, point]}
]
}
});
}
constructor(props) {
super(props);
this.internalChart = undefined;
this.dataStream = new DataStream();
this.dataStream.setUpdateCallback(this.addPoint);
this.state = {
options: {
chart: {
events: {
load: function () {
}
}
},
time: {
useUTC: false
},
rangeSelector: {
buttons: [{
count: 1,
type: 'minute',
text: '1M'
}, {
count: 5,
type: 'minute',
text: '5M'
}, {
type: 'all',
text: 'All'
}],
inputEnabled: false,
selected: 2
},
title: {
text: 'Live random data'
},
exporting: {
enabled: false
},
series: [{
name: 'Random data',
data: [[(new Date()).getTime(), 0]]
}]
}
};
}
render() {
return (
<HighchartsReact
constructorType={"stockChart"}
highcharts={Highcharts}
options={this.state.options}
/>
);
}}
Based on your snippet code, try to make random method by using function
Math.floor(Math.random() * Math.floor(max))
And then assign the options to HighchartsReact,
Full code on sandbox: https://codesandbox.io/s/headless-dream-0lzj1

Why does highcharts shrink on re-render?

I'm using highcharts-react-official to render a HighchartsReact component. It shows up and works appropriately until I re-render the component. By changing the state at all, the chart will shrink vertically.
I've experimented with setting reflow in the chart options as well as toggling allowChartUpdate and immutable flags on the component itself to no avail.
const ChartView = props => {
const { data } = props;
if(data.highstockData && data.startDate && data.endDate) {
const min = parseInt(moment(data.startDate, 'x').startOf('day').format('x'));
const max = parseInt(moment(data.endDate, 'x').startOf('day').format('x'));
const chartOptions = getChartConfig(data.highstockData, min, max);
return (
<HighchartsReact
highcharts={Highcharts}
options={chartOptions}
/>
)
}
return null;
};
And the parent Component's render return:
return (
<div className="vertical-margin">
{isFetching && !data && <LoadingView/>}
{hasError && !data && <ErrorView/>}
{
data &&
<React.Fragment>
{buttonRow}
<ChartView
data={data}
/>
</React.Fragment>
}
</div>
)
As I said re-rendering for any reason causes the highchart to shrink in height with each re-render. For testing, I call this line:
this.setState({});
I could post the chart config if needed, but it's nothing too fancy.
I haven't been able to find anyone else having this issue and have been pulling my hair out searching for an answer.
It turned out to indeed be a highchart option I was passing into the component. Apparently it was because this option:
scrollbar: {
enabled: true
},
Was not nested under the xAxis section of the options as it should be. It still created a scrollbar correctly but caused this weird, shrinking issue on component render.
chart: {
marginRight: 75,
ignoreHiddenSeries: false,
panning: false,
spacingTop: 10,
height: `${Constants.HIGHCHART_TABLE_HEIGHT}px`,
},
time: {
useUTC: false
},
credits: {
enabled: false
},
exporting: {
enabled: false
},
legend: {
align: 'left',
enabled: true,
itemMarginTop: 5,
itemStyle: {
"color": "#333333",
"cursor": "pointer",
"fontSize": "12px",
"fontWeight": "normal",
"width": "240px"
},
layout: 'vertical',
verticalAlign: 'top',
y: 0
},
navigator: {
enabled: false,
xAxis: {
tickPixelInterval: 100
}
},
plotOptions: {
line: {
marker: {
enabled: true,
fillColor: "#ffffff",
lineColor: null,
lineWidth: 1
}
}
},
rangeSelector: {
enabled: false
},
tooltip: {
formatter: function () {
const sortedPoints = this.points.sort((a, b) => {
return a.point.legendOrder - b.point.legendOrder
});
return [
'<b>',
moment(this.x, 'x').format('MM/DD/YYYY HH:mm'),
'</b><br/>',
sortedPoints.map((item) => {
return [
'<br/><span style="color:'+ item.series.color +';">\u258c</span> ',
'<span>' + item.series.name + '</span>: <b>' + item.y + '</b>',
item.comments ? '<p>(' + item.comments + ')</p>' : ''
].join('');
}).join('')
].join('');
},
shared: true,
crosshairs: {
color: '#ddd',
dashStyle: 'solid'
},
},
xAxis: {
type: 'datetime',
labels:{
formatter: function() {
const parsed = moment(this.value, 'x');
return parsed.format('HH:mm') + '<br/>' + parsed.format('MM/DD');
}
},
min,
max,
reversed: true,
scrollbar: {
enabled: true
},
},
yAxis: [{
alignTicks: false,
max: 60,
min: 0,
opposite: false,
tickInterval: 5,
title: {
text: ''
}
}, {
alignTicks: false,
max: 300,
min: 0,
opposite: true,
tickInterval: 25,
title: {
text: ''
}
}],
//The below properties are watched separately for changes.
series: data,
title: {
text: ''
},
loading: false,
};
Also here's the full options file for reference. It wasn't just that scrollbar option causing it. It was a specific combination of options as I discovered by trying to re-create the problem with a new chart.

Zoom and Pan in react-chartjs-2

I have recently implemented chart display using react-chartjs-2 (https://github.com/jerairrest/react-chartjs-2)
I want to enable zooming and panning feature so that it will be more user-friendly in touch based screens. To implement this features, I installed react-hammerjs and chartjs-plugin-zoom.
import {Chart, Line} from 'react-chartjs-2';
import Hammer from 'react-hammerjs';
import zoom from 'chartjs-plugin-zoom'
And I registered the plugin
componentWillMount(){
Chart.plugins.register(zoom)
}
And the render method goes as follows:
render(){
return <Line data={data} options={options} />
}
Pan and Zoom options:
pan:{
enabled=true,
mode:'x'
},
zoom:{
enabled:true,
drag:true,
mode:'xy'
}
I guess this is the correct method to implement. Unfortunately, the above implementation did not work. I will be really grateful if some of you guys already implemented Zooming and Panning using react-chartjs-2 plugin, please share if how you achieved these functionalities. Or you could point out the problem in my code above.
In order to add Zoom and Pan capabilities to your chart components based on react-chartjs-2, you can follow the steps as shown below:
Step 1: you need to install chartjs-plugin-zoom
$ npm install chartjs-plugin-zoom
Step 2: Import chartjs-plugin-zoom in your chart component
import 'chartjs-plugin-zoom';
Step 3: Enable zoom and pan in the ChartJS component options
zoom: {
enabled: true,
mode: 'x',
},
pan: {
enabled: true,
mode: 'x',
},
That's it. So now your chart component should look like this:
import React from 'react';
import { Line } from 'react-chartjs-2';
import 'chartjs-plugin-zoom';
export default function TimelineChart({ dailyDataSets }) {
const lineChart = dailyDataSets[0] ? (
<Line
data={{
labels: dailyDataSets.map(({ date }) => date),
datasets: [
{
data: dailyDataSets.map((data) => data.attr1),
label: 'First data set',
borderColor: 'red',
fill: true,
},
{
data: dailyDataSets.map((data) => data.attr2),
label: 'Second data set',
borderColor: 'green',
fill: true,
},
],
}}
options={{
title: { display: true, text: 'My Chart' },
zoom: {
enabled: true,
mode: 'x',
},
pan: {
enabled: true,
mode: 'x',
},
}}
/>
) : null;
return <div>{lineChart}</div>;
}
Notes:
You don't have to install hammerjs explicitly, as it will be automatically included by installing chartjs-plugin-zoom as its dependency, see below:
$ npm ls
...
├─┬ chartjs-plugin-zoom#0.7.7
│ └── hammerjs#2.0.8
...
One way to zoom as an example (at least for Mac), you can move your mouse pointer into the chart area, and then scroll your mouse down or up. Once zoomed in, you can keep your mouse clicked while dragging left or right.
There's a syntax error under pan object for enabled attribute.
You've mistakenly put = instead of :
Replace this:
pan:{
enabled=true,
...
},
With:
pan:{
enabled:true,
...
},
And also as #Jun Bin suggested:
Install hammerjs as:
npm install hammerjs --save
And in your component, import it as:
import Hammer from "hammerjs";
you imported the wrong hammer it should be from "hammerjs";
You need to add import 'chartjs-plugin-zoom'; and then add zoom options into options.plugins.zoom, like:
const options = {
plugins: {
zoom: {
pan: {
enabled: true,
mode: 'x',
},
zoom: {
enabled: true,
drag: true,
mode: 'xy'
}
}
}
};
I am trying to do this in a NextJS Project. But to no success so far.
I am using a timeseries plot with date-fns/locale for German and English and keep getting this error:
Cannot convert a Symbol value to a string
TypeError: Cannot convert a Symbol value to a string at TypedRegistry.register (webpack-internal:///./node_modules/chart.js/dist/chart.esm.js:4802:50) at Registry._exec (webpack-internal:///./node_modules/chart.js/dist/chart.esm.js:4927:21) at eval (webpack-internal:///./node_modules/chart.js/dist/chart.esm.js:4919:16) at each (webpack-internal:///./node_modules/chart.js/dist/chunks/helpers.segment.js:233:10) at eval (webpack-internal:///./node_modules/chart.js/dist/chart.esm.js:4917:70) at Array.forEach (<anonymous>) at Registry._each (webpack-internal:///./node_modules/chart.js/dist/chart.esm.js:4912:15) at Registry.add (webpack-internal:///./node_modules/chart.js/dist/chart.esm.js:4870:10) at Function.value [as register] (webpack-internal:///./node_modules/chart.js/dist/chart.esm.js:6192:16) at eval (webpack-internal:///./components/Charts/PortfolioPriceLineDual.jsx:39:45) at Module../components/Charts/PortfolioPriceLineDual.jsx (https://dev.domain.de/_next/static/chunks/components_Charts_PortfolioPriceLineDual_jsx.js:7758:1) at Module.options.factory (https://dev.domain.de/_next/static/chunks/webpack.js?ts=1653499440538:655:31) at __webpack_require__ (https://dev.domain.de/_next/static/chunks/webpack.js?ts=1653499440538:37:33) at Function.fn (https://dev.domain.de/_next/static/chunks/webpack.js?ts=1653499440538:310:21)
My Component:
import { Line } from 'react-chartjs-2'
import 'chartjs-adapter-date-fns'
import { de, enGB, ja } from 'date-fns/locale'
import dynamic from 'next/dynamic'
import 'chart.js/auto'
import { useRouter } from 'next/router'
import { Chart } from 'chart.js'
// import zoomPlugin from 'chartjs-plugin-zoom';
const zoomPlugin = dynamic(() => import('chartjs-plugin-zoom'), {
ssr: false,
})
Chart.register(zoomPlugin);
const PortfolioPriceLineDual = ({
title,
data,
unit,
axesOptions,
showLegend = true,
}) => {
const totalDuration = 5000
const delayBetweenPoints = totalDuration / data.datasets[0].data.length
// const animation =
const { locale } = useRouter()
let format
switch (locale) {
case 'de-DE':
format = de
break
case 'en-US':
format = enGB
break
case 'ja-JP':
format = ja
break
default:
break
}
return (
<Line
data={data}
options={{
responsive: true,
// maintainAspectRatio: true,
// aspectRatio: 16 / 9,
resizeDelay: 5,
animation: {
x: {
type: 'number',
easing: 'linear',
duration: delayBetweenPoints,
from: NaN, // the point is initially skipped
delay: (ctx) => {
if (ctx.type !== 'data' || ctx.xStarted) {
return 0
}
ctx.xStarted = true
return ctx.index * delayBetweenPoints
},
},
y: {
type: 'number',
easing: 'linear',
duration: delayBetweenPoints,
from: (ctx) => {
return ctx.index === 0
? ctx.chart.scales.y.getPixelForValue(100)
: ctx.chart
.getDatasetMeta(ctx.datasetIndex)
.data[ctx.index - 1].getProps(['y'], true).y
},
delay: (ctx) => {
if (ctx.type !== 'data' || ctx.yStarted) {
return 0
}
ctx.yStarted = true
return ctx.index * delayBetweenPoints
},
},
y1: {
type: 'number',
easing: 'linear',
duration: delayBetweenPoints,
from: (ctx) => {
return ctx.index === 0
? ctx.chart.scales.y.getPixelForValue(100)
: ctx.chart
.getDatasetMeta(ctx.datasetIndex)
.data[ctx.index - 1].getProps(['y'], true).y
},
delay: (ctx) => {
if (ctx.type !== 'data' || ctx.yStarted) {
return 0
}
ctx.yStarted = true
return ctx.index * delayBetweenPoints
},
},
},
interaction: {
mode: 'index',
intersect: false,
},
scales: {
x: {
type: 'time',
time: {
unit: 'year',
displayFormats: {
quarter: 'yyyy',
},
tooltipFormat: 'MMMM yyyy',
},
adapters: {
date: {
locale: format,
},
},
ticks: {
align: 'start',
color: '#122a42',
font: {
size: 14,
weight: 'bold',
},
},
grid: {
display: true,
drawBorder: false,
drawOnChartArea: true,
drawTicks: true,
},
},
y: {
type: 'logarithmic',
grid: {
display: true,
drawBorder: false,
drawOnChartArea: true,
drawTicks: true,
},
ticks: {
color: '#122a42',
align: 'end',
font: {
size: 10,
weight: 'normal',
},
// Include a dollar sign in the ticks
// stepSize: 1000,
callback: function (value) {
// callback: function (value, index, ticks) {
return `${new Intl.NumberFormat(locale, axesOptions).format(
value
)}`
},
},
},
y1: {
type: 'linear',
display: true,
position: 'right',
// grid line settings
grid: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
ticks: {
color: '#122a42',
align: 'end',
font: {
size: 10,
weight: 'normal',
},
// Include a dollar sign in the ticks
// stepSize: 1000,
callback: function (value) {
// callback: function (value, index, ticks) {
return `${new Intl.NumberFormat(locale, axesOptions).format(
value
)}`
},
},
},
},
zoom: {
enabled: true,
mode: 'x',
},
pan: {
enabled: true,
mode: 'x',
},
plugins: {
zoom: {
enabled: true,
mode: 'x',
},
pan: {
enabled: true,
mode: 'x',
},
// zoom: {
// zoom: {
// wheel: {
// enabled: true,
// },
// pinch: {
// enabled: true,
// },
// mode: 'x',
// },
// },
title: {
display: true,
color: '#151C30',
font: {
size: 26,
weight: 'bold',
style: 'normal',
},
padding: {
bottom: 10,
},
text: `${title}`,
},
tooltip: {
enabled: true,
backgroundColor: '#122a42',
itemSort: function (a, b) {
return b.raw - a.raw
},
callbacks: {
label: function (context) {
let label = context.dataset.label || ''
if (label) {
label += ': '
}
if (context.parsed.y !== null) {
label += `${new Intl.NumberFormat(locale, axesOptions).format(
context.parsed.y
)} ${unit}`
}
return label
},
},
},
legend: {
position: 'bottom',
labels: {
// This more specific font property overrides the global property
color: '#151C30',
font: {
size: 12,
weight: 'light',
},
},
},
},
}}
/>
)
}
export default PortfolioPriceLineDual

Resources