Recharts - bar chart dynamic label position - reactjs

How can I have dynamic label positions of Bars like is showing in the picture?
Here is my code till now
const data = [
{ currency: 'CHF', amount: 3, amountLabel: 3023.00 },
{ currency: 'GBP', amount: 6, amountLabel: 6275.00 },
{ currency: 'USD', amount: 10, amountLabel: 9999.00 },
{ currency: 'EUR', amount: 14, amountLabel: 14819.00 },
{ currency: 'LEK', amount: 24, amountLabel: 24023000.00 },
];
<BarChart
width={430}
height={170}
data={data}
layout="vertical">
<XAxis type="number" orientation="top" stroke="#285A64" />
<YAxis type="category" dataKey="currency" axisLine={false} dx={-5} tickLine={false}
style={{ fill: "#285A64" }} />
<Bar background dataKey="amount" fill="#285A64" barSize={{ height: 26 }}>
<LabelList dataKey="amountLabel" position="insideRight" style={{ fill: "white" }} />
</Bar>
</BarChart>
Find Sample code here jsfiddle

You should implement a React component to be injected into the prop content (LabelList)
For example (JSFiddle):
const {BarChart, Bar, XAxis, YAxis, LabelList} = Recharts;
const data = [
{ currency: 'CHF', amount: 3, amountLabel: 3023.00 },
{ currency: 'GBP', amount: 6, amountLabel: 6275.00 },
{ currency: 'USD', amount: 10, amountLabel: 9999.00 },
{ currency: 'EUR', amount: 14, amountLabel: 14819.00 },
{ currency: 'LEK', amount: 26, amountLabel: 26023000.00 },
];
const renderCustomizedLabel = (props) => {
const {
x, y, width, height, value,
} = props;
const fireOffset = value.toString().length < 5;
const offset = fireOffset ? -40 : 5;
return (
<text x={x + width -offset} y={y + height - 5} fill={fireOffset ? "#285A64" :"#fff"} textAnchor="end">
{value}
</text>
);
};
const VerticalBarChart = React.createClass({
render () {
return (
<BarChart
width={400}
height={170}
data={data}
layout="vertical">
<XAxis type="number" orientation="top" stroke="#285A64"/>
<YAxis type="category" dataKey="currency" axisLine={false} dx={-10} tickLine={false} style={{ fill: "#285A64" }} />
<Bar background dataKey="amount" fill="#285A64" barSize={{ height: 26 }}>
<LabelList dataKey="amountLabel" content={renderCustomizedLabel} position="insideRight" style={{ fill: "white" }} />
</Bar>
</BarChart>
);
}
})
ReactDOM.render(
<VerticalBarChart />,
document.getElementById('container')
);

Related

Displaying in the tooltip each value along the y axis on hover

Recently started using Recharts for charting.
Here is the data that comes from the server. They need to display Barchart.
[
{
"channel_number": 1,
"data": [
{
"date": 100000,
"consumption": 1231
},
{
"date": 200000,
"consumption": 1234
},
{
"date": 300000,
"consumption": 1234
}
]
},
{
"channel_number": 2,
"data": [
{
"date": 100000,
"consumption": 800
},
{
"date": 200000,
"consumption": 823
},
{
"date": 300000,
"consumption": 1233
}
]
}
]
I need to display each consumption value on the y-axis. That is, so that when hovering over with the mouse, it shows each value. Like this for example
I get a tooltip where only the total value of consumption is displayed
My code:
<ResponsiveContainer width="100%" height={385}>
<BarChart height={300} data={newBar}>
<CartesianGrid vertical={false} />
<XAxis
height={32}
dataKey="date"
allowDuplicatedCategory={false}
tickFormatter={(tick) => convertTimestampToDate(tick, 'dd.MM')}
angle={90}
tick={{ fontSize: '12px', color: '#626C77' }}
textAnchor="start"
/>
<YAxis tick={{ fontSize: '12px', color: '#626C77' }} />
<Tooltip cursor={false} labelFormatter={() => ''} />
<Legend
align="right"
verticalAlign="top"
iconSize={6}
formatter={(value, entry, index) => (
<span style={{ color: '#969FA8' }}>{value}</span>
)}
wrapperStyle={{ fontSize: '12px', top: -51 }}
payload={data?.channel_consumption.map((item: any, index: any) => ({
id: item.channel_number,
value: `${item.id_channel}`,
color: PIE_COLORS[index],
}))}
/>
{newBar.map((el: any, index: any) => (
<Bar
dataKey="consumption"
data={el.data}
key={el.channel_number}
fill={PIE_COLORS[index]}
barSize={8}
radius={71.5}
></Bar>
))}
</BarChart>
</ResponsiveContainer>
Does anyone have any ideas? How to display each value when hovering over a bar? Probably do something with the coordinates, I can't figure it out

