Repository: sgwilym/relay-visual-learners
Branch: master
Commit: a3af2018fc99
Files: 53
Total size: 110.4 KB
Directory structure:
gitextract_qxbkef7i/
├── .babelrc
├── .eslintrc
├── .gitignore
├── LICENSE
├── README.md
├── docs/
│ └── why-relay.html
├── index.html
├── package.json
├── server.js
├── src/
│ ├── components/
│ │ ├── App.jsx
│ │ ├── App.scss
│ │ ├── Diagram.jsx
│ │ ├── Topic.jsx
│ │ └── Topic.scss
│ ├── diagram-data.js
│ ├── diagrams/
│ │ ├── connection-field.xml
│ │ ├── container.xml
│ │ ├── fields.xml
│ │ ├── global-id-field.xml
│ │ ├── graphql-implementation.xml
│ │ ├── mutation-type.xml
│ │ ├── mutations.xml
│ │ ├── network-layer.xml
│ │ ├── object-type.xml
│ │ ├── query-config.xml
│ │ ├── query-type.xml
│ │ ├── react-component.xml
│ │ ├── relay.xml
│ │ ├── root-container.xml
│ │ ├── schema-copy.xml
│ │ ├── schema.xml
│ │ └── web-framework.xml
│ ├── index.js
│ ├── shared/
│ │ └── styles/
│ │ └── colors.scss
│ └── topics/
│ ├── backend.js
│ ├── container.js
│ ├── graphql-impl.js
│ ├── graphql-schema.js
│ ├── mutation-type.js
│ ├── mutations.js
│ ├── network-layer.js
│ ├── object-type-fields.js
│ ├── object-type.js
│ ├── query-config.js
│ ├── query-type.js
│ ├── react-component.js
│ ├── relay.js
│ ├── root-container.js
│ ├── schema-copy.js
│ └── web-framework.js
├── svgGroup.loader.js
├── webpack.config.js
└── webpack.production.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"stage": 0,
"plugins": ["object-assign"]
}
================================================
FILE: .eslintrc
================================================
{
"ecmaFeatures": {
"jsx": true,
"modules": true
},
"env": {
"browser": true,
"node": true
},
"parser": "babel-eslint",
"rules": {
"quotes": [2, "single"],
"strict": [2, "never"],
"react/jsx-uses-react": 2,
"react/jsx-uses-vars": 2,
"react/react-in-jsx-scope": 2
},
"plugins": [
"react"
]
}
================================================
FILE: .gitignore
================================================
node_modules
npm-debug.log
.DS_Store
build
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2014 Dan Abramov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
relay-visual-learners
=====================
Relay is a new framework from Facebook that promises to simplify a problem complex enough that the simplification is rather complex in itself.
I tend to learn things better when I can see how things fit together, so I made this interactive diagram that attempts to explain how Relay’s various parts fit together.
This diagram itself is a small react app using css-modules and webpack. You can clone this repo and run `npm start` to load up a hot loading dev environment.
# Todo
- The diagram is constructed from fragments of a single SVG illustration broken up into separate XML files and linked to each separate topic. While this works well enough, it means changing the diagram is a bit of a brittle process that usually requires changing most of the other diagram files. If you know a better way to component-ise a SVG, please feel free to make an issue or PR!
- Think one of the explanations for one of the topics is inaccurate or could be more succinctly expressed? Contributions are more than welcome.
================================================
FILE: docs/why-relay.html
================================================
The Problem solved by Relay and GraphQL (for Visual Learners Who Respond Well To Cartoons)
We start with our new React Component, RedComponent.
Like most components, RedComponent needs some external data to render. RedComponent expects data with a certain shape, e.g an object with name, birthday and favouriteColor properties.
And let’s introduce our server, who will provide RedComponent with the data it needs.
Great! Ship it.
This works perfectly well — until we decide to introduce a new feature provided by GreenComponent, which will be rendered inside RedComponent. This is a key strength of React: component composability.
GreenComponent expects data too, but it needs a different shape. And because it's rendered by RedComponent, RedComponent has the responsibility of passing down that data to GreenComponent.
And remember: our server now needs to serve the data our new composition of components expects.
With a little work, we eventually get this working. But times change, and now we’re deprecating RedComponent for the latest in UI sophistication: BlueComponent. Again, this is React doing what React does best: cleanly composing new and existing components together.
But guess what? BlueComponentexpects a different shape of data. So unless we make some changes server-side, our components aren't going to render properly!
And what if we build another client with different components, like a mobile client? We’ll have to add another endpoint to our server to satisfy those components’ data expectations.
I've been using the word 'expects' here, because there’s absolutely no way for the server to really know what shape of data our components need. In this setup, the responsibility to keeping the expectations of the components and the data provided by the server in sync belongs to the developer.
That's right: the developer is the one who has to remember whether all the endpoints are providing the right kinds of data to components, and update the server whenever any data requirements are changed. And in the real world, we’re going to be using a lot of components. It's going to mean a lot of repetitive, boring, disciplined work. And who wants any of that?
What we need is a way for components to stop expecting and to start declaring what data they need. And we'd need a way to make a server that understands these declarations and can intelligently compose them.
This utopian solution would look a little something like this:
This is what Relay and GraphQL does.
Of course they do a lot more than that: query validation, data caching, optimistic queries, etc. etc. — but the above is what I feel is Relay and GraphQL’s raison d'etre.
I tend to learn things better when I can see how things fit together, so I made this interactive diagram that attempts to explain how Relay’s various parts fit together.
)
}
}
Topic.propTypes = {
topic: PropTypes.shape({
title: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
officialDocs: PropTypes.string,
children: PropTypes.array,
highlighted: PropTypes.bool.isRequired,
collapsed: PropTypes.bool.isRequired,
underMouseChanged: PropTypes.func.isRequired,
topicClicked: PropTypes.func.isRequired
}).isRequired
}
================================================
FILE: src/components/Topic.scss
================================================
@import '../shared/styles/colors.scss';
.root {
list-style-type: none;
color: $dark-color;
font-family: 'Menlo', monospace;
overflow: visible;
}
.heading {
font-size: 1em;
font-weight: normal;
margin: 0;
padding: .5em;
background: $light-3-color;
border-bottom: 3px solid $light-2-color;
transition: all 100ms;
cursor: pointer;
}
.headingHighlighted {
composes: heading;
color: white;
background: $relay-primary-color;
border-bottom-color: lighten($relay-primary-color, 30);
transform: scale(1.1, 1.1);
transform-origin: right;
z-index: 2;
}
.details {
font-size: .9em;
padding: .75em .5em .5em .5em;
background: $light-2-color;
transition: all 100ms;
p {
margin: 0 0 .5em 0;
line-height: 1.4em;
}
}
.detailsHighlighted {
composes: details;
background-color: $relay-secondary-color;
}
.officialDocs {
font-size: .8em;
color: $relay-primary-color;
}
.childTopics {
padding: 0 0 0 1.5em;
}
================================================
FILE: src/diagram-data.js
================================================
import BackEndTopic from './topics/backend';
import RelayTopic from './topics/relay';
export default [
RelayTopic,
BackEndTopic
];
================================================
FILE: src/diagrams/connection-field.xml
================================================
================================================
FILE: src/diagrams/container.xml
================================================
================================================
FILE: src/diagrams/fields.xml
================================================
================================================
FILE: src/diagrams/global-id-field.xml
================================================
================================================
FILE: src/diagrams/graphql-implementation.xml
================================================
================================================
FILE: src/diagrams/mutation-type.xml
================================================
================================================
FILE: src/diagrams/mutations.xml
================================================
================================================
FILE: src/diagrams/network-layer.xml
================================================
================================================
FILE: src/diagrams/object-type.xml
================================================
================================================
FILE: src/diagrams/query-config.xml
================================================
================================================
FILE: src/diagrams/query-type.xml
================================================
================================================
FILE: src/diagrams/react-component.xml
================================================
================================================
FILE: src/diagrams/relay.xml
================================================
================================================
FILE: src/diagrams/root-container.xml
================================================
================================================
FILE: src/diagrams/schema-copy.xml
================================================
================================================
FILE: src/diagrams/schema.xml
================================================
================================================
FILE: src/diagrams/web-framework.xml
================================================
================================================
FILE: src/index.js
================================================
import React from 'react';
import App from './components/App';
import topics from './diagram-data';
React.render(, document.getElementById('root'));
================================================
FILE: src/shared/styles/colors.scss
================================================
$relay-primary-color: #f26b00;
$relay-secondary-color: #FBFEFA;
$dark-color: #3B3738;
$desaturated-relay-color: desaturate($relay-primary-color, 90);
$light-3-color: lighten($desaturated-relay-color, 35);
$light-2-color: lighten($desaturated-relay-color, 45);
$light-1-color: lighten($desaturated-relay-color, 50);
================================================
FILE: src/topics/backend.js
================================================
import WebFrameworkTopic from './web-framework';
import GraphQLTopic from './graphql-impl';
export default {
title: 'Back End',
description: 'Relay wraps around the view layer of your application, while your app’s back end does the work of responding to its queries. This could be a separate server, or the same server that’s serving your UI.',
width: 519,
height: 359,
children: [
WebFrameworkTopic,
GraphQLTopic
]
}
================================================
FILE: src/topics/container.js
================================================
import ContainerDiagram from '../diagrams/container.xml';
import ReactComponentTopic from './react-component';
export default {
title: 'Relay.Container',
description: 'Relay.Container is a higher order component that wraps around your React Component and ensures that it gets the data it depends on before rendering. This is where you’ll declare your component’s data dependencies with query fragments.',
officialDocs: 'https://facebook.github.io/relay/docs/guides-containers.html#content',
diagram: ContainerDiagram,
children: [
ReactComponentTopic
]
}
================================================
FILE: src/topics/graphql-impl.js
================================================
import GraphQLDiagram from '../diagrams/graphql-implementation.xml';
import SchemaTopic from './graphql-schema.js';
export default {
title: 'GraphQL Implementation',
description: 'You’ll need a GraphQL implementation that will be able to parse and validate your GraphQL schema, as well as execute queries sent to it from Relay. It’s already been implemented in a few different languages, and there are more popping up all the time!',
officialDocs: 'http://facebook.github.io/graphql/',
diagram: GraphQLDiagram,
children: [
SchemaTopic
]
}
================================================
FILE: src/topics/graphql-schema.js
================================================
import SchemaDiagram from '../diagrams/schema.xml';
import ObjectTypeTopic from './object-type';
import QueryTypeTopic from './query-type';
import MutationType from './mutation-type';
export default {
title: 'GraphQL Schema',
description: 'A Schema describes how your data is structured by creating a hierarchy of types. These types can be your application’s objects — e.g users, likes or photos — or other types used to query and mutate your application’s data.',
diagram: SchemaDiagram,
children: [
ObjectTypeTopic,
QueryTypeTopic,
MutationType
]
}
================================================
FILE: src/topics/mutation-type.js
================================================
import MutationTypeDiagram from '../diagrams/mutation-type.xml';
export default {
title: 'Mutation Type',
description: 'If you want Relay to be able to change the data on your server, you’ll need a Mutation Type. Each of your mutations will need to be root fields on this Mutation Type, and return an object representing the successful mutation’s payload.',
officialDocs: 'https://facebook.github.io/relay/docs/graphql-mutations.html#content',
diagram: MutationTypeDiagram
}
================================================
FILE: src/topics/mutations.js
================================================
import MutationDiagram from '../diagrams/mutations.xml';
export default {
title: 'Mutations',
description: 'To change data in your app you’ll need mutations. In order for Relay to know which objects will be changed by mutations, and do things like optimistic updates, you’ll need to let it know up front about how your mutations behave. What arguments does this mutation take? What might the returned payload look like? Which associations will be changed? This rabbit hole goes deep.',
officialDocs: 'https://facebook.github.io/relay/docs/guides-mutations.html#content',
diagram: MutationDiagram
};
================================================
FILE: src/topics/network-layer.js
================================================
import NetworkLayerDiagram from '../diagrams/network-layer.xml';
export default {
title: 'Network Layer',
description: 'Relay’s Network Layer does the work of exchanging data with the GraphQL server. It’s also configurable, so this would be a good place to do something like modify the headers of your requests.',
officialDocs: 'https://facebook.github.io/relay/docs/guides-network-layer.html#content',
diagram: NetworkLayerDiagram
}
================================================
FILE: src/topics/object-type-fields.js
================================================
import FieldsDiagram from '../diagrams/fields.xml';
import ConnectionFieldDiagram from '../diagrams/connection-field.xml';
import GlobalIDFieldDiagram from '../diagrams/global-id-field.xml';
export default [
{
title: 'Fields',
description: 'The fields in your object describe the kinds of properties that can be queried, such as a user’s email address or favourite colour. They can also describe associated object types, like a user’s profile photo.',
diagram: FieldsDiagram
},
{
title: 'Connection Fields',
description: 'To enable fancy stuff like easy pagination, Relay asks that some of your object’s associations are expressed in a standardised way. It involves edges and nodes and other graph theory stuff that can thankfully be handled for you by a helper library.',
officialDocs: 'https://facebook.github.io/relay/docs/graphql-connections.html#content',
diagram: ConnectionFieldDiagram
},
{
title: 'Global ID Field',
description: 'Relay also requires that all objects have a global ID — unique across all types of data — that can be used to refetch any given object without knowing its type.',
officialDocs: 'https://facebook.github.io/relay/docs/graphql-object-identification.html#content',
diagram: GlobalIDFieldDiagram
}
]
================================================
FILE: src/topics/object-type.js
================================================
import ObjectTypeDiagram from '../diagrams/object-type.xml';
import ObjectTypeFields from './object-type-fields';
export default {
title: 'Object Type',
description: 'This is your generic object type. It could be anything in your data model, such as a user, a blog post or product review.',
diagram: ObjectTypeDiagram,
children: ObjectTypeFields
}
================================================
FILE: src/topics/query-config.js
================================================
import QueryConfigDiagram from '../diagrams/query-config.xml';
export default {
title: 'Relay.Route',
description: 'First up: Relay.Route doesn’t have anything to do with URLs or the History API. What Relay.Route does do is complete the query fragments declared by Relay Containers by declaring which object should be fetched from the server, e.g. User with ID 123.',
officialDocs: 'https://facebook.github.io/relay/docs/guides-routes.html#content',
diagram: QueryConfigDiagram
}
================================================
FILE: src/topics/query-type.js
================================================
import QueryTypeDiagram from '../diagrams/query-type.xml';
export default {
title: 'Query Type',
description: 'To query data, your schema will need a Query Type containing fields that fetch your various object types. These fields have resolve methods that describe how the requested data should actually be fetched. Relay will also want a special field here that will allow you to fetch an object of any type given its Global ID.',
officialDocs: 'https://facebook.github.io/relay/docs/graphql-object-identification.html#content',
diagram: QueryTypeDiagram
}
================================================
FILE: src/topics/react-component.js
================================================
import ReactComponentDiagram from '../diagrams/react-component.xml';
export default {
title: 'React.Component',
description: 'Finally something familiar! At the core of all these layers are the React components you know and love. That’s not to say React.Component doesn’t have a few new tricks up its sleeve, like being able to interact with Relay’s queries with setVariables().',
officialDocs: 'https://facebook.github.io/relay/docs/guides-containers.html#content',
diagram: ReactComponentDiagram
}
================================================
FILE: src/topics/relay.js
================================================
import RelayDiagram from '../diagrams/relay.xml';
import RootContainerTopic from './root-container';
import NetworkLayerTopic from './network-layer';
import SchemaCopyTopic from './schema-copy';
import MutationsTopic from './mutations';
export default {
title: 'Relay',
description: 'Relay is a framework for Javascript applications that aims to abstract away the repetitive work of exchanging data with a server. It works together with a GraphQL server to query, deliver and mutate data in a consistent, dependable way. A tall order!',
officialDocs: "https://facebook.github.io/relay/",
diagram: RelayDiagram,
width: 321,
height: 339,
children: [
RootContainerTopic,
NetworkLayerTopic,
SchemaCopyTopic,
MutationsTopic
]
}
================================================
FILE: src/topics/root-container.js
================================================
import RootContainerDiagram from '../diagrams/root-container.xml';
import RelayRouteTopic from './query-config';
import ContainerTopic from './container';
export default {
title: 'Relay.RootContainer',
description: 'Relay.RootContainer takes a Component (wrapped up in a Relay.Container) and a Relay.Route, turns these into a full query and sends it off to the GraphQL server. It’s also responsible for rendering your loading UI (spinners galore!), rendering the UI once the data has been loaded, and rendering a UI for when a query fails.',
officialDocs: 'https://facebook.github.io/relay/docs/guides-root-container.html#content',
diagram: RootContainerDiagram,
children: [
RelayRouteTopic,
ContainerTopic
]
}
================================================
FILE: src/topics/schema-copy.js
================================================
import SchemaCopyDiagram from '../diagrams/schema-copy.xml';
export default {
title: 'Schema Copy',
description: 'Relay needs a copy of the schema from your GraphQL server so that it knows how to properly execute and construct your queries on the front end side.',
diagram: SchemaCopyDiagram
}
================================================
FILE: src/topics/web-framework.js
================================================
import WebFrameworkDiagram from '../diagrams/web-framework.xml';
export default {
title: 'Web Framework',
description: 'A web framework to take HTTP requests and pass them on to your GraphQL implementation and return their results. This could be Express, Rails, Django… this could even be part of your GraphQL implementation!',
diagram: WebFrameworkDiagram
}
================================================
FILE: svgGroup.loader.js
================================================
module.exports = function(content) {
return (
'module.exports = require("react").createElement("g", {' +
'dangerouslySetInnerHTML: {' +
'__html: ' + JSON.stringify(content) +
'}' +
'});'
);
};
================================================
FILE: webpack.config.js
================================================
var path = require('path');
var webpack = require('webpack');
module.exports = {
devtool: 'eval',
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./src/index'
],
output: {
path: path.join(__dirname, 'build'),
filename: 'bundle.js',
publicPath: '/build/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
postcss: [
require('autoprefixer-core')
],
resolve: {
extensions: ['', '.js', '.jsx']
},
module: {
loaders: [
{
test: /\.jsx?$/,
loaders: ['react-hot', 'babel?optional[]=runtime'],
include: path.join(__dirname, 'src')
},
{
test: /\.xml$/,
loader: require.resolve('./svgGroup.loader'),
include: path.join(__dirname, 'src')
},
{
test: /\.scss$/,
loaders: ['style-loader', 'css-loader?modules&importLoaders=2&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader!sass'],
include: path.join(__dirname, 'src')
}
],
}
};
================================================
FILE: webpack.production.config.js
================================================
var path = require('path');
var webpack = require('webpack');
module.exports = {
target: 'web',
entry: [
'./src/index'
],
output: {
path: path.join(__dirname, 'build'),
filename: 'bundle.js',
publicPath: 'build/'
},
plugins: [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin(),
],
postcss: [
require('autoprefixer-core')
],
resolve: {
extensions: ['', '.js', '.jsx']
},
module: {
loaders: [
{
test: /\.jsx?$/,
loaders: ['react-hot', 'babel?optional[]=runtime'],
include: path.join(__dirname, 'src')
},
{
test: /\.xml$/,
loader: require.resolve('./svgGroup.loader'),
include: path.join(__dirname, 'src')
},
{
test: /\.scss$/, loaders: ['style-loader', 'css-loader?modules&importLoaders=2&localIdentName=[hash:base64:5]!postcss-loader!sass'],
include: path.join(__dirname, 'src')
}
]
}
};