Skip to content

Commit

Permalink
feat(stackedColumn): adds stacked column chart (#31)
Browse files Browse the repository at this point in the history
* Stacked column chart

* Normal vs. percentage stacking

* Make syntax more like JSON syntax

* Improved syntax

* Hover behaviour
  • Loading branch information
micapam authored Apr 11, 2017
1 parent 2f5dd42 commit 0a326f9
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/components/widgets/stackedColumn/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {default} from './stackedColumn_widget';
138 changes: 138 additions & 0 deletions src/components/widgets/stackedColumn/stackedColumn_dataHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@

// todo - not import Highcharts again
import Highcharts from 'highcharts';
import last from 'lodash/last';


export const makeChartOptions = ({
emitSetState = () => {},
widget,
}) => {

const categories = Highcharts.getOptions().lang.shortMonths;

return {
// default column options
chart: {
type: 'column',
events: {
load: function() { // equivalent to constructor callback
var seriesData = this.series[0].data;//this is series data
seriesData.forEach((d, idx) => {
if (d.y === null) { //find null value in series
// adds plot band
this.xAxis[0].addPlotBand({
from: idx -.5, // point back
to: idx + .5, // point after
color: 'url(#diagonal-stripe-1)', // this color represents the null value region
});
}
});

let customLegendData = this.series.map(s => {
const lastData = last(s.data);
return {
// key: lastData.category,
key: s.name,
y: lastData.y,
seriesName: s.name,
color: lastData.color,
}
});
emitSetState({'customLegend': customLegendData});
},
},
},
title: {
text: widget.title,
align: 'left',
},
subtitle: {
useHTML: true,
text: `<span>Last updated <time dateTime="${widget.dateUpdated}">${widget.dateUpdated}</time></span>`,
align: 'left',
},
plotOptions: {
column: {
stacking: widget.stacking || 'normal'
},
series: {
animation: false,
point: {
events: {
mouseOver: function() {
const sliceIdx = this.index;
const chartSeries = this.series.chart.series
const customLegendData = chartSeries.map((s, i) => {
const sliceData = s.data[sliceIdx];
sliceData.setState('hover');
return {
key: s.name,
y: sliceData.y,
seriesName: s.category,
color: sliceData.color
}
});
emitSetState({'customLegend': customLegendData});
},
mouseOut: function() {
const sliceIdx = this.index
this.series.chart.series.forEach((s, i) => {
s.data[sliceIdx].setState('');
});
}
}
},
states: {
hover: {
color: 'yellow',
},
select: { // required because it can be selected programatically
enabled: false
}
},
allowPointSelect: false
},
},

// instance props
xAxis: {
labels: {
formatter: function () {
return categories[this.value] + ' 2017';
},
},
},
yAxis: {
title: {
text: null
},
// labels: {
// formatter: function() {
// return this.value + ' (units)';
// }
// }
},
series: [
{
name: "NSW",
data: [29.9, 71.5, 106.4, null, null, 176, 135, 148.5, 216.4, null, 95.6, 54.4]
},
{
name: "VIC",
data: [12.9, 65.5, 98.4, null, null, 45, 75, 34.2, 141, null, 89, 12]
},
{
name: "TAS",
data: [5, 5, 4, null, null, 154, 12, 89, 92, null, 145, 132]
},
{
name: "WA",
data: [29.9, 71.5, 106.4, null, null, 176, 135, 148.5, 216.4, null, 95.6, 54.4]
},
],
tooltip: {
enabled: false,
},
};
};
50 changes: 50 additions & 0 deletions src/components/widgets/stackedColumn/stackedColumn_widget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React, {PureComponent} from 'react';
import last from 'lodash/last';
import Chart from './../../chart';
import {makeChartOptions} from './stackedColumn_dataHelpers';
import Legend from './../../customLegend';

/**
* Renders a Stacked Column Widget with its surrounding state.
*/
class StackedColumnWidget extends PureComponent {
constructor(props) {
super(props);
this.proxiedSetState = this.proxiedSetState.bind(this);

this.chartInstance = null;
this.state = {
customLegend: []
};
}

proxiedSetState(state) {
this.setState(state);
}

render() {
const {customLegend} = this.state;

const chartOptions = makeChartOptions({
emitSetState: this.proxiedSetState,
widget: this.props.widget
});

return (
<article className={`chart--column`} role="article">
<section>
<Chart ref={el => this.chartInstance = el}
options={chartOptions}
callback={this.chartCallback}>
<div>
{customLegend && customLegend.length && <Legend data={customLegend} />}
</div>
</Chart>
</section>
</article>
)
}
}

export default StackedColumnWidget;

19 changes: 19 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {render} from 'react-dom';
import ColumnWidget from './components/widgets/column'
import DonutWidget from './components/widgets/donut'
import LineWidget from './components/widgets/line';
import StackedColumnWidget from './components/widgets/stackedColumn'


/*
Expand Down Expand Up @@ -40,6 +41,16 @@ const donutWidget = {
dateUpdated: '22 Feb 2016',
};

const stackedColumnWidgetNormal = {
title: 'Page views by state (normal stacking)',
dateUpdated: '22 Feb 2016'
}

const stackedColumnWidgetPercentage = {
title: 'Page views by state (percentage stacking)',
dateUpdated: '22 Feb 2016',
stacking: 'percent'
}

render(
<div>
Expand All @@ -66,6 +77,14 @@ render(

<DonutWidget widget={donutWidget} />

<br />

<StackedColumnWidget widget={stackedColumnWidgetNormal} />

<br />

<StackedColumnWidget widget={stackedColumnWidgetPercentage} />

</div>, document.getElementById('root')
);

0 comments on commit 0a326f9

Please sign in to comment.