JSX element doesn't render inside a loop

I have an array of stock data for my shopping store which I would like to render in the screen with the help of a for loop. But only the first item in the array is being rendered, while the array have 9 items.
I also have tried putting the render function outside the functional component, still the same issue.
Plz help me to identify the problem. Thanks.
code
const Home = () => {
const [isSneakersActive, setIsSneakersActive] = React.useState(false);
const [isWatchActive, setIsWatchActive] = React.useState(false);
const [isBackpackActive, setIsBackpackActive] = React.useState(false);
const screenWidth = Dimensions.get('window').width;
const renderProducts = () => {
for (let i = 0; i < Products.length; i++) {
const element = Products[i];
return (
<ProductCard>
<ProductHeader>
{element.discount === 'none' ? (
<DiscountLabel style={{backgroundColor: '#fff'}} />
) : (
<DiscountLabel>
<Text size={13} weight="bold">
{element.discount}
</Text>
</DiscountLabel>
)}
{element.favourite === 'yes' ? (
<Ionicons name="heart-circle" color="#F75058" size={20} />
) : (
<AntDesign name="heart" color="#B6B8C7" size={15} />
)}
</ProductHeader>
<ProductPicture>
<View style={ProductPictureStyles.border}>
<View style={ProductPictureStyles.circle}>
<Image
source={element.imageSrc}
style={[
ProductPictureStyles.image,
{
height: (screenWidth / 1731) * 433,
},
]}
/>
</View>
</View>
</ProductPicture>
<ProductName>
<Text color="#3D44AA" size={(screenWidth / 380) * 18} weight="bold">
{element.name}
</Text>
</ProductName>
<ProductPrice>
<Text color="#3D44AA" size={(screenWidth / 380) * 18} weight="bold">
{element.price}
</Text>
</ProductPrice>
<ProductRating>
<AntDesign name="star" color="#fdd344" size={18} />
<AntDesign name="star" color="#fdd344" size={18} />
<AntDesign name="star" color="#fdd344" size={18} />
<AntDesign name="star" color="#fdd344" size={18} />
<AntDesign name="staro" color="#f0e8c9" size={18} />
</ProductRating>
</ProductCard>
);
}
};
return (
<View style={{flex: 1}}>
<Section5 style={{paddingHorizontal: 0}}>
<ScrollView showsVerticalScrollIndicator={false}>
{isSneakersActive ? (
<Section5 style={{paddingHorizontal: 0}}></Section5>
) : isWatchActive ? (
<Section5 style={{paddingHorizontal: 0}}></Section5>
) : isBackpackActive ? (
<Section5 style={{paddingHorizontal: 0}}></Section5>
) : (
<Section5 style={{paddingHorizontal: 0}}>
{renderProducts()}
</Section5>
)}
</ScrollView>
</Section5>
</View>
);
};
export default Home;
stock data
export const Products = [
{
id: 1,
category: 'shoe',
name: 'Air Max Motion',
imageSrc: require('../images/sneekers.png'),
price: '$ 250',
rating: 5,
discount: '8%',
favourite: 'yes',
},
{
id: 2,
category: 'backpack',
name: 'Hiking Starlet',
imageSrc: require('../images/backpack.png'),
price: '$ 350',
rating: 5,
discount: '5%',
favourite: 'yes',
},
{
id: 3,
category: 'backpack',
name: 'Hand Bag',
imageSrc: require('../images/backpack02.png'),
price: '$ 80',
rating: 3.5,
discount: '8%',
favourite: 'no',
},
{
id: 4,
category: 'shoe',
name: 'Nike Air Max',
imageSrc: require('../images/sneekers02.png'),
price: '$ 240',
rating: 4,
discount: 'none',
favourite: 'no',
},
{
id: 5,
category: 'watch',
name: 'Royal Rolex',
imageSrc: require('../images/watch.png'),
price: '$ 50',
rating: 5,
discount: 'none',
favourite: 'no',
},
{
id: 6,
category: 'shoe',
name: 'Hiker Max',
imageSrc: require('../images/sneekers03.png'),
price: '$ 180',
rating: 4.5,
discount: 'none',
favourite: 'no',
},
{
id: 7,
category: 'backpack',
name: 'School Bag',
imageSrc: require('../images/backpack01.png'),
price: '$ 120',
rating: 3.5,
discount: '10%',
favourite: 'no',
},
{
id: 8,
category: 'watch',
name: 'Silver Liner',
imageSrc: require('../images/watch01.png'),
price: '$ 35',
rating: 4.5,
discount: '22%',
favourite: 'yes',
},
{
id: 9,
category: 'shoe',
name: 'Leather Sneaker',
imageSrc: require('../images/sneekers01.png'),
price: '$ 200',
rating: 4,
discount: 'none',
favourite: 'no',
},
];
The problem is that your are looping the array but returning just the first element.
the correct way to do it is via map
const renderProducts = () => {
for (let i = 0; i < Products.length; i++) {
const element = Products[i];
return ( //return just the first
<ProductCard>
...
</ProductCard>
);
}
};
const renderProducts = () => products.map((element, i) => {
return (
<ProductCard key={i}>
...
</ProductCard>
);
};

How to create Histogram with grouped data in Victory (ReactJS)?

I'm trying to create a histogram with Value, Id and Group code. I need to group the Ids based on the group code and put the labels on top. I'm also trying to add a horizontal scroll bar. Can you help me with this?
I tried to do like in this picture but I couldn't.
My data is in the form of this object
{
id: 1,
value: 0.16882,
group_id: 'group_1',
fill: 'red',
}
const App = () => {
const sharedAxisStyles = {
tickLabels: {
fontSize: 10
},
axisLabel: {
padding: 39,
fontSize: 12,
},
grid: {
stroke: "#546E7A",
strokeWidth: 0.1,
}
};
return (
<VictoryChart
domainPadding={{
x: 20,
}}
containerComponent={(
<VictoryZoomContainer
allowZoom={false}
zoomDomain={{
x: [0, 22],
}}
/>
)}
>
<VictoryLabel
x={225}
y={30}
textAnchor="middle"
text="Group code"
/>
<VictoryBar
data={data}
x={(x) => `id_${x.id}`}
y="value"
barWidth={15.8}
style={{
data: {
fill: ({datum}) => datum.fill,
}
}}
/>
<VictoryAxis
tickCount={22}
tickLabelComponent={<VictoryLabel
angle={-90}
textAnchor="end"
style={[
{ fontSize: 3, fontWeight: 'bold', fill: '#78909C' },
]}
/>}
/>
<VictoryAxis
dependentAxis
label="Value"
tickFormat={(t) => {
return (
(t).toFixed(2)
);
}}
style={sharedAxisStyles}
/>
</VictoryChart>
);
};
When I write these codes, the output I get is like this:
you will need to use VictoryGroup : https://formidable.com/open-source/victory/docs/victory-group/
And use labels to add title for each group https://formidable.com/open-source/victory/docs/victory-group/#labels

React, sorting X-axis label in Rechart in from different data sources

I need help figuring how to sort X-Axis label values in ascending order in Rechart graphs.
currently, the data for the chart is gotten from two sources.
Below is the code presented.
const series = [
{ name: 'SourceA: ', data: reportsFromSourceA },
{ name: 'SourceB: ', data: reportsFromSourceB },
]
const selectedCategories= [...]
selectedCategories.map((category, i) => (
<ResponsiveContainer height={350} width='50%' key={category}>
<LineChart
margin={{ top: 40, right: 30, left: 70, bottom: 5 }}
syncId='category'
>
<XAxis dataKey='hour' allowDuplicatedCategory={false} />
<YAxis
label={{
value: category,
position: 'top',
fontSize: 18,
offset: 20,
fontWeight: 'bold',
fill: COLORS[i % 8]
}}
tickFormatter={formatUSD}
/>
<CartesianGrid strokeDasharray='3 3' />
<Tooltip formatter={formatUSD} />
{series.map((s, i) => (
<Line dataKey='rpc' stroke={COLORS[i % 2]} data={s.data[category]} name={s.name} key={s.name} />
))}
<ReferenceArea y2={0} stroke='red' color='red' strokeOpacity={0.5} />
</LineChart>
</ResponsiveContainer>
)))
I had a similar issue, but the solution is to:
first sort out the data coming from your database
or sort the entire array it by key value (in ascending or descending order)
The issue occurs due to some missing entries of the labels in the first data source which the second one has.
The simple solution can be to create an extra data source for labels only and we'll not draw a line for that source.
A relevant sandbox example is here
The full explanation is here
The following solution can solve the issue
const labelSeries = {data: [
{category: 01}, {category: 05}, {category: 09}, {category: 10}, {category: 11},
{category: 12}, {category: 13}, {category: 14},{category: 15}, {category: 16},
{category: 17}, {category: 18}, {category: 19}, {category: 20}, {category: 21},
{category: 22}
]}
const series = [
{ name: 'SourceA: ', data: reportsFromSourceA },
{ name: 'SourceB: ', data: reportsFromSourceB },
]
const selectedCategories= [...]
selectedCategories.map((category, i) => (
<ResponsiveContainer height={350} width='50%' key={category}>
<LineChart
margin={{ top: 40, right: 30, left: 70, bottom: 5 }}
syncId='category'
>
<XAxis dataKey='hour' allowDuplicatedCategory={false} />
<YAxis
label={{
value: category,
position: 'top',
fontSize: 18,
offset: 20,
fontWeight: 'bold',
fill: COLORS[i % 8]
}}
tickFormatter={formatUSD}
/>
<CartesianGrid strokeDasharray='3 3' />
<Tooltip formatter={formatUSD} />
<Line data={labelSeries.data} /> // This line will sort the x-axis label
{series.map((s, i) => (
<Line dataKey='rpc' stroke={COLORS[i % 2]} data={s.data[category]} name={s.name} key={s.name} />
))}
<ReferenceArea y2={0} stroke='red' color='red' strokeOpacity={0.5} />
</LineChart>
</ResponsiveContainer>
)))

