Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: deprecate support for 15, support react 16 features #1107

Merged
merged 4 commits into from
Dec 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/graphiql-webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
14 changes: 7 additions & 7 deletions packages/graphiql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
22 changes: 20 additions & 2 deletions packages/graphiql/src/components/GraphiQL.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ import {

const DEFAULT_DOC_EXPLORER_WIDTH = 350;

const majorVersion = parseInt(React.version.slice(0, 2), 10);

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:',
'https://github.com/graphql/graphiql/blob/master/examples/graphiql-cdn/index.html#L49',
].join('\n'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error is misleading; e.g. if they were to use React 17 or 18 in the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we will likely remove this before react 17. i can add a todo for that

);
}

/**
* The top-level React component for GraphiQL, intended to encompass the entire
* browser viewport.
Expand Down Expand Up @@ -171,8 +183,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;
Expand Down Expand Up @@ -331,6 +344,9 @@ export class GraphiQL extends React.Component {
<div className="graphiql-container">
<div className="historyPaneWrap" style={historyPaneStyle}>
<QueryHistory
ref={node => {
this._queryHistory = node;
}}
operationName={this.state.operationName}
query={this.state.query}
variables={this.state.variables}
Expand Down Expand Up @@ -691,6 +707,8 @@ export class GraphiQL extends React.Component {
operationName,
});

this._queryHistory.updateHistory(editedQuery, variables, operationName);

// _fetchQuery may return a subscription.
const subscription = this._fetchQuery(
editedQuery,
Expand Down
56 changes: 25 additions & 31 deletions packages/graphiql/src/components/QueryHistory.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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) => {
Expand All @@ -114,6 +89,24 @@ export class QueryHistory extends React.Component {
);
}

// Public API
updateHistory = (query, variables, operationName) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed in other areas that public methods were marked with a Public API comment which I didn't add here. Could you add it in for me?

e.g.:

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure yes! we are about to deprecate this “plugin api” possibly

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I figured this would go away once we moved to more React 16 patterns anyways but good to make it clear and consistent until then

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep! this reminded me i need to add a deprecation notice

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,
});
}
};

// Public API
toggleFavorite = (query, variables, operationName, label, favorite) => {
const item = {
query,
Expand All @@ -133,6 +126,7 @@ export class QueryHistory extends React.Component {
});
};

// Public API
editLabel = (query, variables, operationName, label, favorite) => {
const item = {
query,
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
34 changes: 18 additions & 16 deletions packages/graphiql/src/components/__tests__/GraphiQL.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -49,7 +37,7 @@ const wait = () =>
.then(() => Promise.resolve());

Object.defineProperty(window, 'localStorage', {
value: mockStorage,
value: getMockStorage(),
});

describe('GraphiQL', () => {
Expand Down Expand Up @@ -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(<GraphiQL fetcher={noOpFetcher} />);
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;
Expand Down
82 changes: 82 additions & 0 deletions packages/graphiql/src/components/__tests__/HistoryQuery.spec.js
Original file line number Diff line number Diff line change
@@ -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(<HistoryQuery {...props} />);
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(<HistoryQuery {...getMockProps()} />);
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(<HistoryQuery {...getMockProps(otherMockProps)} />);
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(<HistoryQuery {...getMockProps()} />);
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);
});
});
Loading