Victory Charts: Using labels with bar charts - reactjs

I am leveraging VictoryCharts to add charts to my React app. I am trying to accomplish something like:
I combed through the docs and was not able to find a way to add Labels to a single bar chart.
Things I have tried
Nesting <VictoryLabel> andunder``` --> The axes show up and the docs recommend using VictoryGroup
Nesting <VictoryLabel> andunder``` --> VictoryGroup does not support VictoryLabel
Tried making a standalone <VictoryBar> & <VictoryLabel> and embedding it into <svg> --> Cannot see the chart contents on the page
This is the snippet I have right now:
import Box from '#material-ui/core/Box';
import React from 'react';
import { VictoryAxis, VictoryBar, VictoryChart, VictoryContainer, VictoryLabel, VictoryTheme } from 'victory';
const SampleChart = ({ stat=25, title = 'Sample' }) => (
<Box ml={5}>
<svg height={60} width={200}>
<VictoryLabel text={title.toLocaleUpperCase()} textAnchor='start' verticalAnchor='end' />
<VictoryBar
barWidth={10}
data={[{ y: [stat], x: [1] }]}
domain={{ y: [0, 100], x: [0, 1] }}
horizontal
labels={d => d.y}
labelComponent={
<VictoryLabel verticalAnchor='end' textAnchor='start' />
}
standalone
style={{ parent: { height: 'inherit' } }}
theme={VictoryTheme.material}
/>
</svg>
</Box>
);
ReactDOM.render(<SampleChart />, document.querySelector("#app"))
<div id='app'></div>

You could use an axis to this effect like so:
const CHART_WIDTH = 800;
const val = 28
function BarChart() {
return (
<div style={{ width: CHART_WIDTH }}>
<VictoryChart height={200} width={CHART_WIDTH}>
<VictoryAxis
dependentAxis
tickValues={[0,100]}
offsetY={190}
tickFormat={t => t===0 ? 'average age' : val}
style={{axis: { stroke: 'none'}}}
/>
<VictoryBar
barWidth={10}
data={[{ y: [45], x: [1] }]}
domain={{ y: [0, 100], x: [0, 1] }}
horizontal
/>
</VictoryChart>
</div>
);
}
https://codepen.io/anon/pen/RmaxOd?editors=0110#0
Not exactly what you wanted, but it gets the job done, and you can format the tick labels or provide custom components to make it look like your desired result.

Related

react mapbox gl circle radius not working

I want to create circle radius in react. I have used the react-mapbox-gl package. The map is working but I am not able to create a round circle radius.
My current code is as follows:
let Map = ReactMapboxGl({
accessToken:
helper.MapBoxPrimaryKey
});
let Obj = {
"circle-radius": 10,
// Color circles by ethnicity, using a `match` expression.
"circle-color": "purple",
"circle-stroke-color": "purple",
"circle-opacity": 0.5,
"circle-stroke-opacity": 1,
"circle-stroke-width": 5
}
<Map
style={mapStyles.day}
zoom={[location.zoomlevel]}
onRender={(e) => {
console.log('e.boxZoom._container.outerText',e.boxZoom._container.outerText)
setObjectVal(e.boxZoom._container.outerText)
}} //boxZoom._container.outerText
containerStyle={{
height: '300px',
//width: '310px'
}}
center={[location.lng, location.lat]}
// onStyleLoad={onStyleLoad}
>
<Layer type="circle" source="mine" id="circle" paint={Obj}
// layout={{ 'icon-image': 'custom-marker' }}
layout={{ "icon-image": "harbor-15" }}
>
<Feature coordinates={[location.lng, location.lat]} />
</Layer>
<ScaleControl />
<Marker
coordinates={[location.lng, location.lat]}
anchor="bottom">
<div className="mapboxgl-user-location-dot"></div>
</Marker>
</Map>
What am I doing wrong here?

Change color of slices of piechart in plotly.js using react-plotly.js

import Plotly from "plotly.js";
import createPlotlyComponent from "react-plotly.js/factory";
export const Plot = createPlotlyComponent(Plotly);
export function PieChart() {
return <Plot data={data} layout={layout} config={{ displaylogo: false }} useResizeHandler={true} style={{ width: '100%' }} />;
}
I want to change the color of Piechart slices, I referred to the documentation of plotly and added
marker: {
color: 'rgba(255,153,51,0.6)',
width: 1
}
too but it's not working
This is array,
maker.color --> maker.colors
You can do this as below
marker: {
colors: ['rgb(56, 75, 126)', 'rgb(18, 36, 37)', 'rgb(34, 53, 101)'],
width: 1
}
Here is DEMO for this,

How can i switch between react components using framer motion?

