Big improvements in docs pages

This commit is contained in:
Syrus Akbary 2015-11-25 15:25:03 -08:00
parent 0cf6cd65c7
commit 65c9cc7ce8
22 changed files with 494 additions and 117 deletions

4
.gitignore vendored
View File

@ -61,3 +61,7 @@ target/
/tests/django.sqlite /tests/django.sqlite
/graphene/index.json
/graphene/meta.json

View File

@ -6,22 +6,25 @@ python:
- 3.4 - 3.4
- 3.5 - 3.5
- pypy - pypy
node_js:
- 4.1
cache: cache:
- pip directories:
- directories:
- .cache/pip/ - .cache/pip/
- docs/node_modules - $HOME/.cache/pip
- docs/node_modules/
- $HOME/docs/node_modules
install: install:
- pip install --download-cache .cache/pip/ pytest pytest-cov coveralls flake8 six - pip install --download-cache $HOME/.cache/pip/ pytest pytest-cov coveralls flake8 six
blinker pytest-django blinker pytest-django
- pip install --download-cache .cache/pip/ -e .[django] - pip install --download-cache $HOME/.cache/pip/ -e .[django]
- python setup.py develop - python setup.py develop
script: script:
- py.test --cov=graphene - py.test --cov=graphene
- flake8 - flake8
after_success: after_success:
- coveralls - coveralls
- cd docs && npm run deploy - nvm install 4.1 && cd docs && npm run deploy
env: env:
matrix: matrix:

BIN
docs/assets/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

BIN
docs/assets/edit@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

View File

@ -1 +1,17 @@
siteTitle="Graphene" siteTitle = "Graphene"
[docs.quickstart]
name = "Quickstart"
pages = [
"/docs/quickstart/",
"/docs/quickstart-django/",
]
[docs.walkthrough]
name = "Walkthrough"
pages = [
"/docs/interfaces/",
"/docs/objecttypes/",
"/docs/mutations/",
"/docs/basic-types/",
]

70
docs/css/hljs.css Normal file
View File

@ -0,0 +1,70 @@
/**
* GitHub Gist Theme
* Author : Louis Barranqueiro - https://github.com/LouisBarranqueiro
*/
.hljs {
display: block;
background: white;
padding: 0.5em;
color: #333333;
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}

View File

