I have a bubble map chart that shows the location of cities on the map. The map has the default label but I want to use a custom react component as the label on the map. This is my source code but it has error and doesn't work:
import React, { Component, Fragment } from "react";
import Highcharts from "highcharts";
import HighchartsReact from "highcharts-react-official";
import HighchartsMap from "highcharts/modules/map";
import mapData from "#highcharts/map-collection/countries/gb/gb-all.geo.json";
import proj4 from "proj4";
import CustomLabel from "./CustomLabel";
HighchartsMap(Highcharts);
class BubbleMapChart extends Component {
render() {
const options = {
chart: {
map: "countries/gb/gb-all",
proj4
},
series: [
{
name: "countries",
nullColor: "#fff",
showInLegend: false,
mapData: mapData
},
{
// Specify points using lat/lon
type: "mapbubble",
// PAY ATTENTION TO THIS SECTION - USE A CUSTOM LABEL COMPONENT
dataLabels: {
enabled: true,
format: <CustomLabel name={"point.name"} />
},
minSize: "5%",
maxSize: "15%",
showInLegend: true,
data: [
{
name: "London",
lat: 51.507222,
lon: -0.1275
},
{
name: "Birmingham",
lat: 52.483056,
lon: -1.893611
}
]
}
]
};
return (
<HighchartsReact
highcharts={Highcharts}
options={options}
constructorType={"mapChart"}
/>
);
}
}
and this is a customLabel component as an example:
import React, { Component } from "react";
class CustomLabel extends Component {
render() {
return (
<div>
{/* Doesn't show this Division (actually doesn't apply the style ...) */}
<div
style={{ BackgroundColor: "red", width: "10px", height: "10px" }}
></div>
<span>{this.props.name}</span>
<br />
{/* Doesn't show the red bullet inside the text */}
<Badge color="#f50" text={this.props.name} />
</div>
);
}
}
export default CustomLabel;
How can I customize the data label in highcharts? actually I want to use a custom component as the label.
Use ReactDOMServer and renderToStaticMarkup or renderToString method in formatter function for data labels:
dataLabels: {
enabled: true,
formatter: function () {
return renderToStaticMarkup(<CustomLabel name={this.point.name} />);
}
}
Live demo: https://codesandbox.io/s/highcharts-react-demo-forked-40icn?file=/demo.jsx
Docs: https://reactjs.org/docs/react-dom-server.html
API Reference: https://api.highcharts.com/highmaps/series.mapbubble.dataLabels.formatter
Or if you need to use some reactive logic inside CustomLabel take advantage of Portals in React.
Docs: https://reactjs.org/docs/portals.html
Example: https://www.npmjs.com/package/highcharts-react-official#how-to-add-react-component-to-a-charts-element
Related
I trying to make a COVID19 visualization site using Chart.js and VueJs
this is My App.vue that contains the API call and stores the data into arrays
<template>
<div id="app" class="container">
<div class="row mt-5" v-if="PositiveCases.length > 0">
<div class="col">
<h2>Positives</h2>
<lineChart :chartData="PositiveCases" :options="chartOptions" label="Positive" />
</div>
</div>
</div>
</template>
<script>
import axios from "axios";
import moment from 'moment'
import lineChart from "./components/lineChart.vue";
export default {
name: 'App',
components: {
lineChart
},
data(){
return{
PositiveCases : [],
Deaths: [],
Recoverd: [],
chartOptions: {
responsive: true,
maintainAspectRatio: false
}
}
},
async created(){
const {data} = await axios.get('https://api.covid19api.com/live/country/egypt')
//console.log(data);
data.forEach(d => {
const date = moment(d.Date,"YYYYMMDD").format("MM/DD")
const {Confirmed,Deaths,Recovered} = d
this.PositiveCases.push({date, total : Confirmed})
this.Deaths.push({date, total : Deaths})
this.Recoverd.push({date, total : Recovered})
// console.log("PositiveCases",this.PositiveCases);
// console.log("Deaths",this.Deaths);
// console.log("Recoverd",this.Recoverd);
});
}
}
</script>
and this is my lineChart.vue that contains the Line chart code the data stored correctly in both the dates and totals
<script>
import {Line} from 'vue-chartjs'
export default {
extends: Line,
props: {
label:{
type: String,
},
chartData:{
type: Array,
},
options:{
type: Object,
}
},
mounted(){
const dates = this.chartData.map(d => d.date).reverse()
const totals = this.chartData.map(d => d.total).reverse()
console.log("dates",dates);
console.log("totals",totals);
this.renderChart({
labels: dates,
datasets: [{
label: this.label,
data: totals,
}],
},this.options
)
}
}
</script>
the error in the console says
want to know what is the solution, all the data are stored correctly in both files
You are using V4 of vue-chart.js, the chart creation process has been changed as you can read here in the migration guide.
So instead of calling this.renderChart which was the old syntax you now have to use the actual component and pass the data to it like so:
<template>
<Bar :chart-data="chartData" />
</template>
<script>
// DataPage.vue
import { Bar } from 'vue-chartjs'
import { Chart, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js'
Chart.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
export default {
name: 'BarChart',
components: { Bar },
data() {
return {
chartData: {
labels: [ 'January', 'February', 'March'],
datasets: [
{
label: 'Data One',
backgroundColor: '#f87979',
data: [40, 20, 12]
}
]
}
}
}
}
</script>
I am trying to render a Line chart using react-chartjs-2. Right now it works fine when I pass in static props to the Line Component. But, when I fetch data from an API, set it to a variable, then pass that variable as the prop. My Line Component is not re-rendering with the new data.
I am logging the props being passed into the Line Component and I can see it first arrives as null and then I receive the good data from the API. So it looks like the Line Component is not re-rendering after receiving the props? I am probably doing this wrong. Please help.
import React from "react";
import { Line } from "react-chartjs-2";
export default class ExpenseChart extends React.Component {
constructor(props) {
super(props);
this.state = {
marketData: [100, 200, 300],
chartData: {
labels: this.props.monthNames,
datasets: [
{
backgroundColor: "rgba(142, 243, 197, 0.5)",
pointBackgroundColor: "#fff",
pointHoverBackgroundColor: '#fff',
pointStyle: "circle",
label: "Monthly Expenses",
borderColor: "#2be1d8",
borderWidth: 3,
borderJoinStyle: "round",
lineTension: 0.3,
fontColor: "#fff",
hitRadius: 5,
hoverRadius: 8,
radius: 4,
data: this.props.monthExpenses
},
],
},
};
}
render() {
console.log("why no names", this.props.monthNames)
return (
<div className="expenseChart">
<h2 className="expenseChart__name">{this.props.graphname}</h2>
<Line
data={this.state.chartData}
options={{
maintainAspectRatio: false,
responsive: true,
aspectRatio: 3,
scales: {
yAxes: [
{
ticks: {
display: false,
},
},
],
},
layout: {
padding: {
right: 10,
},
},
}}
/>
</div>
);
}
}
And then the parent component is connected to a redux store and it looks like this:
import React from "react";
import { connect } from 'react-redux';
import ExpenseChart from "../elements/ExpenseChart";
import { fetchExpenses } from '../../actions/index';
class Dashboard extends React.Component {
componentDidMount() {
this.props.dispatch(fetchExpenses());
}
render() {
let labels = this.props.months && this.props.months;
return (
<main className="dashboard">
<ExpenseChart
monthNames={labels}
monthExpenses={["123", "123", "12312", "12341", "231231", "1231", "1231"]}
// I am receiving monthExpenses props into the ExpenseChart component
// but not monthNames
/>
</main>
);
}
}
const mapStateToProps = state => ({
auth: state.app.auth,
months: state.app.months,
});
export default connect(mapStateToProps)(Dashboard);
I've done something similar before, fetching data from an API and passing it as props to the Line Component. But only difference is I am using redux here. And obviously, this time the Line Component is not receiving the good data.
The issue might be on this componentDidMount code.
componentDidMount() {
this.props.dispatch(fetchExpenses());
}
As per docs on dispatch
action (Object†): A plain object describing the change that makes
sense for your application. Actions are the only way to get data into
the store, so any data, whether from the UI events, network callbacks,
or other sources such as WebSockets needs to eventually be dispatched
as actions. Actions must have a type field that indicates the type of
action being performed. Types can be defined as constants and imported
from another module. It's better to use strings for type than Symbols
because strings are serializable. Other than type, the structure of an
action object is really up to you. If you're interested, check out
Flux Standard Action for recommendations on how actions could be
constructed.
Here fetchExpenses might be returning a promise and in that case you might need
fetchExpenses().then(apiRes => dipatch({type: "ACTION_TYPE": payload: apiRes}))
Another approach can be to use redux-thunk
Ok I solved this just by checking if the data I was receiving was not null before passing it as props. Which is what I thought this line would do let labels = this.props.months && this.props.months;
<main className="dashboard">
{ this.props.months != null ? <ExpenseChart monthNames={labels}/> : ''; }
</main>
I can load and render custom fonts for text in general, but when i try to specify the font Family for my navigation bar, i get an error: "fontfamily 'poetsenone' is not a system font and has not been loaded through Font.Loadsync.
i've loaded the font in my root comopnent # app.js using Font.Loadasync.
// homescreen.js:
static navigationOptions = {
title: 'Scan',
tabBarOptions: {
labelStyle: {
fontSize: 20,
fontFamily: "poetsenone"
},
tabStyle: {
},
style: {
marginTop: 23,
backgroundColor: '#423D3D',
},
}
};
//app.js
export default class App extends React.Component {
componentDidMount() {
Font.loadAsync({
'poetsenone': require('./assets/fonts/poetsenone.ttf')
});
}
render() {
return (
<AppContainer />
);
}
}
Isn't app.js typically the top-level component? what's going on?
Remove the quotes around 'poetsenone' in loadAsync and it should work.
I am using chartist.js and I am using the chartist within reactjs component.
I am referring this http://gionkunz.github.io/chartist-js/examples.html#simple-pie-chart
chartist.js:
var Chartist = {
version:'0.9.5'
}
(function (window, document, Chartist) {
var options = {
labelInterpolationFnc: function(value) {
return value[0]
}
};
var responsiveOptions = [
['screen and (min-width: 640px)', {
chartPadding: 30,
labelOffset: 100,
labelDirection: 'explode',
labelInterpolationFnc: function(value) {
return value;
}
}],
['screen and (min-width: 1024px)', {
labelOffset: 80,
chartPadding: 20
}]
];
})();
Reactjs component:
import React, { Component } from 'react';
var data = {
labels: ['Bananas', 'Apples', 'Grapes'],
series: [20, 15, 40]
};
showPieChart(data){
new Chartist.Pie('.ct-chart', data, options, responsiveOptions);
}
class Chart extends Component {
render(){
return(
<div>
<div className="center">
{showPieChart}
</div>
</div>
)}
}
export default Chart;
Nothing is displayed on web page. How can I access vanilla javascript inside react component.
Your question is a little bit misleading, and can be interpreted in two ways.
#1. If you're asking how to integrate Chartist library with React, here's how you can do it:
There's a wrapper library, that already did it for us: https://www.npmjs.com/package/react-chartist
You can use it as follow (example taken from their repo):
import React from 'react';
import ReactDOM from 'react-dom';
import ChartistGraph from 'react-chartist';
class Pie extends React.Component {
render() {
var data = {
labels: ['W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'W7', 'W8', 'W9', 'W10'],
series: [
[1, 2, 4, 8, 6, -2, -1, -4, -6, -2]
]
};
var options = {
high: 10,
low: -10,
axisX: {
labelInterpolationFnc: function(value, index) {
return index % 2 === 0 ? value : null;
}
}
};
var type = 'Bar'
return (
<div>
<ChartistGraph data={data} options={options} type={type} />
</div>
)
}
}
ReactDOM.render(<Pie />, document.body)
#2. If you generally asking how to integrate other libraries into React, then I recommend you to check the official React docs, because there's a really good tutorial about the topic - Integrating with Other Libraries
So, if you don't want to use the wrapper library (react-chartist), then you can check its main component too. It's a great starting point (that follows React recommendations) to understand how to create your own wrapper: https://github.com/fraserxu/react-chartist/blob/master/index.js
I'm using Highchart inside React application. I want to make animated effect for Highcart.
For instance, it's to show the progress of uploading other data. I pass the progress via this.props.progress. However, I cannot pass props to data property in Highchart.
Is it possible to make an animated effect with updating data? What could be the best practice?
Highchart.js
class Highchart extends React.Component {
static propTypes = {
data: React.PropTypes.array,
text: React.PropTypes.string,
colors: React.PropTypes.array,
size: React.PropTypes.any,
bgcolor: React.PropTypes.string,
width: React.PropTypes.number
}
constructor (props) {
super(props)
this.state = {
uuid: uuid()
}
}
componentDidMount () {
Highcharts.chart(this.state.uuid, {
chart: {
renderTo: 'container',
type: 'pie',
width: this.props.width,
backgroundColor: this.props.bgcolor
},
title: {
text: this.props.text,
useHTML: true,
verticalAlign: 'middle',
floating: true
},
plotOptions: {
pie: {
shadow: false,
allowPointSelect: false,
size: '100%',
dataLabels: { enabled: false }
},
series: {
states: {
hover: {
enabled: false
}
}
}
},
tooltip: { enabled: false },
credits: { enabled: false },
colors:this.props.colors,
series: [{
data: this.props.data,
size: this.props.size,
innerSize: '90%',
showInLegend:false,
dataLabels: {
enabled: false
}
}]
})
}
render () {
return (
<div id={this.state.uuid} className='high-chart' />
)
}
}
export default Highchart
ProgressMeter.js
import React from 'react'
import Highchart from 'components/Highchart'
class ProgressMeter extends React.Component {
static propTypes = {
progress: React.PropTypes.number,
}
render () {
return (
<div
className='signup-percents-meter'
>
<DonutChart data={[this.props.progress, 100-this.props.progress]}
//this code does not work.
colors={['#ee382a', '#eaeaea']}
/>
</div>
)
}
}
export default ProgressMeter
Changing only options will not work for Highcharts - those options are used once for generating chart. If you want to change those options you should call chart.update with new options or a more dedicated update e.g. for series data you can use series.setData.
How others are handling this:
Official Highcharts React wrapper highcharts-react is using chart.update - the relevant code line.
Third party react-highcharts is rebuilding Highcharts chart using new options. It's less optimal, but generally a more secure approach for them (it's third party code, so in case of any bugs they need to wait for the bug to be resolved). Relevant code: call renderChart on updates and creating the chart in the renderChart.
Third party react-highcharts-wrapper also rebuilds a chart on update - here is explained why.
About animation:
When chart is rebuild (created anew) initial animation runs (unless otherwise specified in chart's animation option) and for dynamic updates like chart.update chart is redrawn with animation enabled by default.