Skip to content

Commit

Permalink
feat(column): column features like null data layer, legend (#20)
Browse files Browse the repository at this point in the history
* feat(column): fix up legend like line chart

* resolve conflict

* docs(charts): leave comments

* feat(nullColumn): adds null column chart

* feat(column): adds null data layer for column chart

* feat(column): adds null data layer +1

* style(column): update comment

* fix(column): remove year from category date for now

* style(column): +1 comment

* feat(column): axis labelling

* feat(column): generate faux legend from Highcharts to simplify column widget
  • Loading branch information
elisechant authored Apr 2, 2017
1 parent 1ca67e7 commit ff8e7fe
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 111 deletions.
1 change: 0 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ module.exports = {
"__DEV__": true
},
"rules": {
"eqeqeq": 0
}
};
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ For more information please contact the [Performance Dashboards Team](mailto:per

## Contributions

Fork and review model for contributions please.
Fork and review for contributions please.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"test:ci": "npm run test -- --coverage --runInBand",
"docs-server": "styleguidist server",
"docs-build": "styleguidist build",
"deploy-docs": "npm run docs-build && surge -p docs/ -d datavizkit.surge.sh"
"docs-deploy": "npm run docs-build && surge -p docs/ -d datavizkit.surge.sh"
},
"jest": {
"collectCoverageFrom": [
Expand Down
29 changes: 16 additions & 13 deletions src/components/chart.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@

/*
todo:
* handle chart updates: http://stackoverflow.com/questions/42848218/using-chart-update-on-a-chart-using-renderto/42850049#42850049
*/

import React, {PureComponent} from 'react';
import Highcharts from 'highcharts';

Expand All @@ -7,10 +15,7 @@ import {onNextFrame} from './../utils/DOM';

const BASE_CHART_OPTIONS = {
title: {
style: {
visibility: 'hidden',
height: 0,
}
text: null,
},
yAxis: {
title: {
Expand Down Expand Up @@ -61,19 +66,17 @@ const ChartFactory = (_Highcharts) => {
this.chart = null;
this.el = null;
this.Highcharts = _Highcharts;
this.firstRendered = false;
}

componentDidMount() {
const chartOptions = {...BASE_CHART_OPTIONS, ...this.props.options};
chartOptions.chart.renderTo = this.el;
this.chart = onNextFrame(() => this.renderChart(
chartOptions,
this.firstRendered && this.props.callback
));
if (!this.firstRendered) {
this.firstRendered = true;
}
onNextFrame(() => {
this.chart = this.renderChart(
chartOptions,
this.props.callback
);
});
}

renderChart(options, callback) {
Expand All @@ -97,7 +100,7 @@ const ChartFactory = (_Highcharts) => {
}

Chart.defaultProps = {
callback: () => {}
callback: () => {},
};

return Chart;
Expand Down
Empty file.
11 changes: 11 additions & 0 deletions src/components/todo.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
* improve mounting. render first, then mount the data in lazily

* try catch support for rendering widget if chart rendering fails



stacking column

// 100% height:
plotOptions: {
column: {
stacking: 'percent'
}
}
57 changes: 46 additions & 11 deletions src/components/widgets/column/column_dataHelpers.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@

export const makeChartOptions = ({
onRender = () => {},
onPointUpdate = () => {}
}) => {
// todo - not import Highcharts again
import Highcharts from 'highcharts';


export const makeChartOptions = ({}) => {

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

return {
// default pie options
// default column options
chart: {
type: 'column',
events: {
render: onRender
load: function() {
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: '#c5d8f7', // this color represents the null value region
});
}
});
}
},
},
yAxis: {
Expand All @@ -17,11 +33,15 @@ export const makeChartOptions = ({
}
},
plotOptions: {
column: {},
series: {
animation: false,
point: {
events: {
mouseOver: onPointUpdate,
mouseOver: function() {
document.getElementById('tooltip').innerHTML = 'TOOLTIP: <br/>' +
this.category + ' ' + this.color + ' ' + this.y + ' ' + this.series.name;
}
}
},
states: {
Expand All @@ -38,13 +58,28 @@ export const makeChartOptions = ({

// instance props
xAxis: {
categories: ["Jan 2017","Feb 2017","Mar 2017","Apr 2017","May 2017","Jun 2017","Jul 2017","Aug 2017","Sep 2017","Oct 2017","Nov 2017","Dec 2017"]
labels: {
formatter: function () {
return categories[this.value] + ' 2017';
},
},
},
yAxis: {
title: null,
// labels: {
// formatter: function() {
// return this.value + ' (units)';
// }
// }
},
series: [
{
"name": "Desktop",
"data": [29.9, 71.5, 106.4, 129.2, 144, 176, 135, 148.5, 216.4, 194.1, 95.6, 54.4]
}
name: "Desktop",
data: [29.9, 71.5, 106.4, null, null, 176, 135, 148.5, 216.4, null, 95.6, 54.4]
},
],
tooltip: {
enabled: false,
},
};
};
99 changes: 23 additions & 76 deletions src/components/widgets/column/column_widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,30 @@
todo
* can have a null data layer
* can have a pattern for high contrast mode
* can include units
* can support column groups
* can have a pattern for null data layer - http://www.highcharts.com/plugin-registry/single/9/Pattern-Fill
*
* include units - use legend.labelFormatter etc
* can include units yAxis: {
format: '${value}'
},
http://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-pointplacement-numeric/
* styled like dashboards
* can support column groups - like line chart
*
* can update chart after first render
*/


import React, {PureComponent} from 'react';
import Emitter from 'tiny-emitter';
import last from 'lodash/last';

import Chart from './../../chart';
import {makeChartOptions} from './column_dataHelpers';


const emitter = new Emitter();

/**
* Renders a Column Widget with it's surrounding state.
*
Expand All @@ -30,93 +35,35 @@ class ColumnWidget extends PureComponent {

constructor(props) {
super(props);
this.chartInstance = null;

emitter.on('set:state', this.manualSetState.bind(this, arguments));

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

manualSetState(self, stateToSet) {
// todo set on next tick
this.setState(stateToSet);
}

onRenderChart() {
// scoped to chart
const fauxLegend = this.series.map(s => {
const latestPointInSeries = last(s.data);
return {
color: latestPointInSeries.color,
key: s.name,
value: latestPointInSeries.y
}
});
emitter.emit('set:state', {'fauxLegend': fauxLegend});
}
chartCallback() {
// scoped to instance

onRenderedChartOnce() {
// "hover" over the last column
const lastIdx = this.chartInstance.series[0].data.length - 1;
this.chartInstance.series[0].data[lastIdx].onMouseOver();
}

onPointUpdate() {
// scoped to point
emitter.emit('set:state', {
'fauxLegend':[
{
seriesName: this.series.name,
color: this.color,
// key: s.name,
key: this.category,
value: this.y
}
]
});
const lastCol = last(this.series[0].data);
if (lastCol) {
lastCol.onMouseOver && lastCol.onMouseOver();
}
}

render() {
const {widget: {title, dateUpdated}} = this.props;
const {fauxLegend} = this.state;
const datetimeUpdate = new Date(dateUpdated).toJSON();

// todo - improve this for update
const chartOptions = makeChartOptions({
onRender: this.onRenderChart,
callback: this.onRenderedChartOnce,
});
const chartOptions = makeChartOptions({});

return (
<article className={`chart--column`} role="article">
<header>
<h1>{title}</h1>
<span>Last updated <time dateTime={datetimeUpdate}>{dateUpdated}</time></span>
{/*<span>What is this?</span>*/}
</header>
<section>
<Chart ref={el => this.chartInstance = el} options={chartOptions} />
{fauxLegend.length && <div className="legend">
<table>
<tbody>
{fauxLegend.map((item, idx) => (
<tr key={idx}>
<td>
<svg width="12" height="12">
<g className="legend--icon">
{item.color && <rect x="0" y="0" width="12" height="12" fill={item.color} visibility="visible" rx="6" ry="6"></rect>}
</g>
</svg>
<span className="legend--data-name">{item.key}</span>
</td>
<td>{item.value}</td>
</tr>
))}
</tbody>
</table>
</div>}
<Chart ref={el => this.chartInstance = el} options={chartOptions}
callback={this.chartCallback} />
<div id="tooltip"></div>
</section>
</article>
)
Expand Down
4 changes: 3 additions & 1 deletion src/components/widgets/donut/donut_widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* can have a pattern for high contrast mode
* guard against sectors that are too small. if i have more than said number of items, combine smallest ones in to an "Other" sector - enforce by a data transformation step
* can display units
* styled like dashboards
*/

Expand Down Expand Up @@ -62,7 +63,7 @@ class DonutWidget extends PureComponent {

// todo - improve this for update
const chartOptions = makeChartOptions({
onRender: this.onRenderChart
onRender: this.onRenderChart,
});

return (
Expand All @@ -73,6 +74,7 @@ class DonutWidget extends PureComponent {
{/*<span>What is this?</span>*/}
</header>
<section>
{fauxLegend.length && <p>{fauxLegend[0].category}</p>}
<Chart ref={el => this.chartInstance = el} options={chartOptions} />
{fauxLegend.length && <div className="legend">
<table>
Expand Down
20 changes: 17 additions & 3 deletions src/components/widgets/line/line_dataHelpers.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@

import Highcharts from 'highcharts';


export const makeChartOptions = ({
onRender = () => {},
onPointMouseOver = () => {},
}) => {
return {
// default pie options
// default options
chart: {
type: 'line',
events: { // todo - abstract
events: {
render: onRender
},
},
xAxis: {
crosshair: true
crosshair: true,
// type: 'datetime', // todo - format x labels to datetime
// Format 24 hour time to AM/PM
// dateTimeLabelFormats: {
// hour: '%I:%M %P',
// minute: '%I %M'
// },
// labels: {
// formatter: function() {
// return Highcharts.dateFormat('%I:%M %P', this.value);
// }
// }
},
plotOptions: {
line: {
Expand Down
2 changes: 1 addition & 1 deletion src/components/widgets/line/line_widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
todo
* can have a pattern for high contrast mode
* styled like dashboards
*/

Expand Down
Loading

0 comments on commit ff8e7fe

Please sign in to comment.