{"id":15089102,"url":"https://github.com/rubygarage/nextjs6-graphql-client-tutorial","last_synced_at":"2025-10-05T21:31:47.055Z","repository":{"id":151091116,"uuid":"136527198","full_name":"rubygarage/nextjs6-graphql-client-tutorial","owner":"rubygarage","description":"Demo app","archived":true,"fork":false,"pushed_at":"2018-10-02T21:30:45.000Z","size":309,"stargazers_count":46,"open_issues_count":4,"forks_count":16,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-01-24T13:37:33.497Z","etag":null,"topics":["apollo-client","atomic-design","graphql","javascript","material-ui","nextjs6","react","storybooks"],"latest_commit_sha":null,"homepage":"https://next-github.herokuapp.com/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rubygarage.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-07T20:23:09.000Z","updated_at":"2024-11-10T03:05:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"30c4f1e7-b3d1-4580-b484-2859765c668e","html_url":"https://github.com/rubygarage/nextjs6-graphql-client-tutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rubygarage/nextjs6-graphql-client-tutorial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubygarage%2Fnextjs6-graphql-client-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubygarage%2Fnextjs6-graphql-client-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubygarage%2Fnextjs6-graphql-client-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubygarage%2Fnextjs6-graphql-client-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rubygarage","download_url":"https://codeload.github.com/rubygarage/nextjs6-graphql-client-tutorial/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubygarage%2Fnextjs6-graphql-client-tutorial/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278525548,"owners_count":26001321,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apollo-client","atomic-design","graphql","javascript","material-ui","nextjs6","react","storybooks"],"created_at":"2024-09-25T08:40:13.805Z","updated_at":"2025-10-05T21:31:47.049Z","avatar_url":"https://github.com/rubygarage.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.com/leksster/nextjs6-graphql-client-tutorial.svg?branch=master)](https://travis-ci.com/leksster/nextjs6-graphql-client-tutorial)\n\n# Nextjs6 with Apollo Graphql and Material-UI Tutorial\n\n## Step 1 - Yarn installation\n\nThere are two options how to install Yarn. The first option is to use npm:\n\n```bash\nnpm install -g yarn\n```\n\nAnother option is to go to the official download page and get the installer for your operating system and run it.\n\nThe other method would be to go to [the official download page](https://yarnpkg.com/en/docs/install) and get the installer for your operating system and run it.\n\n## Step 2 - Project initialization\n\nTo start, create a sample project by running the following commands:\n\n```bash\nmkdir github-client\ncd github-client\nyarn init\nyarn add react react-dom prop-types next\nmkdir pages\n```\n\nThen open the \"package.json\" in the github-client directory and add the following script.\n\n```\n{\n  \"scripts\": {\n    \"dev\": \"next\"\n  }\n}\n```\n\nEverything is ready. To start the dev server, you need to run the following command:\n\n```bash\nyarn dev\n```\n\nWhen you run `localhost:3000` you will see this page:\n\n![404](public/images/nextjs-404.png \"404 Page screenshot\")\n\n## Step 3 - Babel Installation\n\nBabel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in old browsers or environments.\n\nTo install babel compiler core use command:\n\n```bash\nyarn add babel-core -D\n```\n\n#### Babel module resolver\n\nA Babel plugin to add a new resolver for your modules when compiling your code using Babel. This plugin allows you to add new \"root\" directories that contain your modules. It also allows you to setup a custom alias for directories, specific files, or even other npm modules.\n\nLet's install babel module resolver.\n\n```bash\nyarn add babel-plugin-module-resolver -D\n```\n\nAfter that we need to update `.babelrc` config. So we can import dependencies without declaring related path.\n\n`.babelrc`\n\n```js\n\"plugins\": [\n  [\"module-resolver\", {\n    \"root\": [\"./\"],\n    \"alias\": {\n      \"components\": \"./components\",\n      \"containers\": \"./containers\",\n      \"queries\": \"./graphql/queries\"\n    }\n  }]\n]\n```\n\nThis allows us to make this:\n\n```js\nimport { Home } from 'components';\n```\n\nInstead of this:\n\n```js\nimport { Home } from '../../components';\n```\n\n## Step 4 Linters configuration\n\nTo avoid big refactoring in the future, you need to integrate linters to your app. For that, add eslint as development dependency:\n\n```bash\nyarn add eslint -D\n```\n\nYou need a wrapper for babel’s parser used for eslint. Use yarn to install package:\n\n```bash\nyarn add babel-eslint -D\n```\n\nBabel-eslint allows you to lint ALL valid Babel code\n\nThere are few dependencies that you have to install.\nThis package provides Airbnb's .eslintrc as an extensible shared config.\n\n```bash\nyarn add eslint-config-airbnb -D\n```\n\nInstall ESLint plugin with rules that help validate proper imports.\n\n```bash\nyarn add eslint-plugin-import -D\n```\n\nInstall Static AST checker for accessibility rules on JSX elements.\n\n```bash\nyarn add eslint-plugin-jsx-a11y -D\n```\n\nInstall eslint plugin for React.\n\n```bash\nyarn add eslint-plugin-react -D\n```\n\nInitialize eslint config.\n\n```bash\nyarn run eslint --init\n```\n\nWe're going to use airbnb eslint config.\n\nChoose the following settings:\n\nHow would you like to configure ESLint? `Use a popular style guide`\nWhich style guide do you want to follow? `Airbnb`\nDo you use React? `Yes`\nWhat format do you want your config file to be in? `JSON`\nWould you like to install them now with npm? `No`\n\nNow we have `.eslintrc.json` with the following configuration\n\n```js\n{\n  \"extends\": \"airbnb\"\n}\n```\n\nWe're going to use `.js` extensions instead of `.jsx` because JSX is not standard JS, and is not likely to ever be. So we explicitly add this option to the `.eslintc.json`:\n\n```js\n{\n  \"parser\": \"babel-eslint\",\n  \"rules\": {\n    \"react/jsx-filename-extension\": [\"error\", { \"extensions\": [\".js\", \".jsx\"] }]\n  },\n  \"extends\": \"airbnb\"\n}\n```\n\n## Step 5 - Material UI integration\n\nIn a nutshell, Material-UI is an open-source project that features React components that implement Google’s Material Design.\n\nIt kick-started in 2014, not long after React came out to the public, and has grown in popularity ever since. With over 35,000 stars on GitHub, Material-UI is one of the top user interface libraries for React out there.\n\nThere are few additional steps that we need to apply before start using material with NextJS framework\n\nFirst of all install some additional packages.\n\n#### Install JSS:\n\n```\nyarn add jss\n```\n\nJSS is a more powerful abstraction than CSS. It uses JavaScript as a language to describe styles in a declarative and maintainable way. It is a high performance JS to CSS compiler which works at runtime and server-side.\n\n#### Install react-jss:\n\n```\nyarn add react-jss\n```\n\nWe use React-JSS because it provides components for JSS as a layer of abstraction and has the following range of benefits compared to lower level core:\n\n- Theming support.\n- Critical CSS extraction.\n- Lazy evaluation - sheet is created only when the component will mount.\n- Auto attach/detach - sheet will be rendered to the DOM when the component is about to mount, and will be removed when no element needs it.\n- A Style Sheet gets shared between all elements.\n- Function values and rules are updated automatically with props.\n\n#### Install styled-jsx package for server-side rendering:\n\n```\nyarn add styled-jsx\n```\n\nStyled-jsx is a full, scoped and component-friendly CSS support for JSX (rendered on the server or the client).\n\n#### Install material core with icons\n\n```\nyarn add @material-ui/core @material-ui/icons\n```\n\n@material-ui/core is a set of react components that implement Google's Material Design.\n@materail-ui/icons is a set of components with svg icons\n\nThere is an example app that shows how to properly integrate material-ui specifically for nextjs framework. https://github.com/mui-org/material-ui/tree/master/examples/nextjs\n\nNow create `lib/getPageContext.js`\n\n```js\n/* eslint-disable no-underscore-dangle */\n\nimport { SheetsRegistry } from 'jss';\nimport { createMuiTheme, createGenerateClassName } from '@material-ui/core/styles';\n\n// A theme with custom primary and secondary color.\n// It's optional.\nconst theme = createMuiTheme();\n\nfunction createPageContext() {\n  return {\n    theme,\n    // This is needed in order to deduplicate the injection of CSS in the page.\n    sheetsManager: new Map(),\n    // This is needed in order to inject the critical CSS.\n    sheetsRegistry: new SheetsRegistry(),\n    // The standard class name generator.\n    generateClassName: createGenerateClassName(),\n  };\n}\n\nexport default function getPageContext() {\n  // Make sure to create a new context for every server-side request so that data\n  // isn't shared between connections (which would be bad).\n  if (!process.browser) {\n    return createPageContext();\n  }\n\n  // Reuse context on the client-side.\n  if (!global.__INIT_MATERIAL_UI__) {\n    global.__INIT_MATERIAL_UI__ = createPageContext();\n  }\n\n  return global.__INIT_MATERIAL_UI__;\n}\n```\n\nNext.js uses the App component to initialize pages. You can override it and control the page initialization. Which allows you to do amazing things like:\n\n- Persisting layout between page changes\n- Keeping state when navigating pages\n- Custom error handling using componentDidCatch\n- Inject additional data into pages (for example by processing GraphQL queries)\n\nTo override, create the `./pages/_app.js` file and override the App class as shown below:\n\n```js\nimport React from 'react';\nimport App, { Container } from 'next/app';\nimport { MuiThemeProvider } from '@material-ui/core/styles';\nimport CssBaseline from '@material-ui/core/CssBaseline';\nimport JssProvider from 'react-jss/lib/JssProvider';\nimport getPageContext from '../lib/getPageContext';\n\nclass MainApp extends App {\n  constructor(props) {\n    super(props);\n    this.pageContext = getPageContext();\n  }\n\n  pageContext = null;\n\n  componentDidMount() {\n    // Remove the server-side injected CSS.\n    const jssStyles = document.querySelector('#jss-server-side');\n    if (jssStyles \u0026\u0026 jssStyles.parentNode) {\n      jssStyles.parentNode.removeChild(jssStyles);\n    }\n  }\n\n  render() {\n    const { Component, pageProps } = this.props;\n    return (\n      \u003cContainer\u003e\n        {/* Wrap every page in Jss and Theme providers */}\n        \u003cJssProvider\n          registry={this.pageContext.sheetsRegistry}\n          generateClassName={this.pageContext.generateClassName}\n        \u003e\n          {/* MuiThemeProvider makes the theme available down the React\n              tree thanks to React context. */}\n          \u003cMuiThemeProvider\n            theme={this.pageContext.theme}\n            sheetsManager={this.pageContext.sheetsManager}\n          \u003e\n            {/*\n              CssBaseline kickstart an elegant, consistent, and simple baseline to build upon.\n            */}\n            \u003cCssBaseline /\u003e\n            {/*\n              Pass pageContext to the _document though the renderPage enhancer to render collected styles on server side.\n            */}\n            \u003cComponent pageContext={this.pageContext} {...pageProps} /\u003e\n          \u003c/MuiThemeProvider\u003e\n        \u003c/JssProvider\u003e\n      \u003c/Container\u003e\n    );\n  }\n}\n\nexport default MainApp;\n```\n\nYou need to add this line to `.eslintrc.json` to avoid `document is not defined` error:\n\n```\n\"env\": {\n  \"browser\": true\n```\n\nPages in Next.js skip the definition of the surrounding document's markup. For example, you never include `\u003chtml\u003e`, `\u003cbody\u003e`, etc. To override that default behavior, you must create a file at `./pages/_document.js`, where you can extend the Document class.\n\nYou need to use codebase from the material-ui official repo example with nextjs:\n\n```js\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport Document, { Head, Main, NextScript } from 'next/document';\nimport flush from 'styled-jsx/server';\n\nclass MainDocument extends Document {\n  render() {\n    const { pageContext } = this.props;\n\n    return (\n      \u003chtml lang=\"en\" dir=\"ltr\"\u003e\n        \u003cHead\u003e\n          \u003ctitle\u003eGithub Client\u003c/title\u003e\n          \u003cmeta charSet=\"utf-8\" /\u003e\n          {/* Use minimum-scale=1 to enable GPU rasterization */}\n          \u003cmeta\n            name=\"viewport\"\n            content={\n              'user-scalable=0, initial-scale=1, '\n              + 'minimum-scale=1, width=device-width, height=device-height'\n            }\n          /\u003e\n          {/* PWA primary color */}\n          \u003cmeta name=\"theme-color\" content={pageContext.theme.palette.primary.main} /\u003e\n          \u003clink\n            rel=\"stylesheet\"\n            href=\"https://fonts.googleapis.com/css?family=Roboto:300,400,500\"\n          /\u003e\n        \u003c/Head\u003e\n        \u003cbody\u003e\n          \u003cMain /\u003e\n          \u003cNextScript /\u003e\n        \u003c/body\u003e\n      \u003c/html\u003e\n    );\n  }\n}\n\nMainDocument.getInitialProps = (ctx) =\u003e {\n  // Resolution order\n  //\n  // On the server:\n  // 1. app.getInitialProps\n  // 2. page.getInitialProps\n  // 3. document.getInitialProps\n  // 4. app.render\n  // 5. page.render\n  // 6. document.render\n  //\n  // On the server with error:\n  // 1. document.getInitialProps\n  // 2. app.render\n  // 3. page.render\n  // 4. document.render\n  //\n  // On the client\n  // 1. app.getInitialProps\n  // 2. page.getInitialProps\n  // 3. app.render\n  // 4. page.render\n\n  // Render app and page and get the context of the page with collected side effects.\n  let pageContext;\n\n  const page = ctx.renderPage((Component) =\u003e {\n    const WrappedComponent = (props) =\u003e {\n      ({ pageContext } = props);\n\n      return \u003cComponent {...props} /\u003e;\n    };\n\n    WrappedComponent.propTypes = {\n      pageContext: PropTypes.shape({}).isRequired,\n    };\n\n    return WrappedComponent;\n  });\n\n  return {\n    ...page,\n    pageContext,\n    // Styles fragment is rendered after the app and page rendering finish.\n    styles: (\n      \u003cReact.Fragment\u003e\n        \u003cstyle\n          id=\"jss-server-side\"\n          // eslint-disable-next-line react/no-danger\n          dangerouslySetInnerHTML={{ __html: pageContext.sheetsRegistry.toString() }}\n        /\u003e\n        {flush() || null }\n      \u003c/React.Fragment\u003e\n    ),\n  };\n};\n\nexport default MainDocument;\n```\n\nNow we're ready to implement some pages with components.\n\n## Step 6 Storybook Integration\n\nStorybook is a development environment for UI components. It allows you to browse a component library, view the different states of each component, and interactively develop and test components.\n\nHere you can find a [Startguide](https://storybook.js.org/basics/guide-react) for react.\n\nFirst of all, you need to add `@storybook/react` to your project. To do that, simply run:\n\n```bash\nyarn add @storybook/react -D\n```\n\nThen add the following script to your package json in order to start the storybook later in this guide:\n\n```json\n{\n  \"scripts\": {\n    \"storybook\": \"start-storybook -p 9001 -c .storybook\"\n  }\n}\n```\n\nAfter that, create the config file.\n\nStorybook can be configured in several different ways. That’s why we need a config directory. We’ve added a -c option to the above script mentioning .storybook as the config directory.\n\nFor the basic Storybook configuration file, you don’t need to do much, but simply tell Storybook where to find stories.\n\nFor that, simply create a file at .storybook/config.js with the following content:\n\n```js\nimport { configure } from '@storybook/react';\n\nconst req = require.context('../components', true, /stories\\.js$/);\n\nfunction loadStories() {\n  req.keys().forEach(req)\n}\n\nconfigure(loadStories, module);\n```\n\nHere we use Webpack’s require.context to load modules dynamically. You can learn a lot of interesting things about how to use require.context if you take a look at the relevant Webpack docs.\n\nSo, we need to add babel plugin to make it work.\n\n```bash\nyarn add babel-plugin-require-context-hook -D\n```\n\nUpdate babel config:\n\n`.babelrc`\n\n```js\n\"test\": {\n  \"presets\": [\"react\", \"env\", \"stage-0\"],\n  \"plugins\": [\n    \"require-context-hook\"\n  ]\n}\n```\n\nInitialize require context hook in testConfig:\n\n`lib/testConfig.js`\n\n```js\nimport { configure } from 'enzyme';\nimport Adapter from 'enzyme-adapter-react-16';\nimport registerRequireContextHook from 'babel-plugin-require-context-hook/register';\n\nregisterRequireContextHook();\n\nexport default configure({ adapter: new Adapter() });\n```\n\nAll files with .stories extension inside the src/components will be required dynamically.\n\nCreate separate babel config for storybook to avoid conflicts with different environments.\n\n`.babelrc`\n\n```js\n{\n  \"presets\": [\"env\", \"stage-0\", \"react\"]\n}\n```\n\nAdjust eslint config (so we don't see warnings when importing storybook packages):\n\n`.eslintrc.json`\n\n```js\n\"import/no-extraneous-dependencies\": [\"error\", { \"devDependencies\": true }]\n```\n\nStorybook is all about writing stories. Usually a story contains a single state of one of your components. That’s like a visual test case.\n\nTechnically, a story is a function that returns a React element.\nWe're going to use atomic design methodology for our app.\nPopularly known within the design world, Atomic Design helps to build consistent, solid and reusable design systems. Plus, in the world of React that stimulates the componentization, Atomic Design is used unconsciously; but when used in the right way, it becomes a powerful tool for developers.\n\nFirst of all create `index.js` entrypoint for components directory:\n\n`components/index.js`\n\n```js\nconst req = require.context('.', true, /\\.\\/[^/]+\\/[^/]+\\/index\\.js$/);\n\nreq.keys().forEach((key) =\u003e {\n  const componentName = key.replace(/^.+\\/([^/]+)\\/index\\.js/, '$1');\n  module.exports[componentName] = req(key).default;\n});\n```\n\nAtomic Design should be a solution, not another problem. If you want to create a component and don't know where to put it (`atoms`, `molecules`, `organisms` etc.), do not worry, do not think too much, just put it anywhere. After you realize what it is, just move the component folder to the right place. Everything else should work.\n\nThis is possible because all components are dynamically exported on `components/index.js` and imported in a way that Atomic Design structure doesn't matter:\n\nLet's create our first atom - material Button.\n\n## Step 7 - Create Atoms\n\nAtoms are the basic building blocks of matter. Applied to web interfaces, atoms are our HTML tags, such as a form label, an input or a button.\n\nAtoms can also include more abstract elements like color palettes, fonts and even more invisible aspects of an interface like animations.\n\nLike atoms in nature they’re fairly abstract and often not terribly useful on their own. However, they’re good as a reference in the context of a pattern library as you can see all your global styles laid out at a glance.\n\n#### Buttom atom\n\n`components/atoms/Button/index.js`\n\n```js\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport { Button as MaterialButton } from '@material-ui/core';\n\nconst Button = (props) =\u003e {\n  const { children, ...defaultProps } = props;\n\n  return (\n    \u003cMaterialButton {...defaultProps}\u003e\n      {children}\n    \u003c/MaterialButton\u003e\n  );\n};\n\nButton.propTypes = {\n  children: PropTypes.node.isRequired,\n};\n\nexport default Button;\n```\n\nAnd then let’s write a story for this atom.\n\n`components/atoms/Button/index.stories.js`\n\n```js\nimport React from 'react';\nimport { action } from '@storybook/addon-actions';\nimport { storiesOf } from '@storybook/react';\nimport { Button } from '../..';\n\nstoriesOf('atoms/Button', module)\n  .add('default', () =\u003e (\n    \u003cButton onClick={action('clicked')}\u003e\n      Default\n    \u003c/Button\u003e\n  ))\n  .add('outlined primary', () =\u003e (\n    \u003cButton variant=\"outlined\" color=\"primary\" onClick={action('clicked')}\u003e\n      Outline Primary\n    \u003c/Button\u003e\n  ))\n  .add('contained secondary', () =\u003e (\n    \u003cButton variant=\"contained\" color=\"secondary\" onClick={action('clicked')}\u003e\n      Contained Secondary\n    \u003c/Button\u003e\n  ))\n  .add('circle button', () =\u003e (\n    \u003cButton variant=\"fab\" color=\"primary\" aria-label=\"Add\" onClick={action('clicked')}\u003e\n      CB\n    \u003c/Button\u003e\n  ))\n  .add('disabled button', () =\u003e (\n    \u003cButton variant=\"contained\" color=\"primary\" onClick={action('clicked')} disabled\u003e\n      Disabled Button\n    \u003c/Button\u003e\n  ));\n```\n\nNow, let’s run it:\n\n```bash\nyarn storybook\n```\n\nThen we will see this:\n\n![StoryBook](public/images/storybook-initial.png \"Storybook screenshot\")\n\n## Step 8 - Add jest for testing\n\nYou need to add Jest as development dependency\n\n```bash\nyarn add jest -D\n```\n\nJest is a complete and ready to set-up JavaScript testing solution. Works out-of-the-box for any React project.\n\nAdd enzyme\n\n```bash\nyarn add enzyme enzyme-adapter-react-16 -D\n```\n\nEnzyme is a JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components' output.\n\nWe need different babel presets for test environment specifically for nextjs app.\n\n`.babelrc`\n\n```js\n{\n  \"env\": {\n    \"development\": {\n      \"presets\": [\"next/babel\"]\n    },\n    \"production\": {\n      \"presets\": [\"next/babel\"]\n    },\n    \"test\": {\n      \"presets\": [\"react\", \"env\", \"stage-0\"]\n    }\n  }\n}\n```\n\nEnzyme expects an adapter to be configured\n\n`lib/testConfig.js`\n\n```js\nimport { configure } from 'enzyme';\nimport Adapter from 'enzyme-adapter-react-16';\n\nexport default configure({ adapter: new Adapter() });\n```\n\nConnect test config:\n\n`package.json`\n\n```js\n\"jest\": {\n  \"setupTestFrameworkScriptFile\": \"./lib/testConfig.js\"\n}\n```\n\nAdd to `eslintrc.json`:\n\n```js\n\"env\": {\n  \"jest\": true\n},\n```\n\nThis will add all the jest related things to your environment, eliminating the linter errors/warnings\n\nAdd to `package.json`:\n\n```js\n{\n  \"scripts\": {\n    \"test\": \"jest\"\n  }\n}\n```\n\nIt allows us to use `yarn test` to run all jest specs.\n\nAdd simple test for our Button component:\n\n```js\nimport React from 'react';\nimport { shallow } from 'enzyme';\nimport Button from '.';\n\ndescribe('Button', () =\u003e {\n  it('renders children when passed in', () =\u003e {\n    const wrapper = shallow(\u003cButton\u003eTest\u003c/Button\u003e);\n    expect(wrapper.contains('Test')).toBe(true);\n  });\n});\n```\n\nNow if we run yarn test we should have 1 passed spec.\n\nGot it? Now, let's add more simple components (atoms) which we will use for our home page.\n\n#### AppBar atom\n\n`components/atoms/AppBar/index.js`\n\n```js\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport { AppBar as MaterialAppBar } from '@material-ui/core';\n\nconst AppBar = (props) =\u003e {\n  const { children, ...defaultProps } = props;\n\n  return (\n    \u003cMaterialAppBar {...defaultProps}\u003e\n      {children}\n    \u003c/MaterialAppBar\u003e\n  );\n};\n\nAppBar.propTypes = {\n  children: PropTypes.node.isRequired,\n};\n\nexport default AppBar;\n```\n\n`components/atoms/AppBar/index.stories.js`\n\n```js\nimport React from 'react';\nimport { storiesOf } from '@storybook/react';\nimport { AppBar } from '../..';\n\nstoriesOf('atoms/AppBar', module)\n  .add('default', () =\u003e (\n    \u003cAppBar\u003e\n      \u003cdiv\u003eExample of AppBar\u003c/div\u003e\n    \u003c/AppBar\u003e\n  ))\n  .add('secondary', () =\u003e (\n    \u003cAppBar color=\"secondary\"\u003e\n      \u003cdiv\u003eSecondary color\u003c/div\u003e\n    \u003c/AppBar\u003e\n  ));\n```\n\n`components/atoms/AppBar/index.test.js`\n\n```js\nimport React from 'react';\nimport { shallow } from 'enzyme';\nimport AppBar from '.';\n\ndescribe('AppBar', () =\u003e {\n  it('renders children when passed in', () =\u003e {\n    const wrapper = shallow(\u003cAppBar\u003e\u003cdiv\u003etest\u003c/div\u003e\u003c/AppBar\u003e);\n    expect(wrapper.contains(\u003cdiv\u003etest\u003c/div\u003e)).toBe(true);\n  });\n});\n```\n\n#### Card atom\n\n`components/atoms/Card/index.js`\n\n```js\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport { Card as MaterialCard } from '@material-ui/core';\n\nconst Card = (props) =\u003e {\n  const { children, ...defaultProps } = props;\n\n  return (\n    \u003cMaterialCard {...defaultProps}\u003e\n      {children}\n    \u003c/MaterialCard\u003e\n  );\n};\n\nCard.propTypes = {\n  children: PropTypes.node.isRequired,\n};\n\nexport default Card;\n```\n\n`components/atoms/Card/index.stories.js`\n\n```js\nimport React from 'react';\nimport { storiesOf } from '@storybook/react';\nimport { Card } from '../..';\n\nstoriesOf('atoms/Card', module)\n  .add('default', () =\u003e (\n    \u003cCard\u003e\n      Default\n    \u003c/Card\u003e\n  ));\n```\n\n`components/atoms/Card/index.test.js`\n\n```js\nimport React from 'react';\nimport { shallow } from 'enzyme';\nimport Card from '.';\n\ndescribe('Card', () =\u003e {\n  it('renders children when passed in', () =\u003e {\n    const wrapper = shallow(\n      \u003cCard\u003e\n        \u003cp\u003eSome text\u003c/p\u003e\n        \u003cp\u003eTest\u003c/p\u003e\n      \u003c/Card\u003e,\n    );\n    expect(wrapper.contains('Test')).toBe(true);\n    expect(wrapper.contains('Some text')).toBe(true);\n  });\n});\n```\n\n#### CardActions atom\n\n`components/atoms/CardActions/index.js`\n\n```js\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport { CardActions as MaterialCardActions } from '@material-ui/core';\n\nconst CardActions = (props) =\u003e {\n  const { children, ...defaultProps } = props;\n\n  return (\n    \u003cMaterialCardActions {...defaultProps}\u003e\n      {children}\n    \u003c/MaterialCardActions\u003e\n  );\n};\n\nCardActions.propTypes = {\n  children: PropTypes.node.isRequired,\n};\n\nexport default CardActions;\n```\n\n`components/atoms/CardActions/index.stories.js`\n\n```js\nimport React from 'react';\nimport { storiesOf } from '@storybook/react';\nimport { CardActions, Button } from '../..';\n\nstoriesOftoms/CardActions', module)\n  .add('with button', () =\u003e (\n    \u003cCardActions\u003e\n      \u003cButton\u003eButton\u003c/Button\u003e\n    \u003c/CardActions\u003e\n  ));\n```\n\n`components/atoms/CardActions/index.test.js`\n\n```js\nimport React from 'react';\nimport { shallow } from 'enzyme';\nimport CardActions from '.';\n\ndescribe('CardActions', () =\u003e {\n  it('renders children when passed in', () =\u003e {\n    const wrapper = shallow(\n      \u003cCardActions\u003e\n        \u003cp\u003eSome text\u003c/p\u003e\n        \u003cp\u003eTest\u003c/p\u003e\n      \u003c/CardActions\u003e,\n    );\n    expect(wrapper.contains('Test')).toBe(true);\n    expect(wrapper.contains('Some text')).toBe(true);\n  });\n});\n```\n\n#### CardContent atom\n\n`components/atoms/CardContent/index.js`\n\n```js\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport { CardContent as MaterialCardContent } from '@material-ui/core';\n\nconst CardContent = (props) =\u003e {\n  const { children, ...defaultProps } = props;\n\n  return (\n    \u003cMaterialCardContent {...defaultProps}\u003e\n      {children}\n    \u003c/MaterialCardContent\u003e\n  );\n};\n\nCardContent.propTypes = {\n  children: PropTypes.node.isRequired,\n};\n\nexport default CardContent;\n```\n\n`components/atoms/CardContent/index.stories.js`\n\n```js\nimport React from 'react';\nimport { storiesOf } from '@storybook/react';\nimport { CardContent } from '../..';\n\nstoriesOfardContent', module)\n  .add('default', () =\u003e (\n    \u003cCardContent\u003e\n      \u003cp\u003eLorem\u003c/p\u003e\n      \u003cp\u003eLorem Ipsum\u003c/p\u003e\n    \u003c/CardContent\u003e\n  ));\n```\n\n`components/atoms/CardContent/index.test.js`\n\n```js\nimport React from 'react';\nimport { shallow } from 'enzyme';\nimport CardContent from '.';\n\ndescribe('CardContent', () =\u003e {\n  it('renders children when passed in', () =\u003e {\n    const wrapper = shallow(\n      \u003cCardContent\u003e\n        \u003cp\u003eLorem\u003c/p\u003e\n        \u003cp\u003eIpsum\u003c/p\u003e\n      \u003c/CardContent\u003e,\n    );\n    expect(wrapper.contains('Lorem')).toBe(true);\n    expect(wrapper.contains('Ipsum')).toBe(true);\n  });\n});\n```\n\nWe're gonna create few more atoms using same approach:\n\n- IconButton\n- List\n- ListItem\n- ListItemIcon\n- ListItemText\n- Loader\n- MenuIcon\n- SwipeableDrawer\n- Toolbar\n- Typography\n- Grid\n\n## Step 9 - Creating Moleculus\n\nThings are getting more interesting and tangible when we start combining atoms together. Molecules are groups of atoms bonded together and are the smallest fundamental units of a compound (just like in real world). These molecules take on their own properties and serve as the backbone of our design systems.\n\nFor example, a form label, input or button aren’t too useful by themselves, but combine them together as a form and now they can actually do something together.\n\nBuilding up to molecules from atoms encourages a “do one thing and do it well” mentality. While molecules can be complex, as a rule of thumb they are relatively simple combinations of atoms built for reuse.\n\nNow we need to create first molecule component. It will be SimpleCard that consists of atoms.\n\n#### SimpleCard Molecule\n\n`components/moleculus/SimpleCard/index.js`\n\n```js\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport { withStyles } from '@material-ui/core/styles';\n\nimport {\n  Card, CardContent, Typography, CardActions, Button,\n} from 'components';\n\nconst styles = {\n  card: {\n    minWidth: 100,\n  },\n  bullet: {\n    display: 'inline-block',\n    margin: '0 2px',\n    transform: 'scale(0.8)',\n  },\n  title: {\n    marginBottom: 16,\n    fontSize: 14,\n  },\n  pos: {\n    marginBottom: 12,\n  },\n};\n\nconst SimpleCard = (props) =\u003e {\n  const {\n    classes, title, description, url,\n  } = props;\n\n  return (\n    \u003cCard className={classes.card}\u003e\n      \u003cCardContent\u003e\n        \u003cTypography variant=\"headline\" component=\"h3\"\u003e\n          {title}\n        \u003c/Typography\u003e\n        \u003cTypography className={classes.pos} color=\"textSecondary\"\u003e\n          {description}\n        \u003c/Typography\u003e\n      \u003c/CardContent\u003e\n      \u003cCardActions\u003e\n        \u003cButton target=\"_blank\" href={url} size=\"small\"\u003eLearn More\u003c/Button\u003e\n      \u003c/CardActions\u003e\n    \u003c/Card\u003e\n  );\n};\n\nSimpleCard.propTypes = {\n  classes: PropTypes.shape({}).isRequired,\n  title: PropTypes.string.isRequired,\n  description: PropTypes.string,\n  url: PropTypes.string,\n};\n\nSimpleCard.defaultProps = {\n  description: 'No description',\n  url: null,\n};\n\nexport default withStyles(styles)(SimpleCard);\n```\n\n`components/moleculus/SimpleCard/index.stories.js`\n\n```js\nimport React from 'react';\nimport { storiesOf } from '@storybook/react';\nimport { SimpleCard } from '../..';\n\nstoriesOf('moleculus/SimpleCard', module)\n  .add('default', () =\u003e (\n    \u003cSimpleCard title=\"Default title\" description=\"Default description\" /\u003e\n  ));\n```\n\n`components/moleculus/SimpleCard/index.test.js`\n\n```js\nimport React from 'react';\nimport { mount } from 'enzyme';\nimport { SimpleCard, Typography } from '../..';\n\ndescribe('SimpleCard', () =\u003e {\n  it('renders header with correct title', () =\u003e {\n    const wrapper = mount(\u003cSimpleCard title=\"foo\" description=\"bar\" /\u003e);\n    const typographyNodes = wrapper.find(Typography);\n    expect(typographyNodes.first().text()).toEqual('foo');\n    expect(typographyNodes.last().text()).toEqual('bar');\n  });\n});\n```\n\n#### Create Header molecule\n\n`components/moleculus/Header/index.js`\n\n```js\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport { withStyles } from '@material-ui/core/styles';\n\nimport {\n  AppBar, IconButton,\n  MenuIcon, Toolbar, Typography,\n} from '../..';\n\nconst styles = {\n  root: {\n    flexGrow: 1,\n  },\n  flex: {\n    flexGrow: 1,\n  },\n  menuButton: {\n    marginLeft: -12,\n    marginRight: 20,\n  },\n};\n\nconst Header = (props) =\u003e {\n  const {\n    classes, swipeableMenu, loginButton, title, openMenu,\n  } = props;\n\n  return (\n    \u003cdiv className={classes.root}\u003e\n      \u003cAppBar position=\"static\"\u003e\n        {swipeableMenu}\n        \u003cToolbar\u003e\n          \u003cIconButton onClick={openMenu} className={classes.menuButton} color=\"inherit\" aria-label=\"Menu\"\u003e\n            \u003cMenuIcon /\u003e\n          \u003c/IconButton\u003e\n          \u003cTypography variant=\"title\" color=\"inherit\" className={classes.flex}\u003e\n            {title}\n          \u003c/Typography\u003e\n          {loginButton}\n        \u003c/Toolbar\u003e\n      \u003c/AppBar\u003e\n    \u003c/div\u003e\n  );\n};\n\nHeader.propTypes = {\n  swipeableMenu: PropTypes.node,\n  loginButton: PropTypes.node,\n  classes: PropTypes.shape().isRequired,\n  title: PropTypes.string,\n  openMenu: PropTypes.func,\n};\n\nHeader.defaultProps = {\n  swipeableMenu: null,\n  loginButton: null,\n  title: null,\n  openMenu: null,\n};\n\nexport default withStyles(styles)(Header);\n```\n\n`components/moleculus/Header/index.stories.js`\n\n```js\nimport React from 'react';\nimport { storiesOf } from '@storybook/react';\nimport { Header } from '../..';\n\nstoriesOf('moleculus/Header', module)\n  .add('default', () =\u003e (\n    \u003cHeader /\u003e\n  ))\n  .add('with title', () =\u003e (\n    \u003cHeader title=\"Home\" /\u003e\n  ));\n```\n\n`components/moleculus/Header/index.test.js`\n\n```js\nimport React from 'react';\nimport { mount } from 'enzyme';\nimport { Header, Typography } from '../..';\n\ndescribe('Header', () =\u003e {\n  it('renders header with correct title', () =\u003e {\n    const wrapper = mount(\u003cHeader title=\"foo\" /\u003e);\n    const typographyNode = wrapper.find(Typography);\n\n    expect(typographyNode.text()).toEqual('foo');\n  });\n});\n```\n\n## Step 10 - Build Organisms\n\nOrganisms are groups of molecules joined together to form a relatively complex, distinct section of an interface.\n\nWe’re starting to get increasingly concrete. A client might not be terribly interested in the molecules of a design system, but with organisms we can see the final interface beginning to take shape.\n\nOrganisms can consist of similar and/or different molecule types. For example, a masthead organism might consist of diverse components like a logo, primary navigation, search form, and list of social media channels. But a “product grid” organism might consist of the same molecule (possibly containing a product image, product title and price) repeated over and over again.\n\nBuilding up from molecules to organisms encourages creating standalone, portable, reusable components.\n\n#### Let’s create a Header with swipeable menu organism:\n\n`components/organisms/HeaderWithSwipeableMenu/index.js`\n\n```js\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport { Header, SwipeableMenu } from 'components';\n\nconst HeaderWithSwipeableMenu = (props) =\u003e {\n  const {\n    closeMenu, openMenu, loginButtonContainer, leftMenuIsOpened,\n  } = props;\n\n  const MENU_ITEMS = [\n    {\n      id: 1,\n      url: '/',\n      text: 'Home',\n    },\n    {\n      id: 2,\n      url: '/top_ruby',\n      text: 'Top Ruby Repositories',\n    },\n    {\n      id: 3,\n      url: '/top_js',\n      text: 'Top Javascript Repositories',\n    },\n    {\n      id: 4,\n      url: '/new_js',\n      text: 'New Javascript Repositories',\n    },\n  ];\n\n  return (\n    \u003cHeader\n      openMenu={openMenu}\n      title=\"Home\"\n      swipeableMenu={(\n        \u003cSwipeableMenu\n          isOpenedByDefault={leftMenuIsOpened}\n          closeMenu={closeMenu}\n          openMenu={openMenu}\n          menuItems={MENU_ITEMS}\n        /\u003e\n      )}\n      loginButton={loginButtonContainer}\n    /\u003e\n  );\n};\n\nHeaderWithSwipeableMenu.propTypes = {\n  leftMenuIsOpened: PropTypes.bool.isRequired,\n  openMenu: PropTypes.func.isRequired,\n  closeMenu: PropTypes.func.isRequired,\n  loginButtonContainer: PropTypes.node.isRequired,\n};\n\nexport default HeaderWithSwipeableMenu;\n```\n\n`components/organisms/HeaderWithSwipeableMenu/index.stories.js`\n\n```js\nimport React from 'react';\nimport { storiesOf } from '@storybook/react';\nimport { HeaderWithSwipeableMenu } from 'components';\nimport { action } from '@storybook/addon-actions';\n\nstoriesOf('organisms/HeaderWithSwipeableMenu', module)\n  .add('default with click event', () =\u003e (\n    \u003cHeaderWithSwipeableMenu openMenu={action('open')} /\u003e\n  ))\n  .add('opened by default', () =\u003e (\n    \u003cHeaderWithSwipeableMenu leftMenuIsOpened /\u003e\n  ));\n```\n\n`components/organisms/HeaderWithSwipeableMenu/index.test.js`\n\n```js\nimport React from 'react';\nimport { mount } from 'enzyme';\nimport { HeaderWithSwipeableMenu } from 'components';\n\ndescribe('HeaderWithSwipeableMenu', () =\u003e {\n  it('renders header with swipeable menu', () =\u003e {\n    const mockedOpenMenu = jest.fn();\n    const mockedCloseMenu = jest.fn();\n\n    const wrapper = mount(\n      \u003cHeaderWithSwipeableMenu\n        leftMenuIsOpened\n        openMenu={mockedOpenMenu}\n        closeMenu={mockedCloseMenu}\n        loginButtonContainer={\u003cReact.Fragment /\u003e}\n      /\u003e,\n    );\n\n    const expectedMenuItems = [\n      'Top Javascript Repositories',\n      'Home',\n      'Top Ruby Repositories',\n      'New Javascript Repositories',\n    ];\n\n    wrapper.find('ListItemText').find('Typography').forEach((node) =\u003e {\n      expect(expectedMenuItems).toContain(node.text());\n    });\n\n    wrapper.find('button').simulate('click');\n    expect(mockedOpenMenu).toHaveBeenCalled();\n  });\n});\n```\n\n## Step 11 - Creating Templates\n\nTemplates are page-level objects that place components into a layout and articulate the design’s underlying content structure. To build on our previous example, we can take the HeaderWithMenu organism and apply it to a home template.\n\nThis Home template displays all the necessary page components functioning together, which provides context for these relatively abstract molecules and organisms. When crafting an effective design system, it’s critical to demonstrate how components look and function together in the context of a layout to prove the parts add up to a well-functioning whole.\n\nAnother important characteristic of templates is that they focus on the page’s underlying content structure rather than the page’s final content. Design systems must account for the dynamic nature of content, so it’s very helpful to articulate important properties of components like image sizes and character lengths for headings and text passages.\n\n#### Let’s build Home template:\n\n`components/templates/Home/index.js`\n\n```js\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport { Grid } from '@material-ui/core';\n\nconst Home = (props) =\u003e {\n  const { header, footer, content } = props;\n\n  return (\n    \u003cdiv\u003e\n      {header}\n      \u003cdiv style={{ padding: 12 }}\u003e\n        \u003cGrid container spacing={24} style={{ padding: 24 }}\u003e\n          {content}\n        \u003c/Grid\u003e\n      \u003c/div\u003e\n      {footer}\n    \u003c/div\u003e\n  );\n};\n\nHome.propTypes = {\n  header: PropTypes.node,\n  content: PropTypes.node,\n  footer: PropTypes.string,\n};\n\nHome.defaultProps = {\n  header: null,\n  content: null,\n  footer: null,\n};\n\nexport default (Home);\n```\n\n`components/templates/Home/index.stories.js`\n\n```js\nimport React from 'react';\nimport { storiesOf } from '@storybook/react';\nimport { Home } from '../..';\n\nstoriesOf('templates/Home', module)\n  .add('default', () =\u003e (\n    \u003cHome\n      cards={[\n        { title: 'foo', description: 'bar' },\n        { title: 'baz', description: 'craz' },\n        { title: 'saz', description: 'taz' },\n      ]}\n    /\u003e\n  ));\n```\n\n`components/templates/Home/index.test.js`\n\n```js\nimport React from 'react';\nimport { mount } from 'enzyme';\nimport { Home, SimpleCard } from '../..';\n\ndescribe('Home', () =\u003e {\n  it('renders component with passed card components', () =\u003e {\n    const wrapper = mount(\n      \u003cHome\n        content={[\n          \u003cSimpleCard description=\"desc\" title=\"title1\" key={1} /\u003e,\n          \u003cSimpleCard description=\"desc\" title=\"title2\" key={2} /\u003e,\n        ]}\n      /\u003e,\n    );\n\n    expect(wrapper.find(SimpleCard)).toHaveLength(2);\n  });\n});\n```\n\n## Step 12 - Create Pages\n\nNow we can use nextjs pages as an entry point\n\n`pages/index.js`\n\n```js\nimport React from 'react';\nimport { Home } from 'components';\nimport HeaderContainer from 'containers/HeaderContainer';\nimport ViewerRepoList from 'containers/ViewerRepoList';\n\nconst Index = () =\u003e (\n  \u003cHome\n    header={\u003cHeaderContainer /\u003e}\n    content={\u003cViewerRepoList /\u003e}\n  /\u003e\n);\n\nexport default Index;\n```\n\nThe file-system is the main API. Every .js file becomes a route that gets automatically processed and rendered. If we run yarn dev we can access this page on localhost:3000\n\n## Step 13 - Implementing Authentication\n\nTo communicate with github graphql api we need to create a github application first. Follow these steps to create your github app https://developer.github.com/apps/building-github-apps/creating-a-github-app/\n\nFor development environment Homepage URL - http://localhost:3000, Authorization callback URL - http://localhost:3000/auth/github/callback\n\nTo use github secret keys we will use dotenv package.\n\ncreate `next.config.js` with following commands:\n\n`next.config.js`\n\n```js\nrequire('dotenv').config();\n\nconst path = require('path');\nconst Dotenv = require('dotenv-webpack');\n\nmodule.exports = {\n  webpack: (config) =\u003e {\n    config.plugins = config.plugins || [];\n\n    config.plugins = [\n      ...config.plugins,\n\n      // Read the .env file\n      new Dotenv({\n        path: path.join(__dirname, '.env'),\n        systemvars: true,\n      }),\n    ];\n\n    return config;\n  },\n};\n```\n\nNow, you need to add github app secret keys. We can use `.env` file for storing secret API keys, that  will be available only on server side.\n\n`.env`\n\n```js\nGITHUB_CLIENT_ID = '\u003cCLIENT\u003e'\nGITHUB_CLIENT_SECRET = '\u003cSECRET\u003e'\n```\n\nAdd this file to `.gitignore`\n\n#### Containers\n\nIf we need to implement some component with it's own state management or side effects (in other words smart component) we place it in `containers` folder. All components with graphql/REST requests will be there.\n\nWe can use graphql queries in our containers using `react-apollo` Query component.\n\n#### Github Login Button Container\n\nWe need login button container to make request to github authentication endpoint and redirect to callback page.\n\n`containers/GithubLoginButtonContainer/index.js`\n\n```js\nimport React from 'react';\nimport Router from 'next/router';\nimport { Query } from 'react-apollo';\nimport { Button, Loader } from 'components';\nimport Cookies from 'js-cookie';\nimport viewer from 'graphql/queries/viewer';\n\nclass GithubLoginButtonContainer extends React.Component {\n  handleSignIn = () =\u003e {\n    Router.push({\n      pathname: 'https://github.com/login/oauth/authorize',\n      query: {\n        client_id: '55a16b6d3467b24fdde9',\n      },\n    });\n  };\n\n  handleSignOut = () =\u003e {\n    Cookies.remove('access_token');\n    Router.push('/');\n  };\n\n  render() {\n    const { handleSignIn, handleSignOut } = this;\n\n    return (\n      \u003cQuery query={viewer}\u003e\n        {({ loading, error, data }) =\u003e {\n          if (loading) {\n            return (\n              \u003cLoader color=\"secondary\" /\u003e\n            );\n          }\n          if (error) {\n            return (\n              \u003cButton color=\"secondary\" onClick={handleSignIn}\u003e\n                Sign In\n              \u003c/Button\u003e\n            );\n          }\n          return (\n            \u003cReact.Fragment\u003e\n              {data.viewer.login}\n              \u003cButton color=\"secondary\" onClick={handleSignOut}\u003e\n                Sign Out\n              \u003c/Button\u003e\n            \u003c/React.Fragment\u003e\n          );\n        }}\n      \u003c/Query\u003e\n    );\n  }\n}\n\nexport default GithubLoginButtonContainer;\n```\n\nNext, create tests for our container\n\n`containers/GithubLoginButtonContainer/index.test.js`\n\n```js\nimport React from 'react';\nimport { shallow } from 'enzyme';\nimport GithubLoginButton from 'containers/GithubLoginButton';\n\njest.mock('next/config', () =\u003e () =\u003e ({ publicRuntimeConfig: { GithubClientId: '123' } }));\n\njest.mock('next/router', () =\u003e (\n  { push: () =\u003e ({}) }\n));\n\ndescribe('GithubLoginButton', () =\u003e {\n  it('renders children when passed in', () =\u003e {\n    const wrapper = shallow(\u003cGithubLoginButton\u003eTest\u003c/GithubLoginButton\u003e);\n    expect(wrapper.contains('Test')).toBe(true);\n  });\n\n  it('calls SignIn handler', () =\u003e {\n    const wrapper = shallow(\u003cGithubLoginButton\u003eTest\u003c/GithubLoginButton\u003e);\n    const instance = wrapper.instance();\n    jest.spyOn(instance, 'handleSignIn');\n    instance.forceUpdate();\n    wrapper.find('Button').simulate('click');\n    expect(instance.handleSignIn).toHaveBeenCalled();\n  });\n});\n```\n\n#### Callback page\n\nGitHub redirects to a callback url on your website (which you provided when registering the app with GitHub).\n\nWe need to implement callback page which will be used to obtain `access_token`.\n\nInstall `isomorphic-unfetch` (Tiny 500b `fetch` \"barely-polyfill\"). We will use this package for authentication REST requests only.\n\n```bash\nyarn add isomorphic-unfetch\n```\n\nWe need to store `access_token` on a client side using cookies. A simple, lightweight JavaScript API for handling cookies.\n\n```bash\nyarn add js-cookie\n```\n\n`pages/auth/github/login.js`\n\n```js\nimport React from 'react';\nimport Router, { withRouter } from 'next/router';\nimport fetch from 'isomorphic-unfetch';\nimport Cookies from 'js-cookie';\nimport PropTypes from 'prop-types';\n\nclass Callback extends React.Component {\n  static propTypes = {\n    errorMessage: PropTypes.string,\n    accessToken: PropTypes.string,\n  };\n\n  static defaultProps = {\n    errorMessage: undefined,\n    accessToken: undefined,\n  };\n\n  componentDidMount() {\n    const { accessToken } = this.props;\n\n    if (accessToken) {\n      Cookies.set('access_token', accessToken);\n      Router.push('/');\n    }\n  }\n\n  static async getInitialProps({ query }) {\n    const bodyData = JSON.stringify({\n      client_id: process.env.GITHUB_CLIENT_ID,\n      client_secret: process.env.GITHUB_CLIENT_SECRET,\n      code: query.code,\n    });\n\n    const res = await fetch('https://github.com/login/oauth/access_token', {\n      headers: {\n        Accept: 'application/json',\n        'Content-Type': 'application/json',\n      },\n      method: 'POST',\n      body: bodyData,\n    });\n\n    const json = await res.json();\n    const errorMessage = json.error_description;\n    return { errorMessage, accessToken: json.access_token };\n  }\n\n  render() {\n    const { errorMessage } = this.props;\n\n    if (errorMessage) {\n      return (\n        \u003cp\u003e{errorMessage}\u003c/p\u003e\n      );\n    }\n\n    return null;\n  }\n}\n\nexport default withRouter(Callback);\n```\n\n`getInitialProps` is an async static method. It can asynchronously fetch anything that resolves to a JavaScript plain Object, which populates props.\n\nData returned from `getInitialProps` is serialized when server rendering, similar to a `JSON.stringify`. Make sure the returned object from `getInitialProps` is a plain Object and not using `Date`, `Map` or `Set`.\n\nFor the initial page load, `getInitialProps` will execute on the server only. `getInitialProps` will only be executed on the client when navigating to a different route via the Link component or using the routing APIs.\n\n## Step 14 - GraphQL with Apollo integration\n\nApollo Client is the best way to use GraphQL to build client applications. The client is designed to help you quickly build a UI that fetches data with GraphQL, and can be used with any JavaScript front-end.\n\nWith Apollo’s declarative approach to data fetching, all of the logic for retrieving your data, tracking loading and error states, and updating your UI is encapsulated in a single Query component. This encapsulation makes composing your Query components with your presentational components very easy.\n\nLet’s see how it looks like in practice with React Apollo.\n\nThe easiest way to get started with Apollo Client is by using Apollo Boost. It's a starter kit that configures your client for you with recommended settings.\n\nInstall packages:\n\n```bash\nyarn add apollo-boost react-apollo graphql graphql-tag\n```\n\n- `apollo-boost` Package containing everything you need to set up Apollo Client\n- `react-apollo` View layer integration for React\n- `graphql` Also parses your GraphQL queries\n- `graphql-tag` Provides template literal tag you can use to concisely write a GraphQL query that is parsed into the standard GraphQL AST\n\nNow you have all the dependencies you need, let’s create your Apollo Client. The only thing you need to get started is the endpoint for your GraphQL server.\n\nIf you don’t pass in uri directly, it defaults to the /graphql endpoint on the same host your app is served from.\n\n\n#### GraphQL authentication\n\nApollo Client uses the ultra flexible Apollo Link that includes several options for authentication.\n\nWe will use cookies for storing github access_token and send it as an authorization header. It’s easy to add an authorization header to every HTTP request by adding `headers` to ApolloClient.\n\nIt’s very easy to tell your network interface to send the cookie along with every request. You just need to pass the headers option. e.g. `headers: 'token'`\n\nIn this example, we’ll pull the login token from cookies every time a request is sent:\n\n`pages/_app.js`\n\n```js\nconst token = Cookies.get('access_token');\n\nconst client = new ApolloClient({\n  uri: 'https://api.github.com/graphql',\n  headers: { authorization: `Bearer ${token}` },\n});\n\nclass MainApp extends App {\n  // ...\n  render() {\n    // ...\n    const token = Cookies.get('access_token');\n\n    const client = new ApolloClient({\n      uri: 'https://api.github.com/graphql',\n      headers: { authorization: `Bearer ${token}` },\n    });\n\n    return (\n      \u003cContainer\u003e\n        \u003cApolloProvider client={client}\u003e\n          {/* ... */}\n        \u003c/ApolloProvider\u003e\n      \u003c/Container\u003e\n    );\n  }\n}\n```\n\nAfter that we can make requests to Github API using our token from cookies.\n\n#### GraphQL queries\n\nLet's implement our first graphql query\n\nThis query finds last 50 repositories with more than 10000 stars\n\n`queries/searchTopRubyRepos.js`\n\n```js\nimport gql from 'graphql-tag';\n\nconst searchTopRubyRepos = gql`\n{\n  search(query: \"language:Ruby stars:\u003e10000\", first: 50, type: REPOSITORY) {\n    edges {\n      node {\n        ... on Repository {\n          name\n          description\n          url\n        }\n      }\n    }\n  }\n}\n`;\n\nexport default searchTopRubyRepos;\n```\n\n`containers/SearchRepoList/index.js`\n\n```js\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport {\n  SimpleCard, Loader, Grid, Typography,\n} from 'components';\nimport { Query } from 'react-apollo';\n\nconst SearchRepoList = ({ query }) =\u003e (\n  \u003cQuery query={query}\u003e\n    {({ loading, error, data }) =\u003e {\n      if (loading) {\n        return (\n          \u003cGrid direction=\"row\" justify=\"center\" container spacing={24} style={{ padding: 24 }}\u003e\n            \u003cLoader size={300} /\u003e\n          \u003c/Grid\u003e\n        );\n      }\n      if (error) {\n        return (\n          \u003cGrid direction=\"row\" justify=\"center\" container spacing={24} style={{ padding: 24 }}\u003e\n            \u003cTypography variant=\"headline\"\u003ePlease Sign In to fetch data\u003c/Typography\u003e\n          \u003c/Grid\u003e\n        );\n      }\n      return (\n        \u003cReact.Fragment\u003e\n          {data.search.edges.map(repo =\u003e (\n            \u003cGrid key={repo.node.id} item xs={6} sm={4} lg={3} xl={2}\u003e\n              \u003cSimpleCard\n                title={repo.node.name}\n                description={repo.node.description}\n                url={repo.node.url}\n              /\u003e\n            \u003c/Grid\u003e\n          ))}\n        \u003c/React.Fragment\u003e\n      );\n    }}\n  \u003c/Query\u003e\n);\n\nSearchRepoList.propTypes = {\n  query: PropTypes.node.isRequired,\n};\n\nexport default SearchRepoList;\n```\n\nNow let's implement nextjs page\n\n```js\nimport React from 'react';\nimport { Home } from 'components';\nimport HeaderWithMenu from 'containers/HeaderWithMenu';\nimport SearchRepoList from 'containers/SearchRepoList';\nimport searchTopRubyRepos from 'graphql/queries/searchTopRubyRepos';\n\nconst TopRuby = () =\u003e (\n  \u003cHome\n    header={\u003cHeaderWithMenu /\u003e}\n    content={\u003cSearchRepoList query={searchTopRubyRepos} /\u003e}\n  /\u003e\n);\n\nexport default TopRuby;\n```\n\n## Step 15 - Creating Snapshot tests\n\nSnapshot testing is a very useful tool whenever you want to make sure your UI doesn’t change unexpectedly.\n\nA typical snapshot test case for a mobile app renders a UI component, takes a screenshot, then compares it to a reference image stored alongside the test. The test will fail if the two images don’t match: either the change is unexpected, or the screenshot needs to be updated to the new version of the UI component.\n\nTo add react-test-render, write this command\n\n```bash\nyarn add react-test-renderer -D\n```\n\nThis package provides a React renderer that can be used to render React components to pure JavaScript objects, without depending on the DOM or a native mobile environment.\n\n`__tests__/index.test.js`\n\n```js\nimport React from 'react';\nimport renderer from 'react-test-renderer';\nimport Index from 'pages';\nimport { mount } from 'enzyme';\nimport viewerLast100Repositories from 'graphql/queries/viewerLast100Repositories';\nimport { MockedProvider } from 'react-apollo/test-utils';\n\ndescribe('Home Page', () =\u003e {\n  it('renders loading state initially', () =\u003e {\n    const component = renderer.create(\n      \u003cMockedProvider mocks={[]} addTypename={false}\u003e\n        \u003cIndex /\u003e\n      \u003c/MockedProvider\u003e,\n    );\n\n    const tree = component.toJSON();\n    expect(tree).toMatchSnapshot();\n  });\n\n  it('renders cards with all information on success', async () =\u003e {\n    const mocks = [\n      {\n        request: {\n          query: viewerLast100Repositories,\n        },\n        result: {\n          data: {\n            viewer: {\n              repositories: {\n                edges: [\n                  { node: { id: '1', name: 'repo name', description: 'desc' } },\n                ],\n              },\n            },\n          },\n        },\n      },\n    ];\n\n    const component = renderer.create(\n      \u003cMockedProvider mocks={mocks} addTypename={false}\u003e\n        \u003cIndex /\u003e\n      \u003c/MockedProvider\u003e,\n    );\n    await new Promise(resolve =\u003e setTimeout(resolve));\n\n    const tree = component.toJSON();\n    expect(tree).toMatchSnapshot();\n  });\n\n  it('renders correct message on error', async () =\u003e {\n    const mock = {\n      request: {\n        query: viewerLast100Repositories,\n      },\n      error: new Error('error'),\n    };\n\n    const component = renderer.create(\n      \u003cMockedProvider mocks={[mock]} addTypename={false}\u003e\n        \u003cIndex /\u003e\n      \u003c/MockedProvider\u003e,\n    );\n    await new Promise(resolve =\u003e setTimeout(resolve));\n\n    const tree = component.toJSON();\n    expect(tree).toMatchSnapshot();\n  });\n});\n```\n\n`__tests__/auth/github/callback.test.js`\n\n```js\nimport React from 'react';\nimport renderer from 'react-test-renderer';\nimport Router from 'next/router';\nimport Callback from '../../../pages/auth/github/callback';\n\ndescribe('Github Callback Page', () =\u003e {\n\n  it('matches snapshot', () =\u003e {\n    const mockedRouter = { push: () =\u003e {} };\n    Router.router = mockedRouter;\n    const component = renderer.create(\u003cCallback accessToken=\"sag\" /\u003e);\n    const tree = component.toJSON();\n    expect(tree).toMatchSnapshot();\n  });\n});\n```\n\n## Demo App\n\nhttps://next-github.herokuapp.com/\n\n## Source\n\nhttps://github.com/leksster/nextjs6-graphql-client-tutorial","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frubygarage%2Fnextjs6-graphql-client-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frubygarage%2Fnextjs6-graphql-client-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frubygarage%2Fnextjs6-graphql-client-tutorial/lists"}