├── .gitignore ├── CONTRIBUTING.md ├── README.md ├── branch-step-01-solution ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── index.css │ ├── index.js │ └── views │ │ ├── ListPage.css │ │ └── ListPage.js └── webpack.config.js ├── branch-step-01 ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── index.css │ ├── index.js │ └── views │ │ ├── ListPage.css │ │ └── ListPage.js └── webpack.config.js ├── branch-step-02-solution ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── index.css │ ├── index.js │ └── views │ │ ├── ListPage.css │ │ └── ListPage.js └── webpack.config.js ├── branch-step-02 ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── index.css │ ├── index.js │ └── views │ │ ├── ListPage.css │ │ └── ListPage.js └── webpack.config.js ├── branch-step-03-solution ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── components │ │ ├── PokemonPreview.css │ │ └── PokemonPreview.js │ ├── index.css │ ├── index.js │ └── views │ │ ├── ListPage.css │ │ └── ListPage.js └── webpack.config.js ├── branch-step-03 ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── components │ │ ├── PokemonPreview.css │ │ └── PokemonPreview.js │ ├── index.css │ ├── index.js │ └── views │ │ ├── ListPage.css │ │ └── ListPage.js └── webpack.config.js ├── branch-step-04-solution ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── assets │ │ └── delete.svg │ ├── components │ │ ├── AddNew.css │ │ ├── AddNew.js │ │ ├── PokemonCard.css │ │ ├── PokemonCard.js │ │ ├── PokemonPreview.css │ │ └── PokemonPreview.js │ ├── index.css │ ├── index.js │ └── views │ │ ├── ListPage.css │ │ ├── ListPage.js │ │ ├── PokemonPage.css │ │ └── PokemonPage.js └── webpack.config.js ├── branch-step-04 ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── assets │ │ └── delete.svg │ ├── components │ │ ├── AddNew.css │ │ ├── AddNew.js │ │ ├── PokemonCard.css │ │ ├── PokemonCard.js │ │ ├── PokemonPreview.css │ │ └── PokemonPreview.js │ ├── index.css │ ├── index.js │ └── views │ │ ├── ListPage.css │ │ ├── ListPage.js │ │ ├── PokemonPage.css │ │ └── PokemonPage.js └── webpack.config.js ├── branch-step-05-solution ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── assets │ │ └── delete.svg │ ├── components │ │ ├── AddNew.css │ │ ├── AddNew.js │ │ ├── PokemonCard.css │ │ ├── PokemonCard.js │ │ ├── PokemonPreview.css │ │ └── PokemonPreview.js │ ├── index.css │ ├── index.js │ ├── mutations │ │ └── CreatePokemonMutation.js │ └── views │ │ ├── ListPage.css │ │ ├── ListPage.js │ │ ├── PokemonPage.css │ │ └── PokemonPage.js └── webpack.config.js ├── branch-step-05 ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── assets │ │ └── delete.svg │ ├── components │ │ ├── AddNew.css │ │ ├── AddNew.js │ │ ├── PokemonCard.css │ │ ├── PokemonCard.js │ │ ├── PokemonPreview.css │ │ └── PokemonPreview.js │ ├── index.css │ ├── index.js │ ├── mutations │ │ └── CreatePokemonMutation.js │ └── views │ │ ├── ListPage.css │ │ ├── ListPage.js │ │ ├── PokemonPage.css │ │ └── PokemonPage.js └── webpack.config.js ├── branch-step-06-solution ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── assets │ │ └── delete.svg │ ├── components │ │ ├── AddNew.css │ │ ├── AddNew.js │ │ ├── PokemonCard.css │ │ ├── PokemonCard.js │ │ ├── PokemonPreview.css │ │ └── PokemonPreview.js │ ├── index.css │ ├── index.js │ ├── mutations │ │ ├── CreatePokemonMutation.js │ │ └── DeletePokemonMutation.js │ └── views │ │ ├── ListPage.css │ │ ├── ListPage.js │ │ ├── PokemonPage.css │ │ └── PokemonPage.js └── webpack.config.js ├── branch-step-06 ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── assets │ │ └── delete.svg │ ├── components │ │ ├── AddNew.css │ │ ├── AddNew.js │ │ ├── PokemonCard.css │ │ ├── PokemonCard.js │ │ ├── PokemonPreview.css │ │ └── PokemonPreview.js │ ├── index.css │ ├── index.js │ ├── mutations │ │ ├── CreatePokemonMutation.js │ │ └── DeletePokemonMutation.js │ └── views │ │ ├── ListPage.css │ │ ├── ListPage.js │ │ ├── PokemonPage.css │ │ └── PokemonPage.js └── webpack.config.js ├── branch-step-07-solution ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── assets │ │ └── delete.svg │ ├── components │ │ ├── AddNew.css │ │ ├── AddNew.js │ │ ├── PokemonCard.css │ │ ├── PokemonCard.js │ │ ├── PokemonPreview.css │ │ └── PokemonPreview.js │ ├── index.css │ ├── index.js │ ├── mutations │ │ ├── CreatePokemonMutation.js │ │ ├── DeletePokemonMutation.js │ │ └── UpdatePokemonMutation.js │ └── views │ │ ├── ListPage.css │ │ ├── ListPage.js │ │ ├── PokemonPage.css │ │ └── PokemonPage.js └── webpack.config.js ├── branch-step-07 ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── src │ ├── assets │ │ └── delete.svg │ ├── components │ │ ├── AddNew.css │ │ ├── AddNew.js │ │ ├── PokemonCard.css │ │ ├── PokemonCard.js │ │ ├── PokemonPreview.css │ │ └── PokemonPreview.js │ ├── index.css │ ├── index.js │ ├── mutations │ │ ├── CreatePokemonMutation.js │ │ ├── DeletePokemonMutation.js │ │ └── UpdatePokemonMutation.js │ └── views │ │ ├── ListPage.css │ │ ├── ListPage.js │ │ ├── PokemonPage.css │ │ └── PokemonPage.js └── webpack.config.js ├── hosted-demo ├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── .netlify ├── README.md ├── favicon.ico ├── index.html ├── package.json ├── public │ └── _redirects ├── src │ ├── assets │ │ └── delete.svg │ ├── components │ │ ├── AddNew.css │ │ ├── AddNew.js │ │ ├── PokemonCard.css │ │ ├── PokemonCard.js │ │ ├── PokemonPreview.css │ │ └── PokemonPreview.js │ ├── index.css │ ├── index.js │ ├── mutations │ │ ├── CreatePokemonMutation.js │ │ ├── DeletePokemonMutation.js │ │ └── UpdatePokemonMutation.js │ └── views │ │ ├── ListPage.css │ │ ├── ListPage.js │ │ ├── PokemonPage.css │ │ └── PokemonPage.js └── webpack.config.js ├── pokedex.schema └── update-branches.sh /.gitignore: -------------------------------------------------------------------------------- 1 | */node_modules 2 | node_modules 3 | .idea 4 | .DB_Store 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pokedex 2 | 3 | Pokedex example app using and teaching Relay and GraphQL 4 | 5 | > **Live Demo: [http://demo.learnrelay.org](http://demo.learnrelay.org/)** 6 | 7 | ![](https://i.gyazo.com/adcc4675cd466195adf727ba8a32b544.gif) 8 | 9 | ## Getting started 10 | 11 | If you haven't done it already, checkout the [interactive Learn Relay tutorial](https://www.learnrelay.org/). 12 | 13 | ```sh 14 | git clone git@github.com:learnrelay/pokedex.git 15 | cd pokedex 16 | git checkout step-01 17 | npm install 18 | npm start # open localhost:3000 19 | ``` 20 | 21 | ## Workflow 22 | 23 | As you're following along the chapters in Learn Relay, you will work on seven small coding excercises each called a "step". Each step and its solution is available in this repository as an individual branch where you can jump between if needed. 24 | 25 | Let's say you want to start with step 3, so you would run `git checkout step-03`. In case you're not too familiar with `git`, this command switches you over to the `step-03` branch. The `node_modules` folder doesn't change, that means you don't need to run `npm install` again. 26 | 27 | After you completed the step, you can compare your results to the official solution by running `git diff origin/step-03-solution`. In the best possible case this command shouldn't give you any output which means your and our solution are identical. It can happen that you found a different solution than we're proposing. That's totally fine, just continue with the next step and feel free to [tell us](http://slack.graph.cool/) about your solution. 💡 28 | 29 | ## Help & Community [![Slack Status](https://slack.graph.cool/badge.svg)](https://slack.graph.cool) 30 | 31 | Join our [Slack community](http://slack.graph.cool/) if you run into issues or have questions. We love talking to you! 32 | 33 | ![](http://i.imgur.com/5RHR6Ku.png) 34 | -------------------------------------------------------------------------------- /branch-step-01-solution/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-01-solution/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-01-solution/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .idea 6 | .DB_Store -------------------------------------------------------------------------------- /branch-step-01-solution/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-01-solution/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-01-solution/favicon.ico -------------------------------------------------------------------------------- /branch-step-01-solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-01-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-01-solution/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-01-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import ListPage from './views/ListPage' 5 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 6 | import useRelay from 'react-router-relay' 7 | import './index.css' 8 | 9 | Relay.injectNetworkLayer( 10 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 11 | ) 12 | 13 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 14 | 15 | ReactDOM.render( 16 | 22 | 23 | 24 | , document.getElementById('root') 25 | ) 26 | -------------------------------------------------------------------------------- /branch-step-01-solution/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-01-solution/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classes from './ListPage.css' 3 | 4 | export default class ListPage extends React.Component { 5 | static propTypes = { 6 | viewer: React.PropTypes.object, 7 | } 8 | render () { 9 | return ( 10 |
11 | I am a REACT app! 12 |
13 | ) 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /branch-step-01-solution/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-01/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-01/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-01/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .idea 6 | .DB_Store -------------------------------------------------------------------------------- /branch-step-01/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-01/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-01/favicon.ico -------------------------------------------------------------------------------- /branch-step-01/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-01/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-01/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-01/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import ListPage from './views/ListPage' 5 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 6 | import useRelay from 'react-router-relay' 7 | import './index.css' 8 | 9 | Relay.injectNetworkLayer( 10 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 11 | ) 12 | 13 | ReactDOM.render( 14 | 20 | 21 | 22 | , document.getElementById('root') 23 | ) 24 | -------------------------------------------------------------------------------- /branch-step-01/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-01/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classes from './ListPage.css' 3 | 4 | export default class ListPage extends React.Component { 5 | static propTypes = { 6 | viewer: React.PropTypes.object, 7 | } 8 | render () { 9 | return ( 10 |
11 | I am a REACT app! 12 |
13 | ) 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /branch-step-01/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-02-solution/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-02-solution/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-02-solution/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .idea 6 | .DB_Store -------------------------------------------------------------------------------- /branch-step-02-solution/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-02-solution/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-02-solution/favicon.ico -------------------------------------------------------------------------------- /branch-step-02-solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-02-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-02-solution/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-02-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import ListPage from './views/ListPage' 5 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 6 | import useRelay from 'react-router-relay' 7 | import './index.css' 8 | 9 | Relay.injectNetworkLayer( 10 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 11 | ) 12 | 13 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 14 | 15 | ReactDOM.render( 16 | 22 | 23 | 24 | , document.getElementById('root') 25 | ) 26 | -------------------------------------------------------------------------------- /branch-step-02-solution/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-02-solution/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import classes from './ListPage.css' 4 | 5 | class ListPage extends React.Component { 6 | static propTypes = { 7 | viewer: React.PropTypes.object, 8 | } 9 | render () { 10 | return ( 11 |
12 | {`Your viewer id is: ${this.props.viewer.id}`} 13 |
14 | ) 15 | } 16 | } 17 | 18 | export default Relay.createContainer( 19 | ListPage, 20 | { 21 | fragments: { 22 | viewer: () => Relay.QL` 23 | fragment on Viewer { 24 | id 25 | } 26 | `, 27 | }, 28 | }, 29 | ) 30 | -------------------------------------------------------------------------------- /branch-step-02-solution/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-02/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-02/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-02/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .idea 6 | .DB_Store -------------------------------------------------------------------------------- /branch-step-02/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-02/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-02/favicon.ico -------------------------------------------------------------------------------- /branch-step-02/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-02/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-02/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-02/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import ListPage from './views/ListPage' 5 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 6 | import useRelay from 'react-router-relay' 7 | import './index.css' 8 | 9 | Relay.injectNetworkLayer( 10 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 11 | ) 12 | 13 | ReactDOM.render( 14 | 20 | 21 | 22 | , document.getElementById('root') 23 | ) 24 | -------------------------------------------------------------------------------- /branch-step-02/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-02/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classes from './ListPage.css' 3 | 4 | export default class ListPage extends React.Component { 5 | static propTypes = { 6 | viewer: React.PropTypes.object, 7 | } 8 | render () { 9 | return ( 10 |
11 | I am a REACT app! 12 |
13 | ) 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /branch-step-02/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-03-solution/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-03-solution/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-03-solution/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .idea 6 | .DB_Store -------------------------------------------------------------------------------- /branch-step-03-solution/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-03-solution/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-03-solution/favicon.ico -------------------------------------------------------------------------------- /branch-step-03-solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-03-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-03-solution/src/components/PokemonPreview.css: -------------------------------------------------------------------------------- 1 | .previewPage{ 2 | background-color: white; 3 | border-radius: 3px; 4 | box-shadow: 0 2px 8px 0 rgba(0,0,0,0.25); 5 | width: 180px; 6 | cursor: pointer; 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: space-between; 10 | padding: 20px; 11 | box-sizing: border-box; 12 | /*height: 100%;*/ 13 | transition: transform .2s ease, box-shadow .2s ease; 14 | backface-visibility: hidden; 15 | } 16 | 17 | .previewImg { 18 | height: auto; 19 | display: block; 20 | width: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | .previewPage:hover{ 25 | transform: scale(1.05); 26 | box-shadow: 0 4px 14px 0 rgba(0,0,0,0.15); 27 | } 28 | 29 | .previewName { 30 | text-align: center; 31 | color: #7F7F7F; 32 | font-size: 24px; 33 | user-select: none; 34 | font-weight: 300; 35 | padding: 20px 0 0; 36 | } 37 | 38 | .link { 39 | position: relative; 40 | text-decoration: none; 41 | padding: 10px; 42 | display: flex; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-03-solution/src/components/PokemonPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import classes from './PokemonPreview.css' 4 | 5 | class PokemonPreview extends React.Component { 6 | 7 | static propTypes = { 8 | pokemon: React.PropTypes.object, 9 | router: React.PropTypes.object, 10 | } 11 | 12 | render () { 13 | return ( 14 |
15 |
16 | Pokemon Image 17 |
18 | {this.props.pokemon.name} 19 |
20 |
21 |
22 | ) 23 | } 24 | } 25 | 26 | export default Relay.createContainer( 27 | PokemonPreview, 28 | { 29 | fragments: { 30 | pokemon: () => Relay.QL` 31 | fragment on Pokemon { 32 | id 33 | name 34 | url 35 | } 36 | `, 37 | }, 38 | } 39 | ) 40 | -------------------------------------------------------------------------------- /branch-step-03-solution/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-03-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import ListPage from './views/ListPage' 5 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 6 | import useRelay from 'react-router-relay' 7 | import './index.css' 8 | 9 | Relay.injectNetworkLayer( 10 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 11 | ) 12 | 13 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 14 | 15 | ReactDOM.render( 16 | 22 | 23 | 24 | , document.getElementById('root') 25 | ) 26 | -------------------------------------------------------------------------------- /branch-step-03-solution/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-03-solution/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import PokemonPreview from '../components/PokemonPreview' 4 | import classes from './ListPage.css' 5 | 6 | class ListPage extends React.Component { 7 | static propTypes = { 8 | viewer: React.PropTypes.object, 9 | } 10 | render () { 11 | return ( 12 |
13 |
14 | {`There are ${this.props.viewer.allPokemons.edges.length} Pokemons in your pokedex`} 15 |
16 |
17 | {this.props.viewer.allPokemons.edges.map((edge) => edge.node).map((pokemon) => 18 | 19 | ) 20 | } 21 |
22 |
23 | ) 24 | } 25 | } 26 | 27 | export default Relay.createContainer( 28 | ListPage, 29 | { 30 | fragments: { 31 | viewer: () => Relay.QL` 32 | fragment on Viewer { 33 | allPokemons (first: 1000) { 34 | edges { 35 | node { 36 | ${PokemonPreview.getFragment('pokemon')} 37 | id 38 | } 39 | } 40 | } 41 | id 42 | } 43 | `, 44 | }, 45 | }, 46 | ) 47 | -------------------------------------------------------------------------------- /branch-step-03-solution/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-03/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-03/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-03/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .idea 6 | .DB_Store -------------------------------------------------------------------------------- /branch-step-03/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-03/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-03/favicon.ico -------------------------------------------------------------------------------- /branch-step-03/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-03/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-03/src/components/PokemonPreview.css: -------------------------------------------------------------------------------- 1 | .previewPage{ 2 | background-color: white; 3 | border-radius: 3px; 4 | box-shadow: 0 2px 8px 0 rgba(0,0,0,0.25); 5 | width: 180px; 6 | cursor: pointer; 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: space-between; 10 | padding: 20px; 11 | box-sizing: border-box; 12 | /*height: 100%;*/ 13 | transition: transform .2s ease, box-shadow .2s ease; 14 | backface-visibility: hidden; 15 | } 16 | 17 | .previewImg { 18 | height: auto; 19 | display: block; 20 | width: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | .previewPage:hover{ 25 | transform: scale(1.05); 26 | box-shadow: 0 4px 14px 0 rgba(0,0,0,0.15); 27 | } 28 | 29 | .previewName { 30 | text-align: center; 31 | color: #7F7F7F; 32 | font-size: 24px; 33 | user-select: none; 34 | font-weight: 300; 35 | padding: 20px 0 0; 36 | } 37 | 38 | .link { 39 | position: relative; 40 | text-decoration: none; 41 | padding: 10px; 42 | display: flex; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-03/src/components/PokemonPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import classes from './PokemonPreview.css' 4 | 5 | class PokemonPreview extends React.Component { 6 | 7 | static propTypes = { 8 | pokemon: React.PropTypes.object, 9 | router: React.PropTypes.object, 10 | } 11 | 12 | render () { 13 | return ( 14 |
15 |
16 | Pokemon Image 17 |
18 | {this.props.pokemon.name} 19 |
20 |
21 |
22 | ) 23 | } 24 | } 25 | 26 | export default Relay.createContainer( 27 | PokemonPreview, 28 | { 29 | fragments: { 30 | pokemon: () => Relay.QL` 31 | fragment on Pokemon { 32 | id 33 | name 34 | url 35 | } 36 | `, 37 | }, 38 | } 39 | ) 40 | -------------------------------------------------------------------------------- /branch-step-03/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-03/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import ListPage from './views/ListPage' 5 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 6 | import useRelay from 'react-router-relay' 7 | import './index.css' 8 | 9 | Relay.injectNetworkLayer( 10 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 11 | ) 12 | 13 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 14 | 15 | ReactDOM.render( 16 | 22 | 23 | 24 | , document.getElementById('root') 25 | ) 26 | -------------------------------------------------------------------------------- /branch-step-03/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-03/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import PokemonPreview from '../components/PokemonPreview' 4 | import classes from './ListPage.css' 5 | 6 | class ListPage extends React.Component { 7 | static propTypes = { 8 | viewer: React.PropTypes.object, 9 | } 10 | render () { 11 | return ( 12 |
13 |
14 | {`There are 28 Pokemons in your pokedex`} 15 |
16 |
17 | {/* Iterate through pokemon here */} 18 |
19 |
20 | ) 21 | } 22 | } 23 | 24 | export default Relay.createContainer( 25 | ListPage, 26 | { 27 | fragments: { 28 | viewer: () => Relay.QL` 29 | fragment on Viewer { 30 | id 31 | } 32 | `, 33 | }, 34 | }, 35 | ) 36 | -------------------------------------------------------------------------------- /branch-step-03/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-04-solution/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-04-solution/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-04-solution/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .idea 6 | .DB_Store -------------------------------------------------------------------------------- /branch-step-04-solution/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-04-solution/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-04-solution/favicon.ico -------------------------------------------------------------------------------- /branch-step-04-solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-04-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-04-solution/src/assets/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /branch-step-04-solution/src/components/AddNew.css: -------------------------------------------------------------------------------- 1 | .page { 2 | background-color: transparent; 3 | /*height: auto;*/ 4 | width: 180px; 5 | transition: transform 0.2s; 6 | cursor: pointer; 7 | border: dashed #d4d4d4; 8 | color: #d4d4d4; 9 | box-sizing: border-box; 10 | border-radius: 3px; 11 | /*height: 100%;*/ 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: center; 15 | transition: border-color .2s ease, color .2s ease; 16 | } 17 | 18 | .page:hover { 19 | border-color: #bcbcbc; 20 | color: #bcbcbc; 21 | } 22 | 23 | .link { 24 | text-decoration: none; 25 | padding: 10px; 26 | min-height: 250px; 27 | display: flex; 28 | } 29 | 30 | .plus { 31 | text-align: center; 32 | font-size: 70px; 33 | line-height: 1; 34 | font-weight: 300; 35 | } 36 | 37 | .title { 38 | margin-top: 14px; 39 | text-align: center; 40 | font-size: 24px; 41 | user-select: none; 42 | font-weight: 300; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-04-solution/src/components/AddNew.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link} from 'react-router' 3 | import classes from './AddNew.css' 4 | 5 | export default class AddNew extends React.Component { 6 | 7 | render () { 8 | return ( 9 | 10 |
11 |
12 | + 13 |
14 |
15 | Add New 16 |
17 |
18 | 19 | ) 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /branch-step-04-solution/src/components/PokemonPreview.css: -------------------------------------------------------------------------------- 1 | .previewPage{ 2 | background-color: white; 3 | border-radius: 3px; 4 | box-shadow: 0 2px 8px 0 rgba(0,0,0,0.25); 5 | width: 180px; 6 | cursor: pointer; 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: space-between; 10 | padding: 20px; 11 | box-sizing: border-box; 12 | /*height: 100%;*/ 13 | transition: transform .2s ease, box-shadow .2s ease; 14 | backface-visibility: hidden; 15 | } 16 | 17 | .previewImg { 18 | height: auto; 19 | display: block; 20 | width: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | .previewPage:hover{ 25 | transform: scale(1.05); 26 | box-shadow: 0 4px 14px 0 rgba(0,0,0,0.15); 27 | } 28 | 29 | .previewName { 30 | text-align: center; 31 | color: #7F7F7F; 32 | font-size: 24px; 33 | user-select: none; 34 | font-weight: 300; 35 | padding: 20px 0 0; 36 | } 37 | 38 | .link { 39 | position: relative; 40 | text-decoration: none; 41 | padding: 10px; 42 | display: flex; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-04-solution/src/components/PokemonPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import { Link } from 'react-router' 4 | import classes from './PokemonPreview.css' 5 | 6 | class PokemonPreview extends React.Component { 7 | 8 | static propTypes = { 9 | pokemon: React.PropTypes.object, 10 | router: React.PropTypes.object, 11 | } 12 | 13 | render () { 14 | return ( 15 | 16 |
17 | Pokemon Image 18 |
19 | {this.props.pokemon.name} 20 |
21 |
22 | 23 | ) 24 | } 25 | } 26 | 27 | export default Relay.createContainer( 28 | PokemonPreview, 29 | { 30 | fragments: { 31 | pokemon: () => Relay.QL` 32 | fragment on Pokemon { 33 | id 34 | name 35 | url 36 | } 37 | `, 38 | }, 39 | } 40 | ) 41 | -------------------------------------------------------------------------------- /branch-step-04-solution/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-04-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import PokemonPage from './views/PokemonPage' 5 | import ListPage from './views/ListPage' 6 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 7 | import useRelay from 'react-router-relay' 8 | import './index.css' 9 | 10 | Relay.injectNetworkLayer( 11 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 12 | ) 13 | 14 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 15 | 16 | ReactDOM.render( 17 | 23 | 24 | 25 | 26 | 27 | , document.getElementById('root') 28 | ) 29 | -------------------------------------------------------------------------------- /branch-step-04-solution/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-04-solution/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import PokemonPreview from '../components/PokemonPreview' 4 | import AddNew from '../components/AddNew' 5 | import classes from './ListPage.css' 6 | 7 | class ListPage extends React.Component { 8 | static propTypes = { 9 | viewer: React.PropTypes.object, 10 | } 11 | render () { 12 | return ( 13 |
14 |
15 | {`There are ${this.props.viewer.allPokemons.edges.length} Pokemons in your pokedex`} 16 |
17 |
18 | {this.props.viewer.allPokemons.edges.map((edge) => edge.node).map((pokemon) => 19 | 20 | ) 21 | } 22 | 23 |
24 |
25 | ) 26 | } 27 | } 28 | 29 | export default Relay.createContainer( 30 | ListPage, 31 | { 32 | fragments: { 33 | viewer: () => Relay.QL` 34 | fragment on Viewer { 35 | allPokemons (first: 1000) { 36 | edges { 37 | node { 38 | ${PokemonPreview.getFragment('pokemon')} 39 | id 40 | } 41 | } 42 | } 43 | id 44 | } 45 | `, 46 | }, 47 | }, 48 | ) 49 | -------------------------------------------------------------------------------- /branch-step-04-solution/src/views/PokemonPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | width: 100vw; 3 | height: 100vh; 4 | display: flex; 5 | background-color: #F1F1F1; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .content { 11 | width: 350px; 12 | display: flex; 13 | flex-direction: column; 14 | /*height: 75vh;*/ 15 | } 16 | 17 | .buttonContainer { 18 | margin: 25px 0; 19 | display: flex; 20 | flex-direction: row; 21 | justify-content: space-between; 22 | flex: 0 0 auto; 23 | align-items: center; 24 | } 25 | 26 | .buttonContainer img { 27 | display: block; 28 | } 29 | 30 | .actionButtonContainer { 31 | display: flex; 32 | justify-content: space-between; 33 | flex-direction: row; 34 | } 35 | 36 | .deleteIcon { 37 | height: 18px; 38 | padding: 10px; 39 | cursor: hand; 40 | cursor: pointer; 41 | } 42 | 43 | .button { 44 | height: 18px; 45 | line-height: 1; 46 | font-size: 18px; 47 | padding: 15px 30px; 48 | cursor: pointer; 49 | flex: 0 0 auto; 50 | font-weight: 300; 51 | } 52 | 53 | .cancelButton { 54 | color: #A3A3A3; 55 | } 56 | 57 | .link { 58 | position: relative; 59 | text-decoration: none; 60 | padding: 10px; 61 | display: flex; 62 | } 63 | 64 | .saveButton { 65 | border-radius: 3px; 66 | color: white; 67 | background-color: #2BC3A1; 68 | } 69 | 70 | .saveButton:hover { 71 | color: #2BC3A1; 72 | background-color: white; 73 | } 74 | -------------------------------------------------------------------------------- /branch-step-04-solution/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-04/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-04/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-04/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .idea 6 | .DB_Store -------------------------------------------------------------------------------- /branch-step-04/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-04/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-04/favicon.ico -------------------------------------------------------------------------------- /branch-step-04/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-04/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-04/src/assets/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /branch-step-04/src/components/AddNew.css: -------------------------------------------------------------------------------- 1 | .page { 2 | background-color: transparent; 3 | /*height: auto;*/ 4 | width: 180px; 5 | transition: transform 0.2s; 6 | cursor: pointer; 7 | border: dashed #d4d4d4; 8 | color: #d4d4d4; 9 | box-sizing: border-box; 10 | border-radius: 3px; 11 | /*height: 100%;*/ 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: center; 15 | transition: border-color .2s ease, color .2s ease; 16 | } 17 | 18 | .page:hover { 19 | border-color: #bcbcbc; 20 | color: #bcbcbc; 21 | } 22 | 23 | .link { 24 | text-decoration: none; 25 | padding: 10px; 26 | min-height: 250px; 27 | display: flex; 28 | } 29 | 30 | .plus { 31 | text-align: center; 32 | font-size: 70px; 33 | line-height: 1; 34 | font-weight: 300; 35 | } 36 | 37 | .title { 38 | margin-top: 14px; 39 | text-align: center; 40 | font-size: 24px; 41 | user-select: none; 42 | font-weight: 300; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-04/src/components/AddNew.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classes from './AddNew.css' 3 | 4 | export default class AddNew extends React.Component { 5 | 6 | render () { 7 | return ( 8 |
9 |
10 |
11 | + 12 |
13 |
14 | Add New 15 |
16 |
17 |
18 | ) 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /branch-step-04/src/components/PokemonCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classes from './PokemonCard.css' 3 | 4 | export default class PokemonCard extends React.Component { 5 | 6 | static propTypes = { 7 | addNew: React.PropTypes.bool, 8 | url: React.PropTypes.string, 9 | name: React.PropTypes.string, 10 | onNameChange: React.PropTypes.func, 11 | onUrlChange: React.PropTypes.func, 12 | } 13 | 14 | render () { 15 | return ( 16 |
17 |
18 |
19 | NAME 20 |
21 | this.props.onNameChange(e.target.value)} 26 | /> 27 |
28 |
29 |
30 |
31 | IMAGE URL 32 |
33 | this.props.onUrlChange(e.target.value)} 38 | /> 39 |
40 |
41 | 42 |
43 |
44 |
45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /branch-step-04/src/components/PokemonPreview.css: -------------------------------------------------------------------------------- 1 | .previewPage{ 2 | background-color: white; 3 | border-radius: 3px; 4 | box-shadow: 0 2px 8px 0 rgba(0,0,0,0.25); 5 | width: 180px; 6 | cursor: pointer; 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: space-between; 10 | padding: 20px; 11 | box-sizing: border-box; 12 | /*height: 100%;*/ 13 | transition: transform .2s ease, box-shadow .2s ease; 14 | backface-visibility: hidden; 15 | } 16 | 17 | .previewImg { 18 | height: auto; 19 | display: block; 20 | width: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | .previewPage:hover{ 25 | transform: scale(1.05); 26 | box-shadow: 0 4px 14px 0 rgba(0,0,0,0.15); 27 | } 28 | 29 | .previewName { 30 | text-align: center; 31 | color: #7F7F7F; 32 | font-size: 24px; 33 | user-select: none; 34 | font-weight: 300; 35 | padding: 20px 0 0; 36 | } 37 | 38 | .link { 39 | position: relative; 40 | text-decoration: none; 41 | padding: 10px; 42 | display: flex; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-04/src/components/PokemonPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import classes from './PokemonPreview.css' 4 | 5 | class PokemonPreview extends React.Component { 6 | 7 | static propTypes = { 8 | pokemon: React.PropTypes.object, 9 | router: React.PropTypes.object, 10 | } 11 | 12 | render () { 13 | return ( 14 |
15 |
16 | Pokemon Image 17 |
18 | {this.props.pokemon.name} 19 |
20 |
21 |
22 | ) 23 | } 24 | } 25 | 26 | export default Relay.createContainer( 27 | PokemonPreview, 28 | { 29 | fragments: { 30 | pokemon: () => Relay.QL` 31 | fragment on Pokemon { 32 | id 33 | name 34 | url 35 | } 36 | `, 37 | }, 38 | } 39 | ) 40 | -------------------------------------------------------------------------------- /branch-step-04/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-04/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import PokemonPage from './views/PokemonPage' 5 | import ListPage from './views/ListPage' 6 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 7 | import useRelay from 'react-router-relay' 8 | import './index.css' 9 | 10 | Relay.injectNetworkLayer( 11 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 12 | ) 13 | 14 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 15 | 16 | ReactDOM.render( 17 | 23 | 24 | 25 | , document.getElementById('root') 26 | ) 27 | -------------------------------------------------------------------------------- /branch-step-04/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-04/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import PokemonPreview from '../components/PokemonPreview' 4 | import AddNew from '../components/AddNew' 5 | import classes from './ListPage.css' 6 | 7 | class ListPage extends React.Component { 8 | static propTypes = { 9 | viewer: React.PropTypes.object, 10 | } 11 | render () { 12 | return ( 13 |
14 |
15 | {`There are ${this.props.viewer.allPokemons.edges.length} Pokemons in your pokedex`} 16 |
17 |
18 | {this.props.viewer.allPokemons.edges.map((edge) => edge.node).map((pokemon) => 19 | 20 | ) 21 | } 22 | 23 |
24 |
25 | ) 26 | } 27 | } 28 | 29 | export default Relay.createContainer( 30 | ListPage, 31 | { 32 | fragments: { 33 | viewer: () => Relay.QL` 34 | fragment on Viewer { 35 | allPokemons (first: 1000) { 36 | edges { 37 | node { 38 | ${PokemonPreview.getFragment('pokemon')} 39 | id 40 | } 41 | } 42 | } 43 | id 44 | } 45 | `, 46 | }, 47 | }, 48 | ) 49 | -------------------------------------------------------------------------------- /branch-step-04/src/views/PokemonPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | width: 100vw; 3 | height: 100vh; 4 | display: flex; 5 | background-color: #F1F1F1; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .content { 11 | width: 350px; 12 | display: flex; 13 | flex-direction: column; 14 | /*height: 75vh;*/ 15 | } 16 | 17 | .buttonContainer { 18 | margin: 25px 0; 19 | display: flex; 20 | flex-direction: row; 21 | justify-content: space-between; 22 | flex: 0 0 auto; 23 | align-items: center; 24 | } 25 | 26 | .buttonContainer img { 27 | display: block; 28 | } 29 | 30 | .actionButtonContainer { 31 | display: flex; 32 | justify-content: space-between; 33 | flex-direction: row; 34 | } 35 | 36 | .deleteIcon { 37 | height: 18px; 38 | padding: 10px; 39 | cursor: hand; 40 | cursor: pointer; 41 | } 42 | 43 | .button { 44 | height: 18px; 45 | line-height: 1; 46 | font-size: 18px; 47 | padding: 15px 30px; 48 | cursor: pointer; 49 | flex: 0 0 auto; 50 | font-weight: 300; 51 | } 52 | 53 | .cancelButton { 54 | color: #A3A3A3; 55 | } 56 | 57 | .link { 58 | position: relative; 59 | text-decoration: none; 60 | padding: 10px; 61 | display: flex; 62 | } 63 | 64 | .saveButton { 65 | border-radius: 3px; 66 | color: white; 67 | background-color: #2BC3A1; 68 | } 69 | 70 | .saveButton:hover { 71 | color: #2BC3A1; 72 | background-color: white; 73 | } 74 | -------------------------------------------------------------------------------- /branch-step-04/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-05-solution/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-05-solution/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-05-solution/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .idea 6 | .DB_Store -------------------------------------------------------------------------------- /branch-step-05-solution/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-05-solution/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-05-solution/favicon.ico -------------------------------------------------------------------------------- /branch-step-05-solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-05-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-05-solution/src/assets/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /branch-step-05-solution/src/components/AddNew.css: -------------------------------------------------------------------------------- 1 | .page { 2 | background-color: transparent; 3 | /*height: auto;*/ 4 | width: 180px; 5 | transition: transform 0.2s; 6 | cursor: pointer; 7 | border: dashed #d4d4d4; 8 | color: #d4d4d4; 9 | box-sizing: border-box; 10 | border-radius: 3px; 11 | /*height: 100%;*/ 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: center; 15 | transition: border-color .2s ease, color .2s ease; 16 | } 17 | 18 | .page:hover { 19 | border-color: #bcbcbc; 20 | color: #bcbcbc; 21 | } 22 | 23 | .link { 24 | text-decoration: none; 25 | padding: 10px; 26 | min-height: 250px; 27 | display: flex; 28 | } 29 | 30 | .plus { 31 | text-align: center; 32 | font-size: 70px; 33 | line-height: 1; 34 | font-weight: 300; 35 | } 36 | 37 | .title { 38 | margin-top: 14px; 39 | text-align: center; 40 | font-size: 24px; 41 | user-select: none; 42 | font-weight: 300; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-05-solution/src/components/AddNew.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link} from 'react-router' 3 | import classes from './AddNew.css' 4 | 5 | export default class AddNew extends React.Component { 6 | 7 | render () { 8 | return ( 9 | 10 |
11 |
12 | + 13 |
14 |
15 | Add New 16 |
17 |
18 | 19 | ) 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /branch-step-05-solution/src/components/PokemonPreview.css: -------------------------------------------------------------------------------- 1 | .previewPage{ 2 | background-color: white; 3 | border-radius: 3px; 4 | box-shadow: 0 2px 8px 0 rgba(0,0,0,0.25); 5 | width: 180px; 6 | cursor: pointer; 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: space-between; 10 | padding: 20px; 11 | box-sizing: border-box; 12 | /*height: 100%;*/ 13 | transition: transform .2s ease, box-shadow .2s ease; 14 | backface-visibility: hidden; 15 | } 16 | 17 | .previewImg { 18 | height: auto; 19 | display: block; 20 | width: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | .previewPage:hover{ 25 | transform: scale(1.05); 26 | box-shadow: 0 4px 14px 0 rgba(0,0,0,0.15); 27 | } 28 | 29 | .previewName { 30 | text-align: center; 31 | color: #7F7F7F; 32 | font-size: 24px; 33 | user-select: none; 34 | font-weight: 300; 35 | padding: 20px 0 0; 36 | } 37 | 38 | .link { 39 | position: relative; 40 | text-decoration: none; 41 | padding: 10px; 42 | display: flex; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-05-solution/src/components/PokemonPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import { Link } from 'react-router' 4 | import classes from './PokemonPreview.css' 5 | 6 | class PokemonPreview extends React.Component { 7 | 8 | static propTypes = { 9 | pokemon: React.PropTypes.object, 10 | router: React.PropTypes.object, 11 | } 12 | 13 | render () { 14 | return ( 15 | 16 |
17 | Pokemon Image 18 |
19 | {this.props.pokemon.name} 20 |
21 |
22 | 23 | ) 24 | } 25 | } 26 | 27 | export default Relay.createContainer( 28 | PokemonPreview, 29 | { 30 | fragments: { 31 | pokemon: () => Relay.QL` 32 | fragment on Pokemon { 33 | id 34 | name 35 | url 36 | } 37 | `, 38 | }, 39 | } 40 | ) 41 | -------------------------------------------------------------------------------- /branch-step-05-solution/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-05-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import PokemonPage from './views/PokemonPage' 5 | import ListPage from './views/ListPage' 6 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 7 | import useRelay from 'react-router-relay' 8 | import './index.css' 9 | 10 | Relay.injectNetworkLayer( 11 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 12 | ) 13 | 14 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 15 | 16 | ReactDOM.render( 17 | 23 | 24 | 25 | 26 | 27 | , document.getElementById('root') 28 | ) 29 | -------------------------------------------------------------------------------- /branch-step-05-solution/src/mutations/CreatePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class CreatePokemonMutation extends Relay.Mutation { 4 | 5 | static fragments = { 6 | viewer: () => Relay.QL` 7 | fragment on Viewer { 8 | id 9 | } 10 | `, 11 | } 12 | 13 | getMutation () { 14 | return Relay.QL`mutation{createPokemon}` 15 | } 16 | 17 | getFatQuery () { 18 | return Relay.QL` 19 | fragment on CreatePokemonPayload { 20 | pokemon 21 | edge 22 | viewer { 23 | allPokemons 24 | } 25 | } 26 | ` 27 | } 28 | 29 | getConfigs () { 30 | return [{ 31 | type: 'RANGE_ADD', 32 | parentName: 'viewer', 33 | parentID: this.props.viewer.id, 34 | connectionName: 'allPokemons', 35 | edgeName: 'edge', 36 | rangeBehaviors: { 37 | '': 'append', 38 | }, 39 | }] 40 | } 41 | 42 | getVariables () { 43 | return { 44 | name: this.props.name, 45 | url: this.props.url, 46 | } 47 | } 48 | 49 | getOptimisticResponse () { 50 | return { 51 | edge: { 52 | node: { 53 | name: this.props.name, 54 | url: this.props.url, 55 | }, 56 | }, 57 | viewer: { 58 | id: this.props.viewer.id, 59 | }, 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /branch-step-05-solution/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-05-solution/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import PokemonPreview from '../components/PokemonPreview' 4 | import AddNew from '../components/AddNew' 5 | import classes from './ListPage.css' 6 | 7 | class ListPage extends React.Component { 8 | static propTypes = { 9 | viewer: React.PropTypes.object, 10 | } 11 | render () { 12 | return ( 13 |
14 |
15 | {`There are ${this.props.viewer.allPokemons.edges.length} Pokemons in your pokedex`} 16 |
17 |
18 | {this.props.viewer.allPokemons.edges.map((edge) => edge.node).map((pokemon) => 19 | 20 | ) 21 | } 22 | 23 |
24 |
25 | ) 26 | } 27 | } 28 | 29 | export default Relay.createContainer( 30 | ListPage, 31 | { 32 | fragments: { 33 | viewer: () => Relay.QL` 34 | fragment on Viewer { 35 | allPokemons (first: 1000) { 36 | edges { 37 | node { 38 | ${PokemonPreview.getFragment('pokemon')} 39 | id 40 | } 41 | } 42 | } 43 | id 44 | } 45 | `, 46 | }, 47 | }, 48 | ) 49 | -------------------------------------------------------------------------------- /branch-step-05-solution/src/views/PokemonPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | width: 100vw; 3 | height: 100vh; 4 | display: flex; 5 | background-color: #F1F1F1; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .content { 11 | width: 350px; 12 | display: flex; 13 | flex-direction: column; 14 | /*height: 75vh;*/ 15 | } 16 | 17 | .buttonContainer { 18 | margin: 25px 0; 19 | display: flex; 20 | flex-direction: row; 21 | justify-content: space-between; 22 | flex: 0 0 auto; 23 | align-items: center; 24 | } 25 | 26 | .buttonContainer img { 27 | display: block; 28 | } 29 | 30 | .actionButtonContainer { 31 | display: flex; 32 | justify-content: space-between; 33 | flex-direction: row; 34 | } 35 | 36 | .deleteIcon { 37 | height: 18px; 38 | padding: 10px; 39 | cursor: hand; 40 | cursor: pointer; 41 | } 42 | 43 | .button { 44 | height: 18px; 45 | line-height: 1; 46 | font-size: 18px; 47 | padding: 15px 30px; 48 | cursor: pointer; 49 | flex: 0 0 auto; 50 | font-weight: 300; 51 | } 52 | 53 | .cancelButton { 54 | color: #A3A3A3; 55 | } 56 | 57 | .link { 58 | position: relative; 59 | text-decoration: none; 60 | padding: 10px; 61 | display: flex; 62 | } 63 | 64 | .saveButton { 65 | border-radius: 3px; 66 | color: white; 67 | background-color: #2BC3A1; 68 | } 69 | 70 | .saveButton:hover { 71 | color: #2BC3A1; 72 | background-color: white; 73 | } 74 | -------------------------------------------------------------------------------- /branch-step-05-solution/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-05/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-05/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-05/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .idea 6 | .DB_Store -------------------------------------------------------------------------------- /branch-step-05/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-05/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-05/favicon.ico -------------------------------------------------------------------------------- /branch-step-05/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-05/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-05/src/assets/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /branch-step-05/src/components/AddNew.css: -------------------------------------------------------------------------------- 1 | .page { 2 | background-color: transparent; 3 | /*height: auto;*/ 4 | width: 180px; 5 | transition: transform 0.2s; 6 | cursor: pointer; 7 | border: dashed #d4d4d4; 8 | color: #d4d4d4; 9 | box-sizing: border-box; 10 | border-radius: 3px; 11 | /*height: 100%;*/ 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: center; 15 | transition: border-color .2s ease, color .2s ease; 16 | } 17 | 18 | .page:hover { 19 | border-color: #bcbcbc; 20 | color: #bcbcbc; 21 | } 22 | 23 | .link { 24 | text-decoration: none; 25 | padding: 10px; 26 | min-height: 250px; 27 | display: flex; 28 | } 29 | 30 | .plus { 31 | text-align: center; 32 | font-size: 70px; 33 | line-height: 1; 34 | font-weight: 300; 35 | } 36 | 37 | .title { 38 | margin-top: 14px; 39 | text-align: center; 40 | font-size: 24px; 41 | user-select: none; 42 | font-weight: 300; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-05/src/components/AddNew.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link} from 'react-router' 3 | import classes from './AddNew.css' 4 | 5 | export default class AddNew extends React.Component { 6 | 7 | render () { 8 | return ( 9 | 10 |
11 |
12 | + 13 |
14 |
15 | Add New 16 |
17 |
18 | 19 | ) 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /branch-step-05/src/components/PokemonCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classes from './PokemonCard.css' 3 | 4 | export default class PokemonCard extends React.Component { 5 | 6 | static propTypes = { 7 | addNew: React.PropTypes.bool, 8 | url: React.PropTypes.string, 9 | name: React.PropTypes.string, 10 | onNameChange: React.PropTypes.func, 11 | onUrlChange: React.PropTypes.func, 12 | } 13 | 14 | render () { 15 | return ( 16 |
17 |
18 |
19 | NAME 20 |
21 | this.props.onNameChange(e.target.value)} 26 | /> 27 |
28 |
29 |
30 |
31 | IMAGE URL 32 |
33 | this.props.onUrlChange(e.target.value)} 38 | /> 39 |
40 |
41 | 42 |
43 |
44 |
45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /branch-step-05/src/components/PokemonPreview.css: -------------------------------------------------------------------------------- 1 | .previewPage{ 2 | background-color: white; 3 | border-radius: 3px; 4 | box-shadow: 0 2px 8px 0 rgba(0,0,0,0.25); 5 | width: 180px; 6 | cursor: pointer; 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: space-between; 10 | padding: 20px; 11 | box-sizing: border-box; 12 | /*height: 100%;*/ 13 | transition: transform .2s ease, box-shadow .2s ease; 14 | backface-visibility: hidden; 15 | } 16 | 17 | .previewImg { 18 | height: auto; 19 | display: block; 20 | width: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | .previewPage:hover{ 25 | transform: scale(1.05); 26 | box-shadow: 0 4px 14px 0 rgba(0,0,0,0.15); 27 | } 28 | 29 | .previewName { 30 | text-align: center; 31 | color: #7F7F7F; 32 | font-size: 24px; 33 | user-select: none; 34 | font-weight: 300; 35 | padding: 20px 0 0; 36 | } 37 | 38 | .link { 39 | position: relative; 40 | text-decoration: none; 41 | padding: 10px; 42 | display: flex; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-05/src/components/PokemonPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import { Link } from 'react-router' 4 | import classes from './PokemonPreview.css' 5 | 6 | class PokemonPreview extends React.Component { 7 | 8 | static propTypes = { 9 | pokemon: React.PropTypes.object, 10 | router: React.PropTypes.object, 11 | } 12 | 13 | render () { 14 | return ( 15 | 16 |
17 | Pokemon Image 18 |
19 | {this.props.pokemon.name} 20 |
21 |
22 | 23 | ) 24 | } 25 | } 26 | 27 | export default Relay.createContainer( 28 | PokemonPreview, 29 | { 30 | fragments: { 31 | pokemon: () => Relay.QL` 32 | fragment on Pokemon { 33 | id 34 | name 35 | url 36 | } 37 | `, 38 | }, 39 | } 40 | ) 41 | -------------------------------------------------------------------------------- /branch-step-05/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-05/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import PokemonPage from './views/PokemonPage' 5 | import ListPage from './views/ListPage' 6 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 7 | import useRelay from 'react-router-relay' 8 | import './index.css' 9 | 10 | Relay.injectNetworkLayer( 11 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 12 | ) 13 | 14 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 15 | 16 | ReactDOM.render( 17 | 23 | 24 | 25 | 26 | 27 | , document.getElementById('root') 28 | ) 29 | -------------------------------------------------------------------------------- /branch-step-05/src/mutations/CreatePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class CreatePokemonMutation extends Relay.Mutation { 4 | 5 | static fragments = { 6 | 7 | } 8 | 9 | getMutation () { 10 | 11 | } 12 | 13 | getFatQuery () { 14 | 15 | } 16 | 17 | getConfigs () { 18 | return [{ 19 | type: 'RANGE_ADD', 20 | parentName: 'viewer', 21 | parentID: this.props.viewer.id, 22 | connectionName: 'allPokemons', 23 | edgeName: 'edge', 24 | rangeBehaviors: { 25 | '': 'append', 26 | }, 27 | }] 28 | } 29 | 30 | getVariables () { 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-05/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-05/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import PokemonPreview from '../components/PokemonPreview' 4 | import AddNew from '../components/AddNew' 5 | import classes from './ListPage.css' 6 | 7 | class ListPage extends React.Component { 8 | static propTypes = { 9 | viewer: React.PropTypes.object, 10 | } 11 | render () { 12 | return ( 13 |
14 |
15 | {`There are ${this.props.viewer.allPokemons.edges.length} Pokemons in your pokedex`} 16 |
17 |
18 | {this.props.viewer.allPokemons.edges.map((edge) => edge.node).map((pokemon) => 19 | 20 | ) 21 | } 22 | 23 |
24 |
25 | ) 26 | } 27 | } 28 | 29 | export default Relay.createContainer( 30 | ListPage, 31 | { 32 | fragments: { 33 | viewer: () => Relay.QL` 34 | fragment on Viewer { 35 | allPokemons (first: 1000) { 36 | edges { 37 | node { 38 | ${PokemonPreview.getFragment('pokemon')} 39 | id 40 | } 41 | } 42 | } 43 | id 44 | } 45 | `, 46 | }, 47 | }, 48 | ) 49 | -------------------------------------------------------------------------------- /branch-step-05/src/views/PokemonPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | width: 100vw; 3 | height: 100vh; 4 | display: flex; 5 | background-color: #F1F1F1; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .content { 11 | width: 350px; 12 | display: flex; 13 | flex-direction: column; 14 | /*height: 75vh;*/ 15 | } 16 | 17 | .buttonContainer { 18 | margin: 25px 0; 19 | display: flex; 20 | flex-direction: row; 21 | justify-content: space-between; 22 | flex: 0 0 auto; 23 | align-items: center; 24 | } 25 | 26 | .buttonContainer img { 27 | display: block; 28 | } 29 | 30 | .actionButtonContainer { 31 | display: flex; 32 | justify-content: space-between; 33 | flex-direction: row; 34 | } 35 | 36 | .deleteIcon { 37 | height: 18px; 38 | padding: 10px; 39 | cursor: hand; 40 | cursor: pointer; 41 | } 42 | 43 | .button { 44 | height: 18px; 45 | line-height: 1; 46 | font-size: 18px; 47 | padding: 15px 30px; 48 | cursor: pointer; 49 | flex: 0 0 auto; 50 | font-weight: 300; 51 | } 52 | 53 | .cancelButton { 54 | color: #A3A3A3; 55 | } 56 | 57 | .link { 58 | position: relative; 59 | text-decoration: none; 60 | padding: 10px; 61 | display: flex; 62 | } 63 | 64 | .saveButton { 65 | border-radius: 3px; 66 | color: white; 67 | background-color: #2BC3A1; 68 | } 69 | 70 | .saveButton:hover { 71 | color: #2BC3A1; 72 | background-color: white; 73 | } 74 | -------------------------------------------------------------------------------- /branch-step-05/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-06-solution/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-06-solution/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-06-solution/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .idea 6 | .DB_Store -------------------------------------------------------------------------------- /branch-step-06-solution/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-06-solution/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-06-solution/favicon.ico -------------------------------------------------------------------------------- /branch-step-06-solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-06-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-06-solution/src/assets/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /branch-step-06-solution/src/components/AddNew.css: -------------------------------------------------------------------------------- 1 | .page { 2 | background-color: transparent; 3 | /*height: auto;*/ 4 | width: 180px; 5 | transition: transform 0.2s; 6 | cursor: pointer; 7 | border: dashed #d4d4d4; 8 | color: #d4d4d4; 9 | box-sizing: border-box; 10 | border-radius: 3px; 11 | /*height: 100%;*/ 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: center; 15 | transition: border-color .2s ease, color .2s ease; 16 | } 17 | 18 | .page:hover { 19 | border-color: #bcbcbc; 20 | color: #bcbcbc; 21 | } 22 | 23 | .link { 24 | text-decoration: none; 25 | padding: 10px; 26 | min-height: 250px; 27 | display: flex; 28 | } 29 | 30 | .plus { 31 | text-align: center; 32 | font-size: 70px; 33 | line-height: 1; 34 | font-weight: 300; 35 | } 36 | 37 | .title { 38 | margin-top: 14px; 39 | text-align: center; 40 | font-size: 24px; 41 | user-select: none; 42 | font-weight: 300; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-06-solution/src/components/AddNew.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link} from 'react-router' 3 | import classes from './AddNew.css' 4 | 5 | export default class AddNew extends React.Component { 6 | 7 | render () { 8 | return ( 9 | 10 |
11 |
12 | + 13 |
14 |
15 | Add New 16 |
17 |
18 | 19 | ) 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /branch-step-06-solution/src/components/PokemonPreview.css: -------------------------------------------------------------------------------- 1 | .previewPage{ 2 | background-color: white; 3 | border-radius: 3px; 4 | box-shadow: 0 2px 8px 0 rgba(0,0,0,0.25); 5 | width: 180px; 6 | cursor: pointer; 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: space-between; 10 | padding: 20px; 11 | box-sizing: border-box; 12 | /*height: 100%;*/ 13 | transition: transform .2s ease, box-shadow .2s ease; 14 | backface-visibility: hidden; 15 | } 16 | 17 | .previewImg { 18 | height: auto; 19 | display: block; 20 | width: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | .previewPage:hover{ 25 | transform: scale(1.05); 26 | box-shadow: 0 4px 14px 0 rgba(0,0,0,0.15); 27 | } 28 | 29 | .previewName { 30 | text-align: center; 31 | color: #7F7F7F; 32 | font-size: 24px; 33 | user-select: none; 34 | font-weight: 300; 35 | padding: 20px 0 0; 36 | } 37 | 38 | .link { 39 | position: relative; 40 | text-decoration: none; 41 | padding: 10px; 42 | display: flex; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-06-solution/src/components/PokemonPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import { Link } from 'react-router' 4 | import classes from './PokemonPreview.css' 5 | 6 | class PokemonPreview extends React.Component { 7 | 8 | static propTypes = { 9 | pokemon: React.PropTypes.object, 10 | router: React.PropTypes.object, 11 | } 12 | 13 | render () { 14 | return ( 15 | 16 |
17 | Pokemon Image 18 |
19 | {this.props.pokemon.name} 20 |
21 |
22 | 23 | ) 24 | } 25 | } 26 | 27 | export default Relay.createContainer( 28 | PokemonPreview, 29 | { 30 | fragments: { 31 | pokemon: () => Relay.QL` 32 | fragment on Pokemon { 33 | id 34 | name 35 | url 36 | } 37 | `, 38 | }, 39 | } 40 | ) 41 | -------------------------------------------------------------------------------- /branch-step-06-solution/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-06-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import PokemonPage from './views/PokemonPage' 5 | import ListPage from './views/ListPage' 6 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 7 | import useRelay from 'react-router-relay' 8 | import './index.css' 9 | 10 | Relay.injectNetworkLayer( 11 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 12 | ) 13 | 14 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 15 | 16 | ReactDOM.render( 17 | 23 | 24 | 25 | 26 | 27 | , document.getElementById('root') 28 | ) 29 | -------------------------------------------------------------------------------- /branch-step-06-solution/src/mutations/CreatePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class CreatePokemonMutation extends Relay.Mutation { 4 | 5 | static fragments = { 6 | viewer: () => Relay.QL` 7 | fragment on Viewer { 8 | id 9 | } 10 | `, 11 | } 12 | 13 | getMutation () { 14 | return Relay.QL`mutation{createPokemon}` 15 | } 16 | 17 | getFatQuery () { 18 | return Relay.QL` 19 | fragment on CreatePokemonPayload { 20 | pokemon 21 | edge 22 | viewer { 23 | allPokemons 24 | } 25 | } 26 | ` 27 | } 28 | 29 | getConfigs () { 30 | return [{ 31 | type: 'RANGE_ADD', 32 | parentName: 'viewer', 33 | parentID: this.props.viewer.id, 34 | connectionName: 'allPokemons', 35 | edgeName: 'edge', 36 | rangeBehaviors: { 37 | '': 'append', 38 | }, 39 | }] 40 | } 41 | 42 | getVariables () { 43 | return { 44 | name: this.props.name, 45 | url: this.props.url, 46 | } 47 | } 48 | 49 | getOptimisticResponse () { 50 | return { 51 | edge: { 52 | node: { 53 | name: this.props.name, 54 | url: this.props.url, 55 | }, 56 | }, 57 | viewer: { 58 | id: this.props.viewer.id, 59 | }, 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /branch-step-06-solution/src/mutations/DeletePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class DeletePokemonMutation extends Relay.Mutation { 4 | 5 | getMutation () { 6 | return Relay.QL`mutation{deletePokemon}` 7 | } 8 | 9 | getFatQuery () { 10 | return Relay.QL` 11 | fragment on DeletePokemonPayload { 12 | viewer 13 | deletedId 14 | } 15 | ` 16 | } 17 | 18 | getConfigs () { 19 | return [{ 20 | type: 'NODE_DELETE', 21 | parentName: 'viewer', 22 | parentID: this.props.viewerId, 23 | connectionName: 'pokemon', 24 | deletedIDFieldName: 'deletedId', 25 | }] 26 | } 27 | 28 | getVariables () { 29 | return { 30 | id: this.props.pokemonId, 31 | } 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /branch-step-06-solution/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-06-solution/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import PokemonPreview from '../components/PokemonPreview' 4 | import AddNew from '../components/AddNew' 5 | import classes from './ListPage.css' 6 | 7 | class ListPage extends React.Component { 8 | static propTypes = { 9 | viewer: React.PropTypes.object, 10 | } 11 | render () { 12 | return ( 13 |
14 |
15 | {`There are ${this.props.viewer.allPokemons.edges.length} Pokemons in your pokedex`} 16 |
17 |
18 | {this.props.viewer.allPokemons.edges.map((edge) => edge.node).map((pokemon) => 19 | 20 | ) 21 | } 22 | 23 |
24 |
25 | ) 26 | } 27 | } 28 | 29 | export default Relay.createContainer( 30 | ListPage, 31 | { 32 | fragments: { 33 | viewer: () => Relay.QL` 34 | fragment on Viewer { 35 | allPokemons (first: 1000) { 36 | edges { 37 | node { 38 | ${PokemonPreview.getFragment('pokemon')} 39 | id 40 | } 41 | } 42 | } 43 | id 44 | } 45 | `, 46 | }, 47 | }, 48 | ) 49 | -------------------------------------------------------------------------------- /branch-step-06-solution/src/views/PokemonPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | width: 100vw; 3 | height: 100vh; 4 | display: flex; 5 | background-color: #F1F1F1; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .content { 11 | width: 350px; 12 | display: flex; 13 | flex-direction: column; 14 | /*height: 75vh;*/ 15 | } 16 | 17 | .buttonContainer { 18 | margin: 25px 0; 19 | display: flex; 20 | flex-direction: row; 21 | justify-content: space-between; 22 | flex: 0 0 auto; 23 | align-items: center; 24 | } 25 | 26 | .buttonContainer img { 27 | display: block; 28 | } 29 | 30 | .actionButtonContainer { 31 | display: flex; 32 | justify-content: space-between; 33 | flex-direction: row; 34 | } 35 | 36 | .deleteIcon { 37 | height: 18px; 38 | padding: 10px; 39 | cursor: hand; 40 | cursor: pointer; 41 | } 42 | 43 | .button { 44 | height: 18px; 45 | line-height: 1; 46 | font-size: 18px; 47 | padding: 15px 30px; 48 | cursor: pointer; 49 | flex: 0 0 auto; 50 | font-weight: 300; 51 | } 52 | 53 | .cancelButton { 54 | color: #A3A3A3; 55 | } 56 | 57 | .link { 58 | position: relative; 59 | text-decoration: none; 60 | padding: 10px; 61 | display: flex; 62 | } 63 | 64 | .saveButton { 65 | border-radius: 3px; 66 | color: white; 67 | background-color: #2BC3A1; 68 | } 69 | 70 | .saveButton:hover { 71 | color: #2BC3A1; 72 | background-color: white; 73 | } 74 | -------------------------------------------------------------------------------- /branch-step-06-solution/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-06/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-06/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-06/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .idea 6 | .DB_Store -------------------------------------------------------------------------------- /branch-step-06/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-06/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-06/favicon.ico -------------------------------------------------------------------------------- /branch-step-06/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-06/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-06/src/assets/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /branch-step-06/src/components/AddNew.css: -------------------------------------------------------------------------------- 1 | .page { 2 | background-color: transparent; 3 | /*height: auto;*/ 4 | width: 180px; 5 | transition: transform 0.2s; 6 | cursor: pointer; 7 | border: dashed #d4d4d4; 8 | color: #d4d4d4; 9 | box-sizing: border-box; 10 | border-radius: 3px; 11 | /*height: 100%;*/ 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: center; 15 | transition: border-color .2s ease, color .2s ease; 16 | } 17 | 18 | .page:hover { 19 | border-color: #bcbcbc; 20 | color: #bcbcbc; 21 | } 22 | 23 | .link { 24 | text-decoration: none; 25 | padding: 10px; 26 | min-height: 250px; 27 | display: flex; 28 | } 29 | 30 | .plus { 31 | text-align: center; 32 | font-size: 70px; 33 | line-height: 1; 34 | font-weight: 300; 35 | } 36 | 37 | .title { 38 | margin-top: 14px; 39 | text-align: center; 40 | font-size: 24px; 41 | user-select: none; 42 | font-weight: 300; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-06/src/components/AddNew.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link} from 'react-router' 3 | import classes from './AddNew.css' 4 | 5 | export default class AddNew extends React.Component { 6 | 7 | render () { 8 | return ( 9 | 10 |
11 |
12 | + 13 |
14 |
15 | Add New 16 |
17 |
18 | 19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /branch-step-06/src/components/PokemonCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classes from './PokemonCard.css' 3 | 4 | export default class PokemonCard extends React.Component { 5 | 6 | static propTypes = { 7 | addNew: React.PropTypes.bool, 8 | url: React.PropTypes.string, 9 | name: React.PropTypes.string, 10 | onNameChange: React.PropTypes.func, 11 | onUrlChange: React.PropTypes.func, 12 | } 13 | 14 | render () { 15 | return ( 16 |
17 |
18 |
19 | NAME 20 |
21 | this.props.onNameChange(e.target.value)} 26 | /> 27 |
28 |
29 |
30 |
31 | IMAGE URL 32 |
33 | this.props.onUrlChange(e.target.value)} 38 | /> 39 |
40 |
41 | 42 |
43 |
44 |
45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /branch-step-06/src/components/PokemonPreview.css: -------------------------------------------------------------------------------- 1 | .previewPage{ 2 | background-color: white; 3 | border-radius: 3px; 4 | box-shadow: 0 2px 8px 0 rgba(0,0,0,0.25); 5 | width: 180px; 6 | cursor: pointer; 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: space-between; 10 | padding: 20px; 11 | box-sizing: border-box; 12 | /*height: 100%;*/ 13 | transition: transform .2s ease, box-shadow .2s ease; 14 | backface-visibility: hidden; 15 | } 16 | 17 | .previewImg { 18 | height: auto; 19 | display: block; 20 | width: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | .previewPage:hover{ 25 | transform: scale(1.05); 26 | box-shadow: 0 4px 14px 0 rgba(0,0,0,0.15); 27 | } 28 | 29 | .previewName { 30 | text-align: center; 31 | color: #7F7F7F; 32 | font-size: 24px; 33 | user-select: none; 34 | font-weight: 300; 35 | padding: 20px 0 0; 36 | } 37 | 38 | .link { 39 | position: relative; 40 | text-decoration: none; 41 | padding: 10px; 42 | display: flex; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-06/src/components/PokemonPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import { Link } from 'react-router' 4 | import classes from './PokemonPreview.css' 5 | 6 | class PokemonPreview extends React.Component { 7 | 8 | static propTypes = { 9 | pokemon: React.PropTypes.object, 10 | router: React.PropTypes.object, 11 | } 12 | 13 | render () { 14 | return ( 15 | 16 |
17 | Pokemon Image 18 |
19 | {this.props.pokemon.name} 20 |
21 |
22 | 23 | ) 24 | } 25 | } 26 | 27 | export default Relay.createContainer( 28 | PokemonPreview, 29 | { 30 | fragments: { 31 | pokemon: () => Relay.QL` 32 | fragment on Pokemon { 33 | id 34 | name 35 | url 36 | } 37 | `, 38 | }, 39 | } 40 | ) 41 | -------------------------------------------------------------------------------- /branch-step-06/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-06/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import PokemonPage from './views/PokemonPage' 5 | import ListPage from './views/ListPage' 6 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 7 | import useRelay from 'react-router-relay' 8 | import './index.css' 9 | 10 | Relay.injectNetworkLayer( 11 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 12 | ) 13 | 14 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 15 | 16 | ReactDOM.render( 17 | 23 | 24 | 25 | 26 | 27 | , document.getElementById('root') 28 | ) 29 | -------------------------------------------------------------------------------- /branch-step-06/src/mutations/CreatePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class CreatePokemonMutation extends Relay.Mutation { 4 | 5 | static fragments = { 6 | viewer: () => Relay.QL` 7 | fragment on Viewer { 8 | id 9 | } 10 | `, 11 | } 12 | 13 | getMutation () { 14 | return Relay.QL`mutation{createPokemon}` 15 | } 16 | 17 | getFatQuery () { 18 | return Relay.QL` 19 | fragment on CreatePokemonPayload { 20 | pokemon 21 | edge 22 | viewer { 23 | allPokemons 24 | } 25 | } 26 | ` 27 | } 28 | 29 | getConfigs () { 30 | return [{ 31 | type: 'RANGE_ADD', 32 | parentName: 'viewer', 33 | parentID: this.props.viewer.id, 34 | connectionName: 'allPokemons', 35 | edgeName: 'edge', 36 | rangeBehaviors: { 37 | '': 'append', 38 | }, 39 | }] 40 | } 41 | 42 | getVariables () { 43 | return { 44 | name: this.props.name, 45 | url: this.props.url, 46 | } 47 | } 48 | 49 | getOptimisticResponse () { 50 | return { 51 | edge: { 52 | node: { 53 | name: this.props.name, 54 | url: this.props.url, 55 | }, 56 | }, 57 | viewer: { 58 | id: this.props.viewer.id, 59 | }, 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /branch-step-06/src/mutations/DeletePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class DeletePokemonMutation extends Relay.Mutation { 4 | 5 | getMutation () { 6 | 7 | } 8 | 9 | getFatQuery () { 10 | 11 | } 12 | 13 | getConfigs () { 14 | 15 | } 16 | 17 | getVariables () { 18 | 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /branch-step-06/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-06/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import PokemonPreview from '../components/PokemonPreview' 4 | import AddNew from '../components/AddNew' 5 | import classes from './ListPage.css' 6 | 7 | class ListPage extends React.Component { 8 | static propTypes = { 9 | viewer: React.PropTypes.object, 10 | } 11 | render () { 12 | return ( 13 |
14 |
15 | {`There are ${this.props.viewer.allPokemons.edges.length} Pokemons in your pokedex`} 16 |
17 |
18 | {this.props.viewer.allPokemons.edges.map((edge) => edge.node).map((pokemon) => 19 | 20 | ) 21 | } 22 | 23 |
24 |
25 | ) 26 | } 27 | } 28 | 29 | export default Relay.createContainer( 30 | ListPage, 31 | { 32 | fragments: { 33 | viewer: () => Relay.QL` 34 | fragment on Viewer { 35 | allPokemons (first: 1000) { 36 | edges { 37 | node { 38 | ${PokemonPreview.getFragment('pokemon')} 39 | id 40 | } 41 | } 42 | } 43 | id 44 | } 45 | `, 46 | }, 47 | }, 48 | ) 49 | -------------------------------------------------------------------------------- /branch-step-06/src/views/PokemonPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | width: 100vw; 3 | height: 100vh; 4 | display: flex; 5 | background-color: #F1F1F1; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .content { 11 | width: 350px; 12 | display: flex; 13 | flex-direction: column; 14 | /*height: 75vh;*/ 15 | } 16 | 17 | .buttonContainer { 18 | margin: 25px 0; 19 | display: flex; 20 | flex-direction: row; 21 | justify-content: space-between; 22 | flex: 0 0 auto; 23 | align-items: center; 24 | } 25 | 26 | .buttonContainer img { 27 | display: block; 28 | } 29 | 30 | .actionButtonContainer { 31 | display: flex; 32 | justify-content: space-between; 33 | flex-direction: row; 34 | } 35 | 36 | .deleteIcon { 37 | height: 18px; 38 | padding: 10px; 39 | cursor: hand; 40 | cursor: pointer; 41 | } 42 | 43 | .button { 44 | height: 18px; 45 | line-height: 1; 46 | font-size: 18px; 47 | padding: 15px 30px; 48 | cursor: pointer; 49 | flex: 0 0 auto; 50 | font-weight: 300; 51 | } 52 | 53 | .cancelButton { 54 | color: #A3A3A3; 55 | } 56 | 57 | .link { 58 | position: relative; 59 | text-decoration: none; 60 | padding: 10px; 61 | display: flex; 62 | } 63 | 64 | .saveButton { 65 | border-radius: 3px; 66 | color: white; 67 | background-color: #2BC3A1; 68 | } 69 | 70 | .saveButton:hover { 71 | color: #2BC3A1; 72 | background-color: white; 73 | } 74 | -------------------------------------------------------------------------------- /branch-step-06/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-07-solution/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-07-solution/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-07-solution/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | 13 | #IDE 14 | .idea/* 15 | -------------------------------------------------------------------------------- /branch-step-07-solution/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-07-solution/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-07-solution/favicon.ico -------------------------------------------------------------------------------- /branch-step-07-solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-07-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/assets/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/components/AddNew.css: -------------------------------------------------------------------------------- 1 | .page { 2 | background-color: transparent; 3 | /*height: auto;*/ 4 | width: 180px; 5 | transition: transform 0.2s; 6 | cursor: pointer; 7 | border: dashed #d4d4d4; 8 | color: #d4d4d4; 9 | box-sizing: border-box; 10 | border-radius: 3px; 11 | /*height: 100%;*/ 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: center; 15 | transition: border-color .2s ease, color .2s ease; 16 | } 17 | 18 | .page:hover { 19 | border-color: #bcbcbc; 20 | color: #bcbcbc; 21 | } 22 | 23 | .link { 24 | text-decoration: none; 25 | padding: 10px; 26 | min-height: 250px; 27 | display: flex; 28 | } 29 | 30 | .plus { 31 | text-align: center; 32 | font-size: 70px; 33 | line-height: 1; 34 | font-weight: 300; 35 | } 36 | 37 | .title { 38 | margin-top: 14px; 39 | text-align: center; 40 | font-size: 24px; 41 | user-select: none; 42 | font-weight: 300; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/components/AddNew.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link} from 'react-router' 3 | import classes from './AddNew.css' 4 | 5 | export default class AddNew extends React.Component { 6 | 7 | render () { 8 | return ( 9 | 10 |
11 |
12 | + 13 |
14 |
15 | Add New 16 |
17 |
18 | 19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/components/PokemonPreview.css: -------------------------------------------------------------------------------- 1 | .previewPage{ 2 | background-color: white; 3 | border-radius: 3px; 4 | box-shadow: 0 2px 8px 0 rgba(0,0,0,0.25); 5 | width: 180px; 6 | cursor: pointer; 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: space-between; 10 | padding: 20px; 11 | box-sizing: border-box; 12 | /*height: 100%;*/ 13 | transition: transform .2s ease, box-shadow .2s ease; 14 | backface-visibility: hidden; 15 | } 16 | 17 | .previewImg { 18 | height: auto; 19 | display: block; 20 | width: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | .previewPage:hover{ 25 | transform: scale(1.05); 26 | box-shadow: 0 4px 14px 0 rgba(0,0,0,0.15); 27 | } 28 | 29 | .previewName { 30 | text-align: center; 31 | color: #7F7F7F; 32 | font-size: 24px; 33 | user-select: none; 34 | font-weight: 300; 35 | padding: 20px 0 0; 36 | } 37 | 38 | .link { 39 | position: relative; 40 | text-decoration: none; 41 | padding: 10px; 42 | display: flex; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/components/PokemonPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import { Link } from 'react-router' 4 | import classes from './PokemonPreview.css' 5 | 6 | class PokemonPreview extends React.Component { 7 | 8 | static propTypes = { 9 | pokemon: React.PropTypes.object, 10 | router: React.PropTypes.object, 11 | } 12 | 13 | render () { 14 | return ( 15 | 16 |
17 | Pokemon Image 18 |
19 | {this.props.pokemon.name} 20 |
21 |
22 | 23 | ) 24 | } 25 | } 26 | 27 | export default Relay.createContainer( 28 | PokemonPreview, 29 | { 30 | fragments: { 31 | pokemon: () => Relay.QL` 32 | fragment on Pokemon { 33 | id 34 | name 35 | url 36 | } 37 | `, 38 | }, 39 | } 40 | ) 41 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import PokemonPage from './views/PokemonPage' 5 | import ListPage from './views/ListPage' 6 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 7 | import useRelay from 'react-router-relay' 8 | import './index.css' 9 | 10 | Relay.injectNetworkLayer( 11 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 12 | ) 13 | 14 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 15 | 16 | ReactDOM.render( 17 | 23 | 24 | 25 | 26 | 27 | , document.getElementById('root') 28 | ) 29 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/mutations/CreatePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class AddPokemonMutation extends Relay.Mutation { 4 | 5 | static fragments = { 6 | viewer: () => Relay.QL` 7 | fragment on Viewer { 8 | id 9 | } 10 | `, 11 | } 12 | 13 | getMutation () { 14 | return Relay.QL`mutation{createPokemon}` 15 | } 16 | 17 | getFatQuery () { 18 | return Relay.QL` 19 | fragment on CreatePokemonPayload { 20 | pokemon 21 | edge 22 | viewer { 23 | allPokemons 24 | } 25 | } 26 | ` 27 | } 28 | 29 | getConfigs () { 30 | return [{ 31 | type: 'RANGE_ADD', 32 | parentName: 'viewer', 33 | parentID: this.props.viewer.id, 34 | connectionName: 'allPokemons', 35 | edgeName: 'edge', 36 | rangeBehaviors: { 37 | '': 'append', 38 | }, 39 | }] 40 | } 41 | 42 | getVariables () { 43 | return { 44 | name: this.props.name, 45 | url: this.props.url, 46 | } 47 | } 48 | 49 | getOptimisticResponse () { 50 | return { 51 | edge: { 52 | node: { 53 | name: this.props.name, 54 | url: this.props.url, 55 | }, 56 | }, 57 | viewer: { 58 | id: this.props.viewer.id, 59 | }, 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/mutations/DeletePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class DeletePokemonMutation extends Relay.Mutation { 4 | 5 | getMutation () { 6 | return Relay.QL`mutation{deletePokemon}` 7 | } 8 | 9 | getFatQuery () { 10 | return Relay.QL` 11 | fragment on DeletePokemonPayload { 12 | viewer 13 | deletedId 14 | } 15 | ` 16 | } 17 | 18 | getConfigs () { 19 | return [{ 20 | type: 'NODE_DELETE', 21 | parentName: 'viewer', 22 | parentID: this.props.viewerId, 23 | connectionName: 'pokemon', 24 | deletedIDFieldName: 'deletedId', 25 | }] 26 | } 27 | 28 | getVariables () { 29 | return { 30 | id: this.props.pokemonId, 31 | } 32 | } 33 | 34 | getOptimisticResponse () { 35 | return { 36 | deletedId: this.props.pokemonId, 37 | } 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/mutations/UpdatePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class UpdatePokemonMutation extends Relay.Mutation { 4 | 5 | getMutation () { 6 | return Relay.QL`mutation{updatePokemon}` 7 | } 8 | 9 | getFatQuery () { 10 | return Relay.QL` 11 | fragment on UpdatePokemonPayload { 12 | viewer 13 | pokemon 14 | } 15 | ` 16 | } 17 | 18 | getConfigs () { 19 | return [{ 20 | type: 'FIELDS_CHANGE', 21 | fieldIDs: { 22 | pokemon: this.props.pokemonId, 23 | }, 24 | }] 25 | } 26 | 27 | getVariables () { 28 | return { 29 | id: this.props.pokemonId, 30 | name: this.props.name, 31 | url: this.props.url, 32 | } 33 | } 34 | 35 | getOptimisticResponse () { 36 | return { 37 | model: { 38 | id: this.props.pokemonId, 39 | name: this.props.name, 40 | url: this.props.url, 41 | }, 42 | } 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import PokemonPreview from '../components/PokemonPreview' 4 | import AddNew from '../components/AddNew' 5 | import classes from './ListPage.css' 6 | 7 | class ListPage extends React.Component { 8 | static propTypes = { 9 | viewer: React.PropTypes.object, 10 | } 11 | render () { 12 | return ( 13 |
14 |
15 | {`There are ${this.props.viewer.allPokemons.edges.length} Pokemons in your pokedex`} 16 |
17 |
18 | {this.props.viewer.allPokemons.edges.map((edge) => edge.node).map((pokemon) => 19 | 20 | ) 21 | } 22 | 23 |
24 |
25 | ) 26 | } 27 | } 28 | 29 | export default Relay.createContainer( 30 | ListPage, 31 | { 32 | fragments: { 33 | viewer: () => Relay.QL` 34 | fragment on Viewer { 35 | allPokemons (first: 1000) { 36 | edges { 37 | node { 38 | ${PokemonPreview.getFragment('pokemon')} 39 | id 40 | } 41 | } 42 | } 43 | id 44 | } 45 | `, 46 | }, 47 | }, 48 | ) 49 | -------------------------------------------------------------------------------- /branch-step-07-solution/src/views/PokemonPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | width: 100vw; 3 | height: 100vh; 4 | display: flex; 5 | background-color: #F1F1F1; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .content { 11 | width: 350px; 12 | display: flex; 13 | flex-direction: column; 14 | /*height: 75vh;*/ 15 | } 16 | 17 | .buttonContainer { 18 | margin: 25px 0; 19 | display: flex; 20 | flex-direction: row; 21 | justify-content: space-between; 22 | flex: 0 0 auto; 23 | align-items: center; 24 | } 25 | 26 | .buttonContainer img { 27 | display: block; 28 | } 29 | 30 | .actionButtonContainer { 31 | display: flex; 32 | justify-content: space-between; 33 | flex-direction: row; 34 | } 35 | 36 | .deleteIcon { 37 | height: 18px; 38 | padding: 10px; 39 | cursor: hand; 40 | cursor: pointer; 41 | } 42 | 43 | .button { 44 | height: 18px; 45 | line-height: 1; 46 | font-size: 18px; 47 | padding: 15px 30px; 48 | cursor: pointer; 49 | flex: 0 0 auto; 50 | font-weight: 300; 51 | } 52 | 53 | .cancelButton { 54 | color: #A3A3A3; 55 | } 56 | 57 | .link { 58 | position: relative; 59 | text-decoration: none; 60 | padding: 10px; 61 | display: flex; 62 | } 63 | 64 | .saveButton { 65 | border-radius: 3px; 66 | color: white; 67 | background-color: #2BC3A1; 68 | } 69 | 70 | .saveButton:hover { 71 | color: #2BC3A1; 72 | background-color: white; 73 | } 74 | -------------------------------------------------------------------------------- /branch-step-07-solution/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /branch-step-07/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /branch-step-07/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /branch-step-07/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | 13 | #IDE 14 | .idea/* 15 | -------------------------------------------------------------------------------- /branch-step-07/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /branch-step-07/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/branch-step-07/favicon.ico -------------------------------------------------------------------------------- /branch-step-07/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /branch-step-07/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/__PROJECT_ID__" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /branch-step-07/src/assets/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /branch-step-07/src/components/AddNew.css: -------------------------------------------------------------------------------- 1 | .page { 2 | background-color: transparent; 3 | /*height: auto;*/ 4 | width: 180px; 5 | transition: transform 0.2s; 6 | cursor: pointer; 7 | border: dashed #d4d4d4; 8 | color: #d4d4d4; 9 | box-sizing: border-box; 10 | border-radius: 3px; 11 | /*height: 100%;*/ 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: center; 15 | transition: border-color .2s ease, color .2s ease; 16 | } 17 | 18 | .page:hover { 19 | border-color: #bcbcbc; 20 | color: #bcbcbc; 21 | } 22 | 23 | .link { 24 | text-decoration: none; 25 | padding: 10px; 26 | min-height: 250px; 27 | display: flex; 28 | } 29 | 30 | .plus { 31 | text-align: center; 32 | font-size: 70px; 33 | line-height: 1; 34 | font-weight: 300; 35 | } 36 | 37 | .title { 38 | margin-top: 14px; 39 | text-align: center; 40 | font-size: 24px; 41 | user-select: none; 42 | font-weight: 300; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-07/src/components/AddNew.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link} from 'react-router' 3 | import classes from './AddNew.css' 4 | 5 | export default class AddNew extends React.Component { 6 | 7 | render () { 8 | return ( 9 | 10 |
11 |
12 | + 13 |
14 |
15 | Add New 16 |
17 |
18 | 19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /branch-step-07/src/components/PokemonPreview.css: -------------------------------------------------------------------------------- 1 | .previewPage{ 2 | background-color: white; 3 | border-radius: 3px; 4 | box-shadow: 0 2px 8px 0 rgba(0,0,0,0.25); 5 | width: 180px; 6 | cursor: pointer; 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: space-between; 10 | padding: 20px; 11 | box-sizing: border-box; 12 | /*height: 100%;*/ 13 | transition: transform .2s ease, box-shadow .2s ease; 14 | backface-visibility: hidden; 15 | } 16 | 17 | .previewImg { 18 | height: auto; 19 | display: block; 20 | width: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | .previewPage:hover{ 25 | transform: scale(1.05); 26 | box-shadow: 0 4px 14px 0 rgba(0,0,0,0.15); 27 | } 28 | 29 | .previewName { 30 | text-align: center; 31 | color: #7F7F7F; 32 | font-size: 24px; 33 | user-select: none; 34 | font-weight: 300; 35 | padding: 20px 0 0; 36 | } 37 | 38 | .link { 39 | position: relative; 40 | text-decoration: none; 41 | padding: 10px; 42 | display: flex; 43 | } 44 | -------------------------------------------------------------------------------- /branch-step-07/src/components/PokemonPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import { Link } from 'react-router' 4 | import classes from './PokemonPreview.css' 5 | 6 | class PokemonPreview extends React.Component { 7 | 8 | static propTypes = { 9 | pokemon: React.PropTypes.object, 10 | router: React.PropTypes.object, 11 | } 12 | 13 | render () { 14 | return ( 15 | 16 |
17 | Pokemon Image 18 |
19 | {this.props.pokemon.name} 20 |
21 |
22 | 23 | ) 24 | } 25 | } 26 | 27 | export default Relay.createContainer( 28 | PokemonPreview, 29 | { 30 | fragments: { 31 | pokemon: () => Relay.QL` 32 | fragment on Pokemon { 33 | id 34 | name 35 | url 36 | } 37 | `, 38 | }, 39 | } 40 | ) 41 | -------------------------------------------------------------------------------- /branch-step-07/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /branch-step-07/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import PokemonPage from './views/PokemonPage' 5 | import ListPage from './views/ListPage' 6 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 7 | import useRelay from 'react-router-relay' 8 | import './index.css' 9 | 10 | Relay.injectNetworkLayer( 11 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/__PROJECT_ID__') 12 | ) 13 | 14 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 15 | 16 | ReactDOM.render( 17 | 23 | 24 | 25 | 26 | 27 | , document.getElementById('root') 28 | ) 29 | -------------------------------------------------------------------------------- /branch-step-07/src/mutations/CreatePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class CreatePokemonMutation extends Relay.Mutation { 4 | 5 | static fragments = { 6 | viewer: () => Relay.QL` 7 | fragment on Viewer { 8 | id 9 | } 10 | `, 11 | } 12 | 13 | getMutation () { 14 | return Relay.QL`mutation{createPokemon}` 15 | } 16 | 17 | getFatQuery () { 18 | return Relay.QL` 19 | fragment on CreatePokemonPayload { 20 | pokemon 21 | edge 22 | viewer { 23 | allPokemons 24 | } 25 | } 26 | ` 27 | } 28 | 29 | getConfigs () { 30 | return [{ 31 | type: 'RANGE_ADD', 32 | parentName: 'viewer', 33 | parentID: this.props.viewer.id, 34 | connectionName: 'allPokemons', 35 | edgeName: 'edge', 36 | rangeBehaviors: { 37 | '': 'append', 38 | }, 39 | }] 40 | } 41 | 42 | getVariables () { 43 | return { 44 | name: this.props.name, 45 | url: this.props.url, 46 | } 47 | } 48 | 49 | getOptimisticResponse () { 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /branch-step-07/src/mutations/DeletePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class DeletePokemonMutation extends Relay.Mutation { 4 | 5 | getMutation () { 6 | return Relay.QL`mutation{deletePokemon}` 7 | } 8 | 9 | getFatQuery () { 10 | return Relay.QL` 11 | fragment on DeletePokemonPayload { 12 | viewer 13 | deletedId 14 | } 15 | ` 16 | } 17 | 18 | getConfigs () { 19 | return [{ 20 | type: 'NODE_DELETE', 21 | parentName: 'viewer', 22 | parentID: this.props.viewerId, 23 | connectionName: 'pokemon', 24 | deletedIDFieldName: 'deletedId', 25 | }] 26 | } 27 | 28 | getVariables () { 29 | return { 30 | id: this.props.pokemonId, 31 | } 32 | } 33 | 34 | getOptimisticResponse () { 35 | 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /branch-step-07/src/mutations/UpdatePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class UpdatePokemonMutation extends Relay.Mutation { 4 | 5 | getMutation () { 6 | 7 | } 8 | 9 | getFatQuery () { 10 | 11 | } 12 | 13 | getConfigs () { 14 | 15 | } 16 | 17 | getVariables () { 18 | 19 | } 20 | 21 | getOptimisticResponse () { 22 | 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /branch-step-07/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .container { 16 | margin-left: 130px; 17 | margin-right: 130px; 18 | justify-content: center; 19 | /*align-content: stretch;*/ 20 | display:flex; 21 | flex-wrap: wrap; 22 | align-items: stretch; 23 | } 24 | -------------------------------------------------------------------------------- /branch-step-07/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import PokemonPreview from '../components/PokemonPreview' 4 | import AddNew from '../components/AddNew' 5 | import classes from './ListPage.css' 6 | 7 | class ListPage extends React.Component { 8 | static propTypes = { 9 | viewer: React.PropTypes.object, 10 | } 11 | render () { 12 | return ( 13 |
14 |
15 | {`There are ${this.props.viewer.allPokemons.edges.length} Pokemons in your pokedex`} 16 |
17 |
18 | {this.props.viewer.allPokemons.edges.map((edge) => edge.node).map((pokemon) => 19 | 20 | ) 21 | } 22 | 23 |
24 |
25 | ) 26 | } 27 | } 28 | 29 | export default Relay.createContainer( 30 | ListPage, 31 | { 32 | fragments: { 33 | viewer: () => Relay.QL` 34 | fragment on Viewer { 35 | allPokemons (first: 1000) { 36 | edges { 37 | node { 38 | ${PokemonPreview.getFragment('pokemon')} 39 | id 40 | } 41 | } 42 | } 43 | id 44 | } 45 | `, 46 | }, 47 | }, 48 | ) 49 | -------------------------------------------------------------------------------- /branch-step-07/src/views/PokemonPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | width: 100vw; 3 | height: 100vh; 4 | display: flex; 5 | background-color: #F1F1F1; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .content { 11 | width: 350px; 12 | display: flex; 13 | flex-direction: column; 14 | /*height: 75vh;*/ 15 | } 16 | 17 | .buttonContainer { 18 | margin: 25px 0; 19 | display: flex; 20 | flex-direction: row; 21 | justify-content: space-between; 22 | flex: 0 0 auto; 23 | align-items: center; 24 | } 25 | 26 | .buttonContainer img { 27 | display: block; 28 | } 29 | 30 | .actionButtonContainer { 31 | display: flex; 32 | justify-content: space-between; 33 | flex-direction: row; 34 | } 35 | 36 | .deleteIcon { 37 | height: 18px; 38 | padding: 10px; 39 | cursor: hand; 40 | cursor: pointer; 41 | } 42 | 43 | .button { 44 | height: 18px; 45 | line-height: 1; 46 | font-size: 18px; 47 | padding: 15px 30px; 48 | cursor: pointer; 49 | flex: 0 0 auto; 50 | font-weight: 300; 51 | } 52 | 53 | .cancelButton { 54 | color: #A3A3A3; 55 | } 56 | 57 | .link { 58 | position: relative; 59 | text-decoration: none; 60 | padding: 10px; 61 | display: flex; 62 | } 63 | 64 | .saveButton { 65 | border-radius: 3px; 66 | color: white; 67 | background-color: #2BC3A1; 68 | } 69 | 70 | .saveButton:hover { 71 | color: #2BC3A1; 72 | background-color: white; 73 | } 74 | -------------------------------------------------------------------------------- /branch-step-07/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /hosted-demo/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react-relay"], 3 | "presets": ["react", "es2015", "stage-0"] 4 | } 5 | -------------------------------------------------------------------------------- /hosted-demo/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "semi": [2, "never"], 12 | "comma-dangle": [2, "always-multiline"], 13 | "space-infix-ops": 0, 14 | "max-len": [2, 120, 2], 15 | "react/jsx-no-bind": [1, { 16 | "allowArrowFunctions": true 17 | }], 18 | "jsx-quotes": [2, "prefer-single"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /hosted-demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | 13 | #IDE 14 | .idea/* 15 | -------------------------------------------------------------------------------- /hosted-demo/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /hosted-demo/.netlify: -------------------------------------------------------------------------------- 1 | {"site_id":"666e6f72-b068-4225-81d4-fdd74e17e701","path":"build"} -------------------------------------------------------------------------------- /hosted-demo/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnrelay/pokedex/c4d2680cb23a047a3900fe22eeb56049089f1664/hosted-demo/favicon.ico -------------------------------------------------------------------------------- /hosted-demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pokedex App 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /hosted-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "webpack-dev-server -d --hot --inline --history-api-fallback --no-info --port 3000", 5 | "build": "webpack -d --inline --history-api-fallback --config webpack.config.js" 6 | }, 7 | "graphql": { 8 | "request": { 9 | "url": "https://api.graph.cool/relay/v1/cj0krqsbvu90z0133orcqpse4" 10 | } 11 | }, 12 | "dependencies": { 13 | "classnames": "2.2.5", 14 | "file-loader": "^0.9.0", 15 | "graphql-config-parser": "^1.0.1", 16 | "history": "3.0.0", 17 | "react": "15.2.1", 18 | "react-dom": "15.2.1", 19 | "react-relay": "^0.9.2", 20 | "react-router": "2.5.2", 21 | "react-router-relay": "0.13.3" 22 | }, 23 | "devDependencies": { 24 | "babel-cli": "6.10.1", 25 | "babel-core": "^6.10.4", 26 | "babel-eslint": "^6.1.2", 27 | "babel-loader": "6.2.4", 28 | "babel-plugin-react-relay": "^0.9.3-3", 29 | "babel-preset-es2015": "6.9.0", 30 | "babel-preset-react": "6.11.1", 31 | "babel-preset-stage-0": "6.5.0", 32 | "css-loader": "^0.23.1", 33 | "eslint": "3.0.1", 34 | "eslint-config-standard": "^5.3.5", 35 | "eslint-config-standard-react": "^3.0.0", 36 | "eslint-loader": "^1.4.1", 37 | "eslint-plugin-babel": "^3.3.0", 38 | "eslint-plugin-promise": "^2.0.0", 39 | "eslint-plugin-react": "^5.2.2", 40 | "eslint-plugin-standard": "^2.0.0", 41 | "html-webpack-plugin": "^2.22.0", 42 | "style-loader": "^0.13.1", 43 | "webpack": "1.13.1", 44 | "webpack-dev-server": "1.14.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /hosted-demo/public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /hosted-demo/src/assets/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /hosted-demo/src/components/AddNew.css: -------------------------------------------------------------------------------- 1 | .page { 2 | background-color: transparent; 3 | /*height: auto;*/ 4 | width: 180px; 5 | transition: transform 0.2s; 6 | cursor: pointer; 7 | border: dashed #d4d4d4; 8 | color: #d4d4d4; 9 | box-sizing: border-box; 10 | border-radius: 3px; 11 | /*height: 100%;*/ 12 | display: flex; 13 | flex-direction: column; 14 | justify-content: center; 15 | transition: border-color .2s ease, color .2s ease; 16 | } 17 | 18 | .page:hover { 19 | border-color: #bcbcbc; 20 | color: #bcbcbc; 21 | } 22 | 23 | .link { 24 | text-decoration: none; 25 | padding: 10px; 26 | min-height: 250px; 27 | display: flex; 28 | } 29 | 30 | .plus { 31 | text-align: center; 32 | font-size: 70px; 33 | line-height: 1; 34 | font-weight: 300; 35 | } 36 | 37 | .title { 38 | margin-top: 14px; 39 | text-align: center; 40 | font-size: 24px; 41 | user-select: none; 42 | font-weight: 300; 43 | } 44 | -------------------------------------------------------------------------------- /hosted-demo/src/components/AddNew.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link} from 'react-router' 3 | import classes from './AddNew.css' 4 | 5 | export default class AddNew extends React.Component { 6 | 7 | render () { 8 | return ( 9 | 10 |
11 |
12 | + 13 |
14 |
15 | Add New 16 |
17 |
18 | 19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /hosted-demo/src/components/PokemonCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classes from './PokemonCard.css' 3 | 4 | export default class PokemonCard extends React.Component { 5 | 6 | static propTypes = { 7 | addNew: React.PropTypes.bool, 8 | url: React.PropTypes.string, 9 | name: React.PropTypes.string, 10 | onNameChange: React.PropTypes.func, 11 | onUrlChange: React.PropTypes.func, 12 | } 13 | 14 | render () { 15 | return ( 16 |
17 |
18 |
19 | NAME 20 |
21 | this.props.onNameChange(e.target.value)} 26 | /> 27 |
28 |
29 |
30 |
31 | IMAGE URL 32 |
33 | this.props.onUrlChange(e.target.value)} 38 | /> 39 |
40 |
41 | 42 |
43 |
44 |
45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /hosted-demo/src/components/PokemonPreview.css: -------------------------------------------------------------------------------- 1 | .previewPage{ 2 | background-color: white; 3 | border-radius: 3px; 4 | box-shadow: 0 2px 8px 0 rgba(0,0,0,0.25); 5 | width: 180px; 6 | cursor: pointer; 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: space-between; 10 | padding: 20px; 11 | box-sizing: border-box; 12 | /*height: 100%;*/ 13 | transition: transform .2s ease, box-shadow .2s ease; 14 | backface-visibility: hidden; 15 | } 16 | 17 | .previewImg { 18 | height: auto; 19 | display: block; 20 | width: 100%; 21 | box-sizing: border-box; 22 | } 23 | 24 | .previewPage:hover{ 25 | transform: scale(1.05); 26 | box-shadow: 0 4px 14px 0 rgba(0,0,0,0.15); 27 | } 28 | 29 | .previewName { 30 | text-align: center; 31 | color: #7F7F7F; 32 | font-size: 24px; 33 | user-select: none; 34 | font-weight: 300; 35 | padding: 20px 0 0; 36 | } 37 | 38 | .link { 39 | position: relative; 40 | text-decoration: none; 41 | padding: 10px; 42 | display: flex; 43 | } 44 | -------------------------------------------------------------------------------- /hosted-demo/src/components/PokemonPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import { Link } from 'react-router' 4 | import classes from './PokemonPreview.css' 5 | 6 | class PokemonPreview extends React.Component { 7 | 8 | static propTypes = { 9 | pokemon: React.PropTypes.object, 10 | router: React.PropTypes.object, 11 | } 12 | 13 | render () { 14 | return ( 15 | 16 |
17 | Pokemon Image 18 |
19 | {this.props.pokemon.name} 20 |
21 |
22 | 23 | ) 24 | } 25 | } 26 | 27 | export default Relay.createContainer( 28 | PokemonPreview, 29 | { 30 | fragments: { 31 | pokemon: () => Relay.QL` 32 | fragment on Pokemon { 33 | id 34 | name 35 | url 36 | } 37 | `, 38 | }, 39 | } 40 | ) 41 | -------------------------------------------------------------------------------- /hosted-demo/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css?family=Open+Sans:300,400'; 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | -------------------------------------------------------------------------------- /hosted-demo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import ReactDOM from 'react-dom' 4 | import PokemonPage from './views/PokemonPage' 5 | import ListPage from './views/ListPage' 6 | import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router' 7 | import useRelay from 'react-router-relay' 8 | import './index.css' 9 | 10 | Relay.injectNetworkLayer( 11 | new Relay.DefaultNetworkLayer('https://api.graph.cool/relay/v1/cj0krqsbvu90z0133orcqpse4') 12 | ) 13 | 14 | const ViewerQueries = { viewer: () => Relay.QL`query { viewer }` } 15 | 16 | ReactDOM.render( 17 | 23 | 24 | 25 | 26 | 27 | , document.getElementById('root') 28 | ) 29 | -------------------------------------------------------------------------------- /hosted-demo/src/mutations/CreatePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class AddPokemonMutation extends Relay.Mutation { 4 | 5 | static fragments = { 6 | viewer: () => Relay.QL` 7 | fragment on Viewer { 8 | id 9 | } 10 | `, 11 | } 12 | 13 | getMutation () { 14 | return Relay.QL`mutation{createPokemon}` 15 | } 16 | 17 | getFatQuery () { 18 | return Relay.QL` 19 | fragment on CreatePokemonPayload { 20 | pokemon 21 | edge 22 | viewer { 23 | allPokemons 24 | } 25 | } 26 | ` 27 | } 28 | 29 | getConfigs () { 30 | return [{ 31 | type: 'RANGE_ADD', 32 | parentName: 'viewer', 33 | parentID: this.props.viewer.id, 34 | connectionName: 'allPokemons', 35 | edgeName: 'edge', 36 | rangeBehaviors: { 37 | '': 'append', 38 | }, 39 | }] 40 | } 41 | 42 | getVariables () { 43 | return { 44 | name: this.props.name, 45 | url: this.props.url, 46 | } 47 | } 48 | 49 | getOptimisticResponse () { 50 | return { 51 | edge: { 52 | node: { 53 | name: this.props.name, 54 | url: this.props.url, 55 | }, 56 | }, 57 | viewer: { 58 | id: this.props.viewer.id, 59 | }, 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /hosted-demo/src/mutations/DeletePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class DeletePokemonMutation extends Relay.Mutation { 4 | 5 | getMutation () { 6 | return Relay.QL`mutation{deletePokemon}` 7 | } 8 | 9 | getFatQuery () { 10 | return Relay.QL` 11 | fragment on DeletePokemonPayload { 12 | viewer 13 | deletedId 14 | } 15 | ` 16 | } 17 | 18 | getConfigs () { 19 | return [{ 20 | type: 'NODE_DELETE', 21 | parentName: 'viewer', 22 | parentID: this.props.viewerId, 23 | connectionName: 'pokemon', 24 | deletedIDFieldName: 'deletedId', 25 | }] 26 | } 27 | 28 | getVariables () { 29 | return { 30 | id: this.props.pokemonId, 31 | } 32 | } 33 | 34 | getOptimisticResponse () { 35 | return { 36 | deletedId: this.props.pokemonId, 37 | } 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /hosted-demo/src/mutations/UpdatePokemonMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay' 2 | 3 | export default class UpdatePokemonMutation extends Relay.Mutation { 4 | 5 | getMutation () { 6 | return Relay.QL`mutation{updatePokemon}` 7 | } 8 | 9 | getFatQuery () { 10 | return Relay.QL` 11 | fragment on UpdatePokemonPayload { 12 | viewer 13 | pokemon 14 | } 15 | ` 16 | } 17 | 18 | getConfigs () { 19 | return [{ 20 | type: 'FIELDS_CHANGE', 21 | fieldIDs: { 22 | pokemon: this.props.pokemonId, 23 | }, 24 | }] 25 | } 26 | 27 | getVariables () { 28 | return { 29 | id: this.props.pokemonId, 30 | name: this.props.name, 31 | url: this.props.url, 32 | } 33 | } 34 | 35 | getOptimisticResponse () { 36 | return { 37 | model: { 38 | id: this.props.pokemonId, 39 | name: this.props.name, 40 | url: this.props.url, 41 | }, 42 | } 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /hosted-demo/src/views/ListPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: #F2F2F2; 3 | min-height: 100vh; 4 | } 5 | 6 | .title { 7 | color: #7F7F7F; 8 | font-size: 32px; 9 | text-align: center; 10 | padding-top: 120px; 11 | padding-bottom: 50px; 12 | font-weight: 300; 13 | } 14 | 15 | .footer { 16 | color: #7F7F7F; 17 | font-size: 16px; 18 | text-align: center; 19 | padding-top: 120px; 20 | padding-bottom: 50px; 21 | font-weight: 300; 22 | } 23 | 24 | .container { 25 | margin-left: 130px; 26 | margin-right: 130px; 27 | justify-content: center; 28 | /*align-content: stretch;*/ 29 | display:flex; 30 | flex-wrap: wrap; 31 | align-items: stretch; 32 | } 33 | -------------------------------------------------------------------------------- /hosted-demo/src/views/ListPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Relay from 'react-relay' 3 | import PokemonPreview from '../components/PokemonPreview' 4 | import AddNew from '../components/AddNew' 5 | import classes from './ListPage.css' 6 | 7 | class ListPage extends React.Component { 8 | static propTypes = { 9 | viewer: React.PropTypes.object, 10 | } 11 | render () { 12 | return ( 13 |
14 |
15 | {`There are ${this.props.viewer.allPokemons.edges.length} Pokemons in your pokedex`} 16 |
17 |
18 | {this.props.viewer.allPokemons.edges.map((edge) => edge.node).map((pokemon) => 19 | 20 | ) 21 | } 22 | 23 |
24 |
25 | Powered by Graphcool and 26 | deployed with Netlify 27 |
28 |
29 | ) 30 | } 31 | } 32 | 33 | export default Relay.createContainer( 34 | ListPage, 35 | { 36 | fragments: { 37 | viewer: () => Relay.QL` 38 | fragment on Viewer { 39 | allPokemons (first: 1000) { 40 | edges { 41 | node { 42 | ${PokemonPreview.getFragment('pokemon')} 43 | id 44 | } 45 | } 46 | } 47 | id 48 | } 49 | `, 50 | }, 51 | }, 52 | ) 53 | -------------------------------------------------------------------------------- /hosted-demo/src/views/PokemonPage.css: -------------------------------------------------------------------------------- 1 | .root { 2 | width: 100vw; 3 | height: 100vh; 4 | display: flex; 5 | background-color: #F1F1F1; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .content { 11 | width: 350px; 12 | display: flex; 13 | flex-direction: column; 14 | /*height: 75vh;*/ 15 | } 16 | 17 | .buttonContainer { 18 | margin: 25px 0; 19 | display: flex; 20 | flex-direction: row; 21 | justify-content: space-between; 22 | flex: 0 0 auto; 23 | align-items: center; 24 | } 25 | 26 | .buttonContainer img { 27 | display: block; 28 | } 29 | 30 | .actionButtonContainer { 31 | display: flex; 32 | justify-content: space-between; 33 | flex-direction: row; 34 | } 35 | 36 | .deleteIcon { 37 | height: 18px; 38 | padding: 10px; 39 | cursor: hand; 40 | cursor: pointer; 41 | } 42 | 43 | .button { 44 | height: 18px; 45 | line-height: 1; 46 | font-size: 18px; 47 | padding: 15px 30px; 48 | cursor: pointer; 49 | flex: 0 0 auto; 50 | font-weight: 300; 51 | } 52 | 53 | .cancelButton { 54 | color: #A3A3A3; 55 | } 56 | 57 | .link { 58 | position: relative; 59 | text-decoration: none; 60 | padding: 10px; 61 | display: flex; 62 | } 63 | 64 | .saveButton { 65 | border-radius: 3px; 66 | color: white; 67 | background-color: #2BC3A1; 68 | } 69 | 70 | .saveButton:hover { 71 | color: #2BC3A1; 72 | background-color: white; 73 | } 74 | -------------------------------------------------------------------------------- /hosted-demo/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: '[name].[hash].js', 7 | path: __dirname + '/build', 8 | publicPath: '/' 9 | }, 10 | module: { 11 | preLoaders: [{ 12 | test: /\.js$/, 13 | loader: 'eslint', 14 | exclude: /node_modules/ 15 | }], 16 | loaders: [{ 17 | test: /\.css/, 18 | loader: 'style?sourceMap!css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 19 | }, { 20 | test: /\.js$/, 21 | loader: 'babel', 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file' 26 | }] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: 'index.html' 31 | }) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /pokedex.schema: -------------------------------------------------------------------------------- 1 | type Pokemon { 2 | id: ID! 3 | name: String! 4 | url: String! 5 | } 6 | -------------------------------------------------------------------------------- /update-branches.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | message="$1" 6 | 7 | for dir in branch-step-* 8 | do 9 | cp -r $dir tmp 10 | cp -rf .git tmp 11 | cd tmp 12 | branch=${dir/"branch-"/""} 13 | git checkout -B $branch 14 | git add . 15 | git commit -m "$message" 16 | git push -f origin $branch 17 | cd .. 18 | rm -rf tmp 19 | done 20 | --------------------------------------------------------------------------------