How to customize border style on React Chart.js 2 - reactjs

I am trying to add dashed border to my bar charts. I am following this example here- https://jsfiddle.net/jt100/ghksq1xv/3/
I am not getting much luck I have followed the instruction very carefully and passing in the correct values but I am not adding the dashed border to my bar chart. Any help will be very much appreciated
This is what I have done
1) passed in 4 arguments: my chart instance, dataset, data and dash
```
this.dashedBorder(myLineChart, 0, 2, [15, 10]);
2) This is my function.
dashedBorder(chart, dataset, data, dash) {
chart.config.data.datasets[dataset]._meta[0].data[data].draw = function() {
chart.chart.ctx.setLineDash(dash);
Chart.elements.Rectangle.prototype.draw.apply(this,
arguments,
);
};
}
3) my whole react component. You can see what I have done here.
import React, { PureComponent } from "react";
import classes from "./YourLineGraph.module.css";
import Chart from "chart.js";
let myLineChart;
let myChartRef;
let ctx;
//--Chart Style Options--//
// Chart.defaults.global.defaultFontFamily = "'PT Sans', sans-serif";
Chart.defaults.global.defaultFontFamily = "'Cooper Hewitt'";
Chart.defaults.global.legend.display = false;
Chart.defaults.global.elements.line.tension = 0;
Chart.defaults.global.scaleLineColor = "tranparent";
Chart.defaults.global.tooltipenabled = false;
//--Chart Style Options--//
export default class YourLineGraph extends PureComponent {
chartRef = React.createRef();
componentDidMount() {
this.buildChart();
}
componentDidUpdate() {
this.buildChart();
}
buildChart = () => {
myChartRef = this.chartRef.current.getContext("2d");
ctx = document.getElementById("myChart").getContext("2d");
const { data, average, labels, attribute } = this.props;
if (typeof myLineChart !== "undefined") myLineChart.destroy();
myLineChart = new Chart(myChartRef, {
type: "bar",
data: {
//Bring in data
labels:
labels.length === data.length
? labels
: new Array(data.length).fill("Data"),
datasets: [
{
label: "Sales",
data: data,
borderColor: "#98B9AB",
borderWidth: 3,
borderStyle: "dash" //has no effect
}
]
},
options: {
plugins: {
datalabels: {
formatter: function(value, context) {
return attribute === "pounds" ? `£ ${value}` : value;
},
anchor: "end",
align: "end",
color: "#888"
}
},
scales: {
yAxes: [
{
gridLines: {
drawBorder: false,
display: false
},
ticks: {
display: false //this will remove only the label
}
}
],
xAxes: [
{
gridLines: {
drawBorder: false,
display: false
},
ticks: {
display: false //this will remove only the label
}
}
]
}
}
});
this.dashedBorder(myLineChart, 0, 2, [15, 10]);
};
dashedBorder(chart, dataset, data, dash) {
chart.config.data.datasets[dataset]._meta[0].data[data].draw = function() {
chart.chart.ctx.setLineDash(dash);
Chart.elements.Rectangle.prototype.draw.apply(this, arguments);
chart.chart.ctx.setLineDash([1, 0]);
};
}
render() {
return (
<div className={classes.graphContainer}>
<canvas id="myChart" ref={this.chartRef} />
</div>
);
}
}

Related

ReactJS - Moving vertical line when hovering over Line chart using chart.js v3.7.1

I am working on the latest version of Chart.js, i.e., v3.7.1. However, I notice that most of the configurations have been changed from those of version 2.x. My problem is that I would like to draw a vertical line everytime there is a mouseover the chart, but not necessary having the mouse on the data points, but can be above or below, as long as the mouse point is in line with a data point, a verical line is drawn. This is clearly illustrated by this code on JSFiddle. I tried this Solution on this platform but it fails to work since it is based on ChartJS v2.6.0. Anyone with an idea of doing this? Here are my codes:
LineChart.js
import React from "react";
import Chart from 'chart.js/auto';
import { Line } from "react-chartjs-2";
import 'chartjs-adapter-moment';
const newOptions = {
responsive: true,
plugins: {
responsive: true,
title: {
display: true,
text: 'Weekly Logs'
},
},
scales: {
x: {
type: "time",
time: {
displayFormats: {
'day': 'dddd'
}
}
}
},
interaction: {
mode: "index",
axis: 'y'
}
};
const LineChart = ({ dataChart }) => {
return <Line
data={dataChart} options={newOptions}
/>;
};
export default LineChart;
You can use a custom plugin for this:
const plugin = {
id: 'corsair',
afterInit: (chart) => {
chart.corsair = {
x: 0,
y: 0
}
},
afterEvent: (chart, evt) => {
const {
chartArea: {
top,
bottom,
left,
right
}
} = chart;
const {
x,
y
} = evt.event;
if (x < left || x > right || y < top || y > bottom) {
chart.corsair = {
x,
y,
draw: false
}
chart.draw();
return;
}
chart.corsair = {
x,
y,
draw: true
}
chart.draw();
},
afterDatasetsDraw: (chart, _, opts) => {
const {
ctx,
chartArea: {
top,
bottom,
left,
right
}
} = chart;
const {
x,
y,
draw
} = chart.corsair;
if (!draw) {
return;
}
ctx.lineWidth = opts.width || 0;
ctx.setLineDash(opts.dash || []);
ctx.strokeStyle = opts.color || 'black'
ctx.save();
ctx.beginPath();
if (opts.vertical) {
ctx.moveTo(x, bottom);
ctx.lineTo(x, top);
}
if (opts.horizontal) {
ctx.moveTo(left, y);
ctx.lineTo(right, y);
}
ctx.stroke();
ctx.restore();
}
}
Chart.register(plugin)
const options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink'
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderColor: 'orange'
}
]
},
options: {
plugins: {
corsair: {
horizontal: false,
vertical: true,
color: 'red',
dash: [],
width: 2
}
}
},
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.js"></script>
</body>
For you to use this in react, you only need the Chart.register line like so:
import React from "react";
import Chart from 'chart.js/auto';
import { Line } from "react-chartjs-2";
import 'chartjs-adapter-moment';
const plugin = {
// Plugin code, see stack snipet above
}
Chart.register(plugin)

Unable to convert timestamp to hours minutes and secondes in React apex-chart

I am using react apex chart to create a chart that will display the average response time for each agent.
I have managed to get the result in a timestamp format but i am unable to convert that into hours, minutes and seconds to display that in yaxis, i have checked the documentation docs link but they are giving examples for date time only.
here is the result that i am getting with the component bellow
import React, { useState } from 'react';
import Chart from 'react-apexcharts';
const AvgResponseTimeChart = (props) => {
const { prod_data } = props;
const [ data, setData ] = useState([
{
x: 'Agent one',
y: 1589670005
},
{
x: 'Agent one',
y: 1589670307
}
]);
const [ series, setSeries ] = useState([ { data } ]);
const [ options, setOptions ] = useState({
chart: {
type: 'bar',
height: 350
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: '25%',
endingShape: 'rounded'
}
},
dataLabels: {
enabled: false
},
stroke: {
show: true,
width: 2,
colors: [ 'transparent' ]
},
xaxis: {
type: 'category'
},
yaxis: {
labels: {
datetimeFormatter: {
formatter: function(value, timestamp) {
return new Date(timestamp).toLocaleTimeString();
}
}
}
},
fill: {
opacity: 1
},
tooltip: {
y: {
formatter: function(value, timestamp) {
return new Date(timestamp);
}
}
}
});
return (
<div id="chart">
<Chart options={options} series={series} type="bar" height={350} />
</div>
);
};
export default AvgResponseTimeChart;
I have searched for similar issues without success if, someone can help me with that i will be really grateful
Try to add lables to yaxis in chartOptions this way:
labels: {
show: true,
formatter: (val) => { return new Date(val); }
}
And remove the tooltip as well.

(Lifecycle problem?) React Chart js-2 not updating data from componentDidMount

I am trying to populate chart data from my backend.
Although I am fetching data and pushing data in componentDidMount, the Bars or Scatters are not loaded on page load.
If I change my screen width in inspect mode in google dev tools, it starts loading which leads me to believe this is a lifecyle problem.
However, changing it to componentWillMount did not change anything. Putting a if statement before render like below just stops loading the chart altogether.
if(this.state.data.datasets[0].data.length ===0){
return null;
}
Any way to fix this problem?
import React, { Component } from "react";
import axios from "axios";
import { Bar, Scatter } from "react-chartjs-2";
export class Data extends Component {
constructor(props) {
super(props);
this.state = {
provider: [],
data: {
labels: ["Action", "Anime", "Children"],
datasets: [
{
label: "Total",
backgroundColor: "rgba(255, 159, 64, 0.4)",
borderColor: "white",
borderWidth: 1,
stack: 1,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: []
},
{
label: "Above ⭐️8.5",
backgroundColor: "white",
type: "scatter",
showLine: false,
stack: 1,
data: []
}
]
}
};
}
componentDidMount() {
axios.get("http://localhost:8001/provider").then(res =>
this.setState({ provider: res.data }, () => {
this.pushAction();
})
);
}
pushAction() {
const dataState = this.state.data;
const oldDataTotal = this.state.data.datasets[0].data;
const oldDataGood = this.state.data.datasets[1].data;
oldDataTotal.push(this.state.provider[0].huluAction);
oldDataTotal.push(this.state.provider[0].huluAnime);
oldDataTotal.push(this.state.provider[0].huluChildren);
oldDataGood.push(this.state.provider[0].Action);
oldDataGood.push(this.state.provider[0].Anime);
oldDataGood.push(this.state.provider[0].Children);
}
render() {
console.log(this.state.musicalData);
const options = {
responsive: true,
maintainAspectRatio: false,
legend: {
display: true
},
type: "bar",
scales: {
xAxes: [
{
stacked: true
}
],
yAxes: [
{
stacked: true
}
]
}
};
return (
<div>
<Bar data={this.state.data} height={300} options={options} />
</div>
);
}
}
export default Data;
you have to update the state after changing the datasets,
pushAction = () => {
const dataState = this.state.data;
const oldDataTotal = this.state.data.datasets[0].data;
const oldDataGood = this.state.data.datasets[1].data;
oldDataTotal.push(this.state.provider[0].huluAction);
oldDataTotal.push(this.state.provider[0].huluAnime);
oldDataTotal.push(this.state.provider[0].huluChildren);
oldDataGood.push(this.state.provider[0].Action);
oldDataGood.push(this.state.provider[0].Anime);
oldDataGood.push(this.state.provider[0].Children);
this.setState({data: {...dataState, datasets : [...oldDataTotal, oldDataGood]}});
}

react-chartjs-2 vertical line when hovering over chart

I'm trying to create a linechart, using react-chartjs-2, that has a vertical line when hovering over the datapoints in the chart. Like in this picture below:
Chart requirements
I tried using the chartjs-plugin-annotation plugin, but with mixed results. I managed to create a static line, not understanding how or why it works. How should I achieve this? Am I onto something?
const data = {
labels: [...week(d)],
datasets: [
{
...
data: [10000, 9500, 7000, 4500, 2500, 1500, 500, 0],
}
]
};
var line = [{
type: "line",
mode: "vertical",
// ???
scaleID: "y-axis-0",
value: -20000,
borderColor: "#2984c5",
borderWidth: 1,
}];
const options = {
tooltips: {
xPadding: 20,
yPadding: 10,
displayColors: false,
bodyFontSize: 16,
bodyFontStyle: 'bold',
},
annotation: {
annotations: line,
},
scales: {
yAxes: [{
gridLines: {
drawBorder: false,
tickMarkLength: 0,
},
ticks: {
fontSize: 14,
padding: 15,
max: data.maxY,
min: 0,
maxTicksLimit: 6,
fontColor: "#485465"
}
}],
xAxes: [{
ticks: {
padding: 5,
fontSize: 14,
fontColor: "#485465",
},
gridLines: {
display: false,
},
},
],
},
responsive: false,
}
I have my full code available here: https://codesandbox.io/s/y28mk3rn4z
In case someone needs it for "react-chartjs-2": "^4.0.1". This one worked for me based on previous answers:
import { Chart } from 'chart.js';
import { Line } from 'react-chartjs-2';
Chart.register(
{
id: 'uniqueid5', //typescript crashes without id
afterDraw: function (chart: any, easing: any) {
if (chart.tooltip._active && chart.tooltip._active.length) {
const activePoint = chart.tooltip._active[0];
const ctx = chart.ctx;
const x = activePoint.element.x;
const topY = chart.scales.y.top;
const bottomY = chart.scales.y.bottom;
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = 2;
ctx.strokeStyle = '#e23fa9';
ctx.stroke();
ctx.restore();
}
}
}
);
.
.
.
<Line
options={{
...options,
interaction: {
mode: 'index',
intersect: false,
}}}
data={data}
/>
The below answer is correct it only needs some tweaks, Thanks Jordan.
Chart.pluginService.register({
afterDraw: function(chart, easing) {
if (chart.tooltip._active && chart.tooltip._active.length) {
const activePoint = chart.controller.tooltip._active[0];
const ctx = chart.ctx;
const x = activePoint.tooltipPosition().x;
const topY = chart.scales['y-axis-0'].top;
const bottomY = chart.scales['y-axis-0'].bottom;
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = 2;
ctx.strokeStyle = '#e23fa9';
ctx.stroke();
ctx.restore();
}
}
});
In the chart options we need to add the below config to display the line on near by hover.
tooltips: {
mode: 'index',
intersect: false
},
hover: {
mode: 'index',
intersect: false
}
PS: if there are multiple chart plotted together then above piece of code might cause problems. if we want to have this effect on a specific chart(e.g line) then we can add the below condition.
if (
chart.tooltip._active &&
chart.tooltip._active.length &&
chart.config.type === 'line'
)
This worked for me, hope this help.
Just looked into this exact thing and this will get you there! It won't do the fills on either side of the line, but it creates the vertical line!
componentWillMount() {
Chart.pluginService.register({
afterDraw: function (chart, easing) {
if (chart.tooltip._active && chart.tooltip._active.length) {
const activePoint = chart.controller.tooltip._active[0];
const ctx = chart.ctx;
const x = activePoint.tooltipPosition().x;
const topY = chart.scales['y-axis-1'].top;
const bottomY = chart.scales['y-axis-1'].bottom;
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = 2;
ctx.strokeStyle = '#e23fa9';
ctx.stroke();
ctx.restore();
}
}
});
}
Also for anyone with multiple datasets you can add this into your options for tooltips on all lines at once.
tooltips: {
mode: 'x'
},

Cannot read property 'lineWidth' of undefined , chat type is columnrange highcharts

i am getting Cannot read property 'lineWidth' of undefined, when using columnrange chart highcharts, this chart i am trying in reactJS
Below is the sample code
import React from 'react';
import Highcharts from 'highcharts';
import ReactHighcharts from 'react-highcharts';
// import ReactHighstock from 'react-highstock';
import HighchartsMore from 'highcharts-more';
import HighchartsExporting from 'highcharts-exporting';
export default class MainChartRange extends React.Component {
componentWillMount(){
this.setState({
type:"column"
})
}
componentDidMount(){
setTimeout(function(e){
HighchartsMore(ReactHighcharts.Highcharts);
HighchartsExporting(ReactHighcharts.Highcharts);
},5000);
}
render() {
HighchartsMore(ReactHighcharts.Highcharts);
HighchartsExporting(ReactHighcharts.Highcharts);
var config = {
chart: {
type: 'columnrange',
inverted: true
},
title: {
text: null
},
subTitle: {
text: null
},
legend: {
enabled: false,
},
plotOptions: {
series: {
pointWidth: 30
}
},
plotOptions: {
series: {
pointWidth: 30
}
},
xAxis: {
categories: ['Woring time'],
title: {
text: null
},
gridLineWidth: 0
},
yAxis: {
type: 'datetime'
},
series: [{
data: [
[1,1483337940000,1483337950000],
[1,1483337970000,1483337990000],
[0,1483338000000,1483338010000],
[0,1483338030000,1483338070000]
]
}]
}
return (
<div className="graph-container">
<ReactHighcharts config={ config } style={{"min-width": "310px", "height": "400px", "margin": "0 auto"}}>
</ReactHighcharts>
</div>
)
}
}
this sample code working for the other chart types but it is giving an error for "columnrange"
highcharts.js:299 -> Uncaught TypeError: Cannot read property 'lineWidth' of undefined
Please help me find some work around for the "columnrange"
I was getting the same error and was able to solve it by explicitly providing 'marker' configuration with labelWidth value in plotOptions object of my chart config.
Following is a sample chart config that I have tested both in my application using react-highcharts as well as using locally running instance of react-highcharts demo:
chart: {
type: 'columnrange',
inverted: true,
height: 300,
width: 400
},
xAxis: {
visible: false
},
yAxis: {
title: {
text: null
},
plotLines: [{
color: '#303030',
width: 2,
value: 67,
zIndex: 5
}, {
color: '#303030',
width: 2,
value: 77.5,
zIndex: 5
}]
},
plotOptions: {
columnrange: {
dataLabels: {
enabled: true,
formatter() {
return `${this.y}°F`;
}
},
marker: {
enabled: false,
lineWidth: 0
},
states: {
hover: {
enabled: false
}
}
}
},
legend: {
enabled: false
},
series: [{
type: 'columnrange',
color: '#4AA013',
data: [
[70, 75]
]
}]
Note: I did not have to make any code changes to highcharts or modify its implementation like previous answer (which did not work for me when tested using react-highcharts).
here is workaround that is got above columnrange to work
in render function add the below code
Highcharts.seriesTypes.line.prototype.pointAttribs = function (point, state) {
var seriesMarkerOptions = this.options.marker,
seriesStateOptions,
pointOptions = point && point.options,
pointMarkerOptions = (pointOptions && pointOptions.marker) || {},
pointStateOptions,
strokeWidth = 1,
color = this.color,
pointColorOption = pointOptions && pointOptions.color,
pointColor = point && point.color,
zoneColor,
fill,
stroke,
zone;
if (point && this.zones.length) {
zone = point.getZone();
if (zone && zone.color) {
zoneColor = zone.color;
}
}
color = pointColorOption || zoneColor || pointColor || color;
fill = pointMarkerOptions.fillColor || point.color || color ;
stroke = pointMarkerOptions.lineColor || point.color || color;
/*// Handle hover and select states
if (state) {
seriesStateOptions = seriesMarkerOptions.states[state];
pointStateOptions = (pointMarkerOptions.states && pointMarkerOptions.states[state]) || {};
strokeWidth = pointStateOptions.lineWidth || seriesStateOptions.lineWidth || strokeWidth + seriesStateOptions.lineWidthPlus;
fill = pointStateOptions.fillColor || seriesStateOptions.fillColor || fill;
stroke = pointStateOptions.lineColor || seriesStateOptions.lineColor || stroke;
}*/
return {
'stroke': stroke,
'stroke-width': strokeWidth,
'fill': fill
};
};
HighchartsMore(ReactHighcharts.Highcharts);
HighchartsExporting(ReactHighcharts.Highcharts);

Resources