chartjs doesn't render dates properly - reactjs

I made a custom chartjs component in reactjs and want to render dates in xAxes and numbers from -1 to 1 in yAxes but it renders data not in a proper way.
import React, { useRef, useEffect } from "react";
import Chart from "chart.js";
const ChartComponent = ({ data, label, min, max }) => {
const canvasRef = useRef(null);
useEffect(() => {
const canvasObj = canvasRef.current;
const context = canvasObj.getContext("2d");
new Chart(context, {
type: "line",
data: {
datasets: [
{
label: label,
backgroundColor: "transparent",
data: data,
},
],
},
options: {
scales: {
xAxes: [
{
type: "time",
distribution: "linear",
time: {
unit: "month",
displayFormats: {
quarter: "YYYY mm dd",
},
},
},
],
yAxes: [
{
ticks: {
suggestedMax: max,
suggestedMin: min,
},
},
],
},
},
});
}, [data, label, min, max]);
return <canvas ref={canvasRef}> </canvas>;
};
export default ChartComponent;
and I'm passing the data like this to the component
<ChartComponent
min={-1}
max={1}
label="date"
data={[
{
x: "30/03/2018",
y: 0.1158,
},
{
x: "24/09/2018",
y: 0.1975,
},
{
x: "23/12/2018",
y: 0.1913,
},
{
x: "23/03/2019",
y: 0.2137,
},
]}
/>;
I have to metion that I have done the same thing and that is working alright this is example below
<ChartComponent
min={1270}
max={1272}
label=" مساحت دریاچه"
data={[
{
x: "04/02/2017",
y: 1270.7,
},
{
x: "06/26/2017",
y: 1270.74,
},
{
x: "09/19/2017",
y: 1270.31,
},
{
x: "12/18/2017",
y: 1270.28,
},
{
x: "06/16/2018",
y: 1270.81,
},
{
x: "09/24/2018",
y: 1270.27,
},
{
x: "12/23/2018",
y: 1270.54,
},
{
x: "05/25/2019",
y: 1271.94,
},
{
x: "06/18/2019",
y: 1271.84,
},
{
x: "09/19/2019",
y: 1271.31,
},
{
x: "12/18/2019",
y: 1271.25,
},
{
x: "03/12/2020",
y: 1271.48,
},
{
x: "06/25/2020",
y: 1271.72,
},
]}
/>;
any recommendation would be appreciated. thank you.

