Add a custom style to victory chart bar - reactjs

I want to create a custom bar chart with Victory like this:
How can I add a horizontal line in the graph every 10%?
This is my sample code:
const data = [
{ day: 1, data: 0 },
{ day: 2, data: 1 },
{ day: 3, data: 2 },
{ day: 4, data: 2 },
{ day: 5, data: 0 },
];
const BarChart = () => {
return (
<VictoryChart domainPadding={20} theme={VictoryTheme.material}>
<VictoryAxis
tickFormat={(x) => `${x.toFixed(0)}`}
style={{
grid: { stroke: "none" },
ticks: { size: 0 },
}}
/>
<VictoryAxis
dependentAxis
tickFormat={(x) => `${x}`}
style={{
grid: { stroke: "none" },
ticks: { size: 0 },
}}
/>
<VictoryStack>
<VictoryBar data={data} style={{ data: { fill: '#379F4B' } }} x="day" y="data" />
</VictoryStack>
</VictoryChart>
);
};

Have you tried to use tickValues? Example:
<VictoryAxis dependentAxis
tickValues={[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]}
...other stuff...
/>

Related

Struggling to correctly update and re-render chart on react-chartjs-2

I am building an app for gym-goers to record and track their progress. For each exercise they input, it should render a chart showing their previous track record. This is working fine.
Users can then add another entry to this track record, but it does not update the chart unless you refresh the page. I can't work out why or how to fix it.
There are a number of different components involved - a parent Exercise.js one, then an ExerciseFooter.js one, which contains the buttons to adjust the target or add a new entry to the exercise, and then AddHistory.js and SetTarget.js components which contain modals and the logic to update the exercise via Redux and MongoDB.
A minimal version of the Exercise.js page is here (I've collapsed the stuff that's mainly styling into single lines as much as possible):
import React, { useState, useEffect } from "react";
import { ExerciseFooter } from "./ExerciseFooter";
import { Line } from "react-chartjs-2";
import { useLocation } from "react-router-dom";
import { useSelector } from "react-redux";
export const Exercise = (props) => {
const location = useLocation();
const users = useSelector((state) => state.auth);
const localUser = JSON.parse(localStorage.getItem("profile"));
const [user, setUser] = useState("");
const [exerciseProp, setExerciseProp] = useState({
history: [""],
target: 0,
});
useEffect(() => {
localUser &&
localUser?.result &&
users.length > 0 &&
setUser(
users.filter(
(filteredUser) => filteredUser._id == props.match.params.userId
)[0]
);
if (!localUser) setUser("");
setExerciseProp(
user?.exercises?.filter(
(exercise) => exercise._id == props.match.params.exerciseId
)[0]
);
}, [users, location]);
//styling for chart
const [barData, setBarData] = useState({
labels: [""],
datasets: [
{ label: "Target", fill: false, radius: 0, data: [""], borderColor: ["rgba(35, 53, 89)"], borderWidth: [3], },
{ label: "You did", data: [""], tension: 0.3, borderColor: ["white"], backgroundColor: ["white"], borderWidth: 3, },
],
});
//updating chart data
var weightArr = [];
var dateArr = [];
var targetArr = [];
if (exerciseProp) {
exerciseProp.history.map((hist) =>
weightArr.push(parseInt(hist.weight) || 0)
);
exerciseProp.history.map((hist) => dateArr.push(hist.date));
for (let i = 0; i < exerciseProp.history.length; i++) {
targetArr.push(exerciseProp.target);
}
}
useEffect(() => {
if (exerciseProp) {
setBarData({
labels: dateArr,
datasets: [
{
label: "Target",
fill: false,
radius: 0,
data: targetArr,
borderColor: ["rgba(35, 53, 89)"], borderWidth: [3],
},
{
label: "Weight",
data: weightArr,
tension: 0.3, borderColor: ["white"], backgroundColor: ["white"], borderWidth: 3,
},
],
});
}
}, [users]);
//render chart ones exerciseProp is populated
if (exerciseProp) {
return (
<div style={{ marginTop: "200px" }}>
<Line
data={barData}
options={{ plugins: { title: { display: false, }, legend: { display: false, }, },
scales: { x: { grid: { color: "white", font: { family: "Dongle", size: 20, }, }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, y: { grid: { color: "white", }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, }, }}
/>
{exerciseProp && <ExerciseFooter user={user} exercise={exerciseProp} />}
</div>
);
} else {
return <>Loading...</>;
}
};
I've tried doing a few different things but nothing has worked. I tried adding an 'update' state variable which was updated by a function passed down to the the various dispatches, and then added it to the dependencies of the useEffects, but that didn't seem to make any difference.
Any help much appreciated! As I say, if I just force a refresh then it works fine but know that's bad practice so trying to work out why it isn't re-rendering correctly.
Thanks!
You just have to enable redraw prop
like this
<Line
redraw={true}
data={barData}
options={{ plugins: { title: { display: false, }, legend: { display: false, }, },
scales: { x: { grid: { color: "white", font: { family: "Dongle", size: 20, }, }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, y: { grid: { color: "white", }, ticks: { color: "white", font: { family: "Dongle", size: 20, }, }, }, }, }}/>
this all you have to do
redraw={true}

Show label as well as value in legend : plotly.js

Right now my current graph behavior is like this
It's a react component
<PieChart
data={[{
textinfo: "none",
values: [60, 20, 10, 5, 5], labels: ['Healthy', 'Mild', 'Moderate', 'Severe', 'Critical'], hole: .6, type: 'pie', marker: {
colors: ['#68B34D', '#FFDF83', '#FE9551', '#FE0801', '#B90000']
}
}]}
layout={{
title: { text: 'Health Status', x: '0.1', size: '14', family: 'Montserrat, sans-serif' }, annotations: [
{
font: {
size: 13,
color: '#323D4B',
family: 'Montserrat, sans-serif'
},
text: `<br>Machines</b>`,
showarrow: false,
}
], showlegend: true,
legend: {orientation:'h', x: 0.5, y: -.7, xanchor: 'center', yanchor: 'bottom', font: { family: 'Montserrat, sans- serif' } },
height: 320,
width: 248,
margin: { l: 0, r: 0, t: 50, b: 20 },
}}
/>
My expected behavior is, legends (Piechart info) should show label respective value too
Healthy: 60
Mild : 10
PieChart code
import createPlotlyComponent from "react-plotly.js/factory";
export const Plot = createPlotlyComponent(Plotly);
export function PieChart(props: PieChartProps) {
const { data, layout } = props;
return <Plot data={data} layout={layout} />;
}

Is there a way to plot a dot on just the last data point on a victory line chart for react native

So currently I have this as my code for the victory line chat
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { VictoryLine, VictoryChart } from 'victory-native'
let data = [
{
x: 1,
y: 0
},
{
x: 2,
y: 0
},
{
x: 3,
y: 0
},
{
x: 4,
y: 70
},
{
x: 5,
y: 73
},
{
x: 5,
y: 73
},
{
x: 5,
y: 73
},
{
x: 5,
y: 73
}
]
export default function Graph() {
return (
<VictoryLine style={styles.graph} height={150} width={350} data={data} style={{data: {stroke: 'orange', strokeWidth: 2.5}}} />
)
}
const styles = StyleSheet.create({
graph: {
marginRight: 0
}
})
which gives me a line chart that looks like this. Is there a way to:
A) Plot dotted point on the line for each data point
B) Just dot the last data point in the data list. Example image here of what I want to achieve
You can wrap your VictoryLine with an VictroyChart and hide the axis, like this sample
<View>
<VictoryChart polar={this.state.polar} height={390}>
<VictoryAxis style={{
axis: {stroke: "transparent"},
ticks: {stroke: "transparent"},
tickLabels: { fill:"transparent"}
}} />
<VictoryLine
interpolation={this.state.interpolation} data={data}
style={{ data: { stroke: "#c43a31" } }}
/>
<VictoryScatter data={[data[data.length-1]]}
size={5}
style={{ data: { fill: "#c43a31" } }}
/>
</VictoryChart>
</View>

How to define a custom tooltip in Apex chart in React?

I am defining four lines chart with Apex Chart.
My data series:
series: [
{
name: "first",
data: [28, 29, 33, 36, 32, 32, 33]
},
{
name: "second",
data: [12, 11, 14, 18, 17, 13, 13]
},
{
name: "third",
data: [48, 29, 33, 16, 32, 62, 3]
},
{
name: "forth",
data: [22, 11, 54, 18, 27, 53, 13]
}
],
I want my tooltip output to be like:
first-label : {series.dataOfFirstLabel}
second-label : {series.dataOfSecondLabel}
third-label : {series.dataOfThirdLabel}
forth-label : {series.dataOfForthLabel}
How can I define my custom tooltip?
Thanks.
Try this:
tooltip: {
custom: function (series: any) {
return `<div>
${series.map((el: any) => {
return `<div class="d-flex flex- column">
<p>${el.name}-label:${el.data.map((element: any)=>element).join("")}</p> </div>`; })
</div>`},}
https://codepen.io/apexcharts/pen/NBdyvV
In chart options
enter code here
tooltip: {
enabled: true,
enabledOnSeries: true,
shared: true,
followCursor: true,
intersect: false,
inverseOrder: false,
custom: undefined,
fillSeriesColor: true,
theme: false,
style: {
fontSize: "12px",
fontFamily: undefined,
},
onDatasetHover: {
highlightDataSeries: false,
},
x: {
show: true,
format: "dd MMM",
formatter: undefined,
},
y: {
show: false,
formatter: undefined,
title: {
formatter: (seriesName) => seriesName,
},
},
z: {
formatter: undefined,
title: "Size: ",
},
marker: {
show: false,
},
items: {
display: "flex",
},
fixed: {
enabled: false,
position: "topRight",
offsetX: 0,
offsetY: 0,
},
},
//sh
you can add these properties
refer - https://apexcharts.com/docs/options/tooltip/
Instead string literal you can use renderToString from react-dom/server.
import { renderToString } from 'react-dom/server'
So the custom function would look like this:
// Some series that you use in the chart
const seriesData = [
{
name: 'South',
data: [
[1486771200000, 40],
[1486857600000, 18],
[1486944000000, 43],
],
},
{
name: 'North',
data: [
[1486771200000, 16],
[1486857600000, 15],
[1486944000000, 10],
],
}]
...
// Tooltip option for apexChart
tooltip: {
custom: ({ series, seriesIndex, dataPointIndex, w }) => {
const xValue = seriesData[seriesIndex].data[dataPointIndex][0]
const xDate = new Date(xValue).toLocaleDateString('cs-CZ')
return renderToString(
<div>
<p>
{xDate}
</p>
{/* Map through series to get labels and values */}
{seriesData.map((s, i) => (
<div key={s.name} className="tooltipItem">
<div style={{
minWidth: "12px",
backgroundColor: w.globals.colors[i]
}}
/>
<p >
MyLabel:{' '}
</p>
<p>
{series[i][dataPointIndex]}
</p>
</div>
))}
</div>
)
},
},

React, ApexCharts, Radial Bar series value from data

I'm new to React and trying to learn. My personal project is homebrewing display site.
I want to show some of the details in radial bar. I followed tutorial and made array data file, and index file where details show. Now I'm stuck getting single values to display in radial bar.
Also values need to be calculated from percent to numeric. I got that working. I tried some push functions but did not get it to work way I wanted. My coding skills are beginner level.
display example
data.js
import product1 from '../../images/product-1.png';
import product2 from '../../images/product-1.png';
export const productData = [
{
id: 1,
img: product1,
alt: 'Beer',
name: 'Lager',
desc: 'Saaz',
ibu: '35',
ebc: '40',
abv: '8.5',
},
{
id: 2,
img: product2,
alt: 'Beer',
name: 'IPA',
desc: 'Mango, Citrus',
ibu: '85',
ebc: '25',
abv: '5.5',
},
];
index.js
import React, { Component } from 'react';
import Chart from 'react-apexcharts';
import {
ProductsContainer,
ProductWrapper,
ProductsHeading,
ProductTitle,
ProductCard,
ProductImg,
ProductInfo,
ProductDesc,
ProductIbu,
ProductEbc,
ProductAbv,
} from './ProductsElements';
const max = 119;
function valueToPercent(value) {
return (value * 100) / max;
}
class IBU extends Component {
constructor(props) {
super(props);
this.state = {
series: [valueToPercent(15)],
options: {
plotOptions: {
radialBar: {
startAngle: -90,
endAngle: 90,
hollow: {
margin: 10,
size: '70%',
background: '#222222',
image: undefined,
imageOffsetX: 0,
imageOffsetY: 0,
position: 'front',
dropShadow: {
enabled: true,
top: 5,
left: 0,
blur: 4,
opacity: 0.9,
},
},
track: {
background: '#d42d2d',
strokeWidth: '67%',
margin: 0, // margin is in pixels
dropShadow: {
enabled: true,
top: 0,
left: 0,
blur: 4,
color: '#000',
opacity: 0.6,
},
},
dataLabels: {
show: true,
name: {
offsetY: -10,
show: true,
color: '#fff',
fontSize: '17px',
},
value: {
formatter: function (value) {
return (parseFloat(value) * max) / 100;
},
color: '#dadada',
fontSize: '36px',
show: true,
},
},
},
},
fill: {
colors: [
function ({ value, seriesIndex, w }) {
if (value < 55) {
return '#7E36AF';
} else if (value >= 55 && value < 80) {
return '#164666';
} else {
return '#D9534F';
}
},
],
},
stroke: {
lineCap: 'round',
},
labels: ['IBU'],
},
};
}
render() {
return <Chart options={this.state.options} series={this.state.series} type="radialBar" height={250} />;
}
}
const Products = ({ data }) => {
return (
<ProductsContainer>
<ProductsHeading>heading</ProductsHeading>
<ProductWrapper>
{data.map((product, index) => {
return (
<ProductCard key={index}>
<ProductImg src={product.img} alt={product.alt} />
<ProductInfo>
<ProductTitle>{product.name}</ProductTitle>
<ProductDesc>{product.desc}</ProductDesc>
<ProductIbu>{product.ibu}</ProductIbu>
<IBU></IBU>
<ProductEbc>{product.ebc}</ProductEbc>
<ProductAbv>{product.abv}</ProductAbv>
</ProductInfo>
</ProductCard>
);
})}
</ProductWrapper>
</ProductsContainer>
);
};
export default Products;

Resources