diff --git a/packages/addon-graphql/.babelrc b/packages/addon-graphql/.babelrc new file mode 100644 index 000000000000..9b7d435ad38f --- /dev/null +++ b/packages/addon-graphql/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015", "stage-0", "react"] +} diff --git a/packages/addon-graphql/.scripts/npm-prepublish.js b/packages/addon-graphql/.scripts/npm-prepublish.js new file mode 100644 index 000000000000..bb13fdba9872 --- /dev/null +++ b/packages/addon-graphql/.scripts/npm-prepublish.js @@ -0,0 +1,6 @@ +var path = require('path'); +var shell = require('shelljs'); +var babel = ['node_modules', '.bin', 'babel'].join(path.sep); + +shell.rm('-rf', 'dist') +shell.exec(babel + ' --ignore __tests__ src --out-dir dist') diff --git a/packages/addon-graphql/.storybook/config.js b/packages/addon-graphql/.storybook/config.js new file mode 100644 index 000000000000..c4434721049e --- /dev/null +++ b/packages/addon-graphql/.storybook/config.js @@ -0,0 +1,2 @@ +import * as storybook from '@kadira/storybook'; +storybook.configure(() => require('./stories'), module); diff --git a/packages/addon-graphql/.storybook/stories.js b/packages/addon-graphql/.storybook/stories.js new file mode 100644 index 000000000000..196a6ba3fda8 --- /dev/null +++ b/packages/addon-graphql/.storybook/stories.js @@ -0,0 +1,13 @@ +import React from 'react'; +import { storiesOf } from '@kadira/storybook' +import { setupGraphiQL } from '../src' + +// setup the graphiql helper which can be used with the add method later +const graphiql = setupGraphiQL({ url: 'http://localhost:3000/graphql' }); + +storiesOf('GraphQL Demo', module) + .add('get user info', graphiql(`{ + user(id: "1") { + name + } + }`)); diff --git a/packages/addon-graphql/CHANGELOG.md b/packages/addon-graphql/CHANGELOG.md new file mode 100644 index 000000000000..f5f29915f804 --- /dev/null +++ b/packages/addon-graphql/CHANGELOG.md @@ -0,0 +1,5 @@ +## Changelog + +### v1.0.0 + +* First stable release diff --git a/packages/addon-graphql/README.md b/packages/addon-graphql/README.md new file mode 100644 index 000000000000..0be08a5c701a --- /dev/null +++ b/packages/addon-graphql/README.md @@ -0,0 +1,60 @@ +# GraphiQL Addon + +The GraphiQL addon can be used to display the GraphiQL IDE with example queries. This addon works with [React Storybook](https://github.com/kadirahq/react-storybook). + +![](docs/screenshot.png) + +## Getting Started + +First, install the addon + +```shell +npm install -D @kadira/storybook-addon-graphql +``` + +Import the `setupGraphiQL` function and use it to create the graphiql helper with a base url. + +```js +import { storiesOf } from '@kadira/storybook' +import { setupGraphiQL } from '@kadira/storybook-addon-graphql' + +// setup the graphiql helper which can be used with the add method later +const graphiql = setupGraphiQL({ url: 'http://localhost:3000/graphql' }); + +storiesOf('GraphQL Demo', module) + .add('get user info', graphiql(`{ + user(id: "1") { + name + } + }`)); +``` + +> Tip: try creating the helper in another file and import the configured graphiql helper from it + +## Advanced Setup + +The `setupGraphiQL` function also accepts a fetcher parameter which can be used to change how graphiql gets data. If the fetcher parameter is not given, it'll create a fetcher which uses the `fetch` api to make requests. The above example can also be written using a custom fetcher. + +```js +import { storiesOf } from '@kadira/storybook' +import { setupGraphiQL } from '@kadira/storybook-addon-graphql' + +const fetcher = function (params) { + const options = { + method: 'post', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(params), + }; + return fetch(url, options).then(res => res.json()); +}; + +// create the helper with a custom fetcher +const graphiql = setupGraphiQL({ fetcher }); + +storiesOf('GraphQL Demo', module) + .add('get user info', graphiql(`{ + user(id: "1") { + name + } + }`)); +``` diff --git a/packages/addon-graphql/demo/data.json b/packages/addon-graphql/demo/data.json new file mode 100644 index 000000000000..e0e399cef11a --- /dev/null +++ b/packages/addon-graphql/demo/data.json @@ -0,0 +1,14 @@ +{ + "1": { + "id": "1", + "name": "Dan" + }, + "2": { + "id": "2", + "name": "Marie" + }, + "3": { + "id": "3", + "name": "Jessie" + } +} diff --git a/packages/addon-graphql/demo/index.js b/packages/addon-graphql/demo/index.js new file mode 100644 index 000000000000..b3852a0f68c7 --- /dev/null +++ b/packages/addon-graphql/demo/index.js @@ -0,0 +1,49 @@ +// Import the required libraries +const graphql = require('graphql'); +const graphqlHTTP = require('express-graphql'); +const express = require('express'); +const cors = require('cors'); + +// Import the data you created above +const data = require('./data.json'); + +// Define the User type with two string fields: `id` and `name`. +// The type of User is GraphQLObjectType, which has child fields +// with their own types (in this case, GraphQLString). +const userType = new graphql.GraphQLObjectType({ + name: 'User', + fields: { + id: { type: graphql.GraphQLString }, + name: { type: graphql.GraphQLString } + } +}); + +// Define the schema with one top-level field, `user`, that +// takes an `id` argument and returns the User with that ID. +// Note that the `query` is a GraphQLObjectType, just like User. +// The `user` field, however, is a userType, which we defined above. +const schema = new graphql.GraphQLSchema({ + query: new graphql.GraphQLObjectType({ + name: 'Query', + fields: { + user: { + type: userType, + // `args` describes the arguments that the `user` query accepts + args: { + id: { type: graphql.GraphQLString } + }, + // The resolve function describes how to "resolve" or fulfill + // the incoming query. + // In this case we use the `id` argument from above as a key + // to get the User from `data` + resolve(_, args) { + return data[args.id]; + } + } + } + }) +}); + +express().use(cors()).use('/graphql', graphqlHTTP({ schema, pretty: true })).listen(3000); + +console.log('GraphQL server running on http://localhost:3000/graphql'); diff --git a/packages/addon-graphql/demo/package.json b/packages/addon-graphql/demo/package.json new file mode 100644 index 000000000000..66d0f0becfce --- /dev/null +++ b/packages/addon-graphql/demo/package.json @@ -0,0 +1,18 @@ +{ + "name": "graphql-demo", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "Muhammed Thanish (http://thanish.me/)", + "license": "MIT", + "dependencies": { + "cors": "^2.8.0", + "express": "^4.14.0", + "express-graphql": "^0.5.4", + "graphql": "^0.7.0" + } +} diff --git a/packages/addon-graphql/docs/screenshot.png b/packages/addon-graphql/docs/screenshot.png new file mode 100644 index 000000000000..716a5bb819d9 Binary files /dev/null and b/packages/addon-graphql/docs/screenshot.png differ diff --git a/packages/addon-graphql/package.json b/packages/addon-graphql/package.json new file mode 100644 index 000000000000..f7f686a62ee2 --- /dev/null +++ b/packages/addon-graphql/package.json @@ -0,0 +1,42 @@ +{ + "name": "@kadira/storybook-addon-graphql", + "version": "1.0.1", + "description": "Storybook addon to display the GraphiQL IDE", + "main": "dist/index.js", + "scripts": { + "deploy-storybook": "storybook-to-ghpages", + "prepublish": "node .scripts/npm-prepublish.js", + "storybook": "start-storybook -p 9001", + "test": "echo \"Error: no test specified\" && exit 0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/kadirahq/storybook-addon-graphql.git" + }, + "keywords": [ + "storybook" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/kadirahq/storybook-addon-graphql/issues" + }, + "homepage": "https://github.com/kadirahq/storybook-addon-graphql#readme", + "devDependencies": { + "@kadira/storybook": "^2.20.1", + "babel-cli": "^6.11.4", + "babel-preset-es2015": "^6.9.0", + "babel-preset-react": "^6.11.1", + "babel-preset-stage-0": "^6.5.0", + "react": "^15.3.2", + "react-dom": "^15.3.2", + "shelljs": "^0.7.3" + }, + "peerDependencies": { + "@kadira/storybook-addons": "^1.3.0", + "react": "^0.14.7 || ^15.0.0" + }, + "dependencies": { + "graphiql": "^0.7.8", + "graphql": "^0.7.0" + } +} diff --git a/packages/addon-graphql/src/components/FullScreen/index.js b/packages/addon-graphql/src/components/FullScreen/index.js new file mode 100644 index 000000000000..07d88447c007 --- /dev/null +++ b/packages/addon-graphql/src/components/FullScreen/index.js @@ -0,0 +1,12 @@ +import React, { Component } from 'react'; +import style from './style'; + +export default class FullScreen extends Component { + render() { + return ( +
+ {this.props.children} +
+ ); + } +} diff --git a/packages/addon-graphql/src/components/FullScreen/style.js b/packages/addon-graphql/src/components/FullScreen/style.js new file mode 100644 index 000000000000..5001a7e1f9cc --- /dev/null +++ b/packages/addon-graphql/src/components/FullScreen/style.js @@ -0,0 +1,9 @@ +export default { + wrapper: { + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0 + } +}; diff --git a/packages/addon-graphql/src/index.js b/packages/addon-graphql/src/index.js new file mode 100644 index 000000000000..ce54fe8c17ce --- /dev/null +++ b/packages/addon-graphql/src/index.js @@ -0,0 +1 @@ +export { setupGraphiQL } from './preview'; diff --git a/packages/addon-graphql/src/preview.js b/packages/addon-graphql/src/preview.js new file mode 100644 index 000000000000..cda20a3ae1e0 --- /dev/null +++ b/packages/addon-graphql/src/preview.js @@ -0,0 +1,35 @@ +import React from 'react'; +import GraphiQL from 'graphiql'; +import FullScreen from './components/FullScreen'; +import 'graphiql/graphiql.css'; + +const FETCH_OPTIONS = { + method: 'post', + headers: { 'Content-Type': 'application/json' } +}; + +function getDefautlFetcher(url) { + return function(params) { + const body = JSON.stringify(params); + const options = Object.assign({ body }, FETCH_OPTIONS); + return fetch(url, options).then(res => res.json()); + }; +} + +function reIndentQuery(query) { + const lines = query.split('\n'); + const spaces = lines[lines.length - 1].length - 1; + return lines.map((l, i) => (i === 0 ? l : l.slice(spaces)).join('\n')); +} + +export function setupGraphiQL(config) { + return function(_query, variables = '{}') { + const query = reIndentQuery(_query); + const fetcher = config.fetcher || getDefautlFetcher(config.url); + return () => ( + + + + ); + }; +}