From 92a40936c28e1acaa5c2036e12601d6c0224df37 Mon Sep 17 00:00:00 2001 From: Rikki Schulte Date: Mon, 16 Dec 2019 17:02:17 -0500 Subject: [PATCH 1/4] feat: deprecate react 15 support, support react 16 features - use UNSAFE_ for one componentWillUpdateProps - refact for another componentWillUpdateProps - deprecate peer resolution for react 15 - dev dependencies bumped to react 16 - react-test-renderer and enzyme bumped to react 16 Created-By: @ryan-m-walker, @acao BREAKING CHANGE: Deprecate support for React 15. Please use React 16.8 or greater for hooks support. --- examples/graphiql-webpack/package.json | 2 +- packages/graphiql/package.json | 14 +- packages/graphiql/src/components/GraphiQL.js | 18 +- .../graphiql/src/components/QueryHistory.js | 53 +++--- resources/enzyme.config.js | 2 +- yarn.lock | 163 ++++++------------ 6 files changed, 95 insertions(+), 157 deletions(-) diff --git a/examples/graphiql-webpack/package.json b/examples/graphiql-webpack/package.json index 8aa9b6018f7..b10e0ed43ac 100644 --- a/examples/graphiql-webpack/package.json +++ b/examples/graphiql-webpack/package.json @@ -13,7 +13,7 @@ "express-graphql": "^0.9.0", "graphiql": "file:../../packages/graphiql", "graphql": "14.5.8", - "react": "16.10.2" + "react": "16.12.0" }, "devDependencies": { "@babel/plugin-proposal-class-properties": "7.7.4", diff --git a/packages/graphiql/package.json b/packages/graphiql/package.json index 84f2e6993b4..553ed4b816c 100644 --- a/packages/graphiql/package.json +++ b/packages/graphiql/package.json @@ -51,15 +51,15 @@ "peerDependencies": { "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0", "prop-types": ">=15.5.0", - "react": "^15.6.0 || ^16.0.0", - "react-dom": "^15.6.0 || ^16.0.0" + "react": "^16.8.0", + "react-dom": "^16.8.0" }, "devDependencies": { "cross-env": "^6.0.3", "css-loader": "3.3.2", "cssnano": "^4.1.10", - "enzyme": "^3.9.0", - "enzyme-adapter-react-15": "^1.4.0", + "enzyme": "^3.10.0", + "enzyme-adapter-react-16": "^1.15.1", "express": "5.0.0-alpha.5", "express-graphql": "0.9.0", "graphql": "14.5.8", @@ -72,10 +72,10 @@ "postcss-loader": "^3.0.0", "postcss-preset-env": "^6.7.0", "prop-types": "15.7.2", - "react": "15.6.2", - "react-dom": "15.6.2", + "react": "^16.12.0", + "react-dom": "^16.12.0", "react-hot-loader": "^4.12.18", - "react-test-renderer": "15.6.2", + "react-test-renderer": "^16.12.0", "rimraf": "^3.0.0", "serve": "^11.2.0", "start-server-and-test": "^1.10.6", diff --git a/packages/graphiql/src/components/GraphiQL.js b/packages/graphiql/src/components/GraphiQL.js index 141d51c13b2..bb1865bb703 100644 --- a/packages/graphiql/src/components/GraphiQL.js +++ b/packages/graphiql/src/components/GraphiQL.js @@ -39,6 +39,19 @@ import { const DEFAULT_DOC_EXPLORER_WIDTH = 350; +// eslint-disable-next-line no-console +const logger = console.log; + +if (!React.version || !React.version.indexOf('16') < 0) { + logger.warn( + [ + 'GraphiQL 0.18.0 and after is not compatible with React 15 or below.', + 'If you are using a CDN source (jsdelivr, unpkg, etc), follow this example:', + 'https://github.com/graphql/graphiql/blob/master/examples/graphiql-cdn/index.html#L49', + ].join('\n'), + ); +} + /** * The top-level React component for GraphiQL, intended to encompass the entire * browser viewport. @@ -171,8 +184,9 @@ export class GraphiQL extends React.Component { global.g = this; } - - componentWillReceiveProps(nextProps) { + // todo: these values should be updated in a reducer imo + // eslint-disable-next-line camelcase + UNSAFE_componentWillReceiveProps(nextProps) { let nextSchema = this.state.schema; let nextQuery = this.state.query; let nextVariables = this.state.variables; diff --git a/packages/graphiql/src/components/QueryHistory.js b/packages/graphiql/src/components/QueryHistory.js index 189a24b9f13..b809bb1c353 100644 --- a/packages/graphiql/src/components/QueryHistory.js +++ b/packages/graphiql/src/components/QueryHistory.js @@ -14,32 +14,26 @@ import HistoryQuery from './HistoryQuery'; const MAX_QUERY_SIZE = 100000; const MAX_HISTORY_LENGTH = 20; -const shouldSaveQuery = (nextProps, current, lastQuerySaved) => { - if (nextProps.queryID === current.queryID) { - return false; - } +const shouldSaveQuery = (query, variables, lastQuerySaved) => { try { - parse(nextProps.query); + parse(query); } catch (e) { return false; } // Don't try to save giant queries - if (nextProps.query.length > MAX_QUERY_SIZE) { + if (query.length > MAX_QUERY_SIZE) { return false; } if (!lastQuerySaved) { return true; } - if ( - JSON.stringify(nextProps.query) === JSON.stringify(lastQuerySaved.query) - ) { + if (JSON.stringify(query) === JSON.stringify(lastQuerySaved.query)) { if ( - JSON.stringify(nextProps.variables) === - JSON.stringify(lastQuerySaved.variables) + JSON.stringify(variables) === JSON.stringify(lastQuerySaved.variables) ) { return false; } - if (!nextProps.variables && !lastQuerySaved.variables) { + if (variables && !lastQuerySaved.variables) { return false; } } @@ -71,25 +65,6 @@ export class QueryHistory extends React.Component { this.state = { queries }; } - componentWillReceiveProps(nextProps) { - if ( - shouldSaveQuery(nextProps, this.props, this.historyStore.fetchRecent()) - ) { - const item = { - query: nextProps.query, - variables: nextProps.variables, - operationName: nextProps.operationName, - }; - this.historyStore.push(item); - const historyQueries = this.historyStore.items; - const favoriteQueries = this.favoriteStore.items; - const queries = historyQueries.concat(favoriteQueries); - this.setState({ - queries, - }); - } - } - render() { const queries = this.state.queries.slice().reverse(); const queryNodes = queries.map((query, i) => { @@ -114,6 +89,22 @@ export class QueryHistory extends React.Component { ); } + updateHistory = (query, variables, operationName) => { + if (shouldSaveQuery(query, variables, this.historyStore.fetchRecent())) { + this.historyStore.push({ + query, + variables, + operationName, + }); + const historyQueries = this.historyStore.items; + const favoriteQueries = this.favoriteStore.items; + const queries = historyQueries.concat(favoriteQueries); + this.setState({ + queries, + }); + } + }; + toggleFavorite = (query, variables, operationName, label, favorite) => { const item = { query, diff --git a/resources/enzyme.config.js b/resources/enzyme.config.js index 4776d67aa70..9fd795c78fa 100644 --- a/resources/enzyme.config.js +++ b/resources/enzyme.config.js @@ -12,7 +12,7 @@ require('@babel/polyfill', { // if (process.env.ENZYME) { const { configure } = require('enzyme'); -const Adapter = require('enzyme-adapter-react-15'); +const Adapter = require('enzyme-adapter-react-16'); configure({ adapter: new Adapter() }); // } diff --git a/yarn.lock b/yarn.lock index 1a7721f66bf..b9971438b93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2433,7 +2433,7 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" -airbnb-prop-types@^2.13.2: +airbnb-prop-types@^2.15.0: version "2.15.0" resolved "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.15.0.tgz#5287820043af1eb469f5b0af0d6f70da6c52aaef" integrity sha512-jUh2/hfKsRjNFC4XONQrxo/n/3GG4Tn6Hl0WlFQN5PY9OMC9loSCoAYKnZsWaP8wEfd5xcrPloK0Zg6iS1xwVA== @@ -2755,7 +2755,7 @@ arrify@^1.0.1: resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= -asap@^2.0.0, asap@~2.0.3: +asap@^2.0.0: version "2.0.6" resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= @@ -4141,11 +4141,6 @@ core-js-compat@^3.4.7: browserslist "^4.8.2" semver "^6.3.0" -core-js@^1.0.0: - version "1.2.7" - resolved "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" - integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= - core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.5: version "2.6.9" resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" @@ -4213,15 +4208,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -create-react-class@^15.6.0: - version "15.6.3" - resolved "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036" - integrity sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg== - dependencies: - fbjs "^0.8.9" - loose-envify "^1.3.1" - object-assign "^4.1.1" - cross-env@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz#4256b71e49b3a40637a0ce70768a6ef5c72ae941" @@ -5090,30 +5076,42 @@ env-paths@^1.0.0: resolved "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA= -enzyme-adapter-react-15@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/enzyme-adapter-react-15/-/enzyme-adapter-react-15-1.4.0.tgz#4abd648205a8f25f88beedb8f431dc4809eb45a4" - integrity sha512-PHLE4JzUUDhk9RpoLb0MPVaGhVe2i/nowVjzPhbk6cL8rqOHegh/KMOVlndelf3GBZfp0axl2C9OCbYhrSBtqg== +enzyme-adapter-react-16@^1.15.1: + version "1.15.1" + resolved "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.1.tgz#8ad55332be7091dc53a25d7d38b3485fc2ba50d5" + integrity sha512-yMPxrP3vjJP+4wL/qqfkT6JAIctcwKF+zXO6utlGPgUJT2l4tzrdjMDWGd/Pp1BjHBcljhN24OzNEGRteibJhA== dependencies: - enzyme-adapter-utils "^1.10.1" + enzyme-adapter-utils "^1.12.1" + enzyme-shallow-equal "^1.0.0" + has "^1.0.3" object.assign "^4.1.0" object.values "^1.1.0" prop-types "^15.7.2" - react-is "^16.8.6" + react-is "^16.10.2" + react-test-renderer "^16.0.0-0" + semver "^5.7.0" -enzyme-adapter-utils@^1.10.1: - version "1.12.0" - resolved "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.12.0.tgz#96e3730d76b872f593e54ce1c51fa3a451422d93" - integrity sha512-wkZvE0VxcFx/8ZsBw0iAbk3gR1d9hK447ebnSYBf95+r32ezBq+XDSAvRErkc4LZosgH8J7et7H7/7CtUuQfBA== +enzyme-adapter-utils@^1.12.1: + version "1.12.1" + resolved "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.12.1.tgz#e828e0d038e2b1efa4b9619ce896226f85c9dd88" + integrity sha512-KWiHzSjZaLEoDCOxY8Z1RAbUResbqKN5bZvenPbfKtWorJFVETUw754ebkuCQ3JKm0adx1kF8JaiR+PHPiP47g== dependencies: - airbnb-prop-types "^2.13.2" - function.prototype.name "^1.1.0" + airbnb-prop-types "^2.15.0" + function.prototype.name "^1.1.1" object.assign "^4.1.0" - object.fromentries "^2.0.0" + object.fromentries "^2.0.1" prop-types "^15.7.2" - semver "^5.6.0" + semver "^5.7.0" -enzyme@^3.9.0: +enzyme-shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.0.tgz#d8e4603495e6ea279038eef05a4bf4887b55dc69" + integrity sha512-VUf+q5o1EIv2ZaloNQQtWCJM9gpeux6vudGVH6vLmfPXFLRuxl5+Aq3U260wof9nn0b0i+P5OEUXm1vnxkRpXQ== + dependencies: + has "^1.0.3" + object-is "^1.0.1" + +enzyme@^3.10.0: version "3.10.0" resolved "https://registry.npmjs.org/enzyme/-/enzyme-3.10.0.tgz#7218e347c4a7746e133f8e964aada4a3523452f6" integrity sha512-p2yy9Y7t/PFbPoTvrWde7JIYB2ZyGC+NgTNbVEGvZ5/EyoYSr9aG/2rSbVvyNvMHEhw9/dmGUJHWtfQIEiX9pg== @@ -5845,19 +5843,6 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fbjs@^0.8.9: - version "0.8.17" - resolved "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" - integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= - dependencies: - core-js "^1.0.0" - isomorphic-fetch "^2.1.1" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.18" - fd-slicer@~1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" @@ -7695,7 +7680,7 @@ is-ssh@^1.3.0: dependencies: protocols "^1.1.0" -is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -7807,14 +7792,6 @@ isobject@^4.0.0: resolved "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== -isomorphic-fetch@^2.1.1: - version "2.2.1" - resolved "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" - integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= - dependencies: - node-fetch "^1.0.1" - whatwg-fetch ">=0.10.0" - isstream@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -8922,7 +8899,7 @@ loglevel@^1.6.4: resolved "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312" integrity sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ== -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -9643,14 +9620,6 @@ node-fetch@2.1.2: resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U= -node-fetch@^1.0.1: - version "1.7.3" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" - integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== - dependencies: - encoding "^0.1.11" - is-stream "^1.0.1" - node-fetch@^2.2.0, node-fetch@^2.3.0, node-fetch@^2.5.0: version "2.6.0" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" @@ -9973,7 +9942,7 @@ object.entries@^1.0.4, object.entries@^1.1.0: function-bind "^1.1.1" has "^1.0.3" -object.fromentries@^2.0.0, object.fromentries@^2.0.1: +object.fromentries@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.1.tgz#050f077855c7af8ae6649f45c80b16ee2d31e704" integrity sha512-PUQv8Hbg3j2QX0IQYv3iAGCbGcu4yY4KQ92/dhA4sFSixBmSmp13UpDLs6jGK8rBtbmhNNIK99LD2k293jpiGA== @@ -11331,13 +11300,6 @@ promise-retry@^1.1.1: err-code "^1.0.0" retry "^0.10.0" -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== - dependencies: - asap "~2.0.3" - prompts@^2.0.1: version "2.2.1" resolved "https://registry.npmjs.org/prompts/-/prompts-2.2.1.tgz#f901dd2a2dfee080359c0e20059b24188d75ad35" @@ -11362,7 +11324,7 @@ prop-types-exact@^1.2.0: object.assign "^4.1.0" reflect.ownkeys "^0.2.0" -prop-types@15.7.2, prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@15.7.2, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -11615,17 +11577,7 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dom@15.6.2: - version "15.6.2" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz#41cfadf693b757faf2708443a1d1fd5a02bef730" - integrity sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA= - dependencies: - fbjs "^0.8.9" - loose-envify "^1.1.0" - object-assign "^4.1.0" - prop-types "^15.5.10" - -react-dom@^16.11.0: +react-dom@^16.11.0, react-dom@^16.12.0: version "16.12.0" resolved "https://registry.npmjs.org/react-dom/-/react-dom-16.12.0.tgz#0da4b714b8d13c2038c9396b54a92baea633fe11" integrity sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw== @@ -11649,7 +11601,7 @@ react-hot-loader@^4.12.18: shallowequal "^1.1.0" source-map "^0.7.3" -react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: +react-is@^16.10.2, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.12.0" resolved "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== @@ -11659,29 +11611,20 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-test-renderer@15.6.2: - version "15.6.2" - resolved "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-15.6.2.tgz#d0333434fc2c438092696ca770da5ed48037efa8" - integrity sha1-0DM0NPwsQ4CSaWyncNpe1IA376g= - dependencies: - fbjs "^0.8.9" - object-assign "^4.1.0" - -react@15.6.2: - version "15.6.2" - resolved "https://registry.npmjs.org/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72" - integrity sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI= +react-test-renderer@^16.0.0-0, react-test-renderer@^16.12.0: + version "16.12.0" + resolved "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.12.0.tgz#11417ffda579306d4e841a794d32140f3da1b43f" + integrity sha512-Vj/teSqt2oayaWxkbhQ6gKis+t5JrknXfPVo+aIJ8QwYAqMPH77uptOdrlphyxl8eQI/rtkOYg86i/UWkpFu0w== dependencies: - create-react-class "^15.6.0" - fbjs "^0.8.9" - loose-envify "^1.1.0" - object-assign "^4.1.0" - prop-types "^15.5.10" + object-assign "^4.1.1" + prop-types "^15.6.2" + react-is "^16.8.6" + scheduler "^0.18.0" -react@16.10.2: - version "16.10.2" - resolved "https://registry.npmjs.org/react/-/react-16.10.2.tgz#a5ede5cdd5c536f745173c8da47bda64797a4cf0" - integrity sha512-MFVIq0DpIhrHFyqLU0S3+4dIcBhhOvBE8bJ/5kHPVOVaGdo0KuiQzpcjCPsf585WvhypqtrMILyoE2th6dT+Lw== +react@16.12.0, react@^16.12.0: + version "16.12.0" + resolved "https://registry.npmjs.org/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83" + integrity sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -12543,7 +12486,7 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" -setimmediate@^1.0.4, setimmediate@^1.0.5: +setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= @@ -13664,11 +13607,6 @@ typescript@^3.6.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3.tgz#b36840668a16458a7025b9eabfad11b66ab85c69" integrity sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw== -ua-parser-js@^0.7.18: - version "0.7.20" - resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.20.tgz#7527178b82f6a62a0f243d1f94fd30e3e3c21098" - integrity sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw== - uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" @@ -14227,11 +14165,6 @@ whatwg-fetch@2.0.4: resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== -whatwg-fetch@>=0.10.0: - version "3.0.0" - resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" - integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== - whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" From c462189764422b66538d1cd13450944c148e4d4f Mon Sep 17 00:00:00 2001 From: ryan-m-walker Date: Tue, 17 Dec 2019 02:14:25 -0500 Subject: [PATCH 2/4] chore: add test coverage --- packages/graphiql/src/components/GraphiQL.js | 5 ++ .../components/__tests__/DocExplorer.spec.js | 6 ++ .../src/components/__tests__/GraphiQL.spec.js | 34 ++++---- .../components/__tests__/HistoryQuery.spec.js | 82 ++++++++++++++++++ .../components/__tests__/QueryHistory.spec.js | 84 +++++++++++++++++++ .../src/components/__tests__/fixtures.js | 25 ++++++ .../components/__tests__/helpers/storage.js | 22 +++++ 7 files changed, 242 insertions(+), 16 deletions(-) create mode 100644 packages/graphiql/src/components/__tests__/HistoryQuery.spec.js create mode 100644 packages/graphiql/src/components/__tests__/QueryHistory.spec.js create mode 100644 packages/graphiql/src/components/__tests__/fixtures.js create mode 100644 packages/graphiql/src/components/__tests__/helpers/storage.js diff --git a/packages/graphiql/src/components/GraphiQL.js b/packages/graphiql/src/components/GraphiQL.js index bb1865bb703..1b2f1197617 100644 --- a/packages/graphiql/src/components/GraphiQL.js +++ b/packages/graphiql/src/components/GraphiQL.js @@ -345,6 +345,9 @@ export class GraphiQL extends React.Component {
{ + this._queryHistory = node; + }} operationName={this.state.operationName} query={this.state.query} variables={this.state.variables} @@ -705,6 +708,8 @@ export class GraphiQL extends React.Component { operationName, }); + this._queryHistory.updateHistory(editedQuery, variables, operationName); + // _fetchQuery may return a subscription. const subscription = this._fetchQuery( editedQuery, diff --git a/packages/graphiql/src/components/__tests__/DocExplorer.spec.js b/packages/graphiql/src/components/__tests__/DocExplorer.spec.js index 7708ecb5794..79dc107a4af 100644 --- a/packages/graphiql/src/components/__tests__/DocExplorer.spec.js +++ b/packages/graphiql/src/components/__tests__/DocExplorer.spec.js @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2019 GraphQL Contributors. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ import React from 'react'; import { mount } from 'enzyme'; import { DocExplorer } from '../DocExplorer'; diff --git a/packages/graphiql/src/components/__tests__/GraphiQL.spec.js b/packages/graphiql/src/components/__tests__/GraphiQL.spec.js index 1e9f921e70d..64bff28ce8e 100644 --- a/packages/graphiql/src/components/__tests__/GraphiQL.spec.js +++ b/packages/graphiql/src/components/__tests__/GraphiQL.spec.js @@ -8,21 +8,9 @@ import React from 'react'; import { mount } from 'enzyme'; import { GraphiQL } from '../GraphiQL'; - -const mockStorage = (function() { - let store = {}; - return { - getItem(key) { - return store.hasOwnProperty(key) ? store[key] : null; - }, - setItem(key, value) { - store[key] = value.toString(); - }, - clear() { - store = {}; - }, - }; -})(); +import { getMockStorage } from './helpers/storage'; +import HistoryQuery from '../HistoryQuery'; +import { mockQuery1, mockVariables1, mockOperationName1 } from './fixtures'; // The smallest possible introspection result that builds a schema. const simpleIntrospection = { @@ -49,7 +37,7 @@ const wait = () => .then(() => Promise.resolve()); Object.defineProperty(window, 'localStorage', { - value: mockStorage, + value: getMockStorage(), }); describe('GraphiQL', () => { @@ -144,6 +132,20 @@ describe('GraphiQL', () => { expect(graphiQL.state().variableEditorOpen).toEqual(false); }); + it('adds a history item when the execute query function button is clicked', () => { + const W = mount(); + W.setState({ + query: mockQuery1, + variables: mockVariables1, + operationName: mockOperationName1, + }); + W.find('button.execute-button') + .first() + .simulate('click'); + W.update(); + expect(W.find(HistoryQuery).length).toBe(1); + }); + describe('children overrides', () => { const MyFunctionalComponent = () => { return null; diff --git a/packages/graphiql/src/components/__tests__/HistoryQuery.spec.js b/packages/graphiql/src/components/__tests__/HistoryQuery.spec.js new file mode 100644 index 00000000000..09d6a2b6049 --- /dev/null +++ b/packages/graphiql/src/components/__tests__/HistoryQuery.spec.js @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2019 GraphQL Contributors. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import React from 'react'; +import { mount } from 'enzyme'; +import HistoryQuery from '../HistoryQuery'; +import { mockOperationName1, mockQuery1, mockVariables1 } from './fixtures'; + +const noOp = () => {}; + +const baseMockProps = { + favorite: false, + handleEditLabel: noOp, + handleToggleFavorite: noOp, + onSelect: noOp, + query: mockQuery1, + variables: mockVariables1, +}; + +function getMockProps(customProps) { + return { + ...baseMockProps, + ...customProps, + }; +} + +describe('HistoryQuery', () => { + it('renders operationName if label is not provided', () => { + const otherMockProps = { operationName: mockOperationName1 }; + const props = getMockProps(otherMockProps); + const W = mount(); + expect( + W.find('button.history-label') + .first() + .text(), + ).toBe(mockOperationName1); + }); + + it('renders a string version of the query if label or operation name are not provided', () => { + const W = mount(); + expect( + W.find('button.history-label') + .first() + .text(), + ).toBe( + mockQuery1 + .split('\n') + .filter(line => line.indexOf('#') !== 0) + .join(''), + ); + }); + + it('calls onSelect with the correct arguments when history label button is clicked', () => { + const onSelectSpy = jest.spyOn(baseMockProps, 'onSelect'); + const otherMockProps = { + operationName: mockOperationName1, + }; + const W = mount(); + W.find('button.history-label').simulate('click'); + W.update(); + expect(onSelectSpy).toHaveBeenCalledWith( + mockQuery1, + mockVariables1, + mockOperationName1, + undefined, + ); + }); + + it('renders label input if the edit label button is clicked', () => { + const W = mount(); + W.find({ 'aria-label': 'Edit label' }) + .first() + .simulate('click'); + W.update(); + expect(W.find('li.editable').length).toBe(1); + expect(W.find('input').length).toBe(1); + expect(W.find('button.history-label').length).toBe(0); + }); +}); diff --git a/packages/graphiql/src/components/__tests__/QueryHistory.spec.js b/packages/graphiql/src/components/__tests__/QueryHistory.spec.js new file mode 100644 index 00000000000..49091520e59 --- /dev/null +++ b/packages/graphiql/src/components/__tests__/QueryHistory.spec.js @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2019 GraphQL Contributors. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import React from 'react'; +import { mount } from 'enzyme'; + +import { QueryHistory } from '../QueryHistory'; +import HistoryQuery from '../HistoryQuery'; +import { getMockStorage } from './helpers/storage'; +import { + mockBadQuery, + mockQuery1, + mockQuery2, + mockVariables1, + mockVariables2, + mockOperationName1, + mockOperationName2, +} from './fixtures'; + +function getMockProps(customProps) { + return { + query: mockQuery1, + variables: mockVariables1, + operationName: mockOperationName1, + queryID: 1, + onSelectQuery: () => {}, + storage: getMockStorage(), + ...customProps, + }; +} + +describe('QueryHistory', () => { + it('will not save invalid queries', () => { + const W = mount(); + const instance = W.instance(); + instance.updateHistory(mockBadQuery); + W.update(); + expect(W.find(HistoryQuery).length).toBe(0); + }); + + it('will save if there was not a previously saved query', () => { + const W = mount(); + const instance = W.instance(); + instance.updateHistory(mockQuery1, mockVariables1, mockOperationName1); + W.update(); + expect(W.find(HistoryQuery).length).toBe(1); + }); + + it('will not save a query if the query is the same as previous query', () => { + const W = mount(); + const instance = W.instance(); + instance.updateHistory(mockQuery1, mockVariables1, mockOperationName1); + W.update(); + expect(W.find(HistoryQuery).length).toBe(1); + instance.updateHistory(mockQuery1, mockVariables1, mockOperationName1); + W.update(); + expect(W.find(HistoryQuery).length).toBe(1); + }); + + it('will save if new query is different than previous query', () => { + const W = mount(); + const instance = W.instance(); + instance.updateHistory(mockQuery1, mockVariables1, mockOperationName1); + W.update(); + expect(W.find(HistoryQuery).length).toBe(1); + instance.updateHistory(mockQuery2, undefined, mockOperationName2); + W.update(); + expect(W.find(HistoryQuery).length).toBe(2); + }); + + it('will save query if variables are different ', () => { + const W = mount(); + const instance = W.instance(); + instance.updateHistory(mockQuery1, mockVariables1, mockOperationName1); + W.update(); + expect(W.find(HistoryQuery).length).toBe(1); + instance.updateHistory(mockQuery1, mockVariables2, mockOperationName1); + W.update(); + expect(W.find(HistoryQuery).length).toBe(2); + }); +}); diff --git a/packages/graphiql/src/components/__tests__/fixtures.js b/packages/graphiql/src/components/__tests__/fixtures.js new file mode 100644 index 00000000000..137ec90fd4a --- /dev/null +++ b/packages/graphiql/src/components/__tests__/fixtures.js @@ -0,0 +1,25 @@ +export const mockBadQuery = `bad {} query`; + +export const mockQuery1 = /* GraphQL */ ` + query Test($string: String) { + test { + hasArgs(string: $string) + } + } +`; + +export const mockQuery2 = /* GraphQL */ ` + query Test2 { + test { + id + } + } +`; + +export const mockVariables1 = JSON.stringify({ string: 'string' }); +export const mockVariables2 = JSON.stringify({ string: 'string2' }); + +export const mockOperationName1 = 'Test'; +export const mockOperationName2 = 'Test2'; + +export const mockHistoryLabel1 = 'Test'; diff --git a/packages/graphiql/src/components/__tests__/helpers/storage.js b/packages/graphiql/src/components/__tests__/helpers/storage.js new file mode 100644 index 00000000000..00fee65e2bb --- /dev/null +++ b/packages/graphiql/src/components/__tests__/helpers/storage.js @@ -0,0 +1,22 @@ +export function getMockStorage() { + return (function() { + let store = {}; + return { + getItem(key) { + return store.hasOwnProperty(key) ? store[key] : null; + }, + setItem(key, value) { + store[key] = value.toString(); + }, + clear() { + store = {}; + }, + get(key) { + return store.hasOwnProperty(key) ? store[key] : null; + }, + set(key, value) { + store[key] = value.toString(); + }, + }; + })(); +} From 2a1226e6138c276b013bcc02e21cccd4af7e80c1 Mon Sep 17 00:00:00 2001 From: Rikki Schulte Date: Tue, 17 Dec 2019 10:48:21 -0500 Subject: [PATCH 3/4] chore: add comments --- packages/graphiql/src/components/QueryHistory.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/graphiql/src/components/QueryHistory.js b/packages/graphiql/src/components/QueryHistory.js index b809bb1c353..fa6f06682e3 100644 --- a/packages/graphiql/src/components/QueryHistory.js +++ b/packages/graphiql/src/components/QueryHistory.js @@ -89,6 +89,7 @@ export class QueryHistory extends React.Component { ); } + // Public API updateHistory = (query, variables, operationName) => { if (shouldSaveQuery(query, variables, this.historyStore.fetchRecent())) { this.historyStore.push({ @@ -105,6 +106,7 @@ export class QueryHistory extends React.Component { } }; + // Public API toggleFavorite = (query, variables, operationName, label, favorite) => { const item = { query, @@ -124,6 +126,7 @@ export class QueryHistory extends React.Component { }); }; + // Public API editLabel = (query, variables, operationName, label, favorite) => { const item = { query, From e1b6ebbb9e80d77c8bc5a03052cbb437c238df12 Mon Sep 17 00:00:00 2001 From: Rikki Schulte Date: Wed, 18 Dec 2019 17:51:33 -0500 Subject: [PATCH 4/4] fix: throw on react <16 --- packages/graphiql/src/components/GraphiQL.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/graphiql/src/components/GraphiQL.js b/packages/graphiql/src/components/GraphiQL.js index 1b2f1197617..73191fa5438 100644 --- a/packages/graphiql/src/components/GraphiQL.js +++ b/packages/graphiql/src/components/GraphiQL.js @@ -39,11 +39,10 @@ import { const DEFAULT_DOC_EXPLORER_WIDTH = 350; -// eslint-disable-next-line no-console -const logger = console.log; +const majorVersion = parseInt(React.version.slice(0, 2), 10); -if (!React.version || !React.version.indexOf('16') < 0) { - logger.warn( +if (majorVersion < 16) { + throw Error( [ 'GraphiQL 0.18.0 and after is not compatible with React 15 or below.', 'If you are using a CDN source (jsdelivr, unpkg, etc), follow this example:',