Annotations not showing on ChartJS - React - reactjs

The datasets plot fine on my line chart but I am trying to get vertical lines to display on the chart using chartjs-plugin-annotation. From looking at these questions & answers:
SOLVED : ChartJS annotation not displaying
https://github.com/chartjs/chartjs-plugin-annotation/issues/209
react-chartjs-2 vertical line when hovering over chart
Chart.js — drawing an arbitrary vertical line
I thought I had the correct configuration but the lines are not appearing.
LineChart.js
import 'chartjs-plugin-annotation'
import React, { useContext } from 'react'
import { Card, CardBody } from 'reactstrap'
import { Line } from 'react-chartjs-2'
import flatten from 'lodash/flatten'
import { rgbaColor, exchangeChartColors, capitalize, exchanges } from '../../helpers/utils'
import AppContext from '../../context/Context'
import dayjs from 'dayjs'
function getDepositsAndWithdrawals(activity) {
return ['DEPOSIT', 'WITHDRAWAL'].reduce((acc, type) => {
if (activity) {
if (activity.betfair) {
acc[type].betfair = activity.betfair.filter(s => {
return s.legacyData.marketName === type
}).map(s => {
return {
exchange: 'BETFAIR',
type: type,
amount: s.amount,
date: getFormattedDate(s.itemDate)
}
})
}
if (activity.betdaq) {
acc[type].betdaq = activity.betdaq.filter(s => {
return s.PostingCategory === 3 && s.Description.toUpperCase().includes(`${type}:`)
}).map(s => {
return {
exchange: 'BETDAQ',
type: type,
amount: s.Amount,
date: getFormattedDate(s.PostedAt)
}
})
}
if (activity.smarkets) {
const typeToGet = (type === 'DEPOSIT') ? 'deposit' : 'withdraw'
acc[type].smarkets = activity.smarkets.filter(s => {
return s.source === typeToGet
}).map(s => {
return {
exchange: 'SMARKETS',
type: type,
amount: parseFloat(s.money_change),
date: getFormattedDate(s.timestamp)
}
})
}
}
return acc
}, {
DEPOSIT: {},
WITHDRAWAL: {}
})
}
function getFormattedDate(date) {
const parsed = dayjs(date)
const day = parsed
.date()
.toString()
.padStart(2, '0')
// Unsure why have to add 1 but don't care really
const mnth = (parsed
.month() + 1)
.toString()
.padStart(2, '0')
const yr = parsed
.year()
.toString()
.padStart(2, '0')
const hr = parsed
.hour()
.toString()
.padStart(2, '0')
const min = parsed
.minute()
.toString()
.padStart(2, '0')
return `${day}/${mnth}/${yr} # ${hr}:${min}`
}
function getXAxis(balances, annotations) {
const balanceDates = balances.map(entry => {
return getFormattedDate(entry.date)
})
const annotationDates = annotations.map(ann => {
return ann.value
})
return [
...balanceDates,
...annotationDates
]
}
const ProfitsLineChart = props => {
const { isDark } = useContext(AppContext)
const depositsAndWithdrawals = getDepositsAndWithdrawals(props.activity)
const annotations = Object.values(depositsAndWithdrawals).reduce((acc, exs) => {
const newEntries = Object.values(exs).map(entries => {
return entries.map(entry => {
return {
type: 'line',
mode: 'vertical',
drawTime: 'afterDatasetDraw',
scaleID: 'x-axis-0',
value: entry.date,
borderColor: isDark
? exchangeChartColors.dark[entry.exchange.toLowerCase()]
: exchangeChartColors.light[entry.exchange.toLowerCase()],
borderWidth: 2,
label: {
content: `${entry.exchange} ~ ${entry.type} ~ ${entry.amount}`,
enabled: true,
position: 'top'
}
}
})
})
return [
...acc,
...flatten(newEntries)
]
}, [])
const config = {
data(canvas) {
let datasets = exchanges.map(exchange => {
return {
exchange,
data: props.balances.reduce((acc, entry) => {
const entryForExchange = entry.balances.find(balance => {
return balance.exchange.toUpperCase() === exchange.toUpperCase()
})
if (entryForExchange) {
acc.push({
date: entry.date,
balance: entryForExchange.balance
})
}
return acc
}, [])
}
})
let labels = getXAxis(props.balances, annotations)
// If not specified time period, only show most recent (30) entries
if (!props.start && !props.end) {
datasets = datasets.map(ds => {
return {
...ds,
data: ds.data.slice(ds.data.length - 50, ds.data.length)
}
})
labels = labels.slice(labels.length - 50, labels.length)
}
return {
labels,
datasets: datasets.map(set => {
return {
label: capitalize(set.exchange),
borderWidth: 2,
fill: false,
data: set.data.map(s => s.balance.toFixed(2)),
borderColor: isDark ? exchangeChartColors.dark[set.exchange.toLowerCase()] : exchangeChartColors.light[set.exchange.toLowerCase()],
backgroundColor: isDark ? exchangeChartColors.dark[set.exchange.toLowerCase()] : exchangeChartColors.light[set.exchange.toLowerCase()]
}
})
}
},
options: {
annotation: {
annotations
},
responsive: true,
title: {
display: true,
text: 'Balances',
fontSize: 20,
fontStyle: 'bold',
lineHeight: 2.5,
fontColor: rgbaColor('#cccccc', 0.7)
},
legend: {
labels: {
fontSize: 16,
fontStyle: 'italic',
fontColor: rgbaColor('#cccccc', 0.7)
},
display: true,
position: 'bottom'
},
tooltips: {
mode: 'index',
displayColors: true
},
hover: {
mode: 'label'
},
scales: {
xAxes: [
{
display: true,
id: 'x-axis-0',
scaleLabel: {
display: true,
labelString: 'Time',
fontSize: 12,
fontColor: rgbaColor('#cccccc', 0.7)
},
ticks: {
callback: () => '',
fontColor: rgbaColor('#cccccc', 0.7),
fontStyle: 600
}
}
],
yAxes: [
{
display: true,
id: 'y-axis-0',
scaleLabel: {
display: true,
labelString: 'Balance (£)',
fontSize: 14,
fontColor: rgbaColor('#cccccc', 0.7)
},
ticks: {
min: 0,
fontColor: rgbaColor('#cccccc', 0.7),
fontStyle: 600
}
}
]
}
}
}
return !props.balances.length ? (
<Card className="text-center mb-3">
<CardBody className="p-5">
<div className="display-2 text-200">No Data</div>
<p className="lead mt-4 text-800 text-sans-serif font-weight-semi-bold">There are no balances to display.</p>
<hr />
<p>Please edit your time period search (or remove it altogether) to see data</p>
</CardBody>
</Card>
) : (
<Card className="mb-3">
<CardBody className="rounded-soft bg-gradient">
<Line data={config.data} options={config.options} />
</CardBody>
</Card>
)
}
export default ProfitsLineChart
This is the config that is spit out that the chart is using:
{
"options": {
"annotation": {
"annotations": [
{
"type": "line",
"mode": "vertical",
"drawTime": "afterDatasetDraw",
"scaleID": "x-axis-0",
"value": "08/03/2020 # 14:47",
"borderColor": "rgba(239, 131, 0, 0.8)",
"borderWidth": 2,
"label": {
"content": "BETFAIR ~ DEPOSIT ~ 22",
"enabled": true,
"position": "top"
}
},
{
"type": "line",
"mode": "vertical",
"drawTime": "afterDatasetDraw",
"scaleID": "x-axis-0",
"value": "03/03/2020 # 23:04",
"borderColor": "rgba(119, 0, 255, 0.8)",
"borderWidth": 2,
"label": {
"content": "BETDAQ ~ DEPOSIT ~ 26.57",
"enabled": true,
"position": "top"
}
},
{
"type": "line",
"mode": "vertical",
"drawTime": "afterDatasetDraw",
"scaleID": "x-axis-0",
"value": "19/03/2020 # 17:57",
"borderColor": "rgba(68, 254, 59, 0.8)",
"borderWidth": 2,
"label": {
"content": "SMARKETS ~ DEPOSIT ~ 21",
"enabled": true,
"position": "top"
}
},
{
"type": "line",
"mode": "vertical",
"drawTime": "afterDatasetDraw",
"scaleID": "x-axis-0",
"value": "27/03/2020 # 12:55",
"borderColor": "rgba(239, 131, 0, 0.8)",
"borderWidth": 2,
"label": {
"content": "BETFAIR ~ WITHDRAWAL ~ -10",
"enabled": true,
"position": "top"
}
},
{
"type": "line",
"mode": "vertical",
"drawTime": "afterDatasetDraw",
"scaleID": "x-axis-0",
"value": "27/03/2020 # 13:02",
"borderColor": "rgba(119, 0, 255, 0.8)",
"borderWidth": 2,
"label": {
"content": "BETDAQ ~ WITHDRAWAL ~ -10",
"enabled": true,
"position": "top"
}
},
{
"type": "line",
"mode": "vertical",
"drawTime": "afterDatasetDraw",
"scaleID": "x-axis-0",
"value": "01/03/2020 # 09:45",
"borderColor": "rgba(68, 254, 59, 0.8)",
"borderWidth": 2,
"label": {
"content": "SMARKETS ~ WITHDRAWAL ~ -26.57",
"enabled": true,
"position": "top"
}
}
]
}
},
"responsive": true,
"title": {
"display": true,
"text": "Balances",
"fontSize": 20,
"fontStyle": "bold",
"lineHeight": 2.5,
"fontColor": "rgba(204,204,204,0.7)"
},
"legend": {
"labels": {
"fontSize": 16,
"fontStyle": "italic",
"fontColor": "rgba(204,204,204,0.7)"
},
"display": true,
"position": "bottom"
},
"tooltips": {
"mode": "index",
"displayColors": true
},
"hover": {
"mode": "label"
},
"scales": {
"xAxes": [
{
"display": true,
"id": "x-axis-0",
"scaleLabel": {
"display": true,
"labelString": "Time",
"fontSize": 12,
"fontColor": "rgba(204,204,204,0.7)"
},
"ticks": {
"fontColor": "rgba(204,204,204,0.7)",
"fontStyle": 600
}
}
],
"yAxes": [
{
"display": true,
"id": "y-axis-0",
"scaleLabel": {
"display": true,
"labelString": "Balance (£)",
"fontSize": 14,
"fontColor": "rgba(204,204,204,0.7)"
},
"ticks": {
"min": 0,
"fontColor": "rgba(204,204,204,0.7)",
"fontStyle": 600
}
}
]
}
}
I can't think as to why this is not working as intended. getXAxis ensures that the string timestamps of the annotations are added to the X-Axis as well. I'm lost for ideas

