Why aren't Victory Charts components composable? - reactjs

I'm trying to create a React component that is a Line and Scatter chart to look like this:
The React component for a single line with circles looks like this:
function Line ({ color, chartData }) {
return (
<VictoryGroup data={chartData}>
<VictoryLine
style={{ data: { stroke: color } }}
/>
<VictoryScatter
style={{
data: {
stroke: color,
fill: 'white',
strokeWidth: 3
}
}}
/>
</VictoryGroup>
);
}
I am trying to consume the component like so:
function MyCoolChart () {
return (
<VictoryChart>
<Line
color='#349CEE'
chartData={data1}
/>
<Line
color='#715EBD'
chartData={data2}
/>
</VictoryChart>
);
}
But the Line components aren't rendered. They're only rendered if I call them directly as a function, like so:
export default function MyCoolChart () {
return (
<VictoryChart>
{Line({ color: '#349CEE', chartData: data1 })}
{Line({ color: '#715EBD', chartData: data2 })}
</VictoryChart>
);
}
I'm trying to make a reusable component, so I'd rather not have to consume it as a function. I also want to understand why this is happening. Thanks!
For reference, the values of data1 and data2:
const data1 = [
{ x: 'M', y: 2 },
{ x: 'Tu', y: 3 },
{ x: 'W', y: 5 },
{ x: 'Th', y: 0 },
{ x: 'F', y: 7 }
];
const data2 = [
{ x: 'M', y: 1 },
{ x: 'Tu', y: 5 },
{ x: 'W', y: 5 },
{ x: 'Th', y: 7 },
{ x: 'F', y: 6 }
];

Thanks to #boygirl for responding to my github issue
Turns out Victory passes a few props down of its own that need to be passed along for things to be rendered correctly. Examples of this are domain and scale. Here's my updated component:
function Line ({ color, ...other }) {
return (
<VictoryGroup {...other}>
<VictoryLine
style={{ data: { stroke: color } }}
/>
<VictoryScatter
style={{
data: {
stroke: color,
fill: 'white',
strokeWidth: 3
}
}}
/>
</VictoryGroup>
);
}
And it is now consumed like so:
function MyCoolChart () {
return (
<VictoryChart>
<Line
color='#349CEE'
data={data1}
/>
<Line
color='#715EBD'
data={data2}
/>
</VictoryChart>
);
}

Related

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 animate prop transform for svg with React-Native and Reanimated 2

import React, { FC } from "react";
import { G } from "react-native-svg";
import Animated, { useAnimatedProps, useDerivedValue, withSpring } from "react-native-reanimated";
import { CanvasControlledValuesProps } from "./helpers/types";
import { Candle } from "./Candle";
import { CANDLE_WIDTH } from "../../constants/sizes";
const AnimatedGroup = Animated.createAnimatedComponent(G);
export const Canvas: FC<CanvasControlledValuesProps> = ({
scaleY,
scaleX,
translateX,
offsetByY,
data,
initialDomain,
}) => {
const props = useAnimatedProps(() => {
return {
x: withSpring(translateX.value, {
damping: 20,
stiffness: 90,
}),
y: withSpring(offsetByY.value, {
damping: 20,
stiffness: 90,
}),
transform: { scale: [scaleX.value, scaleY.value] },
};
});
return (
<AnimatedGroup animatedProps={props}>
{data.map((candle, index) => (
<Candle key={index} width={CANDLE_WIDTH} {...{ candle, index }} domain={initialDomain} />
))}
</AnimatedGroup>
);
};
Good day! I need to increase or decrease the content of the AnimatedGroup, so I decided to use G but there was a problem: the scale is not applied for the AnimatedGroup, why it's like that? I have not used Aniamted.View since the quality of the Svg content, inside which the AnimatedGroup is located, will be lost.
const style = useAnimatedStyle(() => {
return {
transform: [
{
translateX: offsetByX.value,
},
{
translateX: withSpring(translateX.value, springConfig),
},
{
translateY: withSpring(adaptation.value.offsetByY, springConfig),
},
{
scaleX: scaleX.value,
},
{
scaleY: withSpring(adaptation.value.scaleY, springConfig),
},
],
};
});
return (
<AnimatedGroup style={style}>
{data.map((candle, index) => (
<Candle key={index} width={CANDLE_WIDTH} {...{ candle, index }} domain={initialDomain} />
))}
</AnimatedGroup>
);
The solution is to add animated styles for the Animated Group. Or you can use the matrix property like this:
const props = useAnimatedProps(() => {
return {
x: withSpring(translateX.value, {
damping: 20,
stiffness: 90,
}),
y: withSpring(offsetByY.value, {
damping: 20,
stiffness: 90,
}),
matrix: [scaleX.value, 0, 0, scaleY.value, 0, 0],
};
});
You can read about the work of matrix here https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform

