From 38527009d89b5163f65f33dbb4ca974d573adcb4 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Mon, 19 Nov 2018 13:44:03 -0500 Subject: [PATCH 01/41] specify react version for linter --- .eslintrc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 84e39d6..e67de14 100644 --- a/.eslintrc +++ b/.eslintrc @@ -33,5 +33,14 @@ }, "plugins": [ "react" - ] + ], + "settings": { + "react": { + "createClass": "createReactClass", // Regex for Component Factory to use, + // default to "createReactClass" + "pragma": "React", // Pragma to use, default to "React" + "version": "^15.6.1", // React version, default to the latest React stable release + + } + } } From a46527533dff49031d105cd885e768b6e61b3aeb Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Mon, 19 Nov 2018 14:40:05 -0500 Subject: [PATCH 02/41] simplify linter config to use default values --- .eslintrc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.eslintrc b/.eslintrc index e67de14..67b82c5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -36,11 +36,7 @@ ], "settings": { "react": { - "createClass": "createReactClass", // Regex for Component Factory to use, - // default to "createReactClass" - "pragma": "React", // Pragma to use, default to "React" - "version": "^15.6.1", // React version, default to the latest React stable release - + "version": "^15.6.1" } } } From 6a476876b1c5e4fae9bd7f0de8bdc9e2073346ba Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Thu, 22 Nov 2018 15:13:26 -0500 Subject: [PATCH 03/41] add circleCI integration --- .circleci/config.yml | 60 ++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 1 + 2 files changed, 61 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..8535135 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,60 @@ +version: 2.0 + +# Inspired by: +# https://github.com/CircleCI-Public/circleci-demo-workflows/blob/workspace-forwarding/.circleci/config.yml +# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs +# +# For list of official CircleCI node.js images, go to: +# https://hub.docker.com/r/circleci/node/tags/ + +jobs: + build: + docker: + - image: circleci/node:10.9.0 + working_directory: ~/react-plotly.js + steps: + - checkout + - restore_cache: + keys: + - v{{ .Environment.CIRCLE_CACHE_VERSION }}-deps-{{ .Branch }}-{{ checksum "package-lock.json" }} + - v{{ .Environment.CIRCLE_CACHE_VERSION }}-deps-master-{{ checksum "package-lock.json" }} + - run: + name: Install dependencies + command: | + npm install + - run: + name: List dependency versions + command: | + echo "npm: $(npm --version)" + echo "node: $(node --version)" + npm ls || true + - save_cache: + paths: + - node_modules + key: v{{ .Environment.CIRCLE_CACHE_VERSION }}-deps-{{ .Branch }}-{{ checksum "package.json" }} + - persist_to_workspace: + root: . + paths: + - node_modules + + test: + docker: + # need '-browsers' version to test in real (xvfb-wrapped) browsers + - image: circleci/node:10.9.0 + working_directory: ~/react-plotly.js + steps: + - checkout + - attach_workspace: + at: ~/react-plotly.js + - run: + name: Run tests + command: npm run test + +workflows: + version: 2 + build-and-test: + jobs: + - build + - test: + requires: + - build diff --git a/.gitignore b/.gitignore index 0f03570..0b45f15 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ npm-debug.log* *.sublime* .* +!.circleci !.gitignore !.gitattributes !.npmignore From 6007b7ca5c99130548f1aba2647c7705547b0cb5 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Thu, 22 Nov 2018 15:27:41 -0500 Subject: [PATCH 04/41] remove old unrelated comments --- .circleci/config.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8535135..3f1cec9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,5 @@ version: 2.0 -# Inspired by: -# https://github.com/CircleCI-Public/circleci-demo-workflows/blob/workspace-forwarding/.circleci/config.yml -# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs -# -# For list of official CircleCI node.js images, go to: -# https://hub.docker.com/r/circleci/node/tags/ - jobs: build: docker: @@ -39,7 +32,6 @@ jobs: test: docker: - # need '-browsers' version to test in real (xvfb-wrapped) browsers - image: circleci/node:10.9.0 working_directory: ~/react-plotly.js steps: From 5141dd62250a5d5c33204635a4400cca2cd0af79 Mon Sep 17 00:00:00 2001 From: AntoineDoubovetzky Date: Wed, 20 Feb 2019 17:21:38 +0100 Subject: [PATCH 05/41] fix Plot data mode in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18b6634..4984770 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ class App extends React.Component { x: [1, 2, 3], y: [2, 6, 3], type: 'scatter', - mode: 'lines+points', + mode: 'lines+markers', marker: {color: 'red'}, }, {type: 'bar', x: [1, 2, 3], y: [2, 5, 3]}, From 5bb2d18ccef11f70345012c0b5614569792b0b53 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Wed, 20 Feb 2019 15:09:24 -0500 Subject: [PATCH 06/41] Sync dev tools with other plotly react projects; fix linter issues --- .babelrc | 2 +- .eslintrc | 312 ++++++++++++++++++++++++++--- .prettierrc | 7 +- package.json | 9 +- src/__mocks__/plotly.js | 6 +- src/__tests__/react-plotly.test.js | 28 +-- src/factory.js | 55 +++-- 7 files changed, 331 insertions(+), 88 deletions(-) diff --git a/.babelrc b/.babelrc index 0b2bb39..23744b7 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,3 @@ { - "presets": ["react", "es2015"], + "presets": ["react", "env"] } diff --git a/.eslintrc b/.eslintrc index 67b82c5..e7bb059 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,42 +1,298 @@ { + "extends": [ + "eslint:recommended", + "prettier" + ], + "parser": "babel-eslint", "parserOptions": { "ecmaVersion": 6, "sourceType": "module", "ecmaFeatures": { + "arrowFunctions": true, + "blockBindings": true, + "classes": true, + "defaultParams": true, + "destructuring": true, + "forOf": true, + "generators": true, + "modules": true, + "templateStrings": true, "jsx": true } }, - "rules": { - "semi": 2, - "no-unused-vars": 2, - "react/display-name": 2, - "react/jsx-key": 2, - "react/jsx-no-comment-textnodes": 2, - "react/jsx-no-duplicate-props": 2, - "react/jsx-no-target-blank": 2, - "react/jsx-no-undef": 2, - "react/jsx-uses-react": 2, - "react/jsx-uses-vars": 2, - "react/no-children-prop": 2, - "react/no-danger-with-children": 2, - "react/no-deprecated": 2, - "react/no-direct-mutation-state": 2, - "react/no-find-dom-node": 2, - "react/no-is-mounted": 2, - "react/no-render-return-value": 2, - "react/no-string-refs": 2, - "react/no-unescaped-entities": 2, - "react/no-unknown-property": 2, - "react/prop-types": 2, - "react/react-in-jsx-scope": 2, - "react/require-render-return": 2, + "env": { + "browser": true, + "es6": true, + "jasmine": true, + "jest": true, + "node": true + }, + "globals": { + "jest": true }, "plugins": [ - "react" + "react", + "import" ], - "settings": { - "react": { - "version": "^15.6.1" + "overrides": [ + { + "files": [ + "**/*.percy.{js,jsx}" + ], + "env": { + "react-percy/globals": true + } } + ], + "rules": { + "accessor-pairs": [ + "error" + ], + "block-scoped-var": [ + "error" + ], + "consistent-return": [ + "error" + ], + "curly": [ + "error", + "all" + ], + "default-case": [ + "error" + ], + "dot-location": [ + "off" + ], + "dot-notation": [ + "error" + ], + "eqeqeq": [ + "error" + ], + "guard-for-in": [ + "off" + ], + "import/named": [ + "off" + ], + "import/no-duplicates": [ + "error" + ], + "import/no-named-as-default": [ + "error" + ], + "new-cap": [ + "error" + ], + "no-alert": [ + 1 + ], + "no-caller": [ + "error" + ], + "no-case-declarations": [ + "error" + ], + "no-console": [ + "error" + ], + "no-div-regex": [ + "error" + ], + "no-dupe-keys": [ + "error" + ], + "no-else-return": [ + "error" + ], + "no-empty-pattern": [ + "error" + ], + "no-eq-null": [ + "error" + ], + "no-eval": [ + "error" + ], + "no-extend-native": [ + "error" + ], + "no-extra-bind": [ + "error" + ], + "no-extra-boolean-cast": [ + "error" + ], + "no-inline-comments": [ + "error" + ], + "no-implicit-coercion": [ + "error" + ], + "no-implied-eval": [ + "error" + ], + "no-inner-declarations": [ + "off" + ], + "no-invalid-this": [ + "error" + ], + "no-iterator": [ + "error" + ], + "no-labels": [ + "error" + ], + "no-lone-blocks": [ + "error" + ], + "no-loop-func": [ + "error" + ], + "no-multi-str": [ + "error" + ], + "no-native-reassign": [ + "error" + ], + "no-new": [ + "error" + ], + "no-new-func": [ + "error" + ], + "no-new-wrappers": [ + "error" + ], + "no-param-reassign": [ + "error" + ], + "no-process-env": [ + "warn" + ], + "no-proto": [ + "error" + ], + "no-redeclare": [ + "error" + ], + "no-return-assign": [ + "error" + ], + "no-script-url": [ + "error" + ], + "no-self-compare": [ + "error" + ], + "no-sequences": [ + "error" + ], + "no-shadow": [ + "off" + ], + "no-throw-literal": [ + "error" + ], + "no-undefined": [ + "error" + ], + "no-unused-expressions": [ + "error" + ], + "no-use-before-define": [ + "error", + "nofunc" + ], + "no-useless-call": [ + "error" + ], + "no-useless-concat": [ + "error" + ], + "no-with": [ + "error" + ], + "prefer-const": [ + "error" + ], + "radix": [ + "error" + ], + "react/jsx-no-duplicate-props": [ + "error" + ], + "react/jsx-no-undef": [ + "error" + ], + "react/jsx-uses-react": [ + "error" + ], + "react/jsx-uses-vars": [ + "error" + ], + "react/no-did-update-set-state": [ + "error" + ], + "react/no-direct-mutation-state": [ + "error" + ], + "react/no-is-mounted": [ + "error" + ], + "react/no-unknown-property": [ + "error" + ], + "react/prefer-es6-class": [ + "error", + "always" + ], + "react/prop-types": "error", + "valid-jsdoc": [ + "error" + ], + "yoda": [ + "error" + ], + "spaced-comment": [ + "error", + "always", + { + "block": { + "exceptions": [ + "*" + ] + } + } + ], + "no-unused-vars": [ + "error", + { + "args": "after-used", + "argsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^e$" + } + ], + "no-magic-numbers": [ + "error", + { + "ignoreArrayIndexes": true, + "ignore": [ + -1, + 0, + 1, + 2, + 3, + 100, + 10, + 0.5 + ] + } + ], + "no-underscore-dangle": [ + "off" + ] } } diff --git a/.prettierrc b/.prettierrc index 9361dea..73d0133 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,6 @@ { - "singleQuote": true, - "bracketSpacing": false, - "trailingComma": "es5" + "singleQuote": true, + "bracketSpacing": false, + "trailingComma": "es5", + "printWidth": 100 } diff --git a/package.json b/package.json index 90ff178..8906e7b 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ "url": "https://github.com/plotly/react-plotly.js/issues" }, "scripts": { - "make:lib": "mkdirp lib && babel src --out-dir=lib --ignore __tests__/*.js,__mocks__/*.js --presets=es2015,react --source-maps --plugins babel-plugin-add-module-exports && mv lib/* ./ && rmdir lib", - "make:dist": "mkdirp dist && browserify src/factory.js -o ./dist/create-plotly-component.js -t [ babelify --presets [ es2015 react ] --plugins add-module-exports ] -t browserify-global-shim --standalone createPlotlyComponent && uglifyjs ./dist/create-plotly-component.js --compress --mangle --output ./dist/create-plotly-component.min.js --source-map filename=dist/create-plotly-component.min.js.map", + "make:lib": "mkdirp lib && babel src --out-dir=lib --ignore __tests__/*.js,__mocks__/*.js --presets=env,react --source-maps --plugins babel-plugin-add-module-exports && mv lib/* ./ && rmdir lib", + "make:dist": "mkdirp dist && browserify src/factory.js -o ./dist/create-plotly-component.js -t [ babelify --presets [ env react ] --plugins add-module-exports ] -t browserify-global-shim --standalone createPlotlyComponent && uglifyjs ./dist/create-plotly-component.js --compress --mangle --output ./dist/create-plotly-component.min.js --source-map filename=dist/create-plotly-component.min.js.map", "clean": "rimraf lib dist react-plotly.js react-plotly.js.map factory.js factory.js.map", "prepublishOnly": "npm run clean && npm run make:lib && npm run make:dist", "lint": "prettier --trailing-comma es5 --write \"src/**/*.js\" && eslint src", @@ -33,9 +33,10 @@ ], "devDependencies": { "babel-cli": "^6.24.1", + "babel-eslint": "^10.0.1", "babel-plugin-add-module-exports": "^0.2.1", "babel-plugin-transform-class-properties": "^6.24.1", - "babel-preset-es2015": "^6.24.1", + "babel-preset-env": "^1.7.0", "babel-preset-react": "^6.24.1", "babelify": "^7.3.0", "brfs": "^1.4.3", @@ -45,6 +46,8 @@ "dependency-check": "^2.9.1", "enzyme": "^2.9.1", "eslint": "^4.8.0", + "eslint-config-prettier": "^4.0.0", + "eslint-plugin-import": "^2.16.0", "eslint-plugin-react": "^7.4.0", "event-emitter": "^0.3.5", "jest": "^20.0.4", diff --git a/src/__mocks__/plotly.js b/src/__mocks__/plotly.js index 8cc7d89..b6f1f38 100644 --- a/src/__mocks__/plotly.js +++ b/src/__mocks__/plotly.js @@ -12,7 +12,7 @@ export default { }), newPlot: jest.fn(gd => { state.gd = gd; - EventEmitter(state.gd); + EventEmitter(state.gd); // eslint-disable-line new-cap setTimeout(() => { state.gd.emit('plotly_afterplot'); @@ -20,7 +20,7 @@ export default { }), react: jest.fn(gd => { state.gd = gd; - EventEmitter(state.gd); + EventEmitter(state.gd); // eslint-disable-line new-cap setTimeout(() => { state.gd.emit('plotly_afterplot'); @@ -40,6 +40,6 @@ export default { }), update: jest.fn(), purge: jest.fn(() => { - state.gd = nll; + state.gd = null; }), }; diff --git a/src/__tests__/react-plotly.test.js b/src/__tests__/react-plotly.test.js index 4db358c..e6d6e6c 100644 --- a/src/__tests__/react-plotly.test.js +++ b/src/__tests__/react-plotly.test.js @@ -9,11 +9,7 @@ describe('', () => { function createPlot(props) { return new Promise((resolve, reject) => { const plot = mount( - resolve(plot)} - onError={reject} - /> + resolve(plot)} onError={reject} /> ); }); } @@ -24,9 +20,9 @@ describe('', () => { Object.assign( defaultArgs || { data: [], - config: undefined, - layout: undefined, - frames: undefined, + config: undefined, // eslint-disable-line no-undefined + layout: undefined, // eslint-disable-line no-undefined + frames: undefined, // eslint-disable-line no-undefined }, props || {} ) @@ -139,19 +135,11 @@ describe('', () => { }) .then(plot => { // Update with and without revision bumps: + /* eslint-disable no-magic-numbers */ setTimeout(() => plot.setProps({layout: {title: 'test test'}}), 10); - setTimeout( - () => plot.setProps({revision: 1, layout: {title: 'test test'}}), - 20 - ); - setTimeout( - () => plot.setProps({revision: 1, layout: {title: 'test test'}}), - 30 - ); - setTimeout( - () => plot.setProps({revision: 2, layout: {title: 'test test'}}), - 40 - ); + setTimeout(() => plot.setProps({revision: 1, layout: {title: 'test test'}}), 20); + setTimeout(() => plot.setProps({revision: 1, layout: {title: 'test test'}}), 30); + setTimeout(() => plot.setProps({revision: 2, layout: {title: 'test test'}}), 40); }) .catch(err => done.fail(err)); }); diff --git a/src/factory.js b/src/factory.js index c207fcb..3c8cfb3 100644 --- a/src/factory.js +++ b/src/factory.js @@ -78,28 +78,23 @@ export default function plotComponentFactory(Plotly) { .then(this.attachUpdateEvents) .then(() => this.figureCallback(this.props.onInitialized)) .catch(err => { - console.error('Error while plotting:', err); - return this.props.onError && this.props.onError(err); + console.error('Error while plotting:', err); // eslint-disable-line no-console + if (this.props.onError) { + this.props.onError(err); + } }); } componentWillUpdate(nextProps) { - if ( - nextProps.revision !== void 0 && - nextProps.revision === this.props.revision - ) { + if (nextProps.revision !== void 0 && nextProps.revision === this.props.revision) { // if revision is set and unchanged, do nothing return; } const numPrevFrames = - this.props.frames && this.props.frames.length - ? this.props.frames.length - : 0; + this.props.frames && this.props.frames.length ? this.props.frames.length : 0; const numNextFrames = - nextProps.frames && nextProps.frames.length - ? nextProps.frames.length - : 0; + nextProps.frames && nextProps.frames.length ? nextProps.frames.length : 0; if ( nextProps.layout === this.props.layout && nextProps.data === this.props.data && @@ -124,8 +119,10 @@ export default function plotComponentFactory(Plotly) { .then(() => this.syncWindowResize(nextProps)) .then(() => this.figureCallback(nextProps.onUpdate)) .catch(err => { - console.error('Error while plotting:', err); - this.props.onError && this.props.onError(err); + console.error('Error while plotting:', err); // eslint-disable-line no-console + if (this.props.onError) { + this.props.onError(err); + } }); } @@ -143,7 +140,9 @@ export default function plotComponentFactory(Plotly) { } attachUpdateEvents() { - if (!this.el || !this.el.removeListener) return; + if (!this.el || !this.el.removeListener) { + return; + } for (let i = 0; i < updateEvents.length; i++) { this.el.on(updateEvents[i], this.handleUpdate); @@ -151,7 +150,9 @@ export default function plotComponentFactory(Plotly) { } removeUpdateEvents() { - if (!this.el || !this.el.removeListener) return; + if (!this.el || !this.el.removeListener) { + return; + } for (let i = 0; i < updateEvents.length; i++) { this.el.removeListener(updateEvents[i], this.handleUpdate); @@ -165,17 +166,17 @@ export default function plotComponentFactory(Plotly) { figureCallback(callback) { if (typeof callback === 'function') { const {data, layout} = this.el; - const frames = this.el._transitionData - ? this.el._transitionData._frames - : null; - const figure = {data, layout, frames}; // for extra clarity! + const frames = this.el._transitionData ? this.el._transitionData._frames : null; + const figure = {data, layout, frames}; callback(figure, this.el); } } syncWindowResize(propsIn, invoke) { const props = propsIn || this.props; - if (!isBrowser) return; + if (!isBrowser) { + return; + } if (props.useResizeHandler && !this.resizeHandler) { this.resizeHandler = () => { @@ -207,20 +208,14 @@ export default function plotComponentFactory(Plotly) { for (let i = 0; i < eventNames.length; i++) { const eventName = eventNames[i]; const prop = props['on' + eventName]; - const hasHandler = !!this.handlers[eventName]; + const hasHandler = Boolean(this.handlers[eventName]); if (prop && !hasHandler) { this.handlers[eventName] = prop; - this.el.on( - 'plotly_' + eventName.toLowerCase(), - this.handlers[eventName] - ); + this.el.on('plotly_' + eventName.toLowerCase(), this.handlers[eventName]); } else if (!prop && hasHandler) { // Needs to be removed: - this.el.removeListener( - 'plotly_' + eventName.toLowerCase(), - this.handlers[eventName] - ); + this.el.removeListener('plotly_' + eventName.toLowerCase(), this.handlers[eventName]); delete this.handlers[eventName]; } } From 183d8b3a0e8f7f8fa3129e40ecc52f3b809700d0 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Wed, 20 Feb 2019 15:32:20 -0500 Subject: [PATCH 07/41] Fix error when trying to plot after component unmounted --- src/factory.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/factory.js b/src/factory.js index 3c8cfb3..23a0cb3 100644 --- a/src/factory.js +++ b/src/factory.js @@ -64,8 +64,20 @@ export default function plotComponentFactory(Plotly) { } componentDidMount() { + this.unmounting = false; + this.p = this.p .then(() => { + if (!this.el) { + let error; + if (this.unmounting) { + error = new Error('Component is unmounting'); + error.reason = 'unmounting'; + } else { + error = new Error('Missing element reference'); + } + throw error; + } return Plotly.newPlot(this.el, { data: this.props.data, layout: this.props.layout, @@ -78,6 +90,9 @@ export default function plotComponentFactory(Plotly) { .then(this.attachUpdateEvents) .then(() => this.figureCallback(this.props.onInitialized)) .catch(err => { + if (err.reason === 'unmounting') { + return; + } console.error('Error while plotting:', err); // eslint-disable-line no-console if (this.props.onError) { this.props.onError(err); @@ -86,6 +101,8 @@ export default function plotComponentFactory(Plotly) { } componentWillUpdate(nextProps) { + this.unmounting = false; + if (nextProps.revision !== void 0 && nextProps.revision === this.props.revision) { // if revision is set and unchanged, do nothing return; @@ -108,6 +125,16 @@ export default function plotComponentFactory(Plotly) { this.p = this.p .then(() => { + if (!this.el) { + let error; + if (this.unmounting) { + error = new Error('Component is unmounting'); + error.reason = 'unmounting'; + } else { + error = new Error('Missing element reference'); + } + throw error; + } return Plotly.react(this.el, { data: nextProps.data, layout: nextProps.layout, @@ -119,6 +146,9 @@ export default function plotComponentFactory(Plotly) { .then(() => this.syncWindowResize(nextProps)) .then(() => this.figureCallback(nextProps.onUpdate)) .catch(err => { + if (err.reason === 'unmounting') { + return; + } console.error('Error while plotting:', err); // eslint-disable-line no-console if (this.props.onError) { this.props.onError(err); @@ -127,6 +157,8 @@ export default function plotComponentFactory(Plotly) { } componentWillUnmount() { + this.unmounting = true; + this.figureCallback(this.props.onPurge); if (this.resizeHandler && isBrowser) { From 8dd24a6dba616b1d3e2b78e33445faf5e8356860 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Wed, 20 Feb 2019 18:05:29 -0500 Subject: [PATCH 08/41] Fix a link to the logo image --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4984770..23f2836 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # react-plotly.js -![plotly-react-logo](https://static1.squarespace.com/static/5a5adfdea9db09d594a841f3/t/5a5af2c5e2c48307ed4a21b6/1515975253370/) +![plotly-react-logo](https://images.plot.ly/plotly-documentation/thumbnail/react.png) > A [plotly.js](https://github.com/plotly/plotly.js) React component from > [Plotly](https://plot.ly/). The basis of Plotly's From f84d3b210e3749208773572763dc879c6de847e1 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Wed, 20 Feb 2019 18:58:29 -0500 Subject: [PATCH 09/41] Correct react peer dependency version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8906e7b..7fd82ff 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ }, "peerDependencies": { "plotly.js": ">1.34.0", - "react": ">12.0.0" + "react": ">0.13.0" }, "browserify-global-shim": { "react": "React" From a91fae8122034654bb016c0a4ab25cb9fd10089c Mon Sep 17 00:00:00 2001 From: dmt0 Date: Wed, 20 Feb 2019 21:42:10 -0500 Subject: [PATCH 10/41] Package upgrades; babel 6 -> 7 --- .babelrc | 2 +- .eslintrc | 17 +++++------ package.json | 45 +++++++++++++++--------------- src/__tests__/react-plotly.test.js | 4 ++- src/factory.js | 2 +- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.babelrc b/.babelrc index 23744b7..a6cf7e8 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,3 @@ { - "presets": ["react", "env"] + "presets": ["@babel/react", "@babel/env"] } diff --git a/.eslintrc b/.eslintrc index e7bb059..2619698 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,8 +1,15 @@ { "extends": [ "eslint:recommended", + "plugin:react/recommended", "prettier" ], + "settings": { + "react": { + "pragma": "React", + "version": "detect" + } + }, "parser": "babel-eslint", "parserOptions": { "ecmaVersion": 6, @@ -34,16 +41,6 @@ "react", "import" ], - "overrides": [ - { - "files": [ - "**/*.percy.{js,jsx}" - ], - "env": { - "react-percy/globals": true - } - } - ], "rules": { "accessor-pairs": [ "error" diff --git a/package.json b/package.json index 7fd82ff..f877895 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ "url": "https://github.com/plotly/react-plotly.js/issues" }, "scripts": { - "make:lib": "mkdirp lib && babel src --out-dir=lib --ignore __tests__/*.js,__mocks__/*.js --presets=env,react --source-maps --plugins babel-plugin-add-module-exports && mv lib/* ./ && rmdir lib", - "make:dist": "mkdirp dist && browserify src/factory.js -o ./dist/create-plotly-component.js -t [ babelify --presets [ env react ] --plugins add-module-exports ] -t browserify-global-shim --standalone createPlotlyComponent && uglifyjs ./dist/create-plotly-component.js --compress --mangle --output ./dist/create-plotly-component.min.js --source-map filename=dist/create-plotly-component.min.js.map", + "make:lib": "mkdirp lib && babel src --out-dir lib --ignore \"src/__tests__/*.js\",\"src/__mocks__/*.js\" --presets=@babel/preset-env,@babel/preset-react --source-maps && mv lib/* ./ && rmdir lib", + "make:dist": "mkdirp dist && browserify src/factory.js -o ./dist/create-plotly-component.js -t [ babelify --presets [ @babel/preset-env @babel/preset-react ] --plugins [ @babel/plugin-proposal-class-properties ] ] -t browserify-global-shim --standalone createPlotlyComponent && uglifyjs ./dist/create-plotly-component.js --compress --mangle --output ./dist/create-plotly-component.min.js --source-map filename=dist/create-plotly-component.min.js.map", "clean": "rimraf lib dist react-plotly.js react-plotly.js.map factory.js factory.js.map", "prepublishOnly": "npm run clean && npm run make:lib && npm run make:dist", "lint": "prettier --trailing-comma es5 --write \"src/**/*.js\" && eslint src", @@ -32,34 +32,35 @@ "react" ], "devDependencies": { - "babel-cli": "^6.24.1", + "@babel/cli": "^7.2.3", + "@babel/core": "^7.3.3", + "@babel/plugin-proposal-class-properties": "^7.3.3", + "@babel/preset-env": "^7.3.1", + "@babel/preset-react": "^7.0.0", "babel-eslint": "^10.0.1", - "babel-plugin-add-module-exports": "^0.2.1", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-preset-env": "^1.7.0", - "babel-preset-react": "^6.24.1", - "babelify": "^7.3.0", - "brfs": "^1.4.3", - "browserify": "^14.4.0", + "babelify": "^10.0.0", + "brfs": "^2.0.2", + "browserify": "^16.2.3", "browserify-global-shim": "^1.0.3", "cash-mv": "^0.2.0", - "dependency-check": "^2.9.1", - "enzyme": "^2.9.1", - "eslint": "^4.8.0", + "dependency-check": "^3.3.0", + "enzyme": "^3.9.0", + "enzyme-adapter-react-16": "^1.9.1", + "eslint": "^5.14.1", "eslint-config-prettier": "^4.0.0", "eslint-plugin-import": "^2.16.0", - "eslint-plugin-react": "^7.4.0", + "eslint-plugin-react": "^7.12.4", "event-emitter": "^0.3.5", - "jest": "^20.0.4", + "jest": "^24.1.0", "mkdirp": "^0.5.1", - "nodemon": "^1.11.0", - "onetime": "^1.1.0", + "nodemon": "^1.18.10", + "onetime": "^3.0.0", "plotly.js": "^1.35.0", - "prettier": "^1.5.3", - "react": "^15.6.1", + "prettier": "^1.16.4", + "react": "^16.8.2", "react-addons-test-utils": "^15.6.0", - "react-dom": "^15.6.1", - "react-test-renderer": "^15.6.1", + "react-dom": "^16.8.2", + "react-test-renderer": "^16.8.2", "rimraf": "^2.6.2", "semver": "^5.4.1", "uglify-js": "^3.0.26" @@ -72,6 +73,6 @@ "react": "React" }, "dependencies": { - "prop-types": "^15.5.10" + "prop-types": "^15.7.2" } } diff --git a/src/__tests__/react-plotly.test.js b/src/__tests__/react-plotly.test.js index e6d6e6c..e43056e 100644 --- a/src/__tests__/react-plotly.test.js +++ b/src/__tests__/react-plotly.test.js @@ -1,10 +1,12 @@ import React from 'react'; -import {mount} from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import {mount, configure} from 'enzyme'; import createComponent from '../factory'; import once from 'onetime'; describe('', () => { let Plotly, PlotComponent; + configure({adapter: new Adapter()}); function createPlot(props) { return new Promise((resolve, reject) => { diff --git a/src/factory.js b/src/factory.js index 23a0cb3..0070c15 100644 --- a/src/factory.js +++ b/src/factory.js @@ -100,7 +100,7 @@ export default function plotComponentFactory(Plotly) { }); } - componentWillUpdate(nextProps) { + UNSAFE_componentWillUpdate(nextProps) { this.unmounting = false; if (nextProps.revision !== void 0 && nextProps.revision === this.props.revision) { From 0108bbee100ad6d23d100902a75d445fa93b6f0c Mon Sep 17 00:00:00 2001 From: dmt0 Date: Wed, 20 Feb 2019 23:29:41 -0500 Subject: [PATCH 11/41] Update when figure changes regardless of revision --- README.md | 2 +- src/factory.js | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 23f2836..c1a5b98 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ In short, this means that simply adding data points to a trace in `data` or chan | `layout` | `Object` | `undefined` | layout object (see https://plot.ly/javascript/reference/#layout) | | `frames` | `Array` | `undefined` | list of frame objects (see https://plot.ly/javascript/reference/) | | `config` | `Object` | `undefined` | config object (see https://plot.ly/javascript/configuration-options/) | -| `revision` | `Number` | `undefined` | When provided, causes the plot to update _only_ when the revision is incremented. | +| `revision` | `Number` | `undefined` | When provided, causes the plot to update when the revision is incremented. | | `onInitialized` | `Function(figure, graphDiv)` | `undefined` | Callback executed after plot is initialized. See below for parameter information. | | `onUpdate` | `Function(figure, graphDiv)` | `undefined` | Callback executed when when a plot is updated due to new data or layout, or when user interacts with a plot. See below for parameter information. | | `onPurge` | `Function(figure, graphDiv)` | `undefined` | Callback executed when component unmounts, before `Plotly.purge` strips the `graphDiv` of all private attributes. See below for parameter information. | diff --git a/src/factory.js b/src/factory.js index 0070c15..92af98a 100644 --- a/src/factory.js +++ b/src/factory.js @@ -103,23 +103,22 @@ export default function plotComponentFactory(Plotly) { UNSAFE_componentWillUpdate(nextProps) { this.unmounting = false; - if (nextProps.revision !== void 0 && nextProps.revision === this.props.revision) { - // if revision is set and unchanged, do nothing - return; - } - + // frames *always* changes identity so fall back to check length only :( const numPrevFrames = this.props.frames && this.props.frames.length ? this.props.frames.length : 0; const numNextFrames = nextProps.frames && nextProps.frames.length ? nextProps.frames.length : 0; - if ( + + const figureChanged = !( nextProps.layout === this.props.layout && nextProps.data === this.props.data && nextProps.config === this.props.config && numNextFrames === numPrevFrames - ) { - // prevent infinite loops when component is re-rendered after onUpdate - // frames *always* changes identity so fall back to check length only :( + ); + const revisionDefined = nextProps.revision !== void 0; + const revisionChanged = nextProps.revision !== this.props.revision; + + if (!figureChanged && (!revisionDefined || (revisionDefined && !revisionChanged))) { return; } From 4bedf331e8c1015aecf766fc4a0ad8d8d3d611e9 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Thu, 21 Feb 2019 13:08:18 -0500 Subject: [PATCH 12/41] Remove deprecated react methods; refactor --- src/__tests__/react-plotly.test.js | 10 +-- src/factory.js | 108 ++++++++++------------------- 2 files changed, 43 insertions(+), 75 deletions(-) diff --git a/src/__tests__/react-plotly.test.js b/src/__tests__/react-plotly.test.js index e43056e..104e6f4 100644 --- a/src/__tests__/react-plotly.test.js +++ b/src/__tests__/react-plotly.test.js @@ -44,10 +44,10 @@ describe('', () => { }); describe('initialization', function() { - test('calls Plotly.newPlot on instantiation', done => { + test('calls Plotly.react on instantiation', done => { createPlot({}) .then(() => { - expect(Plotly.newPlot).toHaveBeenCalled(); + expect(Plotly.react).toHaveBeenCalled(); }) .catch(err => { done.fail(err); @@ -61,7 +61,7 @@ describe('', () => { layout: {title: 'foo'}, }) .then(() => { - expectPlotlyAPICall(Plotly.newPlot, { + expectPlotlyAPICall(Plotly.react, { data: [{x: [1, 2, 3]}], layout: {title: 'foo'}, }); @@ -75,7 +75,7 @@ describe('', () => { layout: {width: 320, height: 240}, }) .then(() => { - expectPlotlyAPICall(Plotly.newPlot, { + expectPlotlyAPICall(Plotly.react, { layout: {width: 320, height: 240}, }); }) @@ -129,7 +129,7 @@ describe('', () => { // we've checked the third and fourth calls: if (callCnt === 2) { setTimeout(() => { - expect(Plotly.newPlot).not.toHaveBeenCalledTimes(2); + expect(Plotly.react).not.toHaveBeenCalledTimes(2); done(); }, 100); } diff --git a/src/factory.js b/src/factory.js index 92af98a..ac51840 100644 --- a/src/factory.js +++ b/src/factory.js @@ -61,11 +61,10 @@ export default function plotComponentFactory(Plotly) { this.getRef = this.getRef.bind(this); this.handleUpdate = this.handleUpdate.bind(this); this.figureCallback = this.figureCallback.bind(this); + this.updatePlotly = this.updatePlotly.bind(this); } - componentDidMount() { - this.unmounting = false; - + updatePlotly(shouldInvokeResizeHandler, figureCallbackFunction, shouldAttachUpdateEvents) { this.p = this.p .then(() => { if (!this.el) { @@ -78,17 +77,17 @@ export default function plotComponentFactory(Plotly) { } throw error; } - return Plotly.newPlot(this.el, { + return Plotly.react(this.el, { data: this.props.data, layout: this.props.layout, config: this.props.config, frames: this.props.frames, }); }) - .then(() => this.syncWindowResize(null, true)) + .then(() => this.syncWindowResize(shouldInvokeResizeHandler)) .then(this.syncEventHandlers) - .then(this.attachUpdateEvents) - .then(() => this.figureCallback(this.props.onInitialized)) + .then(() => this.figureCallback(figureCallbackFunction)) + .then(shouldAttachUpdateEvents ? this.attachUpdateEvents : () => {}) .catch(err => { if (err.reason === 'unmounting') { return; @@ -100,59 +99,35 @@ export default function plotComponentFactory(Plotly) { }); } - UNSAFE_componentWillUpdate(nextProps) { + componentDidMount() { + this.unmounting = false; + + this.updatePlotly(true, this.props.onInitialized, true); + } + + componentDidUpdate(prevProps) { this.unmounting = false; // frames *always* changes identity so fall back to check length only :( const numPrevFrames = - this.props.frames && this.props.frames.length ? this.props.frames.length : 0; + prevProps.frames && prevProps.frames.length ? prevProps.frames.length : 0; const numNextFrames = - nextProps.frames && nextProps.frames.length ? nextProps.frames.length : 0; + this.props.frames && this.props.frames.length ? this.props.frames.length : 0; const figureChanged = !( - nextProps.layout === this.props.layout && - nextProps.data === this.props.data && - nextProps.config === this.props.config && + prevProps.layout === this.props.layout && + prevProps.data === this.props.data && + prevProps.config === this.props.config && numNextFrames === numPrevFrames ); - const revisionDefined = nextProps.revision !== void 0; - const revisionChanged = nextProps.revision !== this.props.revision; + const revisionDefined = prevProps.revision !== void 0; + const revisionChanged = prevProps.revision !== this.props.revision; if (!figureChanged && (!revisionDefined || (revisionDefined && !revisionChanged))) { return; } - this.p = this.p - .then(() => { - if (!this.el) { - let error; - if (this.unmounting) { - error = new Error('Component is unmounting'); - error.reason = 'unmounting'; - } else { - error = new Error('Missing element reference'); - } - throw error; - } - return Plotly.react(this.el, { - data: nextProps.data, - layout: nextProps.layout, - config: nextProps.config, - frames: nextProps.frames, - }); - }) - .then(() => this.syncEventHandlers(nextProps)) - .then(() => this.syncWindowResize(nextProps)) - .then(() => this.figureCallback(nextProps.onUpdate)) - .catch(err => { - if (err.reason === 'unmounting') { - return; - } - console.error('Error while plotting:', err); // eslint-disable-line no-console - if (this.props.onError) { - this.props.onError(err); - } - }); + this.updatePlotly(false, this.props.onUpdate, false); } componentWillUnmount() { @@ -175,9 +150,9 @@ export default function plotComponentFactory(Plotly) { return; } - for (let i = 0; i < updateEvents.length; i++) { - this.el.on(updateEvents[i], this.handleUpdate); - } + updateEvents.forEach(updateEvent => { + this.el.on(updateEvent, this.handleUpdate); + }); } removeUpdateEvents() { @@ -185,9 +160,9 @@ export default function plotComponentFactory(Plotly) { return; } - for (let i = 0; i < updateEvents.length; i++) { - this.el.removeListener(updateEvents[i], this.handleUpdate); - } + updateEvents.forEach(updateEvent => { + this.el.removeListener(updateEvent, this.handleUpdate); + }); } handleUpdate() { @@ -203,21 +178,18 @@ export default function plotComponentFactory(Plotly) { } } - syncWindowResize(propsIn, invoke) { - const props = propsIn || this.props; + syncWindowResize(invoke) { if (!isBrowser) { return; } - if (props.useResizeHandler && !this.resizeHandler) { - this.resizeHandler = () => { - return Plotly.Plots.resize(this.el); - }; + if (this.props.useResizeHandler && !this.resizeHandler) { + this.resizeHandler = () => Plotly.Plots.resize(this.el); window.addEventListener('resize', this.resizeHandler); if (invoke) { this.resizeHandler(); } - } else if (!props.useResizeHandler && this.resizeHandler) { + } else if (!this.props.useResizeHandler && this.resizeHandler) { window.removeEventListener('resize', this.resizeHandler); this.resizeHandler = null; } @@ -232,13 +204,9 @@ export default function plotComponentFactory(Plotly) { } // Attach and remove event handlers as they're added or removed from props: - syncEventHandlers(propsIn) { - // Allow use of nextProps if passed explicitly: - const props = propsIn || this.props; - - for (let i = 0; i < eventNames.length; i++) { - const eventName = eventNames[i]; - const prop = props['on' + eventName]; + syncEventHandlers() { + eventNames.forEach(eventName => { + const prop = this.props['on' + eventName]; const hasHandler = Boolean(this.handlers[eventName]); if (prop && !hasHandler) { @@ -249,7 +217,7 @@ export default function plotComponentFactory(Plotly) { this.el.removeListener('plotly_' + eventName.toLowerCase(), this.handlers[eventName]); delete this.handlers[eventName]; } - } + }); } render() { @@ -281,9 +249,9 @@ export default function plotComponentFactory(Plotly) { divId: PropTypes.string, }; - for (let i = 0; i < eventNames.length; i++) { - PlotlyComponent.propTypes['on' + eventNames[i]] = PropTypes.func; - } + eventNames.forEach(eventName => { + PlotlyComponent.propTypes['on' + eventName] = PropTypes.func; + }); PlotlyComponent.defaultProps = { debug: false, From 60de2ff5c195b6e810d3d14eae959a2ba556e336 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Mon, 25 Feb 2019 12:54:14 -0500 Subject: [PATCH 13/41] Add test case: data changed, but revision is not --- src/__tests__/react-plotly.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/__tests__/react-plotly.test.js b/src/__tests__/react-plotly.test.js index 104e6f4..d1b7646 100644 --- a/src/__tests__/react-plotly.test.js +++ b/src/__tests__/react-plotly.test.js @@ -102,6 +102,24 @@ describe('', () => { .catch(err => done.fail(err)); }); + test('updates data when revision is defined but not changed', done => { + createPlot({ + revision: 1, + layout: {width: 123, height: 456}, + onUpdate: once(() => { + expectPlotlyAPICall(Plotly.react, { + data: [{x: [1, 2, 3]}], + layout: {width: 123, height: 456}, + }); + done(); + }), + }) + .then(plot => { + plot.setProps({revision: 1, data: [{x: [1, 2, 3]}]}); + }) + .catch(err => done.fail(err)); + }); + test('sets the title', done => { createPlot({ onUpdate: once(() => { From f50361cddbe6d34818d5b0ef0b5a9517a7ffc740 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Mon, 25 Feb 2019 15:53:35 -0500 Subject: [PATCH 14/41] Bugfixes; removed deprecated react methods; refactor; devDep updates --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f877895..38b899b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-plotly.js", - "version": "2.2.0", + "version": "2.3.0", "license": "MIT", "description": "A plotly.js react component from Plotly", "author": "Plotly, Inc.", From 54bf0b25316079510d28fd3de2ccfcc080842146 Mon Sep 17 00:00:00 2001 From: Nicolas Kruchten Date: Tue, 28 May 2019 12:11:10 -0400 Subject: [PATCH 15/41] Create FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..6f1019e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://plot.ly/products/consulting-and-oem/ From 8dd14f37d6d9b74b5fc63ac38672c691b0a1ade2 Mon Sep 17 00:00:00 2001 From: Matt Fehskens Date: Mon, 1 Jul 2019 15:31:23 -0400 Subject: [PATCH 16/41] Allow events to be replaced --- src/factory.js | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/factory.js b/src/factory.js index ac51840..a1f5dc3 100644 --- a/src/factory.js +++ b/src/factory.js @@ -207,19 +207,36 @@ export default function plotComponentFactory(Plotly) { syncEventHandlers() { eventNames.forEach(eventName => { const prop = this.props['on' + eventName]; - const hasHandler = Boolean(this.handlers[eventName]); + const handler = this.handlers[eventName]; + const hasHandler = Boolean(handler); if (prop && !hasHandler) { - this.handlers[eventName] = prop; - this.el.on('plotly_' + eventName.toLowerCase(), this.handlers[eventName]); + this.addEventHandler(eventName, prop); } else if (!prop && hasHandler) { // Needs to be removed: - this.el.removeListener('plotly_' + eventName.toLowerCase(), this.handlers[eventName]); - delete this.handlers[eventName]; + this.removeHandler(eventName); + } else if (prop && hasHandler && prop !== handler) { + // replace the handler + this.removeHandler(eventName); + this.addEventHandler(eventName, prop); } }); } + addEventHandler(eventName, prop) { + this.handlers[eventName] = prop; + this.el.on(this.getPlotlyEventName(eventName), this.handlers[eventName]); + } + + removeEventHandler(eventName) { + this.el.removeListener(this.getPlotlyEventName(eventName), this.handlers[eventName]); + delete this.handlers[eventName]; + } + + getPlotlyEventName(eventName) { + return 'plotly_' + eventName.toLowerCase(); + } + render() { return (
Date: Mon, 1 Jul 2019 16:54:22 -0400 Subject: [PATCH 17/41] Added correct name of remove handler method - Added test for checking that a handler was added --- src/__tests__/react-plotly.test.js | 15 +++++++++++++++ src/factory.js | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/__tests__/react-plotly.test.js b/src/__tests__/react-plotly.test.js index d1b7646..11de80d 100644 --- a/src/__tests__/react-plotly.test.js +++ b/src/__tests__/react-plotly.test.js @@ -164,5 +164,20 @@ describe('', () => { .catch(err => done.fail(err)); }); }); + + describe('manging event handlers', () => { + test('should add an event handler when one does not already exist', (done) => { + const onRelayout = () => {}; + + createPlot({onRelayout}).then((plot) => { + const { handlers } = plot.instance(); + + expect(plot.prop('onRelayout')).toBe(onRelayout); + expect(handlers.Relayout).toBe(onRelayout); + + done(); + }); + }); + }); }); }); diff --git a/src/factory.js b/src/factory.js index a1f5dc3..2bfb798 100644 --- a/src/factory.js +++ b/src/factory.js @@ -214,10 +214,10 @@ export default function plotComponentFactory(Plotly) { this.addEventHandler(eventName, prop); } else if (!prop && hasHandler) { // Needs to be removed: - this.removeHandler(eventName); + this.removeEventHandler(eventName); } else if (prop && hasHandler && prop !== handler) { // replace the handler - this.removeHandler(eventName); + this.removeEventHandler(eventName); this.addEventHandler(eventName, prop); } }); From cd3701eecbfcc9ca6d4691f36b6cd1405aea77af Mon Sep 17 00:00:00 2001 From: Vera Zabeida Date: Thu, 17 Oct 2019 13:28:11 -0400 Subject: [PATCH 18/41] 2.4.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 38b899b..cd0e0d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-plotly.js", - "version": "2.3.0", + "version": "2.4.0", "license": "MIT", "description": "A plotly.js react component from Plotly", "author": "Plotly, Inc.", From 0ada702da79de0f585de4f3f040a1943ab83185c Mon Sep 17 00:00:00 2001 From: Nicolas Kruchten Date: Thu, 9 Jul 2020 08:59:05 -0400 Subject: [PATCH 19/41] Create ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..f3635b2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,9 @@ + From 3d84748cabf7f616abf2e827d4bb4dc7698712c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Mo=C5=A1ko?= Date: Wed, 2 Sep 2020 12:41:08 +0200 Subject: [PATCH 20/41] fix(refs): fixed issue with referencing element of unmounted component #199 --- src/factory.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/factory.js b/src/factory.js index 2bfb798..0a4d07e 100644 --- a/src/factory.js +++ b/src/factory.js @@ -67,16 +67,13 @@ export default function plotComponentFactory(Plotly) { updatePlotly(shouldInvokeResizeHandler, figureCallbackFunction, shouldAttachUpdateEvents) { this.p = this.p .then(() => { + if (this.unmounting) { + return; + } if (!this.el) { - let error; - if (this.unmounting) { - error = new Error('Component is unmounting'); - error.reason = 'unmounting'; - } else { - error = new Error('Missing element reference'); - } - throw error; + throw new Error('Missing element reference'); } + // eslint-disable-next-line consistent-return return Plotly.react(this.el, { data: this.props.data, layout: this.props.layout, @@ -84,15 +81,18 @@ export default function plotComponentFactory(Plotly) { frames: this.props.frames, }); }) - .then(() => this.syncWindowResize(shouldInvokeResizeHandler)) - .then(this.syncEventHandlers) - .then(() => this.figureCallback(figureCallbackFunction)) - .then(shouldAttachUpdateEvents ? this.attachUpdateEvents : () => {}) - .catch(err => { - if (err.reason === 'unmounting') { + .then(() => { + if (this.unmounting) { return; } - console.error('Error while plotting:', err); // eslint-disable-line no-console + this.syncWindowResize(shouldInvokeResizeHandler); + this.syncEventHandlers(); + this.figureCallback(figureCallbackFunction); + if (shouldAttachUpdateEvents) { + this.attachUpdateEvents(); + } + }) + .catch(err => { if (this.props.onError) { this.props.onError(err); } From b83f4926a7b530fd35f2cd2c2d70073168f94c26 Mon Sep 17 00:00:00 2001 From: Lauri Himanen Date: Fri, 4 Sep 2020 12:24:37 +0300 Subject: [PATCH 21/41] Added support for the onRelayouting event. --- src/factory.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/factory.js b/src/factory.js index 2bfb798..3e1dcb5 100644 --- a/src/factory.js +++ b/src/factory.js @@ -22,6 +22,7 @@ const eventNames = [ 'LegendClick', 'LegendDoubleClick', 'Relayout', + 'Relayouting', 'Restyle', 'Redraw', 'Selected', @@ -38,6 +39,7 @@ const updateEvents = [ 'plotly_restyle', 'plotly_redraw', 'plotly_relayout', + 'plotly_relayouting', 'plotly_doubleclick', 'plotly_animated', ]; From 4c3d316832a21be89d1631d38c166425ed71966f Mon Sep 17 00:00:00 2001 From: Little Mike Date: Mon, 14 Sep 2020 11:41:19 -0600 Subject: [PATCH 22/41] Add plotly_subnburstclick event --- README.md | 1 + src/factory.js | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index c1a5b98..266c0ec 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ Event handlers for specific [`plotly.js` events](https://plot.ly/javascript/plot | `onSliderChange` | `Function` | `plotly_sliderchange` | | `onSliderEnd` | `Function` | `plotly_sliderend` | | `onSliderStart` | `Function` | `plotly_sliderstart` | +| `onSunburstClick` | `Function` | `plotly_sunburstclick` | | `onTransitioning` | `Function` | `plotly_transitioning` | | `onTransitionInterrupted` | `Function` | `plotly_transitioninterrupted` | | `onUnhover` | `Function` | `plotly_unhover` | diff --git a/src/factory.js b/src/factory.js index 43a4b2e..fa2ff58 100644 --- a/src/factory.js +++ b/src/factory.js @@ -30,6 +30,7 @@ const eventNames = [ 'SliderChange', 'SliderEnd', 'SliderStart', + 'SunburstClick', 'Transitioning', 'TransitionInterrupted', 'Unhover', From 06dd83d0814fb021100e36f4d5eb407091859719 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Mon, 14 Sep 2020 12:21:45 -0400 Subject: [PATCH 23/41] add onRelayouting, copyright notice update --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 266c0ec..b74a3cb 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,7 @@ Event handlers for specific [`plotly.js` events](https://plot.ly/javascript/plot | `onLegendClick` | `Function` | `plotly_legendclick` | | `onLegendDoubleClick` | `Function` | `plotly_legenddoubleclick` | | `onRelayout` | `Function` | `plotly_relayout` | +| `onRelayouting` | `Function` | `plotly_relayouting` | | `onRestyle` | `Function` | `plotly_restyle` | | `onRedraw` | `Function` | `plotly_redraw` | | `onSelected` | `Function` | `plotly_selected` | @@ -243,4 +244,4 @@ $ npm run test ## License -© 2017 Plotly, Inc. MIT License. +© 2017-2020 Plotly, Inc. MIT License. From 3e68ea553ed344522420f0e3554696deb87e785f Mon Sep 17 00:00:00 2001 From: dmt0 Date: Mon, 14 Sep 2020 13:03:38 -0400 Subject: [PATCH 24/41] Package updates --- package.json | 58 ++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index cd0e0d8..d56b7e6 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "test": "npm run lint && npm run deps && jest", "watch-test": "jest --watch", "watch": "nodemon --exec \"npm run make:lib\" -w src", - "deps": "./node_modules/.bin/dependency-check package.json --entry src/react-plotly.js --missing" + "deps": "./node_modules/.bin/dependency-check src/react-plotly.js --missing" }, "keywords": [ "graphing", @@ -31,39 +31,42 @@ "plotly", "react" ], + "dependencies": { + "prop-types": "^15.7.2" + }, "devDependencies": { - "@babel/cli": "^7.2.3", - "@babel/core": "^7.3.3", - "@babel/plugin-proposal-class-properties": "^7.3.3", - "@babel/preset-env": "^7.3.1", - "@babel/preset-react": "^7.0.0", - "babel-eslint": "^10.0.1", + "@babel/cli": "^7.11.6", + "@babel/core": "^7.11.6", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/preset-env": "^7.11.5", + "@babel/preset-react": "^7.10.4", + "babel-eslint": "^10.1.0", "babelify": "^10.0.0", "brfs": "^2.0.2", - "browserify": "^16.2.3", + "browserify": "^16.5.2", "browserify-global-shim": "^1.0.3", "cash-mv": "^0.2.0", - "dependency-check": "^3.3.0", - "enzyme": "^3.9.0", - "enzyme-adapter-react-16": "^1.9.1", - "eslint": "^5.14.1", - "eslint-config-prettier": "^4.0.0", - "eslint-plugin-import": "^2.16.0", - "eslint-plugin-react": "^7.12.4", + "dependency-check": "^4.1.0", + "enzyme": "^3.11.0", + "enzyme-adapter-react-16": "^1.15.4", + "eslint": "^7.9.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-react": "^7.20.6", "event-emitter": "^0.3.5", - "jest": "^24.1.0", - "mkdirp": "^0.5.1", - "nodemon": "^1.18.10", - "onetime": "^3.0.0", + "jest": "^26.4.2", + "mkdirp": "^1.0.4", + "nodemon": "^2.0.4", + "onetime": "^5.1.2", "plotly.js": "^1.35.0", - "prettier": "^1.16.4", - "react": "^16.8.2", + "prettier": "^2.1.1", + "react": "^16.13.1", "react-addons-test-utils": "^15.6.0", - "react-dom": "^16.8.2", - "react-test-renderer": "^16.8.2", - "rimraf": "^2.6.2", - "semver": "^5.4.1", - "uglify-js": "^3.0.26" + "react-dom": "^16.13.1", + "react-test-renderer": "^16.13.1", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "uglify-js": "^3.10.4" }, "peerDependencies": { "plotly.js": ">1.34.0", @@ -71,8 +74,5 @@ }, "browserify-global-shim": { "react": "React" - }, - "dependencies": { - "prop-types": "^15.7.2" } } From 26e6850594422e0cad143a584e8df14e9ca031a1 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Mon, 14 Sep 2020 13:04:09 -0400 Subject: [PATCH 25/41] prettier v2 + jest v26 --- src/__mocks__/plotly.js | 10 +++---- src/__tests__/react-plotly.test.js | 42 +++++++++++++++--------------- src/factory.js | 10 +++---- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/__mocks__/plotly.js b/src/__mocks__/plotly.js index b6f1f38..fe1ad31 100644 --- a/src/__mocks__/plotly.js +++ b/src/__mocks__/plotly.js @@ -4,13 +4,13 @@ const state = {}; const ASYNC_DELAY = 1; export default { - plot: jest.fn(gd => { + plot: jest.fn((gd) => { state.gd = gd; setTimeout(() => { state.gd.emit('plotly_afterplot'); }, ASYNC_DELAY); }), - newPlot: jest.fn(gd => { + newPlot: jest.fn((gd) => { state.gd = gd; EventEmitter(state.gd); // eslint-disable-line new-cap @@ -18,7 +18,7 @@ export default { state.gd.emit('plotly_afterplot'); }, ASYNC_DELAY); }), - react: jest.fn(gd => { + react: jest.fn((gd) => { state.gd = gd; EventEmitter(state.gd); // eslint-disable-line new-cap @@ -26,13 +26,13 @@ export default { state.gd.emit('plotly_afterplot'); }, ASYNC_DELAY); }), - relayout: jest.fn(gd => { + relayout: jest.fn((gd) => { state.gd = gd; setTimeout(() => { state.gd.emit('plotly_relayout'); }, ASYNC_DELAY); }), - restyle: jest.fn(gd => { + restyle: jest.fn((gd) => { state.gd = gd; setTimeout(() => { state.gd.emit('plotly_restyle'); diff --git a/src/__tests__/react-plotly.test.js b/src/__tests__/react-plotly.test.js index 11de80d..70b5857 100644 --- a/src/__tests__/react-plotly.test.js +++ b/src/__tests__/react-plotly.test.js @@ -33,7 +33,7 @@ describe('', () => { describe('with mocked plotly.js', () => { beforeEach(() => { - Plotly = require.requireMock('../__mocks__/plotly.js').default; + Plotly = jest.requireMock('../__mocks__/plotly.js').default; PlotComponent = createComponent(Plotly); // Override the parent element size: @@ -43,19 +43,19 @@ describe('', () => { }); }); - describe('initialization', function() { - test('calls Plotly.react on instantiation', done => { + describe('initialization', function () { + test('calls Plotly.react on instantiation', (done) => { createPlot({}) .then(() => { expect(Plotly.react).toHaveBeenCalled(); }) - .catch(err => { + .catch((err) => { done.fail(err); }) .then(done); }); - test('passes data', done => { + test('passes data', (done) => { createPlot({ data: [{x: [1, 2, 3]}], layout: {title: 'foo'}, @@ -66,11 +66,11 @@ describe('', () => { layout: {title: 'foo'}, }); }) - .catch(err => done.fail(err)) + .catch((err) => done.fail(err)) .then(done); }); - test('accepts width and height', done => { + test('accepts width and height', (done) => { createPlot({ layout: {width: 320, height: 240}, }) @@ -79,13 +79,13 @@ describe('', () => { layout: {width: 320, height: 240}, }); }) - .catch(err => done.fail(err)) + .catch((err) => done.fail(err)) .then(done); }); }); describe('plot updates', () => { - test('updates data', done => { + test('updates data', (done) => { createPlot({ layout: {width: 123, height: 456}, onUpdate: once(() => { @@ -96,13 +96,13 @@ describe('', () => { done(); }), }) - .then(plot => { + .then((plot) => { plot.setProps({data: [{x: [1, 2, 3]}]}); }) - .catch(err => done.fail(err)); + .catch((err) => done.fail(err)); }); - test('updates data when revision is defined but not changed', done => { + test('updates data when revision is defined but not changed', (done) => { createPlot({ revision: 1, layout: {width: 123, height: 456}, @@ -114,13 +114,13 @@ describe('', () => { done(); }), }) - .then(plot => { + .then((plot) => { plot.setProps({revision: 1, data: [{x: [1, 2, 3]}]}); }) - .catch(err => done.fail(err)); + .catch((err) => done.fail(err)); }); - test('sets the title', done => { + test('sets the title', (done) => { createPlot({ onUpdate: once(() => { expectPlotlyAPICall(Plotly.react, { @@ -129,13 +129,13 @@ describe('', () => { done(); }), }) - .then(plot => { + .then((plot) => { plot.setProps({layout: {title: 'test test'}}); }) - .catch(err => done.fail(err)); + .catch((err) => done.fail(err)); }); - test('revision counter', done => { + test('revision counter', (done) => { var callCnt = 0; createPlot({ revision: 0, @@ -153,7 +153,7 @@ describe('', () => { } }, }) - .then(plot => { + .then((plot) => { // Update with and without revision bumps: /* eslint-disable no-magic-numbers */ setTimeout(() => plot.setProps({layout: {title: 'test test'}}), 10); @@ -161,7 +161,7 @@ describe('', () => { setTimeout(() => plot.setProps({revision: 1, layout: {title: 'test test'}}), 30); setTimeout(() => plot.setProps({revision: 2, layout: {title: 'test test'}}), 40); }) - .catch(err => done.fail(err)); + .catch((err) => done.fail(err)); }); }); @@ -170,7 +170,7 @@ describe('', () => { const onRelayout = () => {}; createPlot({onRelayout}).then((plot) => { - const { handlers } = plot.instance(); + const {handlers} = plot.instance(); expect(plot.prop('onRelayout')).toBe(onRelayout); expect(handlers.Relayout).toBe(onRelayout); diff --git a/src/factory.js b/src/factory.js index fa2ff58..ffe4408 100644 --- a/src/factory.js +++ b/src/factory.js @@ -95,7 +95,7 @@ export default function plotComponentFactory(Plotly) { this.attachUpdateEvents(); } }) - .catch(err => { + .catch((err) => { if (this.props.onError) { this.props.onError(err); } @@ -153,7 +153,7 @@ export default function plotComponentFactory(Plotly) { return; } - updateEvents.forEach(updateEvent => { + updateEvents.forEach((updateEvent) => { this.el.on(updateEvent, this.handleUpdate); }); } @@ -163,7 +163,7 @@ export default function plotComponentFactory(Plotly) { return; } - updateEvents.forEach(updateEvent => { + updateEvents.forEach((updateEvent) => { this.el.removeListener(updateEvent, this.handleUpdate); }); } @@ -208,7 +208,7 @@ export default function plotComponentFactory(Plotly) { // Attach and remove event handlers as they're added or removed from props: syncEventHandlers() { - eventNames.forEach(eventName => { + eventNames.forEach((eventName) => { const prop = this.props['on' + eventName]; const handler = this.handlers[eventName]; const hasHandler = Boolean(handler); @@ -269,7 +269,7 @@ export default function plotComponentFactory(Plotly) { divId: PropTypes.string, }; - eventNames.forEach(eventName => { + eventNames.forEach((eventName) => { PlotlyComponent.propTypes['on' + eventName] = PropTypes.func; }); From 9a7ebd2cd9949ac9a1bc03754c83dc740133f068 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Mon, 14 Sep 2020 13:04:24 -0400 Subject: [PATCH 26/41] Version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d56b7e6..50f29eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-plotly.js", - "version": "2.4.0", + "version": "2.5.0", "license": "MIT", "description": "A plotly.js react component from Plotly", "author": "Plotly, Inc.", From 4064350b687046f87befd9a10caff4477aa62a30 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Mon, 14 Sep 2020 13:14:22 -0400 Subject: [PATCH 27/41] CircleCI Node v12 image --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3f1cec9..1cb6b72 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2.0 jobs: build: docker: - - image: circleci/node:10.9.0 + - image: circleci/node:12.18.3 working_directory: ~/react-plotly.js steps: - checkout @@ -32,7 +32,7 @@ jobs: test: docker: - - image: circleci/node:10.9.0 + - image: circleci/node:12.18.3 working_directory: ~/react-plotly.js steps: - checkout From a57f03e44ac02c3eb06a8a360579677847744d83 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Mon, 14 Sep 2020 13:19:18 -0400 Subject: [PATCH 28/41] Fix CircleCI cache --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1cb6b72..3d930e8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,8 +9,8 @@ jobs: - checkout - restore_cache: keys: - - v{{ .Environment.CIRCLE_CACHE_VERSION }}-deps-{{ .Branch }}-{{ checksum "package-lock.json" }} - - v{{ .Environment.CIRCLE_CACHE_VERSION }}-deps-master-{{ checksum "package-lock.json" }} + - v{{ .Environment.CIRCLE_CACHE_VERSION }}-deps-{{ .Branch }}-{{ checksum "package.json" }} + - v{{ .Environment.CIRCLE_CACHE_VERSION }}-deps-master-{{ checksum "package.json" }} - run: name: Install dependencies command: | From 0dc517962c90f809e922300d2549d26a4350f46d Mon Sep 17 00:00:00 2001 From: dmt0 Date: Mon, 14 Sep 2020 16:58:20 -0400 Subject: [PATCH 29/41] add plotly_sunburstclick to updateEvents --- src/factory.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/factory.js b/src/factory.js index ffe4408..3db9ddf 100644 --- a/src/factory.js +++ b/src/factory.js @@ -43,6 +43,7 @@ const updateEvents = [ 'plotly_relayouting', 'plotly_doubleclick', 'plotly_animated', + 'plotly_sunburstclick', ]; // Check if a window is available since SSR (server-side rendering) From 854e2baf6666ca734e197f3cfc8864232167bea6 Mon Sep 17 00:00:00 2001 From: John Fay Date: Fri, 9 Oct 2020 00:59:05 -0400 Subject: [PATCH 30/41] documentation typo --- README.md | 102 +++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index b74a3cb..c1b53c7 100644 --- a/README.md +++ b/README.md @@ -14,16 +14,16 @@ ## Contents -* [Installation](#installation) -* [Quick start](#quick-start) -* [State management](#state-management) -* [Refreshing the Plot](#refreshing-the-plot) -* [API](#api) - * [Basic props](#basic-props) - * [Event handler props](#event-handler-props) -* [Customizing the `plotly.js` bundle](#customizing-the-plotlyjs-bundle) -* [Loading from a `