The React Easy Line chart
Introduction
A line chart or line graph is a type of chart which displays information as a series of data points called 'markers' connected by straight line segments.(ref)
Data
At the most basic the line chart can just take a single data file supplied in a JSON format and will render a simple line chart.
The format of the data is an array of arrays which allows multiple lines to be generated. The x field represents the x axis and the y the y axis. This is to unify the data across R2-D3 charts.
<LineChart
data={[
[
{ x: 1, y: 20 },
{ x: 2, y: 10 },
{ x: 3, y: 25 }
]
]}
/>
If a second line is needed then this is easily added by adding a new data array to the existing array. The number of lines drawn is infinite but only coloured up to 4 lines.
<LineChart
data={[
[
{ x: 1, y: 20 },
{ x: 2, y: 10 },
{ x: 3, y: 25 }
], [
{ x: 1, y: 10 },
{ x: 2, y: 12 },
{ x: 3, y: 4 }
]
]}
/>
Height and Width
The height and width can be easily set by passing in a numeric y in as a prop.
<LineChart
width={50}
height={50}
data={[
[
{ x: 1, y: 20 },
{ x: 2, y: 10 },
{ x: 3, y: 25 }
], [
{ x: 1, y: 10 },
{ x: 2, y: 12 },
{ x: 3, y: 4 }
]
]}
/>
Margin
The Margin can be overridden by passing in a margin object. The margin object must define the following: top, right, bottom and left.
This can be particulary useful if a label is cut off.
<LineChart
margin={{top: 0, right: 0, bottom: 30, left: 100}}
width={250}
height={250}
data={[
[
{ x: 1, y: 20 },
{ x: 2, y: 10 },
{ x: 3, y: 25 }
], [
{ x: 1, y: 10 },
{ x: 2, y: 12 },
{ x: 3, y: 4 }
]
]}
/>
Axes
The axes can be turned on by simply passing a boolean flag to true for axes.
<LineChart
axes
width={250}
height={250}
data={[
[
{ x: 1, y: 20 },
{ x: 2, y: 10 },
{ x: 3, y: 25 }
], [
{ x: 1, y: 10 },
{ x: 2, y: 12 },
{ x: 3, y: 4 }
]
]}
/>
Axes labels
The axisLabels can be overridden by simply passing axisLabels object with both a x and y y.
<LineChart
axes
margin={{top: 10, right: 10, bottom: 50, left: 50}}
axisLabels={{x: 'My x Axis', y: 'My y Axis'}}
width={250}
height={250}
data={[
[
{ x: 1, y: 20 },
{ x: 2, y: 10 },
{ x: 3, y: 25 }
], [
{ x: 1, y: 10 },
{ x: 2, y: 12 },
{ x: 3, y: 4 }
]
]}
/>
Y Axis orientation
The Y axis can be placed on the right hand side by passing a boolean flag to true for yAxisOrientRight.
<LineChart
axes
axisLabels={{x: 'My x Axis', y: 'My y Axis'}}
yAxisOrientRight
width={450}
height={250}
data={[
[
{ x: 1, y: 20 },
{ x: 2, y: 10 },
{ x: 3, y: 25 }
], [
{ x: 1, y: 10 },
{ x: 2, y: 12 },
{ x: 3, y: 4 }
]
]}
/>
Interpolate (Making the lines smooth)
The Lines drawn can be set to be interpolated by passing in an interpolated param. By default this is set to linear. We can though override this for instance to make a cardinal line. The options that can be chosen can be foundhere under the interpolate section.
<LineChart
axes
margin={{top: 10, right: 10, bottom: 50, left: 50}}
axisLabels={{x: 'My x Axis', y: 'My y Axis'}}
width={250}
interpolate={'cardinal'}
height={250}
data={[
[
{ x: 1, y: 20 },
{ x: 2, y: 10 },
{ x: 3, y: 25 }
], [
{ x: 1, y: 10 },
{ x: 2, y: 12 },
{ x: 3, y: 4 }
]
]}
/>
xType & yType
The data passed associated to the particular axes can be in numeric, date (the default format is for example 1-Jan-15 but can be overridden) or textual formats (used for labelling).
For the example below the data for the x is text and so the xType needs to be changed to text.
<LineChart
xType={'text'}
axes
width={350}
height={250}
interpolate={'cardinal'}
data={[
[
{ x: 'Mon', y: 20 },
{ x: 'Tue', y: 10 },
{ x: 'Wed', y: 33 },
{ x: 'Thu', y: 45 },
{ x: 'Fri', y: 15 }
], [
{ x: 'Mon', y: 10 },
{ x: 'Tue', y: 15 },
{ x: 'Wed', y: 13 },
{ x: 'Thu', y: 15 },
{ x: 'Fri', y: 10 }
]
]}
/>
Setting the xType to be time.
<LineChart
xType={'time'}
axes
interpolate={'cardinal'}
width={750}
height={250}
data={[
[
{ x: '1-Jan-15', y: 20 },
{ x: '1-Feb-15', y: 10 },
{ x: '1-Mar-15', y: 33 },
{ x: '1-Apr-15', y: 45 },
{ x: '1-May-15', y: 15 }
], [
{ x: '1-Jan-15', y: 10 },
{ x: '1-Feb-15', y: 15 },
{ x: '1-Mar-15', y: 13 },
{ x: '1-Apr-15', y: 15 },
{ x: '1-May-15', y: 10 }
]
]}
/>
Setting the yType to be text. (The yDomainRange has also been set to keep the range order.)
<LineChart
yType={'text'}
xType={'text'}
axes
margin={{top: 0, right: 0, bottom: 100, left: 100}}
yDomainRange={['Allot', 'Middle', 'Less']}
interpolate={'cardinal'}
width={350}
height={250}
data={[
[
{ x: 'Mon', y: 'Little' },
{ x: 'Tue', y: 'Perfect' },
{ x: 'Wed', y: 'Allot' },
{ x: 'Thu', y: 'Little' },
{ x: 'Fri', y: 'Perfect' }
]
]}
/>
Setting the yType to be time
<LineChart
axisLabels={{x: 'Total Revenue', y: 'January'}}
margin={{top: 10, right: 30, bottom: 50, left: 70}}
yType={'time'}
axes
width={500}
height={500}
data={[
[
{ x: 10, y: '1-Jan-15' },
{ x: 20, y: '10-Jan-15' },
{ x: 40, y: '21-Jan-15' },
{ x: 80, y: '31-Jan-15' }
], [
{ x: 0, y: '1-Jan-15' },
{ x: 15, y: '10-Jan-15' },
{ x: 20, y: '21-Jan-15' },
{ x: 25, y: '31-Jan-15' }
]
]}
/>
Grid
A grid can be added to the graph by just passing in a boolean.
<LineChart
axisLabels={{x: 'Total Revenue', y: 'January'}}
margin={{top: 10, right: 30, bottom: 50, left: 70}}
yType={'time'}
axes
grid
width={500}
height={500}
data={[
[
{ x: 10, y: '1-Jan-15' },
{ x: 20, y: '10-Jan-15' },
{ x: 40, y: '21-Jan-15' },
{ x: 80, y: '31-Jan-15' }
], [
{ x: 0, y: '1-Jan-15' },
{ x: 15, y: '10-Jan-15' },
{ x: 20, y: '21-Jan-15' },
{ x: 25, y: '31-Jan-15' }
]
]}
/>
Vertical Grid
A vertical grid can be added to the graph by just passing in a boolean for verticalGrid.
<LineChart
xType={'time'}
axes
grid
verticalGrid
interpolate={'cardinal'}
width={750}
height={250}
data={[
[
{ x: '1-Jan-15', y: 20 },
{ x: '1-Feb-15', y: 10 },
{ x: '1-Mar-15', y: 33 },
{ x: '1-Apr-15', y: 45 },
{ x: '1-May-15', y: 15 }
], [
{ x: '1-Jan-15', y: 10 },
{ x: '1-Feb-15', y: 15 },
{ x: '1-Mar-15', y: 13 },
{ x: '1-Apr-15', y: 15 },
{ x: '1-May-15', y: 10 }
]
]}
/>
Domain Range
By default the axis ranges are automatically calculated based on the smallest and the largest ys.
The range can be fixed by passing an array param of 2 numbers for the particular axis. The first number is the bottom of the range the second is the higher point of the range.
<LineChart
axes
xDomainRange={[0, 100]}
yDomainRange={[0, 100]}
margin={{top: 0, right: 0, bottom: 100, left: 100}}
width={250}
height={250}
interpolate={'cardinal'}
data={[
[
{ x: 10, y: 25 },
{ x: 20, y: 10 },
{ x: 30, y: 25 },
{ x: 40, y: 10 },
{ x: 50, y: 12 },
{ x: 60, y: 25 }
], [
{ x: 10, y: 40 },
{ x: 20, y: 30 },
{ x: 30, y: 25 },
{ x: 40, y: 60 },
{ x: 50, y: 22 },
{ x: 60, y: 9 }
]
]}
/>
Tick display format
If the x or y axis has an xType/yType of time then a display for the axis can be overridden by setting the tickTimeDisplayFormat.
The options are very flexible and can be seen hereTime Formatting.
<LineChart
axisLabels={{x: 'Total Revenue', y: 'January'}}
margin={{top: 10, right: 30, bottom: 50, left: 70}}
yType={'time'}
axes
interpolate={'cardinal'}
tickTimeDisplayFormat={'%a'}
width={500}
height={500}
data={[
[
{ x: 10, y: '1-Jan-15' },
{ x: 20, y: '10-Jan-15' },
{ x: 40, y: '21-Jan-15' },
{ x: 80, y: '31-Jan-15' }
], [
{ x: 0, y: '1-Jan-15' },
{ x: 15, y: '10-Jan-15' },
{ x: 20, y: '21-Jan-15' },
{ x: 25, y: '31-Jan-15' }
]
]}
/>
Number of Ticks
The number of ticks on the x and y axis can be set by passing in a number to xTicks or yTicks. This can make the axis easier to read.
<LineChart
axes
xTicks={5}
yTicks={5}
xDomainRange={[0, 100]}
yDomainRange={[0, 100]}
width={500}
height={250}
interpolate={'cardinal'}
data={[
[
{ x: 10, y: 25 },
{ x: 20, y: 10 },
{ x: 30, y: 25 },
{ x: 40, y: 10 },
{ x: 50, y: 12 },
{ x: 60, y: 25 }
], [
{ x: 10, y: 40 },
{ x: 20, y: 30 },
{ x: 30, y: 25 },
{ x: 40, y: 60 },
{ x: 50, y: 22 },
{ x: 60, y: 9 }
]
]}
/>
Line Colors
The colours of the lines can be overridden easily. To do this we can pass in a lineColors array as a prop.
The following example would be to change the color of the 2 lines.
<LineChart
xType={'time'}
axes
grid
verticalGrid
interpolate={'cardinal'}
lineColors={['pink', 'cyan']}
width={750}
height={250}
data={[
[
{ x: '1-Jan-15', y: 20 },
{ x: '1-Feb-15', y: 10 },
{ x: '1-Mar-15', y: 33 },
{ x: '1-Apr-15', y: 45 },
{ x: '1-May-15', y: 15 }
], [
{ x: '1-Jan-15', y: 10 },
{ x: '1-Feb-15', y: 15 },
{ x: '1-Mar-15', y: 13 },
{ x: '1-Apr-15', y: 15 },
{ x: '1-May-15', y: 10 }
]
]}
/>
;
Data Points
Data points can be added to the line chart by simply passing a dataPoints boolean.
<LineChart
axes
dataPoints
xDomainRange={[0, 100]}
yDomainRange={[0, 100]}
width={500}
height={250}
interpolate={'cardinal'}
data={[
[
{ x: 10, y: 25 },
{ x: 20, y: 10 },
{ x: 30, y: 25 },
{ x: 40, y: 10 },
{ x: 50, y: 12 },
{ x: 60, y: 25 }
], [
{ x: 10, y: 40 },
{ x: 20, y: 30 },
{ x: 30, y: 25 },
{ x: 40, y: 60 },
{ x: 50, y: 22 },
{ x: 60, y: 9 }
]
]}
/>
Mouse handlers
The chart will send out a mouseOver event, mouseMove and mouseOut event from the dataPoints (see above). The dataPoints will need to be set. This can be used by your react application in anyway you would require. The event handlers provides the mouse event and the point data. The mouse event can for instance provide the x and y coordinates which can be used for a tool tip. The data is related to the point currently moused over.
mouseOverHandler(d, e) {
this.setState({
showToolTip: true,
top: `${e.screenY - 10}px`,
left: `${e.screenX + 10}px`,
y: d.y,
x: d.x});
}
mouseMoveHandler(e) {
if (this.state.showToolTip) {
this.setState({top: `${e.y - 10}px`, left: `${e.x + 10}px`});
}
}
mouseOutHandler() {
this.setState({showToolTip: false});
}
createTooltip() {
if (this.state.showToolTip) {
return (
<ToolTip
top={this.state.top}
left={this.state.left}
>
The x value is {this.state.x} and the y value is {this.state.y}
</ToolTip>
);
}
return false;
}
<LineChart
axes
dataPoints
grid
xDomainRange={[0, 100]}
yDomainRange={[0, 100]}
mouseOverHandler={this.mouseOverHandler}
mouseOutHandler={this.mouseOutHandler}
mouseMoveHandler={this.mouseMoveHandler}
width={700}
height={350}
interpolate={'cardinal'}
data={[
[
{ x: 10, y: 25 },
{ x: 20, y: 10 },
{ x: 30, y: 25 },
{ x: 40, y: 10 },
{ x: 50, y: 12 },
{ x: 60, y: 25 }
], [
{ x: 10, y: 40 },
{ x: 20, y: 30 },
{ x: 30, y: 25 },
{ x: 40, y: 60 },
{ x: 50, y: 22 },
{ x: 60, y: 9 }
]
]}
/>
Click Handler
The chart will send out a clickHandler event from the dataPoints (see above). The dataPoints will need to be set. This can be used by your react application in anyway you would require. The event handler provides the point data.
Updating the data
By selecting the button below to start the random data you can see a simulation of the performance if a data feed is passed in. React provides the functionality to only update the elements of the dom when required so should just change the line attributes. The data is passed in as a react param only and as soon as that data changes the chart will reflect that change automatically.
// this is generated randomly and updated randomly within a range of -20 to + 20
<LineChart
data={this.data}
datePattern={'%d-%b-%y %H:%M'}
xType={'time'}
width={this.state.componentWidth}
height={this.state.componentWidth / 2}
axisLabels={{x: 'Hour', y: 'Percentage'}}
interpolate={'cardinal'}
yDomainRange={[0, 100]}
axes
grid
style={{
'.line0': {
stroke: 'green'
}
}}
/>
Fluid
Because the width and height of the chart can be passed in by a param then changes to the size of a window or container can change the chart dynamically. If you shrink your browser window width you will see the charts below change in a fluid manor. You can also introduce basic break points such as removing the axes if below a certain width.
constructor(props) {
const initialWidth = window.innerWidth > 0 ? window.innerWidth : 500;
this.state = {showToolTip: false, windowWidth: initialWidth - 100};
}
componentDidMount() {
window.addEventListener('resize', this.handleResize.bind(this));
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
}
handleResize() {
this.setState({windowWidth: window.innerWidth - 100});
}
<LineChart
xType={'time'}
axes={(this.state.componentWidth) > 600 ? true : false}
xTicks={5}
yTicks={3}
grid
width={this.state.componentWidth}
height={this.state.componentWidth / 2}
tickTimeDisplayFormat={'%d %m'}
interpolate={'cardinal'}
data={[
[
{ x: '1-Jan-13', y: 8 },
{ x: '1-Feb-13', y: 17 },
{ x: '1-Mar-13', y: 17 },
{ x: '1-Apr-13', y: 25 },
{ x: '1-May-13', y: 20 }
], [
{ x: '1-Jan-13', y: 5 },
{ x: '1-Feb-13', y: 13 },
{ x: '1-Mar-13', y: 10 },
{ x: '1-Apr-13', y: 25 },
{ x: '1-May-13', y: 30 }
]
]}
/>