diff --git a/src/components/Chart/__test__/chart.spec.js b/src/components/Chart/__test__/chart.spec.js
index 9d1388fcc..48dec5595 100644
--- a/src/components/Chart/__test__/chart.spec.js
+++ b/src/components/Chart/__test__/chart.spec.js
@@ -8,6 +8,10 @@ jest.mock('../helpers/unregisterGlobalPlugins', () => jest.fn());
jest.mock('../chart', () =>
jest.fn(() => ({
+ data: {
+ labels: [],
+ datasets: [],
+ },
update: jest.fn(),
config: {},
})),
@@ -25,18 +29,13 @@ describe('', () => {
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D'],
- datasets: [expect.any(Object)],
},
plugins: null,
options: expect.any(Object),
});
});
it('should call ChartJS update method to update the chart when the component changes', () => {
- const component = mount(
-
-
- ,
- );
+ const component = mount();
const { chartInstance } = component.instance();
component.setProps({
labels: ['A', 'B', 'C', 'D', 'E'],
@@ -46,16 +45,12 @@ describe('', () => {
});
it('should call ChartJS update method to update the chart when the children changes', () => {
- const component = mount(
-
-
- ,
- );
+ const component = mount();
const { chartInstance } = component.instance();
component.setProps({
children: [],
});
- expect(chartInstance.update).toHaveBeenCalledTimes(1);
+ expect(chartInstance.update).toHaveBeenCalledTimes(2);
});
it('should update the chart type when changed dynamically', () => {
diff --git a/src/components/Chart/context.js b/src/components/Chart/context.js
new file mode 100644
index 000000000..213cf3634
--- /dev/null
+++ b/src/components/Chart/context.js
@@ -0,0 +1,4 @@
+import React from 'react';
+
+const ChartContext = React.createContext();
+export default ChartContext;
diff --git a/src/components/Chart/index.js b/src/components/Chart/index.js
index a73fec700..dfdb347d6 100644
--- a/src/components/Chart/index.js
+++ b/src/components/Chart/index.js
@@ -3,9 +3,10 @@ import PropTypes from 'prop-types';
import { withTheme } from 'styled-components';
import ChartJS from './chart';
import resolveOptions from './resolveOptions';
-import resolveDatasets from './resolveDatasets';
import StyledContainer from './styled/container';
+import DatasetContainer from './styled/datasetContainer';
import unregisterGlobalPlugins from './helpers/unregisterGlobalPlugins';
+import ChartContext from './context';
/**
* A chart is a graphical representation of data. Charts allow users to better understand
@@ -18,36 +19,48 @@ export class Chart extends Component {
constructor(props) {
super(props);
this.chartRef = React.createRef();
- this.datasets = [];
+ this.datasets = {};
+ this.registerDataset = this.registerDataset.bind(this);
+ this.unregisterDataset = this.unregisterDataset.bind(this);
+ this.updateDataset = this.updateDataset.bind(this);
}
componentDidMount() {
- const { children } = this.props;
- this.datasets = resolveDatasets(children);
this.renderChart();
}
componentDidUpdate() {
- const { children } = this.props;
- this.datasets = resolveDatasets(children);
this.updateChart();
}
updateChart() {
const { labels, type, ...conditions } = this.props;
- this.chartInstance.data = {
- labels,
- datasets: this.datasets,
- };
+ this.chartInstance.config.type = type;
+ this.chartInstance.data.labels = labels;
+ this.chartInstance.data.datasets = Object.values(this.datasets);
+ this.chartInstance.options = resolveOptions({ type, ...conditions });
+ this.chartInstance.update();
+ }
- if (type && this.chartInstance.config && type !== this.chartInstance.config.type) {
- this.chartInstance.config.type = type;
- }
+ registerDataset(id, dataset) {
+ this.datasets[id] = dataset;
+ this.updateChart();
+ }
- this.chartInstance.options = resolveOptions({ type, ...conditions });
+ unregisterDataset(id) {
+ const { [id]: remove, ...rest } = this.datasets;
+ this.datasets = rest;
this.chartInstance.update();
}
+ updateDataset(id, dataset) {
+ const keys = Object.keys(dataset);
+ keys.forEach(key => {
+ this.datasets[id][key] = dataset[key];
+ });
+ this.updateChart();
+ }
+
renderChart() {
unregisterGlobalPlugins(ChartJS);
const { type, labels, plugins, ...conditions } = this.props;
@@ -57,7 +70,6 @@ export class Chart extends Component {
type,
data: {
labels,
- datasets: this.datasets,
},
plugins: plugins || null,
options: resolveOptions({ type, plugins, ...conditions }),
@@ -65,12 +77,20 @@ export class Chart extends Component {
}
render() {
- const { style, className } = this.props;
+ const { style, className, children } = this.props;
+ const context = {
+ registerDataset: this.registerDataset,
+ unregisterDataset: this.unregisterDataset,
+ updateDataset: this.updateDataset,
+ };
return (
-
-
-
+
+
+
+ {children}
+
+
);
}
}
diff --git a/src/components/Chart/styled/datasetContainer.js b/src/components/Chart/styled/datasetContainer.js
new file mode 100644
index 000000000..5aed7e7d6
--- /dev/null
+++ b/src/components/Chart/styled/datasetContainer.js
@@ -0,0 +1,7 @@
+import styled from 'styled-components';
+
+const StyledDatasetContainer = styled.div`
+ display: none;
+`;
+
+export default StyledDatasetContainer;
diff --git a/src/components/Dataset/__test__/dataset.spec.js b/src/components/Dataset/__test__/dataset.spec.js
new file mode 100644
index 000000000..88b09d153
--- /dev/null
+++ b/src/components/Dataset/__test__/dataset.spec.js
@@ -0,0 +1,78 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import Dataset from '..';
+
+const context = {
+ registerDataset: jest.fn(),
+ unregisterDataset: jest.fn(),
+ updateDataset: jest.fn(),
+};
+
+describe('', () => {
+ beforeEach(() => {
+ jest.resetAllMocks();
+ React.useContext = jest.fn().mockReturnValue(context);
+ });
+
+ it('should call registerDataset with dataset props', () => {
+ mount(
+ ,
+ );
+ expect(context.registerDataset).toHaveBeenCalledWith(expect.any(String), {
+ label: 'Dataset 1',
+ data: [1, 2, 3],
+ backgroundColor: '#ccc',
+ borderColor: '#000',
+ stack: 'stack-1',
+ type: 'line',
+ fill: false,
+ });
+ });
+
+ it('should call unregisterDataset when unmounted', () => {
+ const wrapper = mount(
+ ,
+ );
+ wrapper.unmount();
+ expect(context.unregisterDataset).toHaveBeenCalledTimes(1);
+ });
+
+ it('should call updateDataset when props changes', () => {
+ const wrapper = mount(
+ ,
+ );
+ wrapper.setProps({
+ values: [4, 5, 6],
+ });
+ expect(context.updateDataset).toHaveBeenCalledWith(expect.any(String), {
+ label: 'Dataset 1',
+ data: [4, 5, 6],
+ backgroundColor: '#ccc',
+ borderColor: '#000',
+ stack: 'stack-1',
+ type: 'line',
+ fill: false,
+ });
+ });
+});
diff --git a/src/components/Dataset/index.js b/src/components/Dataset/index.js
index ac9aeb8fb..9f0de1e49 100644
--- a/src/components/Dataset/index.js
+++ b/src/components/Dataset/index.js
@@ -1,10 +1,44 @@
/* eslint-disable react/no-unused-prop-types */
-import React from 'react';
+import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
+import ChartContext from '../Chart/context';
+import { useUniqueIdentifier } from '../../libs/hooks';
/** @category DataView */
-export default function Dataset() {
- return
;
+export default function Dataset(props) {
+ const datasetId = useUniqueIdentifier('dataset');
+ const { title: label, values: data, ...rest } = props;
+ const { registerDataset, unregisterDataset, updateDataset } = React.useContext(ChartContext);
+ const isRegistered = useRef();
+
+ useEffect(() => {
+ if (isRegistered.current) {
+ updateDataset(datasetId, {
+ label,
+ data,
+ ...rest,
+ });
+ }
+ });
+
+ useEffect(() => {
+ registerDataset(datasetId, {
+ label,
+ data,
+ ...rest,
+ });
+ isRegistered.current = true;
+
+ return () => {
+ if (isRegistered.current) {
+ unregisterDataset(datasetId);
+ isRegistered.current = false;
+ }
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return <>>;
}
Dataset.propTypes = {