Look at this:
https://github.com/reactchartjs/react-chartjs-2/issues/201#issuecomment-579630734
and here, for vertical line instead of horizontal, write
mode: 'vertical',

Related

React Highcharts v9.2 Solid Gauge Rounded Ends

I am working with solid gauge. We are upgrading from version 7.2.2 to the latest 9.2. We use to be able to pass in the y-axis plotbands a rounded: true which would round the tips of the bands. Since upgrading this no longer works. Instead the tips are squared off. I am looking for any assistance in how I might handle this.
Here is a fiddle of what it looks like squared off:
fiddle
Highcharts.chart('container', {
chart: {
type: 'solidgauge',
height: '110%',
},
tooltip: {
borderWidth: 0,
backgroundColor: 'none',
shadow: false,
style: {
fontSize: '16px'
},
valueSuffix: '%',
pointFormat: '{series.name}<br><span style="font-size:2em; color: {point.color}; font-weight: bold">{point.y}</span>',
positioner: function (labelWidth) {
return {
x: (this.chart.chartWidth - labelWidth) / 2,
y: (this.chart.plotHeight / 2) + 15
};
}
},
pane: [
{
"startAngle": -140,
"endAngle": 140,
"background": {
"shape": "arc",
"borderWidth": 0,
"backgroundColor": "#FFF0"
}
}
],
yAxis: [
{
"gridLineWidth": 0,
"opposite": false,
"title": {
"text": "",
"useHTML": true
},
"labels": {
"useHTML": true,
"format": "{value}",
"enabled": false
},
"min": 0,
"max": 100,
"lineWidth": 0,
"tickWidth": 0,
"minorTickWidth": 0,
"plotBands": [
{
"thickness": "10%",
rounded: true,
"from": 0,
"to": 73,
"color": "#1792E555",
},
{
"thickness": "10%",
"from": 76,
"to": 88,
"color": "#FFE393"
},
{
"thickness": "10%",
"from": 91,
"to": 100,
"color": "#F9B890"
}
],
"plotLines": [],
"index": 0
}
],
plotOptions: {
solidgauge: {
dataLabels: {
enabled: false
},
linecap: 'round',
stickyTracking: false,
rounded: true
}
},
series: [
{
"data": [
{
"y": 10,
"color": "#1792E5"
}
],
"radius": "100%",
"innerRadius": "90%",
linecap: 'rounded',
"rounded": true,
"useDial": false,
"type": "solidgauge",
"tooltip": {
"enabled": true,
"useHTML": true,
"borderRadius": 0,
"borderWidth": 0,
"shadow": false,
"valueDecimals": 2,
"style": {
"fontFamily": "inherit"
},
"padding": 6,
"shape": "square"
},
"dataLabels": {
"style": {
"textOutline": "none"
}
},
"events": {}
}
]
});
Unfortunately, it's not so easy to do. Since the plotBand is an SVG element you can try to edit its path and round the corners but it might be hard. You can check how the rounded path is created here: https://code.highcharts.com/modules/solid-gauge.src.js?_ga=2.104350400.154582566.1629699669-527407293.1629434064
And you can also check out this plugin: https://github.com/highcharts/rounded-corners
But the easiest solution would be to use another solidGauge series instead of plotBand. Something like that: https://jsfiddle.net/BlackLabel/k35vcsnx/
series[{...}, {
type: "solidgauge",
rounded: true,
data: [{
color: "#1792E555",
radius: '100%%',
innerRadius: '90%',
y: 73
}]
}

How to update data on zoom in apexchart

I am trying to find a way in the apex chart. By which if user zoom on the year graph then the user should be able to get month data when they zoom on month data this should show the day data.
But I am not able to figure out if its possible in apex chart or not.
This is how my graph look like right now.
import React from "react";
import ReactApexChart from "react-apexcharts";
interface StackedGraphProps {}
type SeriesType = {
name: string;
data: number[];
};
interface StackedGraphState {
series: SeriesType[];
options: any;
}
class StackedBarGraph extends React.Component<
StackedGraphProps,
StackedGraphState
> {
constructor(props: any) {
super(props);
this.state = {
series: [
{
name: "Marine Sprite",
data: [44, 55, 41, 37, 22, 43, 21],
},
{
name: "Striking Calf",
data: [53, 32, 33, 52, 13, 43, 32],
},
{
name: "Tank Picture",
data: [12, 17, 11, 9, 15, 11, 20],
},
],
options: {
chart: {
events: {
zoomed: function (chartContext: any, { xaxis, yaxis }) {
console.log("xAxis", xaxis, yaxis);
},
selection: function (chartContext: any, { xaxis, yaxis }) {
console.log("Selecton", xaxis, yaxis);
},
dataPointSelection: (
event: any,
chartContext: any,
config: any
) => {
console.log("datapoint", chartContext, config);
},
},
zoom: {
enabled: true,
type: "x",
autoScaleYaxis: false,
// zoomedArea: {
// fill: {
// color: "#90CAF9",
// opacity: 0.4,
// },
// stroke: {
// color: "#0D47A1",
// opacity: 0.4,
// width: 1,
// },
// },
},
type: "bar",
height: 350,
stacked: true,
},
toolbar: {
show: true,
},
plotOptions: {
bar: {
horizontal: false,
},
},
stroke: {
width: 1,
colors: ["#fff"],
},
grid: {
row: {
colors: ["#fff", "#f2f2f2f2"],
},
},
title: {
text: "",
},
xaxis: {
tickPlacement: "on",
categories: [2008, 2009, 2010, 2011, 2012, 2013, 2014],
labels: {
formatter: function (val: any) {
return val + "K";
},
},
},
yaxis: {
title: {
text: undefined,
},
},
tooltip: {
y: {
formatter: function (val: any) {
return val + "K";
},
},
},
fill: {
opacity: 1,
},
legend: {
position: "top",
horizontalAlign: "left",
offsetX: 40,
},
},
};
}
render() {
return (
<div id="chart">
<ReactApexChart
zoomEnabled={true}
options={this.state.options}
series={this.state.series}
type="bar"
height={350}
/>
</div>
);
}
}
export default StackedBarGraph;

Title is undefined in react-chart

I am building a covid-19 tracker, but when data is displayed on the graph using react charts title is coming out to be undefined
This is my chart code
<div >
{data?.length > 0 && (
<Line
data={{
datasets: [
{
backgroundColor: "rgba(204, 16, 52, 0.5)",
borderColor: "#CC1034",
data: data,
},
],
}}
options={options}
/>
)}
</div>
These are my option parameters
const options = {
legend: {
display: false,
},
elements: {
point: {
radius: 0,
},
},
maintainAspectRatio: false,
tooltips: {
mode: "index",
intersect: false,
callbacks: {
label: function (tooltipItem, data) {
return numeral(tooltipItem.value).format("+0,0");
},
},
},
scales: {
xAxes: [
{
type: "time",
time: {
format: "MM/DD/YY",
tooltipFormat: "ll",
},
},
],
yAxes: [
{
gridLines: {
display: false,
},
ticks: {
// Include a dollar sign in the ticks
callback: function (value, index, values) {
return numeral(value).format("0a");
},
},
},
],
},
};
Although legend is set to false I am getting an error.
Please tell me where I am going wrong, that will be helpful.
Image of a chart.
You need to pass the label property here
data={{
datasets: [
{
backgroundColor: "rgba(204, 16, 52, 0.5)",
borderColor: "#CC1034",
data: data,
label: "your label" // <-- pass this
},
],
}}

Issue with chartjs linear gradient for the mixed bar chart in ReactJS is not calculated for each individual Bars

Our requirement is to render the bar chart as Expected Image. we are using chartJs library to render the chart. we are able to render the bar chart but for one of the bar we need to show
the background color as gradient. To achieve this we used the following below code snippet:
but it render as Rendered graph. Can you please help me out in rendering the chart as expected.
JSFiddleLink
const data = [{
type: "Sample 1",
data: [600, 400, 200, 800]
}, {
type: "Sampel 2",
data: [700, 300, 600, 600]
}, {
type: "Total",
data: [1300, 700, 800, 1400]
}];
const gradient = document.getElementById('myChart').getContext('2d').createLinearGradient(0, 250, 0, 0);
gradient.addColorStop(1, '#acd7fa')
gradient.addColorStop(0.4, '#FFFFFF')
new Chart('myChart', {
type: "bar",
data: {
labels: ["A", "B", "C", "D"],
datasets: [
{
label: data[0].type,
xAxisID: "x1",
data: data[0].data,
// fillColor: background_1,
// highlightFill: background_2,
backgroundColor: "rgb(54, 162, 235)" ,
// backgroundColor: background_1,
barPercentage: 1,
},
{
label: data[1].type,
xAxisID: "x1",
data: data[1].data,
backgroundColor:"rgb(255, 159, 64)",
barPercentage: 1,
},
{
label: data[2].type,
xAxisID: "x2",
data: data[2].data,
backgroundColor: gradient,
barPercentage: 1,
},
],
},
options: {
legend: {
labels: {
filter: item => item.text != 'Total'
}
},
scales: {
yAxes: [{
ticks: {
min: 0,
stepSize: 200
}
}],
xAxes: [{
id: 'x1'
},
{
id: 'x2',
display: false,
offset: true
}
]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="150"></canvas>
The Plugin Core API offers a range of hooks that may be used for performing custom code. You can use the afterLayout hook for creating different gradients for each value of the third dataset (the totals).
Please take a look at your amended code below and see how it works. You may also consult this StackBlitz that illustrates how it can be done with react-chartjs-2.
const data = [
{ type: "Sample 1", data: [600, 400, 200, 800] },
{ type: "Sampel 2", data: [700, 300, 600, 600] },
{ type: "Total", data: [1300, 700, 800, 1400] }
];
new Chart('myChart', {
type: "bar",
plugins: [{
afterLayout: chart => {
let ctx = chart.chart.ctx;
ctx.save();
let yAxis = chart.scales["y-axis-0"];
let yBottom = yAxis.getPixelForValue(0);
let dataset = chart.data.datasets[2];
dataset.backgroundColor = dataset.data.map(v => {
let yTop = yAxis.getPixelForValue(v);
let gradient = ctx.createLinearGradient(0, yBottom, 0, yTop);
gradient.addColorStop(0.4, '#FFFFFF');
gradient.addColorStop(1, '#acd7fa');
return gradient;
});
ctx.restore();
}
}],
data: {
labels: ["A", "B", "C", "D"],
datasets: [{
label: data[0].type,
xAxisID: "x1",
data: data[0].data,
backgroundColor: "rgb(54, 162, 235)",
barPercentage: 1
},
{
label: data[1].type,
xAxisID: "x1",
data: data[1].data,
backgroundColor: "rgb(255, 159, 64)",
barPercentage: 1
},
{
label: data[2].type,
xAxisID: "x2",
data: data[2].data,
barPercentage: 1
}
]
},
options: {
legend: {
labels: {
filter: item => item.text != 'Total'
}
},
scales: {
yAxes: [{
ticks: {
min: 0,
stepSize: 200
}
}],
xAxes: [{
id: 'x1'
},
{
id: 'x2',
display: false,
offset: true
}
]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="150"></canvas>

react-particle-js not updating the number of particles

I have:
return (
<Particles
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: -1
}}
params={{
number: {
value: 400,
density: {
enable: true,
value_area: 800
}
},
line_linked: {
enable: true,
distance: 150,
color: "#ffffff",
opacity: 0.4,
width: 1
},
"shape": {
"type": "circle",
"stroke": {
"width": 0,
"color": "#000000"
},
polygon: {
nb_sides: 7
}
},
"interactivity": {
"detect_on": "canvas",
"events": {
"onhover": {
"enable": true,
"mode": "grab"
},
"onclick": {
"enable": true,
"mode": "push"
},
"resize": true
},
"modes": {
"grab": {
"distance": 400,
"line_linked": {
"opacity": 1
}
},
"repulse": {
"distance": 200,
"duration": 0.4
}
}
},
"retina_detect": true
}} />
No matter how high I make number.value, the number of particles on the screen just doesn't increase. Any thoughts on what I'm doing wrong?
You've missed to enclose this properties inside particles. Also remove the z-index property from styles or set it to 0 that will display the particles
particles: {
number: {
value: 400,
density: {
enable: true,
value_area: 800
}
},
line_linked: {
enable: true,
distance: 150,
color: "#ffffff",
opacity: 0.4,
width: 1
},
"shape": {
"type": "circle",
"stroke": {
"width": 0,
"color": "#000000"
},
polygon: {
nb_sides: 7
}
}
},
"interactivity": {
"detect_on": "canvas",
"events": {
"onhover": {
"enable": true,
"mode": "grab"
},
"onclick": {
"enable": true,
"mode": "push"
},
"resize": true
},
"modes": {
"grab": {
"distance": 400,
"line_linked": {
"opacity": 1
}
},
"repulse": {
"distance": 200,
"duration": 0.4
}
}
},
"retina_detect": true

Resources