Skip to content

Commit ebad2f3

Browse files
authored
Merge pull request #104 from Driftrock/data-argument-as-func
Add option to pass data as function that receives canvas node as argument
2 parents 7c2e81e + ea75b9d commit ebad2f3

File tree

7 files changed

+134
-58
lines changed

7 files changed

+134
-58
lines changed

README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import {Doughnut} from 'react-chartjs-2';
4848

4949
### Properties
5050

51-
* data: PropTypes.object.isRequired,
51+
* data: (PropTypes.object | PropTypes.func).isRequired,
5252
* width: PropTypes.number,
5353
* height: PropTypes.number,
5454
* legend: PropTypes.object,
@@ -87,6 +87,31 @@ render() {
8787
}
8888
```
8989

90+
### Getting context for data generation
91+
Canvas node and hence context, that can be used to create CanvasGradient background,
92+
is passed as argument to data if given as function:
93+
94+
This approach is useful when you want to keep your components pure.
95+
96+
```js
97+
render() {
98+
const data = (canvas) => {
99+
const ctx = canvas.getContext("2d")
100+
const gradient = ctx.createLinearGradient(0,0,100,0);
101+
...
102+
return {
103+
...
104+
backgroundColor: gradient
105+
...
106+
}
107+
}
108+
109+
return (
110+
<Line data={data} />
111+
)
112+
}
113+
```
114+
90115
### Chart.js Defaults
91116
Chart.js defaults can be set by importing the `defaults` object:
92117

dist/react-chartjs-2.js

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1973,13 +1973,22 @@ var ChartComponent = function (_React$Component) {
19731973
return true;
19741974
}
19751975

1976-
return !(0, _lodash2.default)(this.shadowDataProp, nextProps.data);
1976+
var nextData = this.transformDataProp();
1977+
return !(0, _lodash2.default)(this.shadowDataProp, nextData);
19771978
}
19781979
}, {
19791980
key: 'componentWillUnmount',
19801981
value: function componentWillUnmount() {
19811982
this.chart_instance.destroy();
19821983
}
1984+
}, {
1985+
key: 'transformDataProp',
1986+
value: function transformDataProp() {
1987+
var data = this.props.data;
1988+
1989+
var node = _reactDom2.default.findDOMNode(this);
1990+
return typeof data == "function" ? data(node) : data;
1991+
}
19831992

19841993
// Chart.js directly mutates the data.dataset objects by adding _meta proprerty
19851994
// this makes impossible to compare the current and next data changes
@@ -1989,28 +1998,27 @@ var ChartComponent = function (_React$Component) {
19891998
}, {
19901999
key: 'memoizeDataProps',
19912000
value: function memoizeDataProps() {
1992-
var data = this.props.data;
1993-
1994-
1995-
if (!data) {
2001+
if (!this.props.data) {
19962002
return;
19972003
}
19982004

2005+
var data = this.transformDataProp();
2006+
19992007
this.shadowDataProp = _extends({}, data, {
20002008
datasets: data.datasets && data.datasets.map(function (set) {
20012009
return _extends({}, set);
20022010
})
20032011
});
2012+
2013+
return data;
20042014
}
20052015
}, {
20062016
key: 'updateChart',
20072017
value: function updateChart() {
2008-
var _props2 = this.props,
2009-
data = _props2.data,
2010-
options = _props2.options;
2018+
var options = this.props.options;
20112019

20122020

2013-
this.memoizeDataProps();
2021+
var data = this.memoizeDataProps();
20142022

20152023
if (!this.chart_instance) return;
20162024

@@ -2023,6 +2031,11 @@ var ChartComponent = function (_React$Component) {
20232031
var currentDatasets = this.chart_instance.config.data && this.chart_instance.config.data.datasets || [];
20242032
var nextDatasets = data.datasets || [];
20252033

2034+
// Prevent charting of legend items that no longer exist
2035+
while (currentDatasets.length > nextDatasets.length) {
2036+
currentDatasets.pop();
2037+
}
2038+
20262039
nextDatasets.forEach(function (dataset, sid) {
20272040
if (currentDatasets[sid] && currentDatasets[sid].data) {
20282041
currentDatasets[sid].data.splice(nextDatasets[sid].data.length);
@@ -2052,16 +2065,14 @@ var ChartComponent = function (_React$Component) {
20522065
}, {
20532066
key: 'renderChart',
20542067
value: function renderChart() {
2055-
var _props3 = this.props,
2056-
data = _props3.data,
2057-
options = _props3.options,
2058-
legend = _props3.legend,
2059-
type = _props3.type,
2060-
redraw = _props3.redraw;
2068+
var _props2 = this.props,
2069+
options = _props2.options,
2070+
legend = _props2.legend,
2071+
type = _props2.type,
2072+
redraw = _props2.redraw;
20612073

20622074
var node = _reactDom2.default.findDOMNode(this);
2063-
2064-
this.memoizeDataProps();
2075+
var data = this.memoizeDataProps();
20652076

20662077
this.chart_instance = new _chart2.default(node, {
20672078
type: type,
@@ -2072,10 +2083,10 @@ var ChartComponent = function (_React$Component) {
20722083
}, {
20732084
key: 'render',
20742085
value: function render() {
2075-
var _props4 = this.props,
2076-
height = _props4.height,
2077-
width = _props4.width,
2078-
onElementsClick = _props4.onElementsClick;
2086+
var _props3 = this.props,
2087+
height = _props3.height,
2088+
width = _props3.width,
2089+
onElementsClick = _props3.onElementsClick;
20792090

20802091

20812092
return _react2.default.createElement('canvas', {
@@ -2090,7 +2101,7 @@ var ChartComponent = function (_React$Component) {
20902101
}(_react2.default.Component);
20912102

20922103
ChartComponent.propTypes = {
2093-
data: _react.PropTypes.object.isRequired,
2104+
data: _react.PropTypes.oneOfType([_react.PropTypes.object, _react.PropTypes.func]).isRequired,
20942105
getDatasetAtEvent: _react.PropTypes.func,
20952106
getElementAtEvent: _react.PropTypes.func,
20962107
getElementsAtEvent: _react.PropTypes.func,

dist/react-chartjs-2.min.js

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/index.js

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,22 @@ var ChartComponent = function (_React$Component) {
119119
return true;
120120
}
121121

122-
return !(0, _lodash2.default)(this.shadowDataProp, nextProps.data);
122+
var nextData = this.transformDataProp();
123+
return !(0, _lodash2.default)(this.shadowDataProp, nextData);
123124
}
124125
}, {
125126
key: 'componentWillUnmount',
126127
value: function componentWillUnmount() {
127128
this.chart_instance.destroy();
128129
}
130+
}, {
131+
key: 'transformDataProp',
132+
value: function transformDataProp() {
133+
var data = this.props.data;
134+
135+
var node = _reactDom2.default.findDOMNode(this);
136+
return typeof data == "function" ? data(node) : data;
137+
}
129138

130139
// Chart.js directly mutates the data.dataset objects by adding _meta proprerty
131140
// this makes impossible to compare the current and next data changes
@@ -135,28 +144,27 @@ var ChartComponent = function (_React$Component) {
135144
}, {
136145
key: 'memoizeDataProps',
137146
value: function memoizeDataProps() {
138-
var data = this.props.data;
139-
140-
141-
if (!data) {
147+
if (!this.props.data) {
142148
return;
143149
}
144150

151+
var data = this.transformDataProp();
152+
145153
this.shadowDataProp = _extends({}, data, {
146154
datasets: data.datasets && data.datasets.map(function (set) {
147155
return _extends({}, set);
148156
})
149157
});
158+
159+
return data;
150160
}
151161
}, {
152162
key: 'updateChart',
153163
value: function updateChart() {
154-
var _props2 = this.props,
155-
data = _props2.data,
156-
options = _props2.options;
164+
var options = this.props.options;
157165

158166

159-
this.memoizeDataProps();
167+
var data = this.memoizeDataProps();
160168

161169
if (!this.chart_instance) return;
162170

@@ -169,6 +177,11 @@ var ChartComponent = function (_React$Component) {
169177
var currentDatasets = this.chart_instance.config.data && this.chart_instance.config.data.datasets || [];
170178
var nextDatasets = data.datasets || [];
171179

180+
// Prevent charting of legend items that no longer exist
181+
while (currentDatasets.length > nextDatasets.length) {
182+
currentDatasets.pop();
183+
}
184+
172185
nextDatasets.forEach(function (dataset, sid) {
173186
if (currentDatasets[sid] && currentDatasets[sid].data) {
174187
currentDatasets[sid].data.splice(nextDatasets[sid].data.length);
@@ -198,16 +211,14 @@ var ChartComponent = function (_React$Component) {
198211
}, {
199212
key: 'renderChart',
200213
value: function renderChart() {
201-
var _props3 = this.props,
202-
data = _props3.data,
203-
options = _props3.options,
204-
legend = _props3.legend,
205-
type = _props3.type,
206-
redraw = _props3.redraw;
214+
var _props2 = this.props,
215+
options = _props2.options,
216+
legend = _props2.legend,
217+
type = _props2.type,
218+
redraw = _props2.redraw;
207219

208220
var node = _reactDom2.default.findDOMNode(this);
209-
210-
this.memoizeDataProps();
221+
var data = this.memoizeDataProps();
211222

212223
this.chart_instance = new _chart2.default(node, {
213224
type: type,
@@ -218,10 +229,10 @@ var ChartComponent = function (_React$Component) {
218229
}, {
219230
key: 'render',
220231
value: function render() {
221-
var _props4 = this.props,
222-
height = _props4.height,
223-
width = _props4.width,
224-
onElementsClick = _props4.onElementsClick;
232+
var _props3 = this.props,
233+
height = _props3.height,
234+
width = _props3.width,
235+
onElementsClick = _props3.onElementsClick;
225236

226237

227238
return _react2.default.createElement('canvas', {
@@ -236,7 +247,7 @@ var ChartComponent = function (_React$Component) {
236247
}(_react2.default.Component);
237248

238249
ChartComponent.propTypes = {
239-
data: _react.PropTypes.object.isRequired,
250+
data: _react.PropTypes.oneOfType([_react.PropTypes.object, _react.PropTypes.func]).isRequired,
240251
getDatasetAtEvent: _react.PropTypes.func,
241252
getElementAtEvent: _react.PropTypes.func,
242253
getElementsAtEvent: _react.PropTypes.func,

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-chartjs-2",
3-
"version": "2.0.1",
3+
"version": "2.0.4",
44
"description": "react-chartjs-2",
55
"main": "lib/index.js",
66
"author": "Goran Udosic",

src/index.js

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import isEqual from 'lodash.isequal';
55

66
class ChartComponent extends React.Component {
77
static propTypes = {
8-
data: PropTypes.object.isRequired,
8+
data: PropTypes.oneOfType([
9+
PropTypes.object,
10+
PropTypes.func
11+
]).isRequired,
912
getDatasetAtEvent: PropTypes.func,
1013
getElementAtEvent: PropTypes.func,
1114
getElementsAtEvent: PropTypes.func,
@@ -78,24 +81,35 @@ class ChartComponent extends React.Component {
7881
return true;
7982
}
8083

81-
return !isEqual(this.shadowDataProp, nextProps.data);
84+
const nextData = this.transformDataProp(nextProps)
85+
return !isEqual(this.shadowDataProp, nextData);
8286
}
8387

8488
componentWillUnmount() {
8589
this.chart_instance.destroy();
8690
}
8791

92+
transformDataProp(props) {
93+
const { data } = props;
94+
if (typeof(data) == "function") {
95+
const node = ReactDOM.findDOMNode(this);
96+
return data(node)
97+
} else {
98+
return data;
99+
}
100+
}
101+
88102
// Chart.js directly mutates the data.dataset objects by adding _meta proprerty
89103
// this makes impossible to compare the current and next data changes
90104
// therefore we memoize the data prop while sending a fake to Chart.js for mutation.
91105
// see https://github.com/chartjs/Chart.js/blob/master/src/core/core.controller.js#L615-L617
92106
memoizeDataProps() {
93-
const { data } = this.props;
94-
95-
if (!data) {
107+
if (!this.props.data) {
96108
return;
97109
}
98110

111+
const data = this.transformDataProp(this.props);
112+
99113
this.shadowDataProp = {
100114
...data,
101115
datasets: data.datasets && data.datasets.map(set => {
@@ -104,12 +118,14 @@ class ChartComponent extends React.Component {
104118
}
105119
})
106120
};
121+
122+
return data;
107123
}
108124

109125
updateChart() {
110-
const {data, options} = this.props;
126+
const {options} = this.props;
111127

112-
this.memoizeDataProps();
128+
const data = this.memoizeDataProps(this.props);
113129

114130
if (!this.chart_instance) return;
115131

@@ -121,7 +137,7 @@ class ChartComponent extends React.Component {
121137
// seamless transitions
122138
let currentDatasets = (this.chart_instance.config.data && this.chart_instance.config.data.datasets) || [];
123139
const nextDatasets = data.datasets || [];
124-
140+
125141
// Prevent charting of legend items that no longer exist
126142
while (currentDatasets.length > nextDatasets.length) {
127143
currentDatasets.pop();
@@ -158,10 +174,9 @@ class ChartComponent extends React.Component {
158174
}
159175

160176
renderChart() {
161-
const {data, options, legend, type, redraw} = this.props;
177+
const {options, legend, type, redraw} = this.props;
162178
const node = ReactDOM.findDOMNode(this);
163-
164-
this.memoizeDataProps();
179+
const data = this.memoizeDataProps();
165180

166181
this.chart_instance = new Chart(node, {
167182
type,

test/__tests__/Chart_spec.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,4 +209,17 @@ describe('<Chart />', () => {
209209

210210
expect(onElementsClick.called).to.equal(true);
211211
});
212+
213+
describe('props.data function', () => {
214+
it('calls data func with canvas node', () => {
215+
const resultData = { test: 1 }
216+
const dataFn = sinon.spy((canvas) => resultData);
217+
const wrapper = mountComponent({ data: dataFn });
218+
219+
const canvas = wrapper.find('canvas').at(0).node;
220+
221+
expect(dataFn.callCount).to.equal(1);
222+
expect(dataFn.calledWith(canvas)).to.equal(true);
223+
});
224+
})
212225
});

0 commit comments

Comments
 (0)