Can I make a radial bar chart like this in Rechart?

I want to make a chart like this in Recharts.
They have a similar chart but the edges of it's lines are straight and not round like I need. Can it be configured?
Yeah we can make that double radial bar chart using recharts. This is react/ next js Component. You can modify Bar Radius, Inner radius. For Inner text there is a single number given from Recharts you can use Positioning for rendering that inner text and stylings.
import React from 'react';
import { RadialBarChart, PolarAngleAxis, RadialBar } from 'recharts';
const index = () => {
// Sample data
const data = [
{ name: 'A', x: 1, fill: 'green' },
{ name: 'B', x: 2, fill: 'yellow' },
// { name: 'C', x: 3, fill: 'aqua' },
// { name: 'D', x: 4, fill: 'blue' },
// { name: 'E', x: 5, fill: 'orange' },
// { name: 'F', x: 6, fill: 'red' },
// { name: 'G', x: 7, fill: 'black' },
// { name: 'H', x: 8, fill: 'purple' },
// { name: 'I', x: 9, fill: 'gray' },
];
return (
<RadialBarChart width={143} height={143} data={data}
// cx={30 / 2}
// cy={30 / 2}
innerRadius={25}
// outerRadius={18}
barSize={4}
startAngle={90}
endAngle={-270}>
<PolarAngleAxis
type="number"
domain={[0, 10]}
angleAxisId={0}
tick={false}
/>
<RadialBar
background
dataKey="value"
cornerRadius={30 / 2}
fill="#0BEFF2"
/>
<text
x={30 / 2}
y={33 / 2}
textAnchor="middle"
dominantBaseline="middle"
className="progress-label"
>
89
</text>
</RadialBarChart>
);
};
export default index;

Add a custom style to victory chart bar

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...
/>

How to control Victory x axis ticks labels

