I'm adding Chart Js in my react app to display data as bar chart. The code works and I can display data correctly, the only issue I'm facing is that if one of the data values is too high, bar chart will only display it
As you can see on this image, if data for January is 100,000 and for other months it's less than 10,000, we can only see data for January. When I click on element for data (January) I get index 0, I need to get index for all elements for other months, for example when I click on white space for February I should get index 1 etc.
Code Sandbox - link
My code:
import React, { useRef } from "react";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
} from "chart.js";
import { Bar, getElementAtEvent } from "react-chartjs-2";
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
);
export const options = {
responsive: true,
plugins: {
legend: {
position: "top"
},
title: {
display: true,
text: "Chart.js Bar Chart"
}
}
};
const labels = ["January", "February", "March", "April", "May", "June", "July"];
export const data = {
labels,
datasets: [
{
label: "Dataset 1",
data: [100000, 500, 40],
backgroundColor: "rgba(255, 99, 132, 0.5)"
}
]
};
export default function App() {
const barRef = useRef();
const handleGetData = (event) => {
console.log(getElementAtEvent(barRef.current, event)[0]?.index);
};
return (
<Bar ref={barRef} options={options} data={data} onClick={handleGetData} />
);
}
How can I get the index for February in this case? If I click on white space, getElementAtEvent(barRef.current, event)[0]?.index returns undefined.
The index of the bar element corresponds to its numeric x value,
so to get the index corresponding to a click event you just have to convert the event's clientX from pixel to "real" values.
If we get hold of the Chart object, as
const chart = barRef.current;
we can get the x value for a click event using:
const xClick = chart.scales.x.getValueForPixel(event.nativeEvent.offsetX);
this goes from 0 to how many categories you defined for the x axis.
To get the BarElement instance (and to see that there is a bar at that index) you can use
const barElement = chart.getDatasetMeta(0).data[xClick];
which might be undefined if there's no bar there. Here's a fork of your codesandbox with these changes.
Related
I'm making a chart and im kind of confused. ill attach a photo below
So first off, I want the title to not be undefined, how do I change the title? And I want to change the background color of the chart below the lines, heres my code
{data?.length > 0 && (
<Line
options={options}
height={"300%"}
data={{
datasets: [
{
backgroundColor: "rgba(204, 16, 52, 0.5)",
borderColor: "#CC1034",
data: data,
}
]
}}
/>
)}
The legend label will be correctly displayed if you define label on your dataset, this was mentioned by WhiteHat in his comment.
In order to obtain the background color below the lines (basically an area chart), you need to add fill: true to the dataset.
To make it work, you also need to import and register Filler from chart.js.
Please take a look at this Code Sandbox and see how it works.
Here's how I added styled to my chart:
function MyChart({}) {
const { data } = useChartConfig({
height: 200,
grouping: "primary",
dataType: "ordinal",
});
const getSeriesStyle = React.useCallback((series) => {
// Based off my chart bars
const colorPalette = {
series1: "#354657",
series2: "#5597e2",
series3: "#28A96C",
series4: "#d44401",
series5: "#ffe65b",
series6: "#ffab1b",
};
return {
fill: colorPalette[series.label],
};
}, []);
return <Chart data={data} getSeriesStyle={getSeriesStyle} />;
}
I am working on react google-chart react-google-chart and it is working fine.
What I want to do is to add click event to the labels of horizontal axis and get the label name and do what ever I want to do
I have google a lot but haven't found a correct solution
What I have done is this
import React, { Component } from 'react'
import { Chart } from "react-google-charts";
export default class manpowerGraph extends Component {
render() {
const data=[
['Year', 'Sales'],
['jan', 20],
['feb', 100],
['march', 55],
['april', 120],
['may', 200],
['june', 220],
]
const options={
// Material design options
chart: {
title: 'Manpower',
},
}
return (
<div className="manpowerChart">
<Chart
chartType="Bar"
width="100%"
height="400px"
data={data}
options={options}
legendToggle
chartEvents={[
{
eventName: "ready",
callback: ({ chartWrapper, google }) => {
const chart = chartWrapper.getChart();
google.visualization.events.addListener(chart, "onmouseover", e => {
const { row, column } = e;
console.warn("MOUSE OVER ", { row, column });
});
google.visualization.events.addListener(chart, "onmouseout", e => {
const { row, column } = e;
console.warn("MOUSE OUT ", { row, column });
});
}
}
]}
/>
</div>
)
}
}
Working code Working code I want when user click on month label it should fire any event or console
I have found this with Javascript with Javascript
Change the chart type to ColumnChart and then add the click handler from ready handler.
<Chart
chartType="ColumnChart"
width="80%"
height="400px"
data={data}
options={options}
legendToggle
chartEvents={[
{
eventName: "ready",
callback: ({ chartWrapper, google }) => {
const chart = chartWrapper.getChart();
var handler = function(e) {
console.log(e);
var parts = e.targetID.split("#");
if (parts.indexOf("label") >= 0) {
let idx = parts[parts.indexOf("label") + 1];
idx = parseInt(idx);
alert(data[idx + 1][0]);
}
};
google.visualization.events.addListener(
chartWrapper.getChart(),
"click",
handler
);
}
}
]}
/>
https://codesandbox.io/s/react-google-charts-columnchart-with-click-handler-cqe1l
Edit
To answer additional questions:
You can do limited styling of x-axis labels by setting hAxis.textStyle in options, supported styling options can be found here https://developers.google.com/docs/api/reference/rest/v1/documents#TextStyle . However, you can not set cursor using textStyle.
You can not style svg through external css. But you can add style tag inside svg tag. Again, not all css styles work, but fortunately, cursor does work.
One crude way of adding style inside svg is to grab the svg element using document.querySelector and then add style as child. This can be done from your ready handler as svg element has been created by the time ready event is fired.
Updated code now looks like:
import React from "react";
import ReactDOM from "react-dom";
import Chart from "react-google-charts";
const data = [
["Year", "Sales"],
["2004", 1000],
["2005", 1170],
["2006", 660],
["2008", 1030],
["2009", 1000],
["2010", 1170],
["2011", 660],
["2012", 1030]
];
const options = {
title: "Company Performance",
curveType: "function",
legend: { position: "bottom" },
enableInteractivity: true,
hAxis: { textStyle: { color: "blue", underline: true } }
};
class App extends React.Component {
render() {
return (
<div className="App">
<Chart
chartType="ColumnChart"
width="80%"
height="400px"
data={data}
options={options}
legendToggle
chartEvents={[
{
eventName: "ready",
callback: ({ chartWrapper, google }) => {
let svg = document.querySelector("svg");
let styles = 'text[text-anchor="middle"] { cursor: pointer; }';
var css = document.createElement("style");
if (css.styleSheet) {
css.styleSheet.cssText = styles;
} else {
css.appendChild(document.createTextNode(styles));
}
svg.appendChild(css);
const chart = chartWrapper.getChart();
var handler = function(e) {
console.log(e);
var parts = e.targetID.split("#");
if (parts.indexOf("label") >= 0) {
let idx = parts[parts.indexOf("label") + 1];
idx = parseInt(idx);
alert(data[idx + 1][0]);
}
};
google.visualization.events.addListener(
chartWrapper.getChart(),
"click",
handler
);
}
}
]}
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Working sandbox: https://codesandbox.io/s/react-google-charts-columnchart-with-click-handler-k6fv2
It seems that what is available at the moment for clickable tool tips in react-google-charts is limited.
However in order to configure clickable tooltips for react requires you to set the tooltip option like this:
tooltip: { isHtml: true, trigger: "selection" }
(so that it stays shown when you click), and you have setup a select chartEvent event as follows:
chartEvents={[
{
eventName: "select",
callback: ({ chartWrapper, google }) => {
var selection = chartWrapper.getChart().setAction({
id: "alertAction",
text: "Click Me!",
action: function() {
alert("Stay away Corona Virus!!");
}
});
console.warn(selection);
}
}
]}
See my codesandbox here.
And here's some google documentation on setAction() function, just the way I coded it in my example. Addtionally, there are the getAction() and removeAction() functions that tie into chart tooltips found on that same documentation page.
Hopefully this helps you some.
For making the labels clickable, In the chartEvents which is passed as props under Charts
<Chart
chartEvents = {**chartEvents**}
rootProps={{ 'data-testid': '3' }}
/>
Use can pass this as chartEvents
const chartEvents = [
{
eventName: "select",
callback({ chartWrapper }) {
console.log("Selected ", chartWrapper.getChart().Ufa.Ei);
}
}
];
This will return the label name for for the chart on which u have clicked
working example
I'm trying to display a barchart using React-Chartkick and Chart.js, and I'd like to customise the colours of the bars. Currently, I'm able to set all the bars to the same colour by passing a prop like this: <BarChart colours={["#fff"]} />.
Using LineCharts in React-Chartkick, you can set colours of the lines by passing an array of colours through that prop. BarCharts only seems to accept the first colour, however. Is this a limitation of React-Chartkick, or am I doing something wrong?
I've tried passing options (as described here: https://www.chartjs.org/docs/latest/charts/bar.html#styling ) through the library prop as that is how I've customised the colours of the axes and labels, but this doesn't seem to affect the bars.
Here's my current code:
state = {
chartLibraryOptions: {
borderColor: "#e34402", // does nothing here
backgroundColor: "#e34402", // nor this
scales: {
yAxes: [
{
ticks: { fontColor: "#fff", autoSkip: false }
}
],
xAxes: [
{
ticks: { fontColor: "#fff" }
}
]
}
}
};
render() {
return (
<BarChart
data={this.state.chartData}
library={this.state.chartLibraryOptions}
colors={["#e34402", "#e3b502"]} // All bars are the first colour
/>
);
}
I'm expecting to be able to change the colours of each bar, but after all this I'm not sure if that's possible through Chartkick?
Well, I used the same node package in a project with different approach kinda work for me. Almost all the charts take the same attributes.
Basically, this attribute dataset={{ backgroundColor: ['white', 'yellow' ], }}
is all you need to colour each bar. You can either pass string or array of string to backgroundColor.
The backgroundColor in dataset takes two types of data String and Array(object). Typical examples of passing data are below.
When you set backgroundColor to a string, it applied the same colour to each bar. e.g backgroundColor: 'red'
BarChart - <BarChart dataset={{ backgroundColor: 'red', }} />
When you set backgroundColor to an array, it applied each colour in the array to each bar. e.g backgroundColor: ['red', 'yellow'], then you create a loop of colours base on the data length.
column chart - <ColumnChart dataset={{ backgroundColor: ['red', 'yellow' ], }} />
React Implementation Below:
/* eslint-disable no-plusplus */
import React from 'react';
import { ColumnChart, BarChart } from 'react-chartkick';
import { chartOne } from '../common/chartData';
import 'chart.js';
const MonthlyGrowth = () => {
const handleBgColors = () => {
const firstColor = "#A00B16", secondColor = "#FAA226";
const arrOfBgColors = [];
for (let i = 1; i <= chartOne.length; i++) {
if (i % 2 === 0) {
arrOfBgColors.push(secondColor)
} else {arrOfBgColors.push(firstColor)}
}
return arrOfBgColors;
}
return (
<div className="bukka-card uk-card-default bg-white pt-4 pb-2 mr-1 pl-3 pr-2 pl-
lg-5">
<h2 className="mt-2">4,500,000</h2>
<BarChart
dataset={{ borderWidth: 0, width: 0, backgroundColor: handleBgColors(), }}
data={chartOne}
/>
</div>
)
}
export default MonthlyGrowth;
I am using highcharts-react-official in react-redux to create a drilldown chart.
However when I click on a bar to drilldown, I also update some props which causes the component to re-render - which seems to prevent the drilldown event.
I kind of gathered from Change series data dynamically in react-highcharts without re-render of the chart that I should use shouldComponentUpdate and getChart() to prevent re-render and instead dynamically update the data.
My issue is that getChart() doesn't seem to work for the official highcharts react package. I'm getting Uncaught TypeError: this.refs.chart.getChart is not a function
Is there an alternative I'm meant to be using to get and dynamically update the chart? Or some examples that I could look at?
Just including render and shouldComponentUpdate parts here:
shouldComponentUpdate(nextProps, nextState) {
let chart = this.refs.chart.getChart();
//dynamically update data using nextProps
return false;
}
render () {
const options = {
chart: {
type: 'column',
height: 300,
events: {
drillup: (e) => {this.drilledUp(e)}
}
},
plotOptions: {
series: {
events:{
click: (e) => {this.categoryClicked(e)}
}
}
},
xAxis: {type: "category"},
yAxis: {title: {text: 'Amount Spent ($)'}},
series: [{
name: 'weekly spending',
showInLegend: false,
data: this.props.transactionChartData.series_data,
cursor: 'pointer',
events: {
click: (e)=> {this.weekSelected(e)}
}
}],
drilldown: {
series: this.props.transactionChartData.drilldown_data
}
};
return (
<HighchartsReact
highcharts={Highcharts}
options={options}
ref="chart"
/>
)
}
In highcharts-react-official v2.0.0 has been added allowChartUpdate option, which should work great in your case. By using this option you can block updating the chart with updating the component:
categoryClicked() {
this.allowChartUpdate = false;
this.setState({
...
});
}
...
<HighchartsReact
ref={"chartComponent"}
allowChartUpdate={this.allowChartUpdate}
highcharts={Highcharts}
options={...}
/>
Moreover, to get the chart instance use refs:
componentDidMount(){
const chart = this.refs.chartComponent.chart;
}
Live demo: https://codesandbox.io/s/98nl4pp5r4
// react native functional
import React, {useState, useRef,useLayoutEffect} from "react"
import HighchartsReactNative from '#highcharts/highcharts-react-native'
function chartcomponent(props){
const [options, setoptions] = useState({});
const chartRef = useRef(null);
useEffect(() => {
// create the options for your chart.
setoptions({chart:{}, yaxis:{}, xAxis:{},})// etc.
}, []);
useLayoutEffect(() => {
// new point to add, you can get new data via props, fetch, socket, etc.
var x = new Date(hr.timestamp).getTime();
var y = 10
// these are the important parts here:
var series = chartRef.current.props.options.series;
if (!Array.isArray(series)){return;}
var seriesdata = series[0].data;
seriesdata.push([x,y]);
// next line limits points in chart to 10, so new poitn replaces first point
if (seriesdata.length>10){seriesdata.splice(0,1);}
return () => {};
}, [props]);
return(
<View style={styles.charting}><HighchartsReactNative styles={{height:300, width:600}} options={options} ref={chartRef} ></HighchartsReactNative></View>
)
}
I'm creating a React Component in typescript for each Chart type in chart.js. Most of it is working out fine. However, I've hit my first roadblock as I just remembered that you can have multiple datasets within a chart.
I have the following code for a Bar Chart
export class Bar extends React.Component<IBarProps, {}> {
constructor(props: IBarProps) {
super(props)
}
public render() {
const className = classNames('bar-chart', this.props.className)
const ctx = document.getElementById('bar-chart') as HTMLCanvasElement
let bar: Chart = new Chart(ctx, {
type: this.props.horizontal ? 'horizontalBar' : 'bar',
data: {
labels: this.props.labels,
datasets: [
{
label: this.props.label,
xAxisID: this.props.xAxisID,
yAxisID: this.props.yAxisID,
backgroundColor: this.props.backgroundColor,
borderColor: this.props.borderColor,
borderWidth: this.props.borderWidth
}
]
} as ChartData,
options: {
elements: { rectangle: { borderSkipped: this.props.borderSkipped } }
}
})
bar.update()
return (
<div className={className}>
<canvas
id="bar-chart"
width={this.props.width ? this.props.width : '400'}
height={this.props.height ? this.props.height : '400'}
/>
</div>
)
}
// private push() {
// const dataSets = this.props.dataSets
// dataSets.forEach(dataSet => {new Object(label: this.props.label,)})
// return dataSets
// }
}
The issue is that the way I've set it up, it'd only support a single dataset. What i need is that for each dataset that is defined, create an object for it and push it into the datasets array. However I'm not a Javascript expert nor typescript expert.
Would anyone be able to show me how to setup the dynamic datasets array? If you need more info see the Bar Chart Docs