The problem is that the date strings you provide are not of ISO 08601 nor RFC 2822 Date time format, hence they cannot be parsed by Moment.js, which is internally used by Chart.js.
To make it work, you have to define xAxes.time.parser: "DD.MM.YYYY".
Please take a look at below runnable code and see how it works. This is pure JavaScript example but it should also work with react-chartjs-2.
new Chart("myChart", {
type: "line",
data: {
datasets: [{
label: 'My Dataset',
data: [
{ x: "30/03/2018", y: 0.1158 },
{ x: "24/09/2018", y: -0.1975 },
{ x: "23/12/2018", y: 0.1913 },
{ x: "23/03/2019", y: -0.2137 }
],
fill: false
}]
},
options: {
scales: {
xAxes: [{
type: "time",
time: {
parser: "DD.MM.YYYY",
unit: "month",
displayFormats: {
month: "YYYY MM DD",
}
}
}],
yAxes: [{
ticks: {
suggestedMax: 1,
suggestedMin: -1
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.min.js"></script>
<canvas id="myChart" height="100"></canvas>
As I already mentioned, Chart.js internally uses Moment.js for the functionality of the time axis. Therefore make sure to use the bundled version of Chart.js that includes Moment.js in a single file.

Related

Cannot find a way to correctly use chartjs annotation plugin with react

I am currently trying to use the plugin chartjs-plugin-annotation in my react project.
Unfortunately, it is not working...
He is my implementation :
import React, { Component } from "react";
//import "./css/tideComponent.css";
import jsonData from "./ressources/tideOostende2023.json";
import "chart.js/auto";
import { Chart } from "react-chartjs-2";
import * as ChartAnnotation from "chartjs-plugin-annotation";
class Tide extends Component {
state = {
dayDate: new Date().toJSON().slice(5, 10),
highTide: "",
highTide2: "",
lowTide: "",
lowTide2: "",
};
async componentDidMount() {
const index = jsonData.findIndex(
(item) => item.date === this.state.dayDate
);
//TODO store tide in an array(using split method) & filter low to high to have a correct graph
this.setState({
highTide: jsonData[index].highTide,
highTide2: jsonData[index].highTide2,
lowTide: jsonData[index].lowTide,
lowTide2: jsonData[index].lowTide2,
});
}
timeToNumeric(tideTime) {
const tideTimeSplitted = tideTime.split(":");
return tideTimeSplitted[0] * 1 + tideTimeSplitted[1] / 60;
}
handleTideData() {
if (
this.timeToNumeric(this.state.highTide) <
this.timeToNumeric(this.state.lowTide)
)
return [
{ x: -2, y: 0.5 },
{ x: this.timeToNumeric(this.state.highTide), y: 1.5 },
{ x: this.timeToNumeric(this.state.lowTide), y: 0.5 },
{ x: this.timeToNumeric(this.state.highTide2), y: 1.5 },
{ x: this.timeToNumeric(this.state.lowTide2), y: 0.5 },
{ x: 26, y: 1.5 },
];
return [
{ x: -2, y: 1.5 },
{ x: this.timeToNumeric(this.state.lowTide), y: 0.5 },
{ x: this.timeToNumeric(this.state.highTide), y: 1.5 },
{ x: this.timeToNumeric(this.state.lowTide2), y: 0.5 },
{ x: this.timeToNumeric(this.state.highTide2), y: 1.5 },
{ x: 26, y: 0.5 },
];
}
render() {
const data = {
datasets: [
{
data: this.handleTideData(),
fill: false,
backgroundColor: "rgb(35, 71, 89, 0.88)",
borderColor: " rgb(35, 71, 79, 0.88)",
tension: 0.4,
},
],
};
const options = {
annotation: {
annotations: [
{
type: "line",
mode: "horizontal",
scaleID: "x",
value: 1,
borderColor: "white",
borderWidth: 2,
},
],
},
scales: {
x: { min: 0, max: 24, ticks: { stepSize: 1 } },
y: { min: 0, max: 2.2, display: false },
},
showLine: true,
pointStyle: false,
plugins: {
legend: { display: false },
},
};
return (
<div className="tideContainer">
<Chart
type="scatter"
data={data}
options={options}
plugins={ChartAnnotation}
/>
</div>
);
}
}
export default Tide;`
I tried different things but still not working. I also reviewed multiple question on SO but cannot find my solution. Chart Js is correctly working with my implementation, it is only the plugin that does not work.
Thank you in advance for your great help !!!
I think plugins property in react-chartjs-2 should be an array, I guess.
<Chart
type="scatter"
data={data}
options={options}
plugins={[ChartAnnotation]}
/>
The options config for annotation plugin is not in the right node.
It must be added in options.plugins node.
const options = {
plugins: { // <-- to add, was missing
annotation: {
annotations: [
{
type: "line",
mode: "horizontal",
scaleID: "x",
value: 1,
borderColor: "white",
borderWidth: 2,
},
],
},
}

chartjs - progressive line chart - setting x labels as date makes the datapoints on Y axis disappear?

I am trying to follow and modify the answer given here: Link
I got it to work if I keep the data in the same format i.e. {x: date, y: value}.
When I try to use a custom dataset example:
const data = [
{
date: '01.12.2019',
y: 19
},
{
date: '19.03.2020',
y: 29
},
{
date: '01.08.2021',
y: 51
},
{
date: '11.01.2022',
y: 56
}
];
I believe I need to fix the animations but I am having no luck with it.
const totalDuration = 2000;
const delayBetweenPoints = totalDuration / data.length;
const previousY = (ctx) => ctx.index === 0 ? ctx.chart.scales.y.getPixelForValue(100) : ctx.chart.getDatasetMeta(ctx.datasetIndex).data[ctx.index - 1].getProps(['y'], true).y;
const animation = {
x: {
type: 'number',
easing: 'linear',
duration: delayBetweenPoints,
from: NaN,
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: previousY,
delay(ctx) {
if (ctx.type !== 'data' || ctx.yStarted) {
return 0;
}
ctx.yStarted = true;
return ctx.index * delayBetweenPoints;
}
}
}
export const prog_test_config = {
type: 'line',
data: {
labels: data.map((data)=> data.date),
datasets: [
{
label: 'Price History',
data: data,
fill: false,
borderColor: '#FF00FF',
cubicInterpolationMode: 'monotone',
tension: 0.5,
},
],
},
options: {
animation: animation,
interaction: {
mode: 'nearest',
axis: 'x',
intersect: false
},
plugins: {
legend: false
},
scales: {
x: {
type: 'time',
time: {
unit: 'day',
parser: 'dd.MM.yyyy'
},
},
}
},
};
I am unable to get the y values to display. Managed to get the date field to displayed as labels on X-axis.

How to display Range Apex chart when we have [start, end] are same at some time?

//Here #y:[start,end] at some point i have same start and end value but currently apex range chart don't show range bar when [start,end] is same value, but i need to show the bar for same [start,end] also how to do this?
var options = {
series: [
{
name: 'Bob',
data: [
{
x: 'Design',
y: [
4,4
]
},
{
x: 'Code',
y: [
5,7
]
},
{
x: 'Code',
y: [
0,1
]
},
{
x: 'Test',
y: [
10,10
]
},
{
x: 'Test',
y: [
4,4
]
},
{
x: 'Validation',
y: [
2,2
]
},
{
x: 'Design',
y: [
1,2
]
}
]
},
{
name: 'Joe',
data: [
{
x: 'Design',
y: [
1,2
]
},
{
x: 'Test',
y: [
6,6
]
},
{
x: 'Code',
y: [
9,9
]
},
{
x: 'Deployment',
y: [
5,5
]
},
{
x: 'Design',
y: [
4,7
]
}
]
},
{
name: 'Dan',
data: [
{
x: 'Code',
y: [
3,5
]
},
{
x: 'Validation',
y: [
3,3
]
},
]
}
],
chart: {
height: 450,
type: 'rangeBar'
},
plotOptions: {
bar: {
horizontal: true,
barHeight: '80%'
}
},
xaxis: {
min: 0,
max: 10
},
stroke: {
width: 1
},
fill: {
type: 'solid',
opacity: 0.6
},
legend: {
position: 'top',
horizontalAlign: 'left'
}
//i need to show the bar for same [start,end] also how to do this?
//Please help me in this.
For values ​​that have the same range, that is, the same start and end, you can add attributes to control the width and height of the bar, as well as the color, you also have to specify the value:
goals: [
{
value: 3,
// strokeHeight: 10,
strokeWidth: 3,
strokeColor: "#FEB019"
}
]
Example codesandbox
Useful apexcharts links:
https://apexcharts.com/react-chart-demos/column-charts/column-with-markers/
https://apexcharts.com/docs/chart-types/range-bar-chart/

React chart2js Line chart with multiple datasets overlapping

chart
const data = {
labels: Array(coordinates.length).fill("l"),
datasets: buildDataset(),
options: {
animation: false,
scales: {
// ???????
},
legend: {
display: false
},
tooltips: {
callbacks: {
label: function(tooltipItem) {
return tooltipItem.yLabel;
}
}
},
maintainAspectRatio: false,
scales: {
myScale: {
position: 'left',
}
},
elements: {
point:{
radius: 0
}
}
}
}
return (
<Chart>
<Line
data={data}
width={50}
height={20}
options={data.options}>
</Line>
</Chart>
)
// ~~~~~
let obj = {
label: stops[0].miles == 0 ? index : index + 1,
data: points,
backgroundColor: colors[index],
tension: 0.4,
fill: true
}
These charts are built from an array of obj objects. The points variable that data refers is an array of object like: [{x: 0, y: 10257}, {x: 1, y: 10245}]
How do I get my line chart to display these different datasets side by side? I assume it has something to do with the scales parameter but wasn't able to find anything that worked in the docs.
Thanks!
For the object notation to work chart.js needs values to plot them against (not the index in the array) so you cant just provide an array containing only the value l.
You can either provide a labels array containing increasing numbers to which you match it or remove it and set your x scale to linear.
Labels example:
var options = {
type: 'line',
data: {
labels: [0, 1, 2, 3],
datasets: [{
label: '# of Votes',
data: [{
x: 0,
y: 4
}, {
x: 1,
y: 6
}, {
x: 2,
y: 2
}],
borderColor: 'pink'
},
{
label: '# of Points',
data: [{
x: 2,
y: 2
}, {
x: 3,
y: 3
}],
borderColor: 'blue'
}
]
},
options: {
scales: {}
}
}
var 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.5.0/chart.js"></script>
</body>
Linear example:
var options = {
type: 'line',
data: {
datasets: [{
label: '# of Votes',
data: [{
x: 0,
y: 4
}, {
x: 1,
y: 6
}, {
x: 2,
y: 2
}],
borderColor: 'pink'
},
{
label: '# of Points',
data: [{
x: 2,
y: 2
}, {
x: 3,
y: 3
}],
borderColor: 'blue'
}
]
},
options: {
scales: {
x: {
type: 'linear'
}
}
}
}
var 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.5.0/chart.js"></script>
</body>

React-Vega - WARN Infinite extent for field "x": [Infinity, -Infinity]

The code work perfectly in the vega online editor. But there are warnings in the console while using in react component and the X-axis is not rendering in the output.
import React from 'react';
import { Vega } from 'react-vega';
import { VisualizationSpec } from 'vega-embed';
export function LineGraph() {
const specs: VisualizationSpec = {
$schema: 'https://vega.github.io/schema/vega/v5.json',
description: 'A basic line chart example.',
width: 500,
height: 200,
padding: 5,
signals: [],
data: [
{
name: 'table',
format: {
parse: { x: 'date' },
},
},
],
scales: [
{
name: 'x',
type: 'time',
range: 'width',
domain: { data: 'table', field: 'x' },
},
{
name: 'y',
type: 'linear',
range: 'height',
nice: true,
zero: true,
domain: { data: 'table', field: 'y' },
},
],
axes: [
{ orient: 'bottom', scale: 'x' },
{ orient: 'left', scale: 'y' },
],
marks: [
{
type: 'line',
from: { data: 'table' },
encode: {
enter: {
x: { scale: 'x', field: 'x' },
y: { scale: 'y', field: 'y' },
stroke: { value: 'red' },
strokeWidth: { value: 2 },
},
},
},
],
};
const data: any = {
table: [
{ x: '01-08-2020', y: 28, c: 0 },
{ x: '01-03-2020', y: 43, c: 0 },
{ x: '01-01-2020', y: 81, c: 0 },
{ x: '01-09-2020', y: 19, c: 0 },
{ x: '01-02-2020', y: 52, c: 0 },
{ x: '01-04-2020', y: 24, c: 0 },
{ x: '01-07-2020', y: 87, c: 0 },
{ x: '01-07-2020', y: 17, c: 0 },
{ x: '01-08-2020', y: 68, c: 0 },
{ x: '01-09-2020', y: 49, c: 0 },
],
};
const signalListeners = {};
return (
<div>
<Vega data={data} signalListeners={signalListeners} spec={specs} />
</div>
);
}
Warnings:
WARN Infinite extent for field "y": [Infinity, -Infinity]
WARN Infinite extent for field "x": [Infinity, -Infinity]
How to define the extent in vega?
There are two parts to this error - the console warning, and the lack of rendering.
The console warning gets thrown in digest cycles when the data hasn't yet been injected into the spec; not ideal, but AFAIK can be ignored.
The rendering looks to be due to an error in how react-vega handles the date parsing. Instead of passing in date as strings, first convert them to Date objects, then pass in the modified data to the Vega component.

Resources