React Victory Chart: VictoryTooltip is not working - reactjs

I am working on a React JS project. My application needs to display data in chart format. I am using Victory Chart. I can display the chart.
What I am trying to do now is that when the user hovers the mouse cursor on a bar of the bar chart, it should display a tooltip using VictoryTooltip component. But it is not working.
Here is my code:
<VictoryChart
responsive={true}
width={byMonthChartWidth}
height={byMonthChartHeight}
padding={byMonthChartPadding}
domainPadding={byMonthChartDomainPadding}
>
<VictoryAxis
style={{
axis: {
stroke: "transparent"
},
tickLabels: {
fill: props.color,
fontSize: 8,
fontWeight: "bold"
}
}}
fixLabelOverlap={true}
tickValues={[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]}
tickFormat={['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D']}
preserveAspectRatio={"none"}
/>
<VictoryAxis
style={{
axis: {
stroke: "transparent"
},
tickLabels: {
fill: props.color,
fontSize: 8,
fontFamily: "roboto",
fontWeight: "bold"
}
}}
fixLabelOverlap={true}
dependentAxis
tickValues={yAxisTicks}
tickFormat={(x) => (`${x}`)}
preserveAspectRatio={"none"}
/>
<VictoryBar
labelComponent={
<VictoryTooltip
center={{ x: 225, y: 30 }}
pointerOrientation="bottom"
flyoutWidth={150}
flyoutHeight={50}
pointerWidth={150}
cornerRadius={0}
/>
}
animate={byMonthChartBarAnimation}
barWidth={byMonthChartBarWidth}
cornerRadius={{
topLeft: (data) => 5,
topRight: (data) => 5,
bottomLeft: (data) => 5,
bottomRight: (data) => 5
}}
style={{
data: {
fill: props.color,
stroke: props.color,
fontFamily: "roboto",
fillOpacity: 1,
strokeWidth: 1
}
}}
data={data}
x="identifier"
y="value"
/>
</VictoryChart>
As you can see I am trying to display a tooltip using labelComponent prop of VictoryBar component. When I hover the mouse cursor over the bar, it is not showing any tooltips. What is wrong with my code and how can I fix it?

The data variable used inside your data={data} must carry a label property which contains tooltip's contents.
You can modify tooltip's font color by adding labels object into <VictoryBar>'s style property - example below
LIVE DEMO
Example:
<VictoryBar
barRatio={1}
cornerRadius={0}
style={{ data: { fill: "#6DB65B" }, labels: { fill: "red" } }}
alignment="middle"
labels={(d) => d.y}
labelComponent={
<VictoryTooltip
center={{ x: 225, y: 30 }}
pointerOrientation="bottom"
flyoutWidth={150}
flyoutHeight={50}
pointerWidth={150}
cornerRadius={0}
flyoutStyle={{
stroke: "blue", // border-color
fill: "yellow", // background-color
}}
/>
}
data={[
{ x: "Year 1", y: 150000, label: "My label 1" },
{ x: "Year 2", y: 250000, label: "My label 1" },
{ x: "Year 3", y: 500020, label: "My label 1" },
{ x: "Year 4", y: 750000, label: "My label 1" },
{ x: "Year 5", y: 1000000, label: "My label 1" }
]}
/>

Related

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>
)))

Positioning icon in SVG in React Native