I'm using Victory to render a data set:
class App extends React.Component {
render() {
return (
<div style={{ width: 600 }}>
<VictoryChart domainPadding={30}>
<VictoryAxis
dependentAxis={true}
style={{
grid: { stroke: "grey" }
}}
/>
<VictoryAxis />
<VictoryBar
barWidth={20}
style={{ data: { fill: "red" } }}
data={[
{ x: new Date("2019-01-01"), y: 2 },
{ x: new Date("2019-02-01"), y: 3 },
{ x: new Date("2019-03-01"), y: 5 },
{ x: new Date("2019-04-01"), y: 4 },
{ x: new Date("2019-05-01"), y: 8 },
{ x: new Date("2019-06-01"), y: 2 },
{ x: new Date("2019-07-01"), y: 3 },
{ x: new Date("2019-08-01"), y: 5 },
{ x: new Date("2019-09-01"), y: 9 },
{ x: new Date("2019-10-01"), y: 3 },
{ x: new Date("2019-11-01"), y: 5 },
{ x: new Date("2019-12-01"), y: 6 }
]}
/>
</VictoryChart>
</div>
);
}
}
How can I change the x-axis to render a tick for each month ("jan", "feb", "mar" etc.)? Further, I would like each bar to have a width of 40 px (barWidth=40), but when I do that all bars are just merged - I would like to control the margin/padding between the bars as well. How should this be solved?
An example is available in this sandbox:
https://codesandbox.io/s/victory-react-native-4t49q
You may show labels as x-axis ticks. Then to control margin/padding you may set container's width a bit wider to fit bolder bars.
import React from "react";
import { render } from "react-dom";
import { VictoryChart, VictoryBar, VictoryAxis, VictoryContainer, VictoryLabel } from "victory";
class App extends React.Component {
render() {
let month = new Array(12);
month[0] = "January";
month[1] = "February";
month[2] = "March";
month[3] = "April";
month[4] = "May";
month[5] = "June";
month[6] = "July";
month[7] = "August";
month[8] = "September";
month[9] = "October";
month[10] = "November";
month[11] = "December";
return (
<div style={{ width: 600 }}>
<VictoryChart domainPadding={30}
width={900}
containerComponent={<VictoryContainer responsive={false}/>}>
<VictoryAxis
dependentAxis={true}
style={{
grid: { stroke: "grey" }
}}
/>
<VictoryAxis
tickFormat={(x) => ``}
/>
<VictoryBar
barWidth={50}
padding={{ left: 20, right: 60 }}
style={{ data: { fill: "red" } }}
data={[
{ x: new Date("2019-01-01"), y: 2 },
{ x: new Date("2019-02-01"), y: 3 },
{ x: new Date("2019-03-01"), y: 5 },
{ x: new Date("2019-04-01"), y: 4 },
{ x: new Date("2019-05-01"), y: 8 },
{ x: new Date("2019-06-01"), y: 2 },
{ x: new Date("2019-07-01"), y: 3 },
{ x: new Date("2019-08-01"), y: 5 },
{ x: new Date("2019-09-01"), y: 9 },
{ x: new Date("2019-10-01"), y: 3 },
{ x: new Date("2019-11-01"), y: 5 },
{ x: new Date("2019-12-01"), y: 6 }
]}
labels={( datum ) => `${month[datum.x.getMonth()]}`}
labelComponent={<VictoryLabel y={250} verticalAnchor={"start"}/>}
/>
</VictoryChart>
</div>
);
}
}
render(<App />, document.getElementById("root"));
As a sandbox: link
You can try. Added comments where changes are Needed
class App extends React.Component {
render() {
return (
<div>
<VictoryChart
domainPadding={30}
width={700} // Width should be more as we are updating barWidth
>
<VictoryAxis
dependentAxis={true}
style={{
grid: { stroke: "grey" }
}}
/>
<VictoryAxis tickFormat={x => ''} /> // remove existing labels
<VictoryBar
barWidth={40}
style={{ data: { fill: "red" } }}
labels={( datum ) => { return `${datum.x.toLocaleString('default', { month: 'short' })}`}} // get labels as month name
labelComponent={
<VictoryLabel y={270}
verticalAnchor={"start"}
angle={90}
/>
}
data={[
{ x: new Date("2019-01-01"), y: 2 },
{ x: new Date("2019-02-01"), y: 3 },
{ x: new Date("2019-03-01"), y: 5 },
{ x: new Date("2019-04-01"), y: 4 },
{ x: new Date("2019-05-01"), y: 8 },
{ x: new Date("2019-06-01"), y: 2 },
{ x: new Date("2019-07-01"), y: 3 },
{ x: new Date("2019-08-01"), y: 5 },
{ x: new Date("2019-09-01"), y: 9 },
{ x: new Date("2019-10-01"), y: 3 },
{ x: new Date("2019-11-01"), y: 5 },
{ x: new Date("2019-12-01"), y: 6 }
]}
/>
</VictoryChart>
</div>
);
}
}

Resources