Recharts how to display an additional diagonal line with different data set to a line chart

What I am trying to do is the following:
But I can't manage to get it working. I was able to get the data to read in from 2 different data sources but it somehow does not render the second line properly on the x-axis.
Here is the example code:
import React from "react";
import { render } from "react-dom";
import { LineChart, Line, XAxis, YAxis, ReferenceLine } from "recharts";
const styles = {
fontFamily: "sans-serif",
textAlign: "center"
};
const data = [];
const maxBudget = 300;
for (let i = 0; i < 20; i++) {
let d = {
day: i,
value: Math.random() * (maxBudget + 50) + 100
};
data.push(d);
}
const testline = [{ x: 0, y: 300 }, { x: 20, y: 0 }]
const App = () => (
<div style={styles}>
<LineChart
width={500}
height={300}
margin={{ top: 5, right: 20, bottom: 5, left: 0 }}
>
<Line type="monotone" data={data} dataKey="value" stroke="#8884d8" dot={false} />
<Line type="linear" data={testline} dataKey="y" stroke="#FF3333" dot={false} strokeWidth={2} />
<XAxis dataKey="day" type="number" tickCount={11} />
<YAxis />
<ReferenceLine
y={maxBudget}
label={{
position: "center",
value: "Max budget"
}}
strokeDasharray="5 5"
/>
</LineChart>
</div>
);
render(<App />, document.getElementById("root"));
ok I m stupid. I just found it out by myself. Sometimes you don't see the wood for the trees for days :(.
The x value must be the same as defined in the XAxis so I had to change x:0 to day:0:
const testline = [{ day: 0, y: 300 }, { day: 20, y: 0 }]
Now it works

Resources