In my react app i need to switch between components like in a carousel. I found this example to build an image carousel only using framer motion: https://codesandbox.io/s/framer-motion-image-gallery-pqvx3?file=/src/Example.tsx:1715-1725
I want to adapt this to switching between components. At the moment my page looks something like this:
const variants = {
enter: (direction: number) => {
return {
x: direction > 0 ? 100 : -100,
opacity: 0,
}
},
center: {
zIndex: 1,
x: 0,
opacity: 1,
},
exit: (direction: number) => {
return {
zIndex: 0,
x: direction < 0 ? 100 : -100,
opacity: 0,
}
},
}
const Page = () => {
const [[page, direction], setPage] = useState([0, 0])
const paginate = (newDirection: number) => {
setPage([page + newDirection, newDirection])
}
return (
<motion.div
key={page}
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
>
<!-- my components, between which I want to switch, should appear here -->
</motion.div>
)
}
how would I have to build the logic to be able to switch dynamic between my components (slides)? In the codesandbox example the images were changed via an array:
const imageIndex = wrap(0, images.length, page);
<motion.img key={page} src={images[imageIndex]} />
How could i do that to switch between jsx elements?
Edit
The answer from Joshua Wootonn is correct, but you need to add the custom prop also to the TestComp to get the animation working with dynamic variants like this:
const TestComp = ({ bg }: { bg: string }) => (
<motion.div
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
transition={{
x: { type: "spring", stiffness: 100, damping: 30 },
opacity: { duration: 0.2 },
}}
className="absolute w-full h-full"
style={{
background: bg,
}}
/>
)
A couple of things were missing from the above answer to get exit animations working.
If you want exit animations to work within AnimationPresense you need to set keys on its children
<AnimatePresence initial={false} custom={direction}>
{page === 0 && <TestComp key="0" bg="rgb(171, 135, 255)" />}
{page === 1 && <TestComp key="1" bg="rgb(68, 109, 246)" />}
{page === 2 && <TestComp key="2" bg="rgb(172, 236, 161)" />}
</AnimatePresence>
If you want to animate something in while something is still animating out without having massive content shifting, you need to take them out of the flow. (use absolute positioning and wrap with relatively positioned container)
<div style={{ position: "relative", height: "300px", width: "300px" }}>
<AnimatePresence initial={false} custom={direction}>
...
</AnimatePresence>
</div>
and on the child components
height: 100%;
width: 100%;
position: absolute;
Working codesandbox: https://codesandbox.io/s/framer-motion-carousel-animation-wetrf?file=/src/App.tsx:658-708
Your components should return <motion.div> (or <motion.section>, <motion.span> etc.).
And in the page component you should use <AnimatePresence /> component (like in the example):
<AnimatePresence initial={false} custom={direction}>
{COMPONENTS}
</AnimatePresence>
Then you have to decide which component will appear:
{page === 0 && <ComponentOne />}
{page === 1 && <ComponentTwo/>}
{page === 2 && <ComponentThree/>}
The animations you can control with variants.
You can see a quick demo here: https://codesandbox.io/s/quizzical-hypatia-7wqjc?file=/src/App.tsx

How to customize VictoryTooltip

I've implemented a pie graph using VictoryCharts and I added tooltips...
<VictoryPie
labelComponent={<VictoryTooltip cornerRadius={0} />}
colorScale={["tomato", "orange", "gold", "cyan", "navy" ]}
padAngle={0.5}
innerRadius={100}
width={400} height={400}
style={{
labels: { fontSize: 15, fill: "black"},
data: {
fillOpacity: 0.9, stroke: "#c43a31", strokeWidth: 3
}
}}
labelRadius={90}
data = {data_distribution}
/>
The tooltips look as follows...
I want to remove the arrow and make the tooltip a regular rectangle and change the background color. Essentially I want to customize it but this has proved to be harder than I expected.
I tried creating a custom component...
class CustomFlyout extends Component {
render() {
const {x, y} = this.props;
return (
<div style={{"background": "red"}}>
<p>x</p>
</div>
);
}
}
I added to the VictoryTooltip...
<VictoryTooltip
cornerRadius={0}
flyoutComponent={<CustomFlyout/>}
/>
However, this does nothing. I cannot figure out how to make a customized tooltip.
You can customize VictoryTooltip and set the style you need like this ..
<VictoryTooltip
cornerRadius={0}
pointerLength={0}
flyoutStyle={{
stroke: "none",
fill: "blue"
}}
/>
See this example

Resize highcharts using react-grid-layout not working