@ -1,6 +1,7 @@
@import 'nib' @import 'nib'
@import 'jeet' @import 'jeet'
@import 'hljs.css'
@import 'https://fonts.googleapis.com/css?family=Raleway:400,500,600,200,100&.css' @import 'https://fonts.googleapis.com/css?family=Raleway:400,500,600,200,100&.css'
normalize-css() normalize-css()
@ -15,6 +16,9 @@ $wrapper
a, a:hover a, a:hover
text-decoration none text-decoration none
a
color rgb(42,93,173)
html, body html, body
font-family "Helvetica Neue", Helvetica, Arial, sans-serif font-family "Helvetica Neue", Helvetica, Arial, sans-serif
font-weight 300 font-weight 300
@ -85,7 +89,19 @@ html, body
text-transform uppercase text-transform uppercase
font-weight 600 font-weight 600
line-height 15px line-height 15px
position relative
&.active:before
content: ''
width 5px
height 5px
border-radius 100%
display block
position absolute
background white
left 50%
margin-left -3px
bottom -24px
.header-logo .header-logo
font-family 'Raleway' font-family 'Raleway'
font-size 22px font-size 22px
@ -168,45 +184,183 @@ html, body
border-radius: 100px border-radius: 100px
font-size 13px font-size 13px
padding 17px 17px 17px 71px padding 17px 17px 17px 71px
width 226px width 236px
box-sizing border-box box-sizing border-box
color white color white
font-family 'Raleway' font-family 'Raleway'
font-weight 500 font-weight 500
transition all .2s ease-in-out
&:before &:before
content: '' content: ''
display block display block
position absolute position absolute
left 0 left 20px
top 0 top 20px
height 32px height 32px
width 32px width 32px
image '../assets/starwars-icon.png' image '~!file-loader!../assets/starwars-icon.png' 32px 32px
&:hover
transform translateY(-3px)
box-shadow 0px 4px 8px 0px rgba(0,0,0,0.32)
.improve-document-link .improve-document-link
@extend $wrapper position fixed
right 0
bottom 70px
transform-origin 100% 100%
background: #999;
border: 1px solid #919191;
border-radius: 3px 3px 0 0;
border-bottom 0
padding 9px 12px 12px 34px
transform: rotate(270deg) translateX(100%) translateY(3px);
font-size: 11px;
font-weight: 500;
text-transform uppercase
color: #FFFFFF;
letter-spacing: 0.3px;
line-height: 11px;
transition all .2s ease-in-out
&:before
content: ''
display block
position absolute
left 10px
top 8px
height 16px
width 16px
image '~!file-loader!../assets/edit.png' 16px 16px
&:hover
transform: rotate(270deg) translateX(100%)
background #666
border-color #555
+below(600px)
display none
$title
display block
font-family: 'Raleway';
font-weight 500
line-height 1.2em
padding-top .3em
margin-bottom .5em
padding-bottom .5em
color #4A4A4A
.markdown .markdown
.wrapper .wrapper
margin-top 60px margin-top 60px
h1, h2, h3, h4, h5, h6 h1, h2, h3, h4, h5, h6
font-family: 'Raleway'; @extend $title
font-weight 600
color #4A4A4A h1
strong font-size 32px
font-weight 500 h2
font-size 26px
h3
font-size 24px
h4
font-size 21px
h5
font-size 18px
h6
font-size 16px
strong
font-weight 500
pre
line-height 20px
background #FAFAFA
padding 20px
white-space: pre
display: block;
color: #333333;
overflow-x: auto;
p code, ul code
background #FAFAFA
padding 2px 4px
border-radius 2px
border 1px solid #CCC
color #000
code
font-size 14px
line-height 20px
overflow-x: auto;
margin-bottom 40px margin-bottom 40px
.markdown h1:first-child
margin-top 0
padding-top 0
.title .title
background: #F9F9F9; background: #F9F9F9;
padding 30px 0 padding 54px 0
h1 h1
margin 0 auto
@extend $wrapper @extend $wrapper
font-family: 'Raleway'; font-family: 'Raleway';
font-size: 42px; font-size: 42px;
font-weight 200 font-weight 200
color: #585858; color: #585858;
line-height: 49px; line-height: 49px;
.docs
@extend $wrapper
.docs-aside
col(1/4)
margin-top 60px
+below(600px)
padding 20px
width 100%
box-sizing content-box
margin 0 -20px
margin-bottom 30px
background #F9F9F9
.docs-aside-group
display block
margin-bottom 40px
h3
font-family: 'Raleway';
font-weight 500
font-size 12px
text-transform uppercase
line-height 1.2em
margin-bottom 1em
color #AAA
a
display block
font-size 15px
font-weight 400
line-height 22px
height 28px
padding 3px 0
color #4A4A4A
&.active
font-weight 500
line-height 21px
color #E05B49
+below(600px)
display none
.docs-aside-navselect
display none
width 100%
+below(600px)
display block
.docs-content
col(3/4)
margin-top 60px
+below(600px)
margin-top 10px
col(1)
>h1
margin 0
@extend $title
font-size 32px

View File

