Skip to content

Commit f207cb2

Browse files
Change default export to combined EditorControls + Plot component (#384)
1 parent dea11c6 commit f207cb2

File tree

23 files changed

+556
-645
lines changed

23 files changed

+556
-645
lines changed

README.md

Lines changed: 3 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Check out the demo of the latest release of the `DefaultEditor` at https://plotl
1818
```
1919
git clone [this repo]
2020
cd react-chart-editor
21-
cd examples/simple
21+
cd examples/demo
2222
npm install
2323
npm start
2424
```
@@ -28,55 +28,7 @@ See more examples
2828

2929
## Overview
3030

31-
This module's entry point is a React component called `<PlotlyEditor />` which connects to a [plotly.js](https://plot.ly/javascript/)-powered `<Plot />` component care of [`react-plotly.js`](https://github.com/plotly/react-plotly.js). A plotly.js plot is defined by a JSON-serializable object called a _figure_. `<PlotlyEditor />` accepts as children React components whose descendents are input elements wrapped via `connectToContainer()` calls so as to bind them to the `<Plot />`'s figure's values. If no children are passed to the `<PlotlyEditor />`, the `<DefaultEditor />` is used. This module also exposes the [building block components](#Built-in-Components) that comprise the `<DefaultEditor />` so that developers can create their own customized editors.
32-
33-
## Connecting `<PlotlyEditor />` to `<Plot />`
34-
35-
The binding between `<PlotlyEditor />` and `<Plot />` works a little differently that in most React apps because plotly.js mutates its properties. This is mapped onto React's one-way dataflow model via event handlers and shared revision numbers which trigger re-renders of mutated state. The following subset of the [simple example](https://github.com/plotly/react-chart-editor/tree/master/examples/simple) shows how this works using a parent component to store state, but the principle is the same with a different state-manage approach, as shown in the [redux example](https://github.com/plotly/react-chart-editor/tree/master/examples/redux):
36-
37-
```javascript
38-
import PlotlyEditor from 'react-chart-editor';
39-
import Plot from 'react-plotly.js';
40-
41-
class App extends Component {
42-
constructor() {
43-
super();
44-
this.state = {graphDiv: {}, editorRevision: 0, plotRevision: 0};
45-
}
46-
47-
handlePlotUpdate(graphDiv) {
48-
this.setState(({editorRevision: x}) => ({editorRevision: x + 1, graphDiv}));
49-
}
50-
51-
handleEditorUpdate() {
52-
this.setState(({plotRevision: x}) => ({plotRevision: x + 1}));
53-
}
54-
55-
render() {
56-
return (
57-
<div>
58-
<PlotlyEditor
59-
graphDiv={this.state.graphDiv}
60-
onUpdate={this.handleEditorUpdate.bind(this)}
61-
revision={this.state.editorRevision}
62-
{...snip}
63-
/>
64-
<Plot
65-
data={this.state.graphDiv.data}
66-
layout={this.state.graphDiv.layout}
67-
onUpdate={this.handlePlotUpdate.bind(this)}
68-
revision={this.state.plotRevision}
69-
{...snip}
70-
/>
71-
</div>
72-
);
73-
}
74-
}
75-
```
76-
77-
## Data Management
78-
79-
`<PlotlyEditor />` accepts a `dataSources` property which is an object of arrays of data, as well as a `dataSourceOptions` property which contains metadata about the `dataSources`, such as human-readable labels used to populate input elements like dropdown menus. `<PlotlyEditor />` treats these properties as immutable so any changes to them will trigger a rerender, and accepts an `onUpdateTraces` event handler property which is called whenever it needs to access a column from `dataSources`, enabling asynchronous data loading e.g. from remote APIs. The [async-data example](https://github.com/plotly/react-chart-editor/tree/master/examples/async-data) shows how this is done using a dummy asynchronous back-end proxy.
31+
This module's entry point is a React component called `<PlotlyEditor />` which connects an instance of `<EditorControls />` to a [plotly.js](https://plot.ly/javascript/)-powered `<Plot />` component care of [`react-plotly.js`](https://github.com/plotly/react-plotly.js). `<PlotlyEditor />` accepts as children React components whose descendents are input elements wrapped via `connectToContainer()` calls so as to bind them to the `<Plot />`'s figure's values. If no children are passed to the `<PlotlyEditor />`, the `<DefaultEditor />` is used. This module also exposes the [building block components](#Built-in-Components) that comprise the `<DefaultEditor />` so that developers can create their own customized editors.
8032

8133
## Styling the `<DefaultEditor />` and the built-in components
8234

@@ -198,7 +150,7 @@ For use in containers bound to annotations e.g. as children of `<AnnotationAccor
198150

199151
To use Satellite Maps in the Editor, [Mapbox access tokens](https://www.mapbox.com/help/how-access-tokens-work/) are required.
200152

201-
Once you have your tokens, you can provide it as a config prop to the `react-plotly.js` generated `Plot` component: `<Plot config={{mapboxAccessToken: 'your token'}}/>`
153+
Once you have your tokens, you can provide it as a config prop to the `<PlotlyEditor />` component: `<PlotlyEditor config={{mapboxAccessToken: 'your token'}}/>`
202154

203155
## See also
204156

dev/App.js

Lines changed: 21 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import React, {Component} from 'react';
22
import {hot} from 'react-hot-loader';
33
import plotly from 'plotly.js/dist/plotly';
4-
import createPlotComponent from 'react-plotly.js/factory';
5-
import PlotlyEditor from '../src';
64
import '../src/styles/main.scss';
75
import Nav from './Nav';
6+
import PlotlyEditor from '../src';
87

98
// https://github.com/plotly/react-chart-editor#mapbox-access-tokens
109
import ACCESS_TOKENS from '../accessTokens';
@@ -22,18 +21,15 @@ const dataSourceOptions = Object.keys(dataSources).map(name => ({
2221
label: name,
2322
}));
2423

25-
const Plot = createPlotComponent(plotly);
24+
const config = {mapboxAccessToken: ACCESS_TOKENS.MAPBOX, editable: true};
2625

2726
class App extends Component {
2827
constructor() {
2928
super();
3029

31-
// The graphDiv object is passed to Plotly.js, which then causes it to be
32-
// overwritten with a full DOM node that contains data, layout, _fullData,
33-
// _fullLayout etc in handlePlotUpdate()
3430
this.state = {
35-
graphDiv: {},
36-
plotRevision: 0,
31+
data: [],
32+
layout: {},
3733
currentMockIndex: -1,
3834
mocks: [],
3935
};
@@ -49,61 +45,36 @@ class App extends Component {
4945
.then(mocks => this.setState({mocks}));
5046
}
5147

52-
handlePlotUpdate(graphDiv) {
53-
this.setState({graphDiv});
54-
}
55-
56-
handleEditorUpdate() {
57-
this.setState(({plotRevision: x}) => ({plotRevision: x + 1}));
58-
}
59-
6048
loadMock(mockIndex) {
6149
const mock = this.state.mocks[mockIndex];
6250
fetch(mock.url, {
6351
headers: new Headers({Accept: 'application/vnd.github.v3.raw'}),
6452
})
6553
.then(response => response.json())
6654
.then(figure => {
67-
const graphDiv = this.state.graphDiv;
68-
graphDiv.layout = figure.layout;
69-
graphDiv.data = figure.data;
70-
this.setState(({plotRevision: x}) => ({
55+
this.setState({
7156
currentMockIndex: mockIndex,
72-
plotRevision: x + 1,
73-
}));
57+
data: figure.data,
58+
layout: figure.layout,
59+
});
7460
});
7561
}
7662

7763
render() {
7864
return (
79-
<div className="app__container plotly-editor--theme-provider">
80-
<div className="app">
81-
<PlotlyEditor
82-
graphDiv={this.state.graphDiv}
83-
onUpdate={this.handleEditorUpdate.bind(this)}
84-
dataSources={dataSources}
85-
dataSourceOptions={dataSourceOptions}
86-
plotly={plotly}
87-
advancedTraceTypeSelector
88-
/>
89-
<div className="app__main" style={{width: '100%', height: '100%'}}>
90-
<Plot
91-
config={{mapboxAccessToken: ACCESS_TOKENS.MAPBOX, editable: true}}
92-
data={this.state.graphDiv.data}
93-
debug
94-
layout={this.state.graphDiv.layout}
95-
onInitialized={this.handlePlotUpdate.bind(this)}
96-
onUpdate={this.handlePlotUpdate.bind(this)}
97-
revision={this.state.plotRevision}
98-
useResizeHandler
99-
style={{
100-
width: '100%',
101-
height: '100%',
102-
minHeight: 'calc(100vh - 50px)',
103-
}}
104-
/>
105-
</div>
106-
</div>
65+
<div className="app">
66+
<PlotlyEditor
67+
data={this.state.data}
68+
layout={this.state.layout}
69+
config={config}
70+
dataSources={dataSources}
71+
dataSourceOptions={dataSourceOptions}
72+
plotly={plotly}
73+
onUpdate={(data, layout) => this.setState({data, layout})}
74+
useResizeHandler
75+
debug
76+
advancedTraceTypeSelector
77+
/>
10778
<Nav
10879
currentMockIndex={this.state.currentMockIndex}
10980
loadMock={this.loadMock}

dev/styles.css

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,29 @@ body {
44
font-family: sans-serif;
55
}
66

7-
.app__container {
8-
display: flex;
9-
flex-direction: column;
10-
}
11-
127
.app {
13-
display: flex;
14-
/*
15-
We are defining the max height of the app so that the editor knows how big to be
16-
currently the editor will take up whatever space it can if it is not constrained in its parent
17-
*/
18-
flex-grow: 1;
19-
height: calc(100vh - 50px);
20-
max-height: calc(100vh - 50px);
21-
width: 100%;
22-
}
23-
24-
.app__main {
25-
max-width: 100%;
268
height: calc(100vh - 50px);
279
max-height: calc(100vh - 50px);
28-
overflow: auto;
29-
flex-grow: 1;
3010
}
3111

3212
.mock-nav {
3313
height: 50px;
3414
width: 100%;
35-
background-color: var(--color-background-inverse);
15+
background-color: #506784;
3616
display: inline-flex;
3717
color: white;
3818
}
3919

40-
.mock-nav__label{
41-
line-height: 50px;
42-
padding-left: 10px;
20+
.mock-nav__label {
21+
line-height: 50px;
22+
padding-left: 10px;
4323
}
4424

45-
.mock-nav__select{
46-
width: 300px;
47-
margin-left: 20px;
48-
margin-right: 20px;
49-
margin-top: 7px;
25+
.mock-nav__select {
26+
width: 300px;
27+
margin-left: 20px;
28+
margin-right: 20px;
29+
margin-top: 7px;
5030
}
5131

5232
.Select.open-top .Select-menu-outer {

examples/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# `react-chart-editor` examples
22

33
* [Simple `react-chart-editor` example](simple): `DefaultEditor`, synchronous data, top-level component state management
4-
* [Async-data `react-chart-editor` example](async-data): `DefaultEditor`, asynchronous data, top-level component state management
4+
* [Demo `react-chart-editor` example](demo): `DefaultEditor`, top-level component state management, navbar to load mocks (same as dev app but no hot-reloading)
55
* [Custom `react-chart-editor` example](custom): `CustomEditor`, synchronous data, top-level component state management
66
* [Redux `react-chart-editor` example](redux): `DefaultEditor`, synchronous data, Redux state management

examples/custom/src/App.js

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, {Component} from 'react';
22
import plotly from 'plotly.js/dist/plotly';
3-
import createPlotComponent from 'react-plotly.js/factory';
43
import PlotlyEditor from 'react-chart-editor';
54
import CustomEditor from './CustomEditor';
65
import 'react-chart-editor/lib/react-chart-editor.css';
@@ -15,62 +14,41 @@ const dataSourceOptions = Object.keys(dataSources).map(name => ({
1514
label: name,
1615
}));
1716

18-
const Plot = createPlotComponent(plotly);
17+
const config = {editable: true};
1918

2019
class App extends Component {
2120
constructor() {
2221
super();
23-
24-
// The graphDiv object is passed to Plotly.js, which then causes it to be
25-
// overwritten with a full DOM node that contains data, layout, _fullData,
26-
// _fullLayout etc in handlePlotUpdate()
2722
this.state = {
28-
graphDiv: {
29-
data: [
30-
{
31-
type: 'scatter',
32-
x: dataSources.col1,
33-
y: dataSources.col2,
34-
marker: {color: dataSources.col3},
35-
},
36-
],
37-
},
38-
plotRevision: 0,
23+
data: [
24+
{
25+
type: 'scatter',
26+
x: dataSources.col1,
27+
y: dataSources.col2,
28+
marker: {color: dataSources.col3},
29+
},
30+
],
31+
layout: {},
3932
};
4033
}
4134

42-
handlePlotUpdate(graphDiv) {
43-
this.setState({graphDiv});
44-
}
45-
46-
handleEditorUpdate() {
47-
this.setState(({plotRevision: x}) => ({plotRevision: x + 1}));
48-
}
49-
5035
render() {
5136
return (
5237
<div className="app">
5338
<PlotlyEditor
54-
locale="en"
55-
graphDiv={this.state.graphDiv}
56-
onUpdate={this.handleEditorUpdate.bind(this)}
57-
plotly={plotly}
39+
data={this.state.data}
40+
layout={this.state.layout}
41+
config={config}
5842
dataSources={dataSources}
5943
dataSourceOptions={dataSourceOptions}
44+
plotly={plotly}
45+
onUpdate={(data, layout) => this.setState({data, layout})}
46+
useResizeHandler
47+
debug
48+
advancedTraceTypeSelector
6049
>
6150
<CustomEditor />
6251
</PlotlyEditor>
63-
<div className="app__main">
64-
<Plot
65-
debug
66-
data={this.state.graphDiv.data}
67-
layout={this.state.graphDiv.layout}
68-
config={{editable: true}}
69-
onUpdate={this.handlePlotUpdate.bind(this)}
70-
onInitialized={this.handlePlotUpdate.bind(this)}
71-
revision={this.state.plotRevision}
72-
/>
73-
</div>
7452
</div>
7553
);
7654
}

examples/custom/src/index.css

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,7 @@ body {
44
font-family: sans-serif;
55
}
66

7-
.app{
8-
display: flex;
9-
/*
10-
We are defining the max height of the app so that the editor knows how big to be
11-
currently the editor will take up whatever space it can if it is not constrained in its parent
12-
*/
13-
min-height: 100vh;
7+
.app {
8+
height: 100vh;
149
max-height: 100vh;
15-
width: 100%;
16-
}
17-
.app__main {
18-
max-width: 100%;
19-
max-height: 100vh;
20-
overflow: auto;
21-
flex-grow: 1;
2210
}

0 commit comments

Comments
 (0)