I am working in react and using highcharts with react-grid-layout to resize the elements in div. Somehow resizable feature is working for images but not with highchart. Grid.js is a file that makes ResponsiveReactGridLayout and it gets the highchart from chart.js file. Please find the code below.
//Grid.js
import React, { Component } from 'react';
import {Responsive, WidthProvider,ReactGridLayout} from 'react-grid-layout';
import Charts from './charts.js';
const ResponsiveReactGridLayout = WidthProvider(Responsive);
class Grid extends Component {
onLayoutChange(layout) {
console.log(layout);
}
render(){
return (<div style={{borderStyle: 'groove'}}>
<h2> Panel Header </h2>
<ResponsiveReactGridLayout className="layout"
breakpoints={{lg: 1200, md: 96, sm: 768}}
cols={{lg: 5, md: 10, sm: 6}}
onLayoutChange={this.onLayoutChange}>
<div key="c" data-grid={{x: 0, y: 0, w: 1, h: 3}} style={{ border:'1px solid green', borderStyle: 'groove'}}>
<img src="https://cdn.geckoandfly.com/wp-content/uploads/2013/03/530-smiley-face.jpg" style={{width:'inherit', height:'inherit'}} />
</div>
<div key="d" className = 'react-grid-item react-resizable'
data-grid={{x: 1, y: 0, w: 1, h: 3}} style={{ border:'1px solid green', borderStyle: 'groove'}}
>
<Charts style={{width:'inherit'}} id={'Chart 1'}/>
</div>
<div key="e" data-grid={{x: 2, y: 0, w: 1, h: 3}} style={{ border:'1px solid green',borderStyle: 'groove'}}>
<Charts style={{width:'inherit'}} id={'Chart 2'}/>
</div>
<div key="f" data-grid={{x: 3, y: 0, w: 1, h: 3}} style={{ border:'1px solid green',borderStyle: 'groove'}}>
<Charts style={{width:'inherit'}} id={'Chart 3'}/>
</div>
</ResponsiveReactGridLayout>
</div>
)
}
}
export default (Grid);
//Charts.js
import React, { Component } from 'react';
const Highcharts = require('highcharts');
class Charts extends Component{
constructor(props){
super(props);
this.state = {
data : {
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
},
series: [{
data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 295.6, 454.4]
}]
},
pieData: [{name: "Firefox",y: 6},{name: "MSIE",y: 4},{name: "Safari",y: 4},{name: "Opera",y: 1},{name: "Chrome",y: 7}]
};
}
/**
* Inject highcharts markup into the DOM after the rest of the component has mounted
* #return {None}
*/
componentDidMount() {
// Load in any highcharts modules
if (this.props.modules) {
this.props.module.forEach((module) => {
module(Highcharts);
});
}
// Create the actual chart and assign reference
const props = this.processPropsModel(this.props);
const containerRef = `container${props.id}`;
this.chart = new Highcharts.chart(
containerRef,
props.options
);
}
processPropsModel(props) {
const newProps = {};
newProps.id = this.props.id;
newProps.options = this.generateDefaultOptions();
return newProps;
}
/**
* Generating some default chart options for placeholding purposes
* #return {Object} The options to be passed into the chart
*/
generateDefaultOptions() {
return {
title: {
text: this.props.id,
x: -20 //center
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
},
yAxis: {
title: {
text: null
}
},
tooltip: {
valueSuffix: '°C'
},
series: [{
name: 'Tokyo',
data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]
}]
};
}
render(){
const props = this.processPropsModel(this.props);
return (
<div id={`container${props.id}`}>
</div>
);
}
}
export default Charts;
So, I was struggling with the same issue using highcharts-react-official and react-grid-layout.
Here is how I finally got it working.
tl;dr
Give height 100% to all of your chart's parents up to the grid item.
There is an annoying div that highcharts creates by himself. Find a way to identify it and give it height 100%.
Give height 100% to the chart itself.
Use the react highcharts callback to get your chart object.
When your component updates reflow your chart.
Below is my responsive grid layout, just to give some context.
// Component/Grid/Grid.js
<ResponsiveGridLayout
...
>
{this.state.widgets.map((widget) =>
<Card key={widget.DWG_ID}>
<Widget
widget={widget}
/>
</Card>
)}
</ResponsiveGridLayout>
Now, inside the Widget Component, set the height of any div that will be a parent of your highchart to 100%.
// Component/Widget/Widget.js
<CardBody className="widgetContent">
<CardTitle className="widget-title">{this.props.widget.DWG_LABEL}</CardTitle>
<Chart
widget={this.props.widget}
/>}
</CardBody>
For the jsx above I only needed to do this for the element CardBody with class widgetContent, so
// Component/Widget/Widget.css
.widgetContent { height: 100%; }
Now, in the chart component (where all the fun was), I had to create a very ugly div just to be able to identify the outer-most div that highcharts creates.
elements created by highcharts
The infamous div in question can be seen in the image above, right under the div with class highchartsWrapper, with the property data-highcharts-chart .
This div was the only parent of my chart that I could not identify directly to give 100% height. So I created the wrapper to be able to identify it unequivocally.
Note that in the chart options we passed a class name as well, to be able to give the chart itself the css height property.
If anybody has a neater idea of how to identify this problematic div please let me know.
// Component/Chart/Chart.js
options = {
...
chart: { className: 'chart' }
}
<div className="highchartsWrapper">
<HighchartsReact
highcharts={Highcharts}
options={options}
callback={(chart) => this.setChart(chart)}
/>
</div>
So I could give it the css
// Component/Chart/Chart.css
.highchartsWrapper > div {
height: 100%;
}
.chart {
height: 100%;
}
Now your highchart would ideally assume the correct width and height. But there was another complication: when the highchart renders for the first time and checks his parent's height, react-grid-layout isn't yet done with his resizing magic. This means your chart will be teeny-tiny. Moreover, when you resize your grid items you want your highchart to resize to its new parent size. But wait, I've worked with highcharts before, I know how to do this! The good old chart.reflow() ! Unfortunately this ended up not being that easy.
To start with, just getting the chart object on which I can call reflow wasn't very straightforward. If you notice, I gave my HighchartsReact a callback
(chart) => this.setChart(chart)
This is just to store the chart object as a property of my class component. My setChart function does only the following:
setChart(chart) {
this.chart = chart;
}
It might seem stupid to do it like this. Why not just give setChart directly to the callback property of HighchartsReact? Well because if you do, as per the highcharts-react-official documentation, your this inside the setChart function would be your chart object... All very confusing, but it seems to work like this.
Again, if somebody has a neater solution, let me know
Finally, we can call our this.chart.reflow() when our Chart Component is updated. what I did was something like
constructor() {
super(props)
this.firstResize = true;
}
componentDidUpdate(prevProps) {
if (this.didWidgetSizeChange(prevProps) || this.isFirstResize) {
this.chart.reflow();
this.isFirstResize = false;
}
}
When the component updates we call the chart reflow. On the componentDidMount the grid item doesn't have his final size yet, so I used a flag to figure out the first update (that would be exactly that: grid item has finished first resize). Then for any other update I wrote a function that basically compares the previous layout for this grid item with the new to decide if the size or width have changed. If so, we reflow again to resize the highcharts to the new grid item size.
Hope this helps!
Peace!
Here is the sample solution to make it fit in the Grid....
import React from 'react';
import './App.css';
import '/node_modules/react-grid-layout/css/styles.css';
import '/node_modules/react-resizable/css/styles.css';
import GridLayout from 'react-grid-layout';
import Highcharts from "highcharts/highstock";
import HighchartsReact from "highcharts-react-official";
const options = {
series: [
{
data: [1, 2, 3]
}
]
};
class MyFirstGrid extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
this.conRef = React.createRef();
}
render() {
// layout is an array of objects, see the demo for more complete usage
var layout = [
{ i: "a", x: 0, y: 0, w: 5, h: 5 },
{ i: "b", x: 1, y: 0, w: 3, h: 2 },
{ i: "c", x: 4, y: 0, w: 1, h: 2 }
];
return (
<GridLayout
className="layout"
layout={layout}
cols={12}
rowHeight={30}
width={1200}
onResizeStop={function(event) {
this.myRef.current.chart.setSize(this.conRef.current.clientWidth,this.conRef.current.clientHeight)
console.log('hello', event);
}.bind(this)}
>
<div ref={this.conRef} style={{ backgroundColor: "#00000000" }} key="a">
<HighchartsReact
ref= {this.myRef}
containerProps={{ style: { width: '100%', height: '100%' } }}
options={options}
highcharts={Highcharts}
/>
</div>
<div style={{ backgroundColor: "red" }} key="b">
b
</div>
<div style={{ backgroundColor: "blue" }} key="c">
c
</div>
</GridLayout>
);
}
}
export default MyFirstGrid;
I think the reason why the functionality of resizing highchrts is not working you can find it from the documentation of 'reflow' from hightcharts
By default, the chart reflows automatically to its container following a window.resize event, as per the chart.reflow option. However, there are no reliable events for div resize, so if the container is resized without a window resize event, this must be called explicitly.
The situation matches what you did now: you tried to resize the div itself but not the window size, so it doesn't work as you expected.
What I did to make my charts resizable in grid as following:
Create a function to do reflow
const onLayoutChange = () => {
for (let i = 0; i < Highcharts.charts.length; i += 1) {
if (Highcharts.charts[i] !== undefined) {
Highcharts.charts[i].reflow(); // here is the magic to update charts' looking
}
}
};
Use onLayoutChange provided by react-grid-layout
<GridLayout
cols={12}
layout={layout}
rowHeight={30}
width={1200}
onLayoutChange={() => onLayoutChange()}
>
...
</GridLayout>
And then...BANG! you got resizable charts controlled by the resize button in react-grid-layout.
Make it easy to understand, you can view my playground here:
https://codesandbox.io/s/resize-highcharts-in-grid-9e625?file=/src/App.js

Resources