diff --git a/examples/async-data/package-lock.json b/examples/async-data/package-lock.json
index f59576073..da8ff9110 100644
--- a/examples/async-data/package-lock.json
+++ b/examples/async-data/package-lock.json
@@ -2316,6 +2316,16 @@
"sha.js": "2.4.9"
}
},
+ "create-react-class": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.2.tgz",
+ "integrity": "sha1-zx7RXxKq1/FO9fLf4F5sQvke8Co=",
+ "requires": {
+ "fbjs": "0.8.16",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1"
+ }
+ },
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@@ -11081,10 +11091,11 @@
}
},
"react": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/react/-/react-16.2.0.tgz",
- "integrity": "sha512-ZmIomM7EE1DvPEnSFAHZn9Vs9zJl5A9H7el0EGTE6ZbW9FKe/14IYAlPbC8iH25YarEQxZL+E8VW7Mi7kfQrDQ==",
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz",
+ "integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=",
"requires": {
+ "create-react-class": "15.6.2",
"fbjs": "0.8.16",
"loose-envify": "1.3.1",
"object-assign": "4.1.1",
@@ -11117,9 +11128,9 @@
}
},
"react-dom": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.2.0.tgz",
- "integrity": "sha512-zpGAdwHVn9K0091d+hr+R0qrjoJ84cIBFL2uU60KvWBPfZ7LPSrfqviTxGHWN0sjPZb2hxWzMexwrvJdKePvjg==",
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz",
+ "integrity": "sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=",
"requires": {
"fbjs": "0.8.16",
"loose-envify": "1.3.1",
diff --git a/examples/async-data/package.json b/examples/async-data/package.json
index 4e22ce2ad..1b0a0a77b 100644
--- a/examples/async-data/package.json
+++ b/examples/async-data/package.json
@@ -7,6 +7,8 @@
"plotly.js": "^1.31.2",
"react": "^15.6.1",
"react-dom": "^15.6.1",
+ "react-plotly.js": "^1.0.4",
+ "react-plotly.js-editor": "^0.1.0",
"react-scripts": "1.0.17"
},
"scripts": {
diff --git a/examples/async-data/src/App.js b/examples/async-data/src/App.js
index 86be8f9c5..9f1089a33 100644
--- a/examples/async-data/src/App.js
+++ b/examples/async-data/src/App.js
@@ -1,11 +1,7 @@
import './App.css';
import 'react-plotly.js-editor/lib/react-plotly.js-editor.css';
import 'react-select/dist/react-select.css';
-import PlotlyEditor, {
- EDITOR_ACTIONS,
- Hub,
- dereference,
-} from 'react-plotly.js-editor';
+import PlotlyEditor, {dereference} from 'react-plotly.js-editor';
import React, {Component} from 'react';
import createPlotComponent from 'react-plotly.js/factory';
import ee from 'event-emitter';
@@ -49,6 +45,7 @@ class Backend {
ee(Backend.prototype);
const backend = new Backend({
+ // eslint-disable-next-line no-magic-numbers
dataSources: {col1: [1, 2, 3], col2: [4, 3, 2], col3: [17, 13, 9]},
delay: 10,
});
@@ -57,38 +54,25 @@ class App extends Component {
constructor() {
super();
- // A basic starting plotly.js figure object. Instead of assigning
- const figure = {
+ // This object is passed to Plotly.js, which then causes it to be
+ // overwritten with a full DOM node that contains data, layout, _fullData,
+ // _fullLayout etc in handlePlotUpdate()
+ const graphDiv = {
data: [{type: 'scatter'}],
layout: {title: 'Room readings'},
};
// Store the figure, dataSource and dataSourceOptions in state.
- this.state = {figure, dataSources: {}};
-
- // Instantiate a Hub object. The hub is a convenience module that updates
- // the applies Editor updates to the figure object. After an update it
- // will call the onUpdate function with the editor and plot revision numbers.
- // We set these in our state and pass them to react-plotly.js-editor and
- // react-plotly.js plot component respectively. This is necessary because
- // the plotly.js library will mutate the figure object with user interactions.
- // The hub listens for events from plotly.js and alerts us to the mutation here.
- // The Editor follows the same mutation pattern (for good or ill) and the Hub
- // will pick up editor results in the same way.
- this.hub = new Hub({
- debug: true,
- onUpdate: ({editorRevision, plotRevision}) =>
- this.setState(prevState => ({
- ...prevState,
- editorRevision,
- plotRevision,
- })),
- });
+ this.state = {
+ graphDiv,
+ editorRevision: 0,
+ plotRevision: 0,
+ dataSources: {},
+ };
this.getChartingData = this.getChartingData.bind(this);
this.setChartingData = this.setChartingData.bind(this);
this.setChartingDataOptions = this.setChartingDataOptions.bind(this);
- this.onEditorUpdate = this.onEditorUpdate.bind(this);
}
componentDidMount() {
@@ -98,18 +82,18 @@ class App extends Component {
this.getChartingDataColumnsNames();
}
- componentDidUnmount() {
+ componentWillUnmount() {
backend.off('ChartingDataColumnNames', this.setChartingDataOptions);
backend.off('ChartingData', this.setChartingData);
}
setChartingDataOptions(columnNames) {
- const dataSourceOptions = columnNames.map(name => ({
- value: name,
- label: name,
- }));
- this.setState(prevState => ({...prevState, dataSourceOptions}));
- this.hub.editorSourcesUpdated();
+ this.setState({
+ dataSourceOptions: columnNames.map(name => ({
+ value: name,
+ label: name,
+ })),
+ });
}
getChartingDataColumnsNames() {
@@ -127,35 +111,34 @@ class App extends Component {
setChartingData({columnName, data}) {
if (Array.isArray(data) && data.length) {
- const {dataSources, ...otherState} = this.state;
- dataSources[columnName] = data;
- dereference(this.state.figure.data, dataSources);
- this.setState({
- dataSources,
- ...otherState,
+ this.setState(({dataSources, graphDiv}) => {
+ const newDataSources = {...dataSources, [columnName]: data};
+ dereference(graphDiv.data, newDataSources);
+ return {dataSources: newDataSources, graphDiv};
});
- this.hub.figureUpdated();
}
}
- onEditorUpdate(event) {
- const {type, payload} = event;
- if (type === EDITOR_ACTIONS.UPDATE_TRACES) {
- const {update} = payload;
- if (update) {
- for (const key in update) {
- if (key.substr(key.length - 3) === 'src') {
- const columnId = update[key];
- const data = this.state.dataSources[columnId];
- if (!Array.isArray(data).length || !data.length) {
- this.getChartingData(columnId);
- }
+ handleEditorUpdateTraces({update}) {
+ if (update) {
+ for (const key in update) {
+ if (key.substr(key.length - 3) === 'src') {
+ const columnId = update[key];
+ const data = this.state.dataSources[columnId];
+ if (!Array.isArray(data).length || !data.length) {
+ this.getChartingData(columnId);
}
}
}
}
+ }
- this.hub.handleEditorUpdate(event);
+ handlePlotUpdate(graphDiv) {
+ this.setState(({editorRevision: x}) => ({editorRevision: x + 1, graphDiv}));
+ }
+
+ handleEditorUpdate() {
+ this.setState(({plotRevision: x}) => ({plotRevision: x + 1}));
}
render() {
@@ -165,17 +148,18 @@ class App extends Component {
locale="en"
dataSources={this.state.dataSources}
dataSourceOptions={this.state.dataSourceOptions}
- graphDiv={this.hub.graphDiv}
- onUpdate={this.onEditorUpdate}
- plotly={plotly}
+ graphDiv={this.state.graphDiv}
+ onUpdate={this.handleEditorUpdate.bind(this)}
+ onUpdateTraces={this.handleEditorUpdateTraces.bind(this)}
revision={this.state.editorRevision}
+ plotly={plotly}
/>
diff --git a/examples/simple/src/App.js b/examples/simple/src/App.js
index 75a8e7b2e..0e2789476 100644
--- a/examples/simple/src/App.js
+++ b/examples/simple/src/App.js
@@ -1,7 +1,7 @@
import React, {Component} from 'react';
import plotly from 'plotly.js/dist/plotly-basic';
import createPlotComponent from 'react-plotly.js/factory';
-import PlotlyEditor, {Hub, dereference} from 'react-plotly.js-editor';
+import PlotlyEditor, {dereference} from 'react-plotly.js-editor';
import 'react-plotly.js-editor/lib/react-plotly.js-editor.css';
import 'react-select/dist/react-select.css';
@@ -12,6 +12,7 @@ class App extends Component {
super();
// DataSources are a mapping of column or data source ids to data arrays.
+ // eslint-disable-next-line no-magic-numbers
const dataSources = {col1: [1, 2, 3], col2: [4, 3, 2], col3: [17, 13, 9]};
const dataSourceOptions = [
{value: 'col1', label: 'CO2'},
@@ -19,39 +20,32 @@ class App extends Component {
{value: 'col3', label: 'SiO2'},
];
- // A basic starting plotly.js figure object. Instead of assigning
- const figure = {
+ // This object is passed to Plotly.js, which then causes it to be
+ // overwritten with a full DOM node that contains data, layout, _fullData,
+ // _fullLayout etc in handlePlotUpdate()
+ const graphDiv = {
data: [{type: 'scatter', xsrc: 'col1', ysrc: 'col2'}],
layout: {title: 'Room readings'},
};
- dereference(figure.data, dataSources);
+ dereference(graphDiv.data, dataSources);
// Store the figure, dataSource and dataSourceOptions in state.
this.state = {
- figure,
+ graphDiv,
+ editorRevision: 0,
+ plotRevision: 0,
dataSources,
dataSourceOptions,
};
+ }
+
+ handlePlotUpdate(graphDiv) {
+ this.setState(({editorRevision: x}) => ({editorRevision: x + 1, graphDiv}));
+ }
- // Instantiate a Hub object. The hub is a convenience module that updates
- // the applies Editor updates to the figure object. After an update it
- // will call the onUpdate function with the editor and plot revision numbers.
- // We set these in our state and pass them to react-plotly.js-editor and
- // react-plotly.js plot component respectively. This is necessary because
- // the plotly.js library will mutate the figure object with user interactions.
- // The hub listens for events from plotly.js and alerts us to the mutation here.
- // The Editor follows the same mutation pattern (for good or ill) and the Hub
- // will pick up editor results in the same way.
- this.hub = new Hub({
- debug: true,
- onUpdate: ({editorRevision, plotRevision}) =>
- this.setState(prevState => ({
- ...prevState,
- editorRevision,
- plotRevision,
- })),
- });
+ handleEditorUpdate() {
+ this.setState(({plotRevision: x}) => ({plotRevision: x + 1}));
}
render() {
@@ -61,18 +55,18 @@ class App extends Component {
locale="en"
dataSources={this.state.dataSources}
dataSourceOptions={this.state.dataSourceOptions}
- graphDiv={this.hub.graphDiv}
- onUpdate={this.hub.handleEditorUpdate}
- plotly={plotly}
+ graphDiv={this.state.graphDiv}
+ onUpdate={this.handleEditorUpdate.bind(this)}
revision={this.state.editorRevision}
+ plotly={plotly}
/>
diff --git a/package-lock.json b/package-lock.json
index 15fa8ad98..e67e2ec73 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4,6 +4,28 @@
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+ "3d-view": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/3d-view/-/3d-view-2.0.0.tgz",
+ "integrity": "sha1-gxrpQtdQjFCAHj4G+v4ejFdOF74=",
+ "requires": {
+ "matrix-camera-controller": "2.1.3",
+ "orbit-camera-controller": "4.0.0",
+ "turntable-camera-controller": "3.0.1"
+ }
+ },
+ "3d-view-controls": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/3d-view-controls/-/3d-view-controls-2.2.0.tgz",
+ "integrity": "sha1-RK7JxEjCe+NLPdUR/5ICq2FQ3KU=",
+ "requires": {
+ "3d-view": "2.0.0",
+ "mouse-change": "1.4.0",
+ "mouse-event-offset": "3.0.2",
+ "mouse-wheel": "1.2.0",
+ "right-now": "1.0.0"
+ }
+ },
"@babel/code-frame": {
"version": "7.0.0-beta.32",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.32.tgz",
@@ -160,27 +182,20 @@
"integrity": "sha1-OWs1r4JvpmqtRyyMt7jV4nf05tg=",
"dev": true
},
- "3d-view": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/3d-view/-/3d-view-2.0.0.tgz",
- "integrity": "sha1-gxrpQtdQjFCAHj4G+v4ejFdOF74=",
+ "JSONStream": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
+ "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=",
+ "dev": true,
"requires": {
- "matrix-camera-controller": "2.1.3",
- "orbit-camera-controller": "4.0.0",
- "turntable-camera-controller": "3.0.1"
+ "jsonparse": "1.3.1",
+ "through": "2.3.8"
}
},
- "3d-view-controls": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/3d-view-controls/-/3d-view-controls-2.2.0.tgz",
- "integrity": "sha1-RK7JxEjCe+NLPdUR/5ICq2FQ3KU=",
- "requires": {
- "3d-view": "2.0.0",
- "mouse-change": "1.4.0",
- "mouse-event-offset": "3.0.2",
- "mouse-wheel": "1.2.0",
- "right-now": "1.0.0"
- }
+ "JSV": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
+ "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c="
},
"a-big-triangle": {
"version": "1.0.3",
@@ -386,7 +401,7 @@
"array-bounds": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/array-bounds/-/array-bounds-1.0.1.tgz",
- "integrity": "sha512-8wdW3ZGk6UjMPJx/glyEt0sLzzwAE1bhToPsO1W2pbpR2gULyxe3BjSiuJFheP50T/GgODVPz2fuMUmIywt8cQ=="
+ "integrity": "sha1-2hE1a04Y4HWk8MhuHxeaZ7fX6jE="
},
"array-equal": {
"version": "1.0.0",
@@ -1714,9 +1729,9 @@
"integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=",
"dev": true,
"requires": {
+ "JSONStream": "1.3.1",
"combine-source-map": "0.7.2",
"defined": "1.0.0",
- "JSONStream": "1.3.1",
"through2": "2.0.3",
"umd": "3.0.1"
}
@@ -1744,6 +1759,7 @@
"integrity": "sha1-C7vOUhrNbk0dVNjpNlAI77hanMU=",
"dev": true,
"requires": {
+ "JSONStream": "1.3.1",
"assert": "1.4.1",
"browser-pack": "6.0.2",
"browser-resolve": "1.11.2",
@@ -1765,7 +1781,6 @@
"https-browserify": "1.0.0",
"inherits": "2.0.3",
"insert-module-globals": "7.0.1",
- "JSONStream": "1.3.1",
"labeled-stream-splicer": "2.0.0",
"module-deps": "4.1.1",
"os-browserify": "0.3.0",
@@ -2178,7 +2193,7 @@
"color-id": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/color-id/-/color-id-1.1.0.tgz",
- "integrity": "sha512-2iRtAn6dC/6/G7bBIo0uupVrIne1NsQJvJxZOBCzQOfk7jRq97feaDZ3RdzuHakRXXnHGNwglto3pqtRx1sX0g==",
+ "integrity": "sha1-XpFZuZpzrJj3SCDLmKFf3j1+A0w=",
"requires": {
"clamp": "1.0.1"
}
@@ -2220,7 +2235,7 @@
"colormap": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/colormap/-/colormap-2.3.0.tgz",
- "integrity": "sha512-Mkk6mQUMbCleXEeStFm2xLwv5zbRakZMUFB1T1+iNEv58VKBByfPwYIjMQDwSRmXNM1gvo5y3WTYAhmdMn/rbg==",
+ "integrity": "sha1-9yXHV8XG8JQKU0KnI8aARKwGzBU=",
"requires": {
"lerp": "1.0.3"
}
@@ -2589,7 +2604,7 @@
"d3-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz",
- "integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw=="
+ "integrity": "sha1-0coz3i9qwx76244FCgIdfiOW1dw="
},
"d3-collection": {
"version": "1.0.4",
@@ -2609,7 +2624,7 @@
"d3-force": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz",
- "integrity": "sha512-2HVQz3/VCQs0QeRNZTYb7GxoUCeb6bOzMp/cGcLa87awY9ZsPvXOGeZm0iaGBjXic6I1ysKwMn+g+5jSAdzwcg==",
+ "integrity": "sha1-zr88aU8QePzD1Nr45Wey+9cNTqM=",
"requires": {
"d3-collection": "1.0.4",
"d3-dispatch": "1.0.3",
@@ -2633,7 +2648,7 @@
"d3-timer": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz",
- "integrity": "sha512-vMZXR88XujmG/L5oB96NNKH5lCWwiLM/S2HyyAQLcjWJCloK5shxta4CwOFYLZoY3AWX73v8Lgv4cCAdWtRmOA=="
+ "integrity": "sha1-35ZQylh/bJZgf/TmDMOCKejdhTE="
},
"dashdash": {
"version": "1.14.1",
@@ -2938,7 +2953,7 @@
"duplexify": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.1.tgz",
- "integrity": "sha512-j5goxHTwVED1Fpe5hh3q9R93Kip0Bg2KVAt4f8CEYM3UEwYcPSvWbXaUQOzdX/HtiNomipv+gU7ASQPDbV7pGQ==",
+ "integrity": "sha1-ThUWvmiDi8kKSZlPCzmm5ZYL780=",
"requires": {
"end-of-stream": "1.4.0",
"inherits": "2.0.3",
@@ -2949,7 +2964,7 @@
"earcut": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/earcut/-/earcut-2.1.2.tgz",
- "integrity": "sha512-ji2b8qOVwK4WChYTbpKo983518wEqY2wrpkd85Us/LLw+3O7G0jGvGbHgQERuovrv3Cop9cEpiNkhqVQSkgTtA=="
+ "integrity": "sha1-VCrdDKOntxNFJyDh0FOTfT2vN4Q="
},
"ecc-jsbn": {
"version": "0.1.1",
@@ -3682,7 +3697,7 @@
"font-atlas-sdf": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/font-atlas-sdf/-/font-atlas-sdf-1.3.3.tgz",
- "integrity": "sha512-GxUpcdkdoHgC3UrpMuA7JmG1Ty/MY0BhfmV8r7ZSv3bkqBY5vmRIjcj7Pg8iqj20B03vlU6fUhdpyIgEo/Z35w==",
+ "integrity": "sha1-gyPxNsadc6I1qoxq2mQOWPGAuMA=",
"requires": {
"optical-properties": "1.0.0",
"tiny-sdf": "1.0.2"
@@ -4525,14 +4540,6 @@
}
}
},
- "string_decoder": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "safe-buffer": "5.0.1"
- }
- },
"string-width": {
"version": "1.0.2",
"bundled": true,
@@ -4543,6 +4550,14 @@
"strip-ansi": "3.0.1"
}
},
+ "string_decoder": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.0.1"
+ }
+ },
"stringstream": {
"version": "0.0.5",
"bundled": true,
@@ -7118,10 +7133,10 @@
"integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=",
"dev": true,
"requires": {
+ "JSONStream": "1.3.1",
"combine-source-map": "0.7.2",
"concat-stream": "1.5.2",
"is-buffer": "1.1.6",
- "JSONStream": "1.3.1",
"lexical-scope": "1.2.0",
"process": "0.11.10",
"through2": "2.0.3",
@@ -8342,16 +8357,6 @@
"integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
"dev": true
},
- "JSONStream": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
- "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=",
- "dev": true,
- "requires": {
- "jsonparse": "1.3.1",
- "through": "2.3.8"
- }
- },
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@@ -8363,11 +8368,6 @@
"verror": "1.10.0"
}
},
- "JSV": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
- "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c="
- },
"jsx-ast-utils": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz",
@@ -8962,6 +8962,7 @@
"integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=",
"dev": true,
"requires": {
+ "JSONStream": "1.3.1",
"browser-resolve": "1.11.2",
"cached-path-relative": "1.0.1",
"concat-stream": "1.5.2",
@@ -8969,7 +8970,6 @@
"detective": "4.6.0",
"duplexer2": "0.1.4",
"inherits": "2.0.3",
- "JSONStream": "1.3.1",
"parents": "1.0.1",
"readable-stream": "2.3.3",
"resolve": "1.5.0",
@@ -9545,7 +9545,7 @@
"object-inspect": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.3.0.tgz",
- "integrity": "sha512-OHHnLgLNXpM++GnJRyyhbr2bwl3pPVm4YvaraHrRvDt/N3r+s/gDVHciA7EJBTkijKXj61ssgSAikq1fb0IBRg=="
+ "integrity": "sha1-Wx645nQuLugzQqY3A02ESSi6L20="
},
"object-is": {
"version": "1.0.1",
@@ -9623,7 +9623,7 @@
"optical-properties": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/optical-properties/-/optical-properties-1.0.0.tgz",
- "integrity": "sha512-XnBQYbIIzDVr7U3L7d3xyAEqp1W+HTkqmw/G4L/Ae/+dq57bT1jqW2uDwV0wCUzO8gsTDIZhGQsGrMb17VSkEA=="
+ "integrity": "sha1-w6aUu6t8xFhwcIhsR/Q8jDpszq4="
},
"optimist": {
"version": "0.6.1",
@@ -9967,8 +9967,8 @@
"resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.31.2.tgz",
"integrity": "sha1-G0EAvGCVf+dYnOcs/M1O5Jnfrts=",
"requires": {
- "@plotly/d3-sankey": "0.5.0",
"3d-view": "2.0.0",
+ "@plotly/d3-sankey": "0.5.0",
"alpha-shape": "1.0.0",
"color-rgba": "1.1.1",
"convex-hull": "1.0.3",
@@ -11689,14 +11689,6 @@
"readable-stream": "2.3.3"
}
},
- "string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
- "requires": {
- "safe-buffer": "5.1.1"
- }
- },
"string-length": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz",
@@ -11761,6 +11753,14 @@
"function-bind": "1.1.1"
}
},
+ "string_decoder": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+ "requires": {
+ "safe-buffer": "5.1.1"
+ }
+ },
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
@@ -11929,7 +11929,7 @@
"tape": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/tape/-/tape-4.8.0.tgz",
- "integrity": "sha512-TWILfEnvO7I8mFe35d98F6T5fbLaEtbFTG/lxWvid8qDfFTxt19EBijWmB4j3+Hoh5TfHE2faWs73ua+EphuBA==",
+ "integrity": "sha1-9qn+xBzFCh3lD6M2A6tYCZH2Bo4=",
"requires": {
"deep-equal": "1.0.1",
"defined": "1.0.0",
@@ -12354,7 +12354,7 @@
"unassertify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/unassertify/-/unassertify-2.1.0.tgz",
- "integrity": "sha512-CB3C3vbOwrZydRuGdU8H421r4/qhM8RLuEOo3G+wEFf7kDP4TR+7oDuj1yOik5pUzXMaJmzxICM7akupP1AlJw==",
+ "integrity": "sha1-awer9cZZi6OFKiemdsqtHfUmqW0=",
"requires": {
"acorn": "5.2.1",
"convert-source-map": "1.5.1",
@@ -12572,7 +12572,7 @@
"webworkify": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/webworkify/-/webworkify-1.5.0.tgz",
- "integrity": "sha512-AMcUeyXAhbACL8S2hqqdqOLqvJ8ylmIbNwUIqQujRSouf4+eUFaXbG6F1Rbu+srlJMmxQWsiU7mOJi0nMBfM1g=="
+ "integrity": "sha1-c0rYendN5uvdVG4dPgJ9pbj0pCw="
},
"wgs84": {
"version": "0.0.0",
diff --git a/src/PlotlyEditor.js b/src/PlotlyEditor.js
index 9e82586e5..080072870 100644
--- a/src/PlotlyEditor.js
+++ b/src/PlotlyEditor.js
@@ -4,6 +4,9 @@ import React, {Component} from 'react';
import dictionaries from './locales';
import {bem} from './lib';
import {noShame} from './shame';
+import {EDITOR_ACTIONS} from './lib/constants';
+import isNumeric from 'fast-isnumeric';
+import nestedProperty from 'plotly.js/src/lib/nested_property';
class PlotlyEditor extends Component {
constructor(props, context) {
@@ -18,9 +21,15 @@ class PlotlyEditor extends Component {
}
shouldComponentUpdate(nextProps) {
- const nextRevision = nextProps.revision;
- const currRevision = this.props.revision;
- return nextRevision === void 0 || nextRevision !== currRevision;
+ if (
+ nextProps.revision === void 0 ||
+ nextProps.revision !== this.props.revision ||
+ nextProps.dataSources !== this.props.dataSources ||
+ nextProps.dataSourceOptions !== this.props.dataSourceOptions
+ ) {
+ return true;
+ }
+ return false;
}
getChildContext() {
@@ -35,16 +44,87 @@ class PlotlyEditor extends Component {
graphDiv: gd,
layout: gd.layout,
locale: this.props.locale,
- onUpdate: this.updateProp.bind(this),
+ onUpdate: this.handleUpdate.bind(this),
plotSchema: this.plotSchema,
plotly: this.props.plotly,
};
}
- updateProp(event) {
+ handleUpdate({type, payload}) {
const {graphDiv} = this.props;
- if (this.props.onUpdate) {
- this.props.onUpdate({graphDiv, ...event});
+
+ switch (type) {
+ case EDITOR_ACTIONS.UPDATE_TRACES:
+ if (this.props.onUpdateTraces) {
+ this.props.onUpdateTraces(payload);
+ }
+ for (let i = 0; i < payload.traceIndexes.length; i++) {
+ for (const attr in payload.update) {
+ const traceIndex = payload.traceIndexes[i];
+ const prop = nestedProperty(graphDiv.data[traceIndex], attr);
+ const value = payload.update[attr];
+ if (value !== void 0) {
+ prop.set(value);
+ }
+ }
+ }
+ if (this.props.onUpdate) {
+ this.props.onUpdate();
+ }
+ break;
+
+ case EDITOR_ACTIONS.UPDATE_LAYOUT:
+ if (this.props.onUpdateLayout) {
+ this.props.onUpdateLayout(payload);
+ }
+ for (const attr in payload.update) {
+ const prop = nestedProperty(graphDiv.layout, attr);
+ const value = payload.update[attr];
+ if (value !== void 0) {
+ prop.set(value);
+ }
+ }
+ if (this.props.onUpdate) {
+ this.props.onUpdate();
+ }
+ break;
+
+ case EDITOR_ACTIONS.ADD_TRACE:
+ if (this.props.onAddTrace) {
+ this.props.onAddTrace(payload);
+ }
+ graphDiv.data.push({x: [], y: []});
+ if (this.props.onUpdate) {
+ this.props.onUpdate();
+ }
+ break;
+
+ case EDITOR_ACTIONS.DELETE_TRACE:
+ if (this.props.onDeleteTrace) {
+ this.props.onDeleteTrace(payload);
+ }
+ if (payload.traceIndexes && payload.traceIndexes.length) {
+ graphDiv.data.splice(payload[0], 1);
+ if (this.props.onUpdate) {
+ this.props.onUpdate();
+ }
+ }
+ break;
+
+ case EDITOR_ACTIONS.DELETE_ANNOTATION:
+ if (this.props.onDeleteAnnotation) {
+ this.props.onDeleteAnnotation(payload);
+ }
+ if (isNumeric(payload.annotationIndex)) {
+ graphDiv.layout.annotations.splice(payload.annotationIndex, 1);
+ if (this.props.onUpdate) {
+ this.props.onUpdate();
+ }
+ }
+ break;
+
+ default:
+ throw new Error('must specify an action type to handleEditorUpdate');
}
}
@@ -52,6 +132,7 @@ class PlotlyEditor extends Component {
return (
{this.props.graphDiv &&
+ this.props.graphDiv._fullLayout &&
(this.props.children ? this.props.children : )}
);
@@ -66,6 +147,11 @@ PlotlyEditor.propTypes = {
locale: PropTypes.string,
revision: PropTypes.any,
onUpdate: PropTypes.func,
+ onUpdateTraces: PropTypes.func,
+ onUpdateLayout: PropTypes.func,
+ onAddTrace: PropTypes.func,
+ onDeleteTrace: PropTypes.func,
+ onDeleteAnnotation: PropTypes.func,
plotly: PropTypes.object,
};
diff --git a/src/components/containers/__tests__/AnnotationAccordion-test.js b/src/components/containers/__tests__/AnnotationAccordion-test.js
index 3f28d3eee..80c9e8a7a 100644
--- a/src/components/containers/__tests__/AnnotationAccordion-test.js
+++ b/src/components/containers/__tests__/AnnotationAccordion-test.js
@@ -1,7 +1,6 @@
import React from 'react';
import {AnnotationAccordion, Panel, Fold} from '..';
import {Numeric} from '../../fields';
-import {EDITOR_ACTIONS} from '../../../lib/constants';
import {TestEditor, fixtures, mount} from '../../../lib/test-utils';
import {connectLayoutToPlot} from '../../../lib';
@@ -30,9 +29,9 @@ describe('', () => {
it('can add annotations', () => {
const fixture = fixtures.scatter();
- const onUpdate = jest.fn();
+ const onUpdateLayout = jest.fn();
const editor = mount(
-
+
@@ -43,8 +42,7 @@ describe('', () => {
editor.find('.panel__add-button').simulate('click');
- const {type, payload} = onUpdate.mock.calls[0][0];
- expect(type).toBe(EDITOR_ACTIONS.UPDATE_LAYOUT);
+ const payload = onUpdateLayout.mock.calls[0][0];
expect(payload.update).toEqual({'annotations[0]': {text: 'new text'}});
});
@@ -52,9 +50,9 @@ describe('', () => {
const fixture = fixtures.scatter({
layout: {annotations: [{text: 'hodor'}, {text: 'rodoh'}]},
});
- const onUpdate = jest.fn();
+ const onDeleteAnnotation = jest.fn();
const editor = mount(
-
+
@@ -67,7 +65,7 @@ describe('', () => {
.at(0)
.simulate('click');
- const update = onUpdate.mock.calls[0][0];
- expect(update.type).toBe(EDITOR_ACTIONS.DELETE_ANNOTATION);
+ const update = onDeleteAnnotation.mock.calls[0][0];
+ expect(update.annotationIndex).toBe(0);
});
});
diff --git a/src/components/containers/__tests__/Fold-test.js b/src/components/containers/__tests__/Fold-test.js
index c007f65a7..6a83b1ebb 100644
--- a/src/components/containers/__tests__/Fold-test.js
+++ b/src/components/containers/__tests__/Fold-test.js
@@ -1,7 +1,6 @@
import {Numeric} from '../../fields';
import {Fold} from '..';
import React from 'react';
-import {EDITOR_ACTIONS} from '../../../lib/constants';
import {TestEditor, fixtures, mount} from '../../../lib/test-utils';
import {connectTraceToPlot} from '../../../lib';
@@ -29,9 +28,9 @@ describe('', () => {
});
it('calls deleteContainer when function present', () => {
- const onUpdate = jest.fn();
+ const onDeleteTrace = jest.fn();
mount(
-
+
@@ -40,8 +39,7 @@ describe('', () => {
.find('.fold__delete')
.simulate('click');
- const {type, payload} = onUpdate.mock.calls[0][0];
- expect(type).toBe(EDITOR_ACTIONS.DELETE_TRACE);
+ const payload = onDeleteTrace.mock.calls[0][0];
expect(payload).toEqual({traceIndexes: [0]});
});
});
diff --git a/src/components/containers/__tests__/Layout-test.js b/src/components/containers/__tests__/Layout-test.js
index 3b728876e..bfa33a42d 100644
--- a/src/components/containers/__tests__/Layout-test.js
+++ b/src/components/containers/__tests__/Layout-test.js
@@ -2,7 +2,6 @@ import {Numeric} from '../../fields';
import {Fold, Panel, Section} from '..';
import NumericInput from '../../widgets/NumericInput';
import React from 'react';
-import {EDITOR_ACTIONS} from '../../../lib/constants';
import {TestEditor, fixtures} from '../../../lib/test-utils';
import {connectLayoutToPlot} from '../../../lib';
import {mount} from 'enzyme';
@@ -27,10 +26,10 @@ Layouts.forEach(Layout => {
});
it(`sends updates to gd._layout`, () => {
- const onUpdate = jest.fn();
+ const onUpdateLayout = jest.fn();
const wrapper = mount(
@@ -43,9 +42,8 @@ Layouts.forEach(Layout => {
const widthUpdate = 200;
wrapper.prop('onChange')(widthUpdate);
- const event = onUpdate.mock.calls[0][0];
- expect(event.type).toBe(EDITOR_ACTIONS.UPDATE_LAYOUT);
- expect(event.payload).toEqual({update: {width: widthUpdate}});
+ const payload = onUpdateLayout.mock.calls[0][0];
+ expect(payload).toEqual({update: {width: widthUpdate}});
});
});
});
diff --git a/src/components/containers/__tests__/TraceAccordion-test.js b/src/components/containers/__tests__/TraceAccordion-test.js
index 0a8297001..ee0b212fc 100644
--- a/src/components/containers/__tests__/TraceAccordion-test.js
+++ b/src/components/containers/__tests__/TraceAccordion-test.js
@@ -1,7 +1,6 @@
import React from 'react';
import {TraceAccordion, Fold} from '..';
import {Numeric} from '../../fields';
-import {EDITOR_ACTIONS} from '../../../lib/constants';
import {TestEditor, fixtures, mount} from '../../../lib/test-utils';
describe('', () => {
@@ -21,9 +20,9 @@ describe('', () => {
it('can add traces', () => {
const fixture = fixtures.scatter();
- const onUpdate = jest.fn();
+ const onAddTrace = jest.fn();
const editor = mount(
-
+
@@ -32,15 +31,14 @@ describe('', () => {
editor.find('.panel__add-button').simulate('click');
- const update = onUpdate.mock.calls[0][0];
- expect(update.type).toBe(EDITOR_ACTIONS.ADD_TRACE);
+ expect(onAddTrace).toBeCalled();
});
it('can delete traces', () => {
const fixture = fixtures.scatter({data: [{name: 'hodor'}]});
- const onUpdate = jest.fn();
+ const onDeleteTrace = jest.fn();
const editor = mount(
-
+
@@ -52,7 +50,8 @@ describe('', () => {
.at(0)
.simulate('click');
- const update = onUpdate.mock.calls[0][0];
- expect(update.type).toBe(EDITOR_ACTIONS.DELETE_TRACE);
+ expect(onDeleteTrace).toBeCalled();
+ const update = onDeleteTrace.mock.calls[0][0];
+ expect(update.traceIndexes[0]).toBe(0);
});
});
diff --git a/src/components/fields/__tests__/AnnotationRef-test.js b/src/components/fields/__tests__/AnnotationRef-test.js
index 68163782e..7fc63504c 100644
--- a/src/components/fields/__tests__/AnnotationRef-test.js
+++ b/src/components/fields/__tests__/AnnotationRef-test.js
@@ -32,15 +32,15 @@ describe('', () => {
});
it('sends update for a[x|y]ref attr on [x|y]ref change', () => {
- const onUpdate = jest.fn();
+ const onUpdateLayout = jest.fn();
const fixtureProps = fixtures.scatter({
layout: {annotations: [{text: 'thor', ayref: 'y'}]},
});
- const drop = render({onUpdate, ...fixtureProps}).find(DropdownWidget);
+ const drop = render({onUpdateLayout, ...fixtureProps}).find(DropdownWidget);
drop.prop('onChange')('y2');
- const {payload: {update}} = onUpdate.mock.calls[0][0];
+ const {update} = onUpdateLayout.mock.calls[0][0];
expect(update).toEqual({
'annotations[0].ayref': 'y2',
'annotations[0].yref': 'y2',
@@ -48,28 +48,28 @@ describe('', () => {
});
it('does not send update for a[x|y]ref attr on "paper" change', () => {
- const onUpdate = jest.fn();
+ const onUpdateLayout = jest.fn();
const fixtureProps = fixtures.scatter({
layout: {annotations: [{text: 'thor', ayref: 'y'}]},
});
- const drop = render({onUpdate, ...fixtureProps}).find(DropdownWidget);
+ const drop = render({onUpdateLayout, ...fixtureProps}).find(DropdownWidget);
drop.prop('onChange')('paper');
- const {payload: {update}} = onUpdate.mock.calls[0][0];
+ const {update} = onUpdateLayout.mock.calls[0][0];
expect(update).toEqual({
'annotations[0].yref': 'paper',
});
});
it('does not send update for a[x|y]ref when a[x|y]ref is pixel', () => {
- const onUpdate = jest.fn();
+ const onUpdateLayout = jest.fn();
const fixtureProps = fixtures.scatter({
layout: {annotations: [{text: 'thor', yref: 'y', ayref: 'pixel'}]},
});
- const drop = render({onUpdate, ...fixtureProps}).find(DropdownWidget);
+ const drop = render({onUpdateLayout, ...fixtureProps}).find(DropdownWidget);
drop.prop('onChange')('y2');
- const {payload: {update}} = onUpdate.mock.calls[0][0];
+ const {update} = onUpdateLayout.mock.calls[0][0];
expect(update).toEqual({
'annotations[0].yref': 'y2',
});
diff --git a/src/components/fields/__tests__/DataSelector-test.js b/src/components/fields/__tests__/DataSelector-test.js
index b0c39a951..cc84b5a87 100644
--- a/src/components/fields/__tests__/DataSelector-test.js
+++ b/src/components/fields/__tests__/DataSelector-test.js
@@ -35,10 +35,10 @@ describe('DataSelector', () => {
it('uses gd.data dataSrc value not fullValue when arrayOk', () => {});
it('calls updatePlot with srcAttr and data when present', () => {
- const onUpdate = jest.fn();
- const wrapper = render({onUpdate}).find(DropdownWidget);
+ const onUpdateTraces = jest.fn();
+ const wrapper = render({onUpdateTraces}).find(DropdownWidget);
wrapper.prop('onChange')('y1');
- expect(onUpdate.mock.calls[0][0].payload).toEqual({
+ expect(onUpdateTraces.mock.calls[0][0]).toEqual({
update: {xsrc: 'y1', x: [2, 3, 4]},
traceIndexes: [0],
});
diff --git a/src/components/fields/__tests__/TraceSelector-test.js b/src/components/fields/__tests__/TraceSelector-test.js
index 8431456ee..f9db1ca1a 100644
--- a/src/components/fields/__tests__/TraceSelector-test.js
+++ b/src/components/fields/__tests__/TraceSelector-test.js
@@ -46,10 +46,10 @@ describe('TraceSelector', () => {
});
it('updates type=scatter mode=lines when type=line', () => {
- const onUpdate = jest.fn();
+ const onUpdateTraces = jest.fn();
const editorProps = {
...fixtures.scatter({data: [{type: 'scatter', mode: 'markers'}]}),
- onUpdate,
+ onUpdateTraces,
plotly,
};
const wrapper = mount(
@@ -63,7 +63,7 @@ describe('TraceSelector', () => {
const innerDropdown = wrapper.find(Dropdown);
innerDropdown.prop('onChange')('line');
- const {payload} = onUpdate.mock.calls[0][0];
+ const payload = onUpdateTraces.mock.calls[0][0];
expect(payload.update).toEqual({
fill: 'none',
mode: 'lines',
@@ -72,10 +72,10 @@ describe('TraceSelector', () => {
});
it('updates type=scatter fill=tozeroy when type=area', () => {
- const onUpdate = jest.fn();
+ const onUpdateTraces = jest.fn();
const editorProps = {
...fixtures.scatter({data: [{type: 'scatter', mode: 'markers'}]}),
- onUpdate,
+ onUpdateTraces,
plotly,
};
const wrapper = mount(
@@ -89,7 +89,7 @@ describe('TraceSelector', () => {
const innerDropdown = wrapper.find(Dropdown);
innerDropdown.prop('onChange')('area');
- const {payload} = onUpdate.mock.calls[0][0];
+ const payload = onUpdateTraces.mock.calls[0][0];
expect(payload.update).toEqual({fill: 'tozeroy', type: 'scatter'});
});
});
diff --git a/src/hub.js b/src/hub.js
deleted file mode 100644
index 2bbdf2f00..000000000
--- a/src/hub.js
+++ /dev/null
@@ -1,165 +0,0 @@
-import nestedProperty from 'plotly.js/src/lib/nested_property';
-import {EDITOR_ACTIONS} from './lib/constants';
-import isNumeric from 'fast-isnumeric';
-
-/* eslint-disable no-console */
-const log = console.log.bind(console);
-/* eslint-enable no-console */
-
-// Revisions: An optional system to prevent unnecessary rerenders.
-// There are two revision counters. One for the react-plotly.js
-// component and one for react-plotly.js-editor. They are uncoupled because
-// the plot can update the data internally (for example if a user updates the
-// title in the plot directly). This should trigger a editorSourcesUpdated counter
-// but not a figureUpdated counter since in this case the plot has already updated
-// and only the editor needs updating. Similarly there is the case where dataSources
-// may update but the Plotly.react component may determine that nothing needs to
-// be rerendered. In this case the Editor may not need to update.
-// If the revisions are not passed to react-plotly.js or react-plotly.js-editor
-// they will always update.
-export default class PlotlyHub {
- constructor(config = {}) {
- this.config = config;
- this.editorRevision = config.editorRevision || 0;
- this.plotRevision = config.plotRevision || 0;
-
- this.figureUpdated = this.figureUpdated.bind(this);
- this.editorSourcesUpdated = this.editorSourcesUpdated.bind(this);
- this.handlePlotUpdate = this.handlePlotUpdate.bind(this);
- this.handlePlotInitialized = this.handlePlotInitialized.bind(this);
- this.handleEditorUpdate = this.handleEditorUpdate.bind(this);
- }
-
- //
- // @method figureUpdates
- //
- // The underlying figure has changed. Calling this function
- // will alert the Plotly.js component that it must redraw.
- figureUpdated() {
- this.plotRevision++;
- const {plotRevision, editorRevision} = this;
- if (this.config.debug) {
- log('hub.figureUpdated', {plotRevision, editorRevision});
- }
- if (this.config.onUpdate) {
- this.config.onUpdate({
- plotRevision,
- editorRevision,
- graphDiv: this.graphDiv,
- });
- }
- }
-
- //
- // @method editorSourcesUpdated
- //
- // The plot has rendered or other editor data such as dataSourceOptions
- // has changed. Calling this function will alert the Editor that it must redraw.
- editorSourcesUpdated() {
- this.editorRevision++;
- const {plotRevision, editorRevision} = this;
- if (this.config.debug) {
- log('hub.editorSourcesUpdated', {plotRevision, editorRevision});
- }
- if (this.config.onUpdate) {
- this.config.onUpdate({
- plotRevision,
- editorRevision,
- graphDiv: this.graphDiv,
- });
- }
- }
-
- //
- // @method handlePlotUpdate
- //
- // Triggers editor UI update when the plot has been modified,
- // whether by a restyle, a redraw, or by some other interaction.
- //
- // @param {object} graphDiv - graph div
- //
- handlePlotUpdate(graphDiv) {
- if (this.config.debug) {
- log('hub.handlePlotUpdate');
- }
- this.graphDiv = graphDiv;
- this.editorSourcesUpdated();
- }
-
- //
- // @method handlePlotUpdate
- //
- // Triggers editor UI update when the plot has been modified,
- // whether by a restyle, a
- // redraw, or by some other interaction.
- //
- // @param {object} graphDiv - graph div
- //
- handlePlotInitialized(graphDiv) {
- if (this.config.debug) {
- log('hub.handlePlotInitialized');
- }
- this.graphDiv = graphDiv;
- this.editorSourcesUpdated();
- }
-
- //
- // @method handleEditorUpdate
- //
- handleEditorUpdate({graphDiv, type, payload}) {
- if (this.config.debug) {
- log('hub.handleEditorUpdate', {type, payload});
- }
-
- this.graphDiv = graphDiv;
-
- switch (type) {
- case EDITOR_ACTIONS.UPDATE_TRACES:
- for (let i = 0; i < payload.traceIndexes.length; i++) {
- for (const attr in payload.update) {
- const traceIndex = payload.traceIndexes[i];
- const prop = nestedProperty(graphDiv.data[traceIndex], attr);
- const value = payload.update[attr];
- if (value !== void 0) {
- prop.set(value);
- }
- }
- }
- this.figureUpdated();
- break;
-
- case EDITOR_ACTIONS.UPDATE_LAYOUT:
- for (const attr in payload.update) {
- const prop = nestedProperty(graphDiv.layout, attr);
- const value = payload.update[attr];
- if (value !== void 0) {
- prop.set(value);
- }
- }
- this.figureUpdated();
- break;
-
- case EDITOR_ACTIONS.ADD_TRACE:
- graphDiv.data.push({x: [], y: []});
- this.figureUpdated();
- break;
-
- case EDITOR_ACTIONS.DELETE_TRACE:
- if (payload.traceIndexes && payload.traceIndexes.length) {
- graphDiv.data.splice(payload[0], 1);
- this.figureUpdated();
- }
- break;
-
- case EDITOR_ACTIONS.DELETE_ANNOTATION:
- if (isNumeric(payload.annotationIndex)) {
- graphDiv.layout.annotations.splice(payload.annotationIndex, 1);
- this.figureUpdated();
- }
- break;
-
- default:
- throw new Error('must specify an action type to handleEditorUpdate');
- }
- }
-}
diff --git a/src/index.js b/src/index.js
index a7cdbf152..84b314887 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,4 +1,3 @@
-import Hub from './hub';
import PlotlyEditor from './PlotlyEditor';
import {
connectAnnotationToLayout,
@@ -59,7 +58,6 @@ export {
Flaglist,
Fold,
FontSelector,
- Hub,
Info,
Layout,
LayoutNumericFraction,
diff --git a/src/lib/__tests__/connectAnnotationToLayout-test.js b/src/lib/__tests__/connectAnnotationToLayout-test.js
index 5b204b43a..1cf546f9a 100644
--- a/src/lib/__tests__/connectAnnotationToLayout-test.js
+++ b/src/lib/__tests__/connectAnnotationToLayout-test.js
@@ -1,13 +1,12 @@
import NumericInput from '../../components/widgets/NumericInput';
import React from 'react';
-import {EDITOR_ACTIONS} from '../constants';
import {Numeric} from '../../components/fields';
import {TestEditor, fixtures, mount} from '../test-utils';
import {connectLayoutToPlot, connectAnnotationToLayout} from '..';
describe('connectAnnotationToLayout', () => {
it('sends update to layout.annotation[index]', () => {
- const onUpdate = jest.fn();
+ const onUpdateLayout = jest.fn();
const fixture = fixtures.scatter({
layout: {annotations: [{text: 'hodor'}]},
});
@@ -16,7 +15,7 @@ describe('connectAnnotationToLayout', () => {
);
mount(
-
+
)
@@ -24,9 +23,7 @@ describe('connectAnnotationToLayout', () => {
.find('.js-numeric-increase')
.simulate('click');
- const update = onUpdate.mock.calls[0][0];
- const {type, payload} = update;
- expect(type).toBe(EDITOR_ACTIONS.UPDATE_LAYOUT);
+ const payload = onUpdateLayout.mock.calls[0][0];
expect(payload.update).toEqual({'annotations[0].textangle': 1});
});
});
diff --git a/src/lib/__tests__/connectLayoutToPlot-test.js b/src/lib/__tests__/connectLayoutToPlot-test.js
index 297922340..0403743e3 100644
--- a/src/lib/__tests__/connectLayoutToPlot-test.js
+++ b/src/lib/__tests__/connectLayoutToPlot-test.js
@@ -1,7 +1,6 @@
import NumericInput from '../../components/widgets/NumericInput';
import React from 'react';
import connectLayoutToPlot from '../connectLayoutToPlot';
-import {EDITOR_ACTIONS} from '../constants';
import {Fold, Panel, Section} from '../../components/containers';
import {Numeric} from '../../components/fields';
import {TestEditor, fixtures, plotly} from '../test-utils';
@@ -29,10 +28,10 @@ Layouts.forEach(Layout => {
});
it(`sends updates to gd._layout`, () => {
- const onUpdate = jest.fn();
+ const onUpdateLayout = jest.fn();
const wrapper = mount(
@@ -45,9 +44,8 @@ Layouts.forEach(Layout => {
const widthUpdate = 200;
wrapper.prop('onChange')(widthUpdate);
- const event = onUpdate.mock.calls[0][0];
- expect(event.type).toBe(EDITOR_ACTIONS.UPDATE_LAYOUT);
- expect(event.payload).toEqual({update: {width: widthUpdate}});
+ const payload = onUpdateLayout.mock.calls[0][0];
+ expect(payload).toEqual({update: {width: widthUpdate}});
});
it(`automatically computes min and max defaults`, () => {
diff --git a/src/lib/__tests__/connectTraceToPlot-test.js b/src/lib/__tests__/connectTraceToPlot-test.js
index 5c36074b1..b8a9f0633 100644
--- a/src/lib/__tests__/connectTraceToPlot-test.js
+++ b/src/lib/__tests__/connectTraceToPlot-test.js
@@ -1,7 +1,6 @@
import NumericInput from '../../components/widgets/NumericInput';
import React from 'react';
import connectTraceToPlot from '../connectTraceToPlot';
-import {EDITOR_ACTIONS} from '../constants';
import {Fold, Panel, Section} from '../../components/containers';
import {Numeric} from '../../components/fields';
import {TestEditor, fixtures, plotly} from '../test-utils';
@@ -31,9 +30,9 @@ Traces.forEach(Trace => {
});
it('sends updates to gd.data', () => {
- const onUpdate = jest.fn();
+ const onUpdateTraces = jest.fn();
const wrapper = mount(
-
+
@@ -44,18 +43,16 @@ Traces.forEach(Trace => {
const sizeUpdate = 200;
wrapper.prop('onChange')(sizeUpdate);
- const event = onUpdate.mock.calls[0][0];
- expect(event.type).toBe(EDITOR_ACTIONS.UPDATE_TRACES);
- expect(event.payload).toEqual({
+ const payload = onUpdateTraces.mock.calls[0][0];
+ expect(payload).toEqual({
update: {'marker.size': sizeUpdate},
traceIndexes: [0],
});
});
it('automatically computes min and max defaults', () => {
- const onUpdate = jest.fn();
const wrapper = mount(
-
+
diff --git a/src/lib/__tests__/multiValued-test.js b/src/lib/__tests__/multiValued-test.js
index 21294b463..b9b128ec0 100644
--- a/src/lib/__tests__/multiValued-test.js
+++ b/src/lib/__tests__/multiValued-test.js
@@ -36,7 +36,7 @@ describe('multiValued Numeric', () => {
});
it('uses multiValued defaultContainer as default increment value', () => {
- const onUpdate = jest.fn();
+ const onUpdateLayout = jest.fn();
const xaxisLowerRange = -30;
const fixtureProps = fixtures.scatter({
layout: {xaxis: {range: [xaxisLowerRange, 3]}, yaxis: {range: [0, 3]}},
@@ -44,16 +44,15 @@ describe('multiValued Numeric', () => {
const AxesNumeric = connectLayoutToPlot(connectAxesToLayout(Numeric));
mount(
-
+
)
.find('.js-numeric-increase')
.simulate('click');
- expect(onUpdate).toBeCalled();
- const update = onUpdate.mock.calls[0][0];
- const {payload} = update;
+ expect(onUpdateLayout).toBeCalled();
+ const payload = onUpdateLayout.mock.calls[0][0];
expect(payload.update).toEqual({
'xaxis.range[0]': xaxisLowerRange,
'yaxis.range[0]': xaxisLowerRange,
diff --git a/src/lib/__tests__/nestedContainerConnections-test.js b/src/lib/__tests__/nestedContainerConnections-test.js
index 709f4abcd..f7e47a04d 100644
--- a/src/lib/__tests__/nestedContainerConnections-test.js
+++ b/src/lib/__tests__/nestedContainerConnections-test.js
@@ -1,6 +1,5 @@
import NumericInput from '../../components/widgets/NumericInput';
import React from 'react';
-import {EDITOR_ACTIONS} from '../constants';
import {Numeric, Section, Panel} from '../../components';
import {TestEditor, fixtures, mount} from '../test-utils';
import {
@@ -14,13 +13,13 @@ import {
describe('Plot Connection', () => {
it('can connect Field directly with full connection pipeline', () => {
- const onUpdate = jest.fn();
+ const onUpdateLayout = jest.fn();
const fixtureProps = fixtures.scatter({layout: {xaxis: {range: [0, 10]}}});
const LayoutAxesNumeric = connectLayoutToPlot(
connectAxesToLayout(connectToContainer(Numeric))
);
mount(
-
+
)
@@ -29,21 +28,19 @@ describe('Plot Connection', () => {
.find('.js-numeric-increase')
.simulate('click');
- expect(onUpdate).toBeCalled();
- const update = onUpdate.mock.calls[0][0];
- const {type, payload} = update;
- expect(type).toBe(EDITOR_ACTIONS.UPDATE_LAYOUT);
+ expect(onUpdateLayout).toBeCalled();
+ const payload = onUpdateLayout.mock.calls[0][0];
expect(payload).toEqual({update: {'xaxis.range[0]': 1}});
});
it('can connect to layout when connected within trace context', () => {
- const onUpdate = jest.fn();
+ const onUpdateLayout = jest.fn();
const fixtureProps = fixtures.scatter({layout: {width: 10}});
const TraceLayoutNumeric = connectTraceToPlot(
connectLayoutToPlot(connectToContainer(Numeric))
);
mount(
-
+
)
@@ -52,16 +49,13 @@ describe('Plot Connection', () => {
.find('.js-numeric-increase')
.simulate('click');
- expect(onUpdate).toBeCalled();
- const update = onUpdate.mock.calls[0][0];
- const {type, payload} = update;
- expect(type).toBe(EDITOR_ACTIONS.UPDATE_LAYOUT);
+ expect(onUpdateLayout).toBeCalled();
+ const payload = onUpdateLayout.mock.calls[0][0];
expect(payload).toEqual({update: {width: 11}});
});
// see https://github.com/plotly/react-plotly.js-editor/issues/58#issuecomment-345492794
it("can't find correct Container when Section divides Trace and Layout", () => {
- const onUpdate = jest.fn();
const fixtureProps = fixtures.scatter({layout: {width: 10}});
const DeeplyConnectedNumeric = connectTraceToPlot(
connectLayoutToPlot(
@@ -74,7 +68,7 @@ describe('Plot Connection', () => {
);
const wrapper = mount(
-
+
@@ -90,7 +84,7 @@ describe('Plot Connection', () => {
});
it('can supplyPlotProps within and nested Layout and Trace', () => {
- const onUpdate = jest.fn();
+ const onUpdateLayout = jest.fn();
const fixtureProps = fixtures.scatter({layout: {width: 10}});
const TracePanel = connectTraceToPlot(Panel);
const LayoutConnectedNumeric = connectLayoutToPlot(
@@ -105,7 +99,7 @@ describe('Plot Connection', () => {
);
mount(
-
+
@@ -118,10 +112,8 @@ describe('Plot Connection', () => {
.find('.js-numeric-increase')
.simulate('click');
- expect(onUpdate).toBeCalled();
- const update = onUpdate.mock.calls[0][0];
- const {type, payload} = update;
- expect(type).toBe(EDITOR_ACTIONS.UPDATE_LAYOUT);
+ expect(onUpdateLayout).toBeCalled();
+ const payload = onUpdateLayout.mock.calls[0][0];
expect(payload).toEqual({update: {width: 11}});
});