@ -1,12 +1,13 @@
var nib = require("nib"); var nib = require("nib");
var jeet = require("jeet"); var jeet = require("jeet");
var rupture = require("rupture");
var ExtractTextPlugin = require("extract-text-webpack-plugin"); var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = function(config, env) { module.exports = function(config, env) {
var IS_STATIC = env === 'static'; var IS_STATIC = env === 'static';
config.merge({ config.merge({
stylus: { stylus: {
use: [nib(), jeet()] use: [nib(), jeet(), rupture()]
} }
}); });
if (IS_STATIC) { if (IS_STATIC) {

View File

@ -5,7 +5,7 @@
"main": "n/a", "main": "n/a",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"build": "npm install -g gatsby && npm install", "build": "npm install -g gatsby && npm install && gatsby build",
"deploy": "npm run build" "deploy": "npm run build"
}, },
"keywords": [ "keywords": [

View File

@ -1,6 +1,13 @@
var IN_BROWSER = typeof window != 'undefined'; var IN_BROWSER = typeof window != 'undefined';
import React from 'react'; import React from 'react';
var browser_supported;
if (IN_BROWSER) { if (IN_BROWSER) {
var isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 &&
navigator.userAgent && !navigator.userAgent.match('CriOS');
var browser_supported = !isSafari;
}
console.log(browser_supported);
if (IN_BROWSER && browser_supported) {
var glfx = require('../vendor/glfx.optim') var glfx = require('../vendor/glfx.optim')
var particlesJS = require('../vendor/particles.js') var particlesJS = require('../vendor/particles.js')
} }
@ -23,12 +30,12 @@ class Header extends React.Component {
this.mounted = false; this.mounted = false;
} }
componentDidMount() { componentDidMount() {
if (!IN_BROWSER) return; if (!(IN_BROWSER && browser_supported)) return;
this.mounted = true; this.mounted = true;
new particlesJS('header-background', { new particlesJS('header-background', {
"particles": { "particles": {
"number": { "number": {
"value": 36, "value": 40,
"density": { "density": {
"enable": true, "enable": true,
"value_area": 800 "value_area": 800

View File

@ -19,10 +19,10 @@ class Template extends React.Component {
Graphene Graphene
</Link> </Link>
<nav className="header-nav"> <nav className="header-nav">
<Link to="/docs/quickstart/">Get started</Link> <Link to="/try/">Try it out</Link>
<Link to="/">Playground</Link> <Link to="/docs/quickstart/" className={path.indexOf('/docs')==0?"active":null}>Docs</Link>
<Link to="/">Docs</Link>
<Link to="/community/">Community</Link> <Link to="/community/">Community</Link>
<a href="https://github.com/graphql-python/graphene/">Github</a>
</nav> </nav>
</div> </div>
{isIndex? {isIndex?

View File

@ -17,9 +17,17 @@ If you think working with Graphene is fun, there are many ways you can contribut
- **GraphQL Relay**: [Source Code][1] - [PyPI package][2] - **GraphQL Relay**: [Source Code][1] - [PyPI package][2]
- **Graphene**: [Source Code][3] - [PyPI package][4] - **Graphene**: [Source Code][3] - [PyPI package][4]
Django integration:
- **graphql-django-view**: [Source Code][5] - [PyPI package][6]
- **django-graphiql**: [Source Code][7] - [PyPI package][8]
[Source Code]: https://github.com/graphql-python/graphql-core [Source Code]: https://github.com/graphql-python/graphql-core
[PyPI package]: https://pypi.python.org/pypi/graphql-core [PyPI package]: https://pypi.python.org/pypi/graphql-core
[1]: https://github.com/graphql-python/graphql-relay [1]: https://github.com/graphql-python/graphql-relay
[2]: https://pypi.python.org/pypi/graphql-relay [2]: https://pypi.python.org/pypi/graphql-relay
[3]: https://github.com/graphql-python/graphene [3]: https://github.com/graphql-python/graphene
[4]: https://pypi.python.org/pypi/graphene [4]: https://pypi.python.org/pypi/graphene
[5]: https://github.com/graphql-python/graphql-django-view
[6]: https://pypi.python.org/pypi/graphql-django-view
[7]: https://github.com/graphql-python/django-graphiql
[8]: https://pypi.python.org/pypi/django-graphiql

View File

@ -0,0 +1,48 @@
import React from 'react';
import { RouteHandler, Link, State } from 'react-router';
import _ from 'lodash';
class Template extends React.Component {
goToPage(event) {
var page = event.target.value;
this.context.router.transitionTo(page);
}
render() {
var docs = this.props.config.docs;
var docs_index = _.indexBy(this.props.pages, 'path');
return (
<div className="docs">
<aside className="docs-aside">
{Object.keys(docs).map((key) => {
let group = docs[key];
return <div className="docs-aside-group" key={key}>
<h3>{group.name}</h3>
{group.pages.map((page) => {
return <Link key={page} to={page}>{docs_index[page].data.title}</Link>
})}
</div>;
})}
<select className="docs-aside-navselect" onChange={this.goToPage.bind(this)}>
{Object.keys(docs).map((key) => {
let group = docs[key];
return <optgroup key={key} label={group.name}>
{group.pages.map((page) => {
return <option key={page} value={page}>{docs_index[page].data.title}</option>
})}
</optgroup>;
})}
</select>
</aside>
<div className="docs-content">
<RouteHandler {...this.props} docs={true}/>
</div>
</div>
);
}
}
Template.contextTypes = {
router: React.PropTypes.func
};
module.exports = Template;

View File

@ -0,0 +1,52 @@
---
title: Basic Types
description: Walkthrough Basic Types
---
# Basic Types
Graphene define the following base Types:
- `graphene.String`
- `graphene.Int`
- `graphene.Float`
- `graphene.Boolean`
- `graphene.ID`
Also, define:
- `graphene.List`
- `graphene.NonNull`
## Mounting in ClassTypes
This types if are mounted in a `ObjectType`, `Interface` or `Mutation`,
would act as `Field`s.
So, the following examples will behave exactly the same:
```python
class Person(graphene.ObjectType):
name = graphene.String()
```
and
```python
class Person(graphene.ObjectType):
name = graphene.Field(graphene.String())
```
## Mounting in Fields
If this types are mounted in a `Field`, would act as `Argument`s.
So, the following examples will behave exactly the same:
```python
class Person(graphene.ObjectType):
say_hello = graphene.Field(graphene.String(),
to=graphene.String())
```
and
```python
class Person(graphene.ObjectType):
say_hello = graphene.Field(graphene.String(),
to=graphene.Argument(graphene.String()))
```

View File

@ -1,9 +1,13 @@
---
title: Interfaces
description: Walkthrough Interfaces
---
# Interfaces # Interfaces
An Interface contains the essential fields that will be shared among multiple ObjectTypes. An Interface contains the essential fields that will be shared among multiple ObjectTypes.
The basics: The basics:
- Each Interface is a Python class that inherits from graphene.Interface. - Each Interface is a Python class that inherits from graphene.Interface.
- Each attribute of the Interface represents a GraphQL field. - Each attribute of the Interface represents a GraphQL field.
@ -16,19 +20,19 @@ import graphene
# Character is an Interface # Character is an Interface
class Character(graphene.Interface): class Character(graphene.Interface):
name = graphene.String() name = graphene.String()
# Human is an ObjectType, as inherits an interface # Human is an ObjectType, as inherits an interface
class Human(Character): class Human(Character):
born_in = graphene.String() born_in = graphene.String()
# Droid is an ObjectType, as inherits an interface # Droid is an ObjectType, as inherits an interface
class Droid(Character): class Droid(Character):
function = graphene.String() function = graphene.String()
``` ```
**name** is a field in the `Character` interface that will be in both `Human` and `Droid` ObjectTypes (as those inherit from Character). Each ObjectType also define extra fields at the same time. **name** is a field in the `Character` interface that will be in both `Human` and `Droid` ObjectTypes (as those inherit from `Character`). Each ObjectType also define extra fields at the same time.
The above types would have the following representation in a schema: The above types would have the following representation in a schema:

View File

@ -0,0 +1,75 @@
---
title: Mutations
description: Walkthrough Mutations
---
# Mutations
A Mutation is a special ObjectType that define also an Input.
## Quick example
This example defines a Mutation:
```python
import graphene
class CreatePerson(graphene.Mutation):
class Input:
name = graphene.String()
ok = graphene.String()
person = graphene.Field('Person')
@classmethod
def mutate(cls, args, info):
person = Person(name=args.get('name'))
ok = True
return CreatePerson(person=person, ok=ok)
```
**person** and **ok** are the output fields of the Mutation when is resolved.
**Input** attributes are the arguments that the Mutation `CreatePerson` needs for resolving, in this case **name** will be the only argument for the mutation.
**mutate** is the function that will be applied once the mutation is called.
So, we can finish our schema like this:
```python
# ... the Mutation Class
class Person(graphene.ObjectType):
name = graphene.String()
class MyMutations(graphene.ObjectType):
create_person = graphene.Field(CreatePerson)
schema = graphene.Schema(mutation=MyMutations)
```
## Executing the Mutation
Then, if we query (`schema.execute(query_str)`) the following:
```graphql
mutation myFirstMutation {
createPerson(name:"Peter") {
person {
name
}
ok
}
}
```
We should receive:
```json
{
"createPerson": {
"person" : {
name: "Peter"
},
"ok": true
}
}

View File

@ -1,10 +1,14 @@
---
title: ObjectTypes
description: Walkthrough ObjectTypes
---
# ObjectTypes # ObjectTypes
An ObjectType is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data youre querying. An ObjectType is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data youre querying.
The basics: The basics:
- Each ObjectType is a Python class that inherits graphene.ObjectType or inherits an implemented [interfaces](/docs/interfaces/).
- Each ObjectType is a Python class that inherits graphene.ObjectType or inherits an implemented [interface](/docs/interface).
- Each attribute of the ObjectType represents a GraphQL field. - Each attribute of the ObjectType represents a GraphQL field.
## Quick example ## Quick example
@ -20,7 +24,7 @@ class Person(graphene.ObjectType):
full_name = graphene.String() full_name = graphene.String()
def resolve_full_name(self, args, info): def resolve_full_name(self, args, info):
return '{} {}'.format(self.first_name, self.last_name) return '{} {}'.format(self.first_name, self.last_name)
``` ```
**first_name** and **last_name** are fields of the ObjectType. Each field is specified as a class attribute, and each attribute maps to a GraphQL field. **first_name** and **last_name** are fields of the ObjectType. Each field is specified as a class attribute, and each attribute maps to a GraphQL field.

View File

@ -1,13 +1,10 @@
--- ---
title: Django Quickstart Guide title: Guide to Django
active_tab: quickstart
description: A Quick guide to Graphene in Django description: A Quick guide to Graphene in Django
--- ---
## Django Quickstart
In our previous quickstart page we created a very simple schema. In our previous quickstart page we created a very simple schema.
Now we will adapt the schema for our Django models. Now we will adapt the schema to map automatically some Django models.
## Project setup ## Project setup

View File

@ -1,10 +1,10 @@
--- ---
title: Quickstart Guide title: Getting started
active_tab: quickstart
description: A Quick guide to Graphene description: A Quick guide to Graphene
--- ---
Graphene is a powerful framework for creating GraphQL schemas easily in Python. Let's build a basic GraphQL schema from scratch.
## Requirements ## Requirements
@ -23,6 +23,7 @@ cd tutorial
virtualenv env virtualenv env
source env/bin/activate # On Windows use `env\Scripts\activate` source env/bin/activate # On Windows use `env\Scripts\activate`
# Install Graphene
pip install graphene pip install graphene
``` ```
@ -30,7 +31,7 @@ pip install graphene
A GraphQL schema describes your data model, and provides a GraphQL server with an associated set of resolve methods that know how to fetch data. A GraphQL schema describes your data model, and provides a GraphQL server with an associated set of resolve methods that know how to fetch data.
We are going to create a very simple schema, with a `Query` with only one field: `hello`. And when we query it should return 'World'. We are going to create a very simple schema, with a `Query` with only one field: `hello`. And when we query it should return `"World"`.
```python ```python

View File

@ -1,67 +0,0 @@
# Mutations
A Mutation is a special ObjectType that define also an Input.
## Quick example
This example model defines a Mutation:
```python
import graphene
class CreatePerson(graphene.Mutation):
class Input:
name = graphene.String()
ok = graphene.String()
person = graphene.Field('Person')
@classmethod
def mutate(cls, args, info):
person = Person(name=args.get('name'))
ok = True
return CreatePerson(person=person, ok=ok)
```
**person** and **ok** are the output fields of the Mutation when is resolved.
**Input** attributes are the arguments that the Mutation needs for resolving. **mutate** is the function that will be applied once the mutation is called.
So, we can finish our schema like this:
```python
# ... the Mutation Class
class Person(graphene.ObjectType):
name = graphene.String()
class MyMutations(graphene.ObjectType):
create_person = graphene.Field(CreatePerson)
schema = graphene.Schema(mutation=MyMutations)
```
## Executing the Mutation
Then, if we query (`schema.execute(query_str)`) the following:
```graphql
mutation myFirstMutation {
createPerson(name:"Peter") {
person {
name
}
ok
}
}
```
We should receive:
```json
{
"createPerson": {
"person" : {
name: "Peter"
},
"ok": true
}
}

View File

@ -1,7 +1,7 @@
--- ---
path: / path: /
--- ---
<a class="starwars-example" href="http://swapi.graphene-python.org/">Check our starwars API example!</a> <div><a class="starwars-example" href="http://swapi.graphene-python.org/">Check our Django Starwars API example!</a></div>
## About Graphene ## About Graphene

View File

@ -9,15 +9,15 @@ class Markdown extends React.Component {
var post = this.props.page.data; var post = this.props.page.data;
var pagePath = this.props.page.requirePath; var pagePath = this.props.page.requirePath;
var documentUrl = `${DOCS_BASEURL}${pagePath}`; var documentUrl = `${DOCS_BASEURL}${pagePath}`;
var showTitle = post.title && !this.props.docs;
return ( return (
<DocumentTitle title={`${post.title?post.title+' - ':''}${this.props.config.siteTitle}`}> <DocumentTitle title={`${post.title?post.title+' - ':''}${this.props.config.siteTitle}`}>
<div className="markdown"> <div className="markdown">
{post.title?<div className="title"> {showTitle?<div className="title">
<h1>{post.title}</h1> <h1>{post.title}</h1>
</div>:null} </div>:null}
<div className="wrapper" dangerouslySetInnerHTML={{__html: post.body}}/> <div className={!this.props.docs?"wrapper":null} dangerouslySetInnerHTML={{__html: post.body}}/>
<a href={documentUrl} className="improve-document-link">Improve this document!</a> <a href={documentUrl} className="improve-document-link">Edit page</a>
</div> </div>
</DocumentTitle> </DocumentTitle>
); );