Background:
I am trying to add a tooltip to a react-native-svg chart following this tutorial.
The link to the tutorial: Link
Current Code Implementation:
import React, {useState} from 'react';
import {View, Text, Dimensions} from 'react-native';
import {LineChart} from 'react-native-chart-kit';
import {Rect, Text as TextSVG, Svg} from 'react-native-svg';
const Charts = () => {
let [tooltipPos, setTooltipPos] = useState({
x: 0,
y: 0,
visible: false,
value: 0,
});
return (
<View>
<LineChart
data={{
labels: ['January', 'February', 'March', 'April', 'May', 'June'],
datasets: [
{
data: [100, 110, 90, 130, 80, 103],
},
],
}}
width={Dimensions.get('window').width}
height={250}
yAxisLabel="$"
yAxisSuffix="k"
yAxisInterval={1}
chartConfig={{
backgroundColor: 'white',
backgroundGradientFrom: '#fbfbfb',
backgroundGradientTo: '#fbfbfb',
decimalPlaces: 2,
color: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
labelColor: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
style: {
borderRadius: 0,
},
propsForDots: {
r: '6',
strokeWidth: '0',
stroke: '#fbfbfb',
},
}}
bezier
style={{
marginVertical: 8,
borderRadius: 6,
}}
decorator={() => {
return tooltipPos.visible ? (
<View>
<Svg>
<Rect
x={tooltipPos.x - 15}
y={tooltipPos.y + 10}
width="40"
height="30"
fill="black"
/>
<MaterialCommunityIcons
name="run"
size={32}
color="rgb(67, 67, 67)"
/>
<TextSVG
x={tooltipPos.x + 5}
y={tooltipPos.y + 30}
fill="white"
fontSize="16"
fontWeight="bold"
textAnchor="middle">
{tooltipPos.value}
</TextSVG>
</Svg>
</View>
) : null;
}}
onDataPointClick={(data) => {
let isSamePoint = tooltipPos.x === data.x && tooltipPos.y === data.y;
isSamePoint
? setTooltipPos((previousState) => {
return {
...previousState,
value: data.value,
visible: !previousState.visible,
};
})
: setTooltipPos({
x: data.x,
value: data.value,
y: data.y,
visible: true,
});
}}
/>
</View>
);
};
Question:
I want to add the icon(Running icon) as seen on the image above, to be next to the tool-tip text.
The Icon, then the text inside the rectangle filled in Black. When I try to position it,, it shows up on the extreme top left for some reason. How do I position it?
You can use the ForeignObject component from react-native-svg and change your decorator to something like this:
decorator={() => {
return tooltipPos.visible ? (
<ForeignObject x={tooltipPos.x} y={tooltipPos.y}>
<View
style={{
width: 70,
flexDirection: 'row',
backgroundColor: 'black',
}}>
<MaterialCommunityIcons
name="run"
size={32}
color="rgb(67, 67, 67)"
/>
<Text
style={{
color: 'white',
fontSize: 16,
fontWeight: 'bold',
textAnchor: 'middle',
}}>
{tooltipPos.value}
</Text>
</View>
</ForeignObject>
) : null;
}}
The problem with what you had before is that the react-native-svg Text and Rect components use x and y coordinates and your icon doesn't, so the positioning will be off.
The benefit of the approach shown above is that you only have to specify the x and y props of the ForeignObject. Everything inside the ForeignObject can be regular views, positioned as you normally would (https://github.com/react-native-community/react-native-svg#foreignobject).
I've chosen the tooltipPos.x and tooltipPos.y for the x and y prop values of the ForeignObject respectively, but you could add an offset if necessary.
Be sure to import ForeignObject from react-native-svg.

How to render items in react native FlatList with 2 columns when some of them must be full width

I want to render product list in React Native with 2 columns by default, but if any item is a special product, then make it full width.
const data = [
{id: '1', name: 'Item1', price: 25, special_product: false},
{id: '2', name: 'Item2', price: 15, special_product: false},
{id: '3', name: 'Item3', price: 11, special_product: true},
{id: '4', name: 'Item4', price: 22, special_product: false},
{id: '5', name: 'Item5', price: 32, special_product: false},
{id: '6', name: 'Item6', price: 22, special_product: false},
{id: '7', name: 'Item7', price: 22, special_product: false},
]
And I want to get FlatList structure like this:
[Item 1][Item2]
[Special Item3]
[Item4] [Item5]
[Item6] [Item7]
My code is really simple now:
<FlatList
ref={(ref) => { this.flatListRef = ref; }}
contentContainerStyle={{ flexGrow: 1 }}
horizontal={false}
data={data}
keyExtractor={({ id }) => id}
onRefresh={this.onListRefresh}
refreshing={refreshing}
renderItem={this.renderItems}
ListHeaderComponent={this.renderHeader}
ListEmptyComponent={this.renderEmptyList.bind(this, data, use)}
/>
My item container styles looks like this:
const Styles = StyleSheet.create({
container: {
flex: 0.5,
margin: Layout.Margin.Narrow,
shadowColor: ShadowColor.Standard,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.5,
shadowRadius: 4,
elevation: 5,
borderBottomLeftRadius: 5,
borderBottomRightRadius: 5,
},
...
});
Item that is rendered by renderItem function:
// Item
...
return (
<TouchableWithoutFeedback
onPress={() => console.log('user clicked on the item')}>
<View style={{
...itemStyle.container
}}
<StyledText
type={"small"}
numberOfLines={1}
allowFontScaling={false}
style={itemStyle.centerText}>
{name}
</StyledText>
</View>
</TouchableWithoutFeedback>
How can I do this? I'm using React Native v0.60.5
You can use the number of columns prop and provide a width of 100% this will give the output you need.
This is a working sample you can apply it your item styles in anyway you need.
Flatlist code
<FlatList
numColumns={2}
data={data}
renderItem={({ item }) => (
<Item
id={item.id}
title={item.name}
selected={item.special_product}
/>
)}
keyExtractor={(item) => item.id}
/>
Render item function
function Item({ id, title, selected }) {
return (
<TouchableOpacity
onPress={() => {}}
style={[
selected ? { width: '100%' } : { width: '50%' },
{ backgroundColor: selected ? '#6e3b6e' : '#f9c2ff' },
]}>
<Text style={{}}>{title}</Text>
</TouchableOpacity>
);
}
As you can see I've set the width conditionally based the selected flag.

Nivo bar graph - add a shared y axis?

I'm using nivo responsive-bar to put a plot in my app. I've been asked to add a line showing the cumulative total on the same graph. Is there any way to do this? From the documentation I'm struggling to work out how to do it.
Here's the code I have so far (I've obfuscated the axis label and function names for security reasons!)
class CChart extends React.Component {
render = () => {
return (
<div style={{ height: '40em' }}>
<h3 className='text-center'>C</h3>
<ResponsiveBar
margin={{ top: 20, right: 0, bottom: 60, left: 80 }}
data={this.props.results}
indexBy='year'
colorBy={() => '#1f77b4'}
keys={['C']}
enableLabel={false}
axisLeft={{
legend: 'C(millions)',
legendPosition: 'center',
legendOffset: -60,
tickSize: 5,
tickPadding: 15,
format: v => Math.floor(v / 1000000).toLocaleString()
}}
axisBottom={{
legend: 'Year',
legendPosition: 'center',
legendOffset: 50,
tickSize: 5,
tickPadding: 15,
tickValues: this.props.results.map(x => x.year).filter(x => x % 5 === 0)
}}
theme={{
axis: {
fontSize: '1em',
legendFontSize: '1.2em'
}
}}
tooltipFormat={value => value.toLocaleString()}
animate={false}
markers={[
{ axis: 'y', value: 0 }
]}
/>
</div>
)
}
static propTypes = {
results: PropTypes.arrayOf(PropTypes.shape({
year: PropTypes.string.isRequired,
C: PropTypes.number.isRequired
})).isRequired
}
static mapStateToProps = state => ({
results: getCResults(state)
})
}
According to the #plouc (the founder of the Nivo library), as of September, 2018 you should be able to compose graphs.
https://github.com/plouc/nivo/issues/139
(scroll down until Sept 12)
It is not possible currently (https://github.com/plouc/nivo/issues/139)

Resources