diff --git a/src/components/widgets/stackedColumn/index.js b/src/components/widgets/stackedColumn/index.js new file mode 100644 index 0000000..4698185 --- /dev/null +++ b/src/components/widgets/stackedColumn/index.js @@ -0,0 +1 @@ +export {default} from './stackedColumn_widget'; diff --git a/src/components/widgets/stackedColumn/stackedColumn_dataHelpers.js b/src/components/widgets/stackedColumn/stackedColumn_dataHelpers.js new file mode 100644 index 0000000..7dd3614 --- /dev/null +++ b/src/components/widgets/stackedColumn/stackedColumn_dataHelpers.js @@ -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: `Last updated `, + 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, + }, + }; +}; diff --git a/src/components/widgets/stackedColumn/stackedColumn_widget.js b/src/components/widgets/stackedColumn/stackedColumn_widget.js new file mode 100644 index 0000000..c95fcba --- /dev/null +++ b/src/components/widgets/stackedColumn/stackedColumn_widget.js @@ -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 ( +
+
+ this.chartInstance = el} + options={chartOptions} + callback={this.chartCallback}> +
+ {customLegend && customLegend.length && } +
+
+
+
+ ) + } +} + +export default StackedColumnWidget; + diff --git a/src/index.js b/src/index.js index c170455..ed7032b 100644 --- a/src/index.js +++ b/src/index.js @@ -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' /* @@ -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(
@@ -66,6 +77,14 @@ render( +
+ + + +
+ + +
, document.getElementById('root') );