├── .editorconfig ├── .gitignore ├── .prettierrc ├── 00-start ├── .env.example ├── .gitignore ├── .prettierrc ├── README.md ├── global.types.d.ts ├── package-lock.json ├── package.json ├── src │ ├── common-app │ │ └── mock-posts │ │ │ ├── first-post.md │ │ │ └── second-post.md │ ├── common │ │ └── components │ │ │ ├── app-bar │ │ │ ├── app-bar.business.ts │ │ │ ├── app-bar.component.tsx │ │ │ ├── app-bar.styles.ts │ │ │ └── index.ts │ │ │ ├── footer │ │ │ ├── footer.component.tsx │ │ │ ├── footer.styles.ts │ │ │ └── index.ts │ │ │ └── index.ts │ ├── core │ │ ├── images │ │ │ ├── favicon.png │ │ │ └── home-logo.png │ │ ├── routes.ts │ │ └── theme │ │ │ ├── index.ts │ │ │ ├── theme-provider.component.tsx │ │ │ └── theme.ts │ ├── layouts │ │ ├── app-layout.styles.ts │ │ ├── app-layout.tsx │ │ └── index.ts │ └── pods │ │ ├── blog │ │ ├── blog.component.tsx │ │ ├── blog.styles.ts │ │ └── index.ts │ │ └── home │ │ ├── home.component.tsx │ │ ├── home.styles.ts │ │ └── index.ts └── tsconfig.json ├── 01-hello-gatsby ├── .babelrc ├── .env.example ├── .gitignore ├── .prettierrc ├── README.md ├── gatsby-config.js ├── global.types.d.ts ├── package-lock.json ├── package.json ├── src │ ├── common-app │ │ └── mock-posts │ │ │ ├── first-post.md │ │ │ └── second-post.md │ ├── common │ │ └── components │ │ │ ├── app-bar │ │ │ ├── app-bar.business.ts │ │ │ ├── app-bar.component.tsx │ │ │ ├── app-bar.styles.ts │ │ │ └── index.ts │ │ │ ├── footer │ │ │ ├── footer.component.tsx │ │ │ ├── footer.styles.ts │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── seo.component.tsx │ ├── core │ │ ├── images │ │ │ ├── favicon.png │ │ │ └── home-logo.png │ │ ├── routes.ts │ │ └── theme │ │ │ ├── index.ts │ │ │ ├── theme-provider.component.tsx │ │ │ └── theme.ts │ ├── layouts │ │ ├── app-layout.styles.ts │ │ ├── app-layout.tsx │ │ └── index.ts │ ├── pages │ │ └── index.tsx │ └── pods │ │ ├── blog │ │ ├── blog.component.tsx │ │ ├── blog.styles.ts │ │ └── index.ts │ │ └── home │ │ ├── home.component.tsx │ │ ├── home.styles.ts │ │ └── index.ts └── tsconfig.json ├── 02-navigation ├── .babelrc ├── .env.example ├── .gitignore ├── .prettierrc ├── README.md ├── gatsby-config.js ├── global.types.d.ts ├── package-lock.json ├── package.json ├── src │ ├── common-app │ │ └── mock-posts │ │ │ ├── first-post.md │ │ │ └── second-post.md │ ├── common │ │ └── components │ │ │ ├── app-bar │ │ │ ├── app-bar.business.ts │ │ │ ├── app-bar.component.tsx │ │ │ ├── app-bar.styles.ts │ │ │ └── index.ts │ │ │ ├── footer │ │ │ ├── footer.component.tsx │ │ │ ├── footer.styles.ts │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── seo.component.tsx │ ├── core │ │ ├── images │ │ │ ├── favicon.png │ │ │ └── home-logo.png │ │ ├── routes.ts │ │ └── theme │ │ │ ├── index.ts │ │ │ ├── theme-provider.component.tsx │ │ │ └── theme.ts │ ├── layouts │ │ ├── app-layout.styles.ts │ │ ├── app-layout.tsx │ │ └── index.ts │ ├── pages │ │ ├── blog.tsx │ │ └── index.tsx │ └── pods │ │ ├── blog │ │ ├── blog.component.tsx │ │ ├── blog.styles.ts │ │ └── index.ts │ │ └── home │ │ ├── home.component.tsx │ │ ├── home.styles.ts │ │ └── index.ts └── tsconfig.json ├── 03-handling-images ├── .babelrc ├── .env.example ├── .gitignore ├── .prettierrc ├── README.md ├── gatsby-config.js ├── global.types.d.ts ├── package-lock.json ├── package.json ├── src │ ├── common-app │ │ └── mock-posts │ │ │ ├── first-post.md │ │ │ └── second-post.md │ ├── common │ │ └── components │ │ │ ├── app-bar │ │ │ ├── app-bar.business.ts │ │ │ ├── app-bar.component.tsx │ │ │ ├── app-bar.styles.ts │ │ │ └── index.ts │ │ │ ├── footer │ │ │ ├── footer.component.tsx │ │ │ ├── footer.styles.ts │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── seo.component.tsx │ ├── core │ │ ├── images │ │ │ ├── favicon.png │ │ │ └── home-logo.png │ │ ├── routes.ts │ │ └── theme │ │ │ ├── index.ts │ │ │ ├── theme-provider.component.tsx │ │ │ └── theme.ts │ ├── layouts │ │ ├── app-layout.styles.ts │ │ ├── app-layout.tsx │ │ └── index.ts │ ├── pages │ │ ├── blog.tsx │ │ └── index.tsx │ └── pods │ │ ├── blog │ │ ├── blog.component.tsx │ │ ├── blog.styles.ts │ │ └── index.ts │ │ └── home │ │ ├── home.component.tsx │ │ ├── home.styles.ts │ │ └── index.ts └── tsconfig.json ├── 04-blog-from-md ├── .babelrc ├── .env.example ├── .gitignore ├── .prettierrc ├── README.md ├── gatsby-config.js ├── gatsby-node.js ├── global.types.d.ts ├── package-lock.json ├── package.json ├── src │ ├── common-app │ │ └── mock-posts │ │ │ ├── first-post.md │ │ │ └── second-post.md │ ├── common │ │ └── components │ │ │ ├── app-bar │ │ │ ├── app-bar.business.ts │ │ │ ├── app-bar.component.tsx │ │ │ ├── app-bar.styles.ts │ │ │ └── index.ts │ │ │ ├── footer │ │ │ ├── footer.component.tsx │ │ │ ├── footer.styles.ts │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── seo.component.tsx │ ├── core │ │ ├── images │ │ │ ├── favicon.png │ │ │ └── home-logo.png │ │ ├── routes.ts │ │ └── theme │ │ │ ├── index.ts │ │ │ ├── theme-provider.component.tsx │ │ │ └── theme.ts │ ├── layouts │ │ ├── app-layout.styles.ts │ │ ├── app-layout.tsx │ │ └── index.ts │ ├── pages │ │ ├── blog.tsx │ │ └── index.tsx │ └── pods │ │ ├── blog │ │ ├── blog.component.tsx │ │ ├── blog.styles.ts │ │ └── index.ts │ │ ├── home │ │ ├── home.component.tsx │ │ ├── home.styles.ts │ │ └── index.ts │ │ └── post │ │ ├── post.component.tsx │ │ ├── post.styles.ts │ │ └── post.template.tsx └── tsconfig.json ├── 05-blog-from-contentful ├── .babelrc ├── .env.example ├── .gitignore ├── .prettierrc ├── README.md ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── global.types.d.ts ├── package-lock.json ├── package.json ├── readme-resources │ ├── 01-add-space.png │ ├── 02-add-content-type.png │ ├── 03-create-post-model.png │ ├── 04-add-fields.png │ ├── 05-required-validation.png │ ├── 06-custom-validation.png │ ├── 07-save-changes.png │ ├── 08-add-content.png │ ├── 09-publishing-post.png │ ├── 10-add-media.png │ ├── 11-add-logo.png │ ├── 12-link-existing-media.png │ ├── 13-add-media-to-content.png │ ├── 14-access-to-api-key-settings.png │ └── 15-copy-space-id-access-token.png ├── src │ ├── common-app │ │ └── mock-posts │ │ │ ├── first-post.md │ │ │ └── second-post.md │ ├── common │ │ └── components │ │ │ ├── app-bar │ │ │ ├── app-bar.business.ts │ │ │ ├── app-bar.component.tsx │ │ │ ├── app-bar.styles.ts │ │ │ └── index.ts │ │ │ ├── footer │ │ │ ├── footer.component.tsx │ │ │ ├── footer.styles.ts │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── seo.component.tsx │ ├── core │ │ ├── images │ │ │ ├── favicon.png │ │ │ └── home-logo.png │ │ ├── routes.ts │ │ └── theme │ │ │ ├── index.ts │ │ │ ├── theme-provider.component.tsx │ │ │ └── theme.ts │ ├── layouts │ │ ├── app-layout.styles.ts │ │ ├── app-layout.tsx │ │ └── index.ts │ ├── pages │ │ ├── blog.tsx │ │ └── index.tsx │ └── pods │ │ ├── blog │ │ ├── blog.component.tsx │ │ ├── blog.styles.ts │ │ └── index.ts │ │ ├── home │ │ ├── home.component.tsx │ │ ├── home.styles.ts │ │ └── index.ts │ │ └── post │ │ ├── post.component.tsx │ │ ├── post.styles.ts │ │ └── post.template.tsx └── tsconfig.json ├── 06-deploy-to-netlify ├── .babelrc ├── .env.example ├── .gitignore ├── .prettierrc ├── README.md ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── global.types.d.ts ├── package-lock.json ├── package.json ├── readme-resources │ ├── 01-add-space.png │ ├── 02-add-content-type.png │ ├── 03-create-post-model.png │ ├── 04-add-fields.png │ ├── 05-required-validation.png │ ├── 06-custom-validation.png │ ├── 07-save-changes.png │ ├── 08-add-content.png │ ├── 09-publishing-post.png │ ├── 10-add-media.png │ ├── 11-add-logo.png │ ├── 12-link-existing-media.png │ ├── 13-add-media-to-content.png │ ├── 14-access-to-api-key-settings.png │ ├── 15-copy-space-id-access-token.png │ ├── 16-connect-netlify-with-repo.png │ ├── 17-select-repository.png │ ├── 18-show-advance-settings.png │ ├── 19-add-env-variables.png │ ├── 20-access-to-site.png │ ├── 21-access-to-site-settings.png │ ├── 22-add-build-hook.png │ ├── 23-add-contentful-hook.png │ ├── 24-add-webhook-in-contentful.png │ ├── 25-create-contentful-web-hook.png │ └── 26-update-triggers.png ├── src │ ├── common-app │ │ └── mock-posts │ │ │ ├── first-post.md │ │ │ └── second-post.md │ ├── common │ │ └── components │ │ │ ├── app-bar │ │ │ ├── app-bar.business.ts │ │ │ ├── app-bar.component.tsx │ │ │ ├── app-bar.styles.ts │ │ │ └── index.ts │ │ │ ├── footer │ │ │ ├── footer.component.tsx │ │ │ ├── footer.styles.ts │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── seo.component.tsx │ ├── core │ │ ├── images │ │ │ ├── favicon.png │ │ │ └── home-logo.png │ │ ├── routes.ts │ │ └── theme │ │ │ ├── index.ts │ │ │ ├── theme-provider.component.tsx │ │ │ └── theme.ts │ ├── layouts │ │ ├── app-layout.styles.ts │ │ ├── app-layout.tsx │ │ └── index.ts │ ├── pages │ │ ├── blog.tsx │ │ └── index.tsx │ └── pods │ │ ├── blog │ │ ├── blog.component.tsx │ │ ├── blog.styles.ts │ │ └── index.ts │ │ ├── home │ │ ├── home.component.tsx │ │ ├── home.styles.ts │ │ └── index.ts │ │ └── post │ │ ├── post.component.tsx │ │ ├── post.styles.ts │ │ └── post.template.tsx └── tsconfig.json ├── LICENSE └── README.md /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /00-start/.env.example: -------------------------------------------------------------------------------- 1 | CONTENTFUL_SPACE_ID="spaceId" 2 | CONTENTFUL_ACCESS_TOKEN="accessToken" 3 | -------------------------------------------------------------------------------- /00-start/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | -------------------------------------------------------------------------------- /00-start/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "endOfLine": "lf" 5 | } 6 | -------------------------------------------------------------------------------- /00-start/README.md: -------------------------------------------------------------------------------- 1 | # 00-start 2 | 3 | Usually, we are going to start with Gatsby installing the `gatsby-cli` to create initial project with some [Gatsby starter](https://www.gatsbyjs.org/docs/starters/): 4 | 5 | ```bash 6 | npm install gatsby-cli -g 7 | 8 | gatsby new my-site 9 | ``` 10 | 11 | This time, we will start from `00-start` example. 12 | 13 | # About Basefactor + Lemoncode 14 | 15 | We are an innovating team of Javascript experts, passionate about turning your ideas into robust products. 16 | 17 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services. 18 | 19 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services. 20 | 21 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend 22 | -------------------------------------------------------------------------------- /00-start/global.types.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png'; 2 | -------------------------------------------------------------------------------- /00-start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-by-sample", 3 | "version": "1.0.0", 4 | "description": "Project with examples using Gatsby step by step", 5 | "main": "index.js", 6 | "scripts": {}, 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/Lemoncode/gatsby-by-sample.git" 10 | }, 11 | "keywords": [ 12 | "gatsby", 13 | "lemoncode", 14 | "step", 15 | "by", 16 | "step" 17 | ], 18 | "author": "Lemoncode", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/Lemoncode/gatsby-by-sample/issues" 22 | }, 23 | "homepage": "https://github.com/Lemoncode/gatsby-by-sample#readme", 24 | "dependencies": { 25 | "@emotion/css": "^11.1.3", 26 | "@material-ui/core": "^4.11.4", 27 | "@material-ui/icons": "^4.11.2", 28 | "normalize.css": "^8.0.1", 29 | "react": "^17.0.2", 30 | "react-dom": "^17.0.2" 31 | }, 32 | "devDependencies": { 33 | "@types/react": "^17.0.11", 34 | "@types/react-dom": "^17.0.8", 35 | "dotenv": "^10.0.0", 36 | "typescript": "^4.3.4" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /00-start/src/common-app/mock-posts/first-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/first-post' 3 | date: '2019-05-24' 4 | title: 'My first post' 5 | --- 6 | 7 | # This is a title 8 | 9 | Hello this is my first post using markdown. 10 | 11 | `This is inline code` 12 | -------------------------------------------------------------------------------- /00-start/src/common-app/mock-posts/second-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/second-post' 3 | date: '2019-05-25' 4 | title: 'My second post' 5 | --- 6 | 7 | ## This is a subtitle 8 | 9 | Hello this is my second post using markdown. 10 | 11 | ### This is a list 12 | 13 | - With one 14 | - Two 15 | - And three items 16 | -------------------------------------------------------------------------------- /00-start/src/common/components/app-bar/app-bar.business.ts: -------------------------------------------------------------------------------- 1 | import { routes } from 'core/routes'; 2 | 3 | export const getPageName = (pathname: string) => 4 | pathname === routes.home ? 'Home' : 'Blog'; 5 | -------------------------------------------------------------------------------- /00-start/src/common/components/app-bar/app-bar.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import { navigate } from 'gatsby'; 4 | import Bar from '@material-ui/core/AppBar'; 5 | import Toolbar from '@material-ui/core/Toolbar'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import Menu from '@material-ui/core/Menu'; 8 | import MenuItem from '@material-ui/core/MenuItem'; 9 | import MenuIcon from '@material-ui/icons/Menu'; 10 | import IconButton from '@material-ui/core/IconButton'; 11 | import { getPageName } from './app-bar.business'; 12 | import { routes } from 'core/routes'; 13 | import * as classes from './app-bar.styles'; 14 | 15 | interface Props { 16 | className?: string; 17 | pathname: string; 18 | } 19 | 20 | export const AppBar: React.FunctionComponent = props => { 21 | const { pathname, className } = props; 22 | const [isOpen, setOpen] = React.useState(false); 23 | const [element, setElement] = React.useState(null); 24 | 25 | const navigateTo = route => () => { 26 | onCloseMenu(); 27 | navigate(route); 28 | }; 29 | 30 | const onOpenMenu = e => { 31 | setElement(e.currentTarget); 32 | setOpen(true); 33 | }; 34 | 35 | const onCloseMenu = () => { 36 | setElement(null); 37 | setOpen(false); 38 | }; 39 | 40 | return ( 41 | 42 | 43 | 48 | 49 | 50 | 51 | Home 52 | Blog 53 | 54 | 55 | {getPageName(pathname)} 56 | 57 | 58 | 59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /00-start/src/common/components/app-bar/app-bar.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | background-color: ${theme.palette.primary.main}; 6 | color: ${theme.palette.secondary.main}; 7 | `; 8 | 9 | export const title = css` 10 | font-size: ${theme.typography.h6.fontSize}; 11 | font-weight: ${theme.typography.h6.fontWeight}; 12 | color: ${theme.palette.secondary.main}; 13 | margin-left: 1rem; 14 | `; 15 | -------------------------------------------------------------------------------- /00-start/src/common/components/app-bar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar.component'; 2 | -------------------------------------------------------------------------------- /00-start/src/common/components/footer/footer.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import * as classes from './footer.styles'; 4 | 5 | interface Props { 6 | className?: string; 7 | } 8 | 9 | export const Footer: React.FunctionComponent = props => { 10 | const { className } = props; 11 | return ( 12 |
13 |
14 |
15 | Copyright 2019 Lemoncode. All Rights Reserved. 16 |
17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /00-start/src/common/components/footer/footer.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | padding: 1rem; 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | `; 10 | 11 | export const line = css` 12 | height: 1px; 13 | width: 90%; 14 | background-color: ${theme.palette.primary.main}; 15 | `; 16 | 17 | export const footer = css` 18 | padding-top: 1rem; 19 | font-size: ${theme.typography.subtitle2.fontSize}; 20 | color: ${theme.palette.secondary.main}; 21 | `; 22 | -------------------------------------------------------------------------------- /00-start/src/common/components/footer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './footer.component'; 2 | -------------------------------------------------------------------------------- /00-start/src/common/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar'; 2 | export * from './footer'; 3 | -------------------------------------------------------------------------------- /00-start/src/core/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/00-start/src/core/images/favicon.png -------------------------------------------------------------------------------- /00-start/src/core/images/home-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/00-start/src/core/images/home-logo.png -------------------------------------------------------------------------------- /00-start/src/core/routes.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | home: '/', 3 | blog: '/blog', 4 | }; 5 | -------------------------------------------------------------------------------- /00-start/src/core/theme/index.ts: -------------------------------------------------------------------------------- 1 | export * from './theme'; 2 | export * from './theme-provider.component'; 3 | -------------------------------------------------------------------------------- /00-start/src/core/theme/theme-provider.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StylesProvider } from '@material-ui/styles'; 3 | import ThemeProvider from '@material-ui/styles/ThemeProvider'; 4 | import { theme } from './theme'; 5 | 6 | export const ThemeProviderComponent = props => { 7 | const { children } = props; 8 | 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /00-start/src/core/theme/theme.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createMuiTheme, 3 | Theme as DefaultTheme, 4 | } from '@material-ui/core/styles'; 5 | 6 | const defaultTheme = createMuiTheme({ 7 | typography: { 8 | fontFamily: '"Open Sans", "Roboto", "Helvetica", "Arial", sans-serif', 9 | }, 10 | palette: { 11 | primary: { 12 | main: '#d9d900', 13 | }, 14 | secondary: { 15 | main: '#333326', 16 | }, 17 | }, 18 | }); 19 | 20 | type Theme = DefaultTheme; 21 | 22 | export const theme: Theme = { 23 | ...defaultTheme, 24 | }; 25 | -------------------------------------------------------------------------------- /00-start/src/layouts/app-layout.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 99.9vh; 5 | display: grid; 6 | grid-template-columns: 1fr; 7 | grid-template-rows: auto 1fr auto; 8 | grid-template-areas: 9 | "header" 10 | "body" 11 | "footer"; 12 | grid-column-gap: 1rem; 13 | grid-row-gap: 0.5rem; 14 | ` 15 | 16 | export const header = css` 17 | grid-area: header; 18 | `; 19 | 20 | export const body = css` 21 | margin-left: 10%; 22 | margin-right: 10%; 23 | grid-area: body; 24 | `; 25 | 26 | export const footer = css` 27 | grid-area: footer; 28 | `; 29 | 30 | -------------------------------------------------------------------------------- /00-start/src/layouts/app-layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProviderComponent } from 'core/theme'; 3 | import { AppBar, Footer } from 'common/components'; 4 | import * as classes from './app-layout.styles'; 5 | 6 | interface Props { 7 | seoComponent: React.ReactNode; 8 | pathname: string; 9 | } 10 | 11 | export const AppLayout: React.StatelessComponent = props => { 12 | const { 13 | seoComponent, 14 | children, 15 | pathname, 16 | } = props; 17 | return ( 18 | 19 | {seoComponent} 20 |
21 | 22 |
{children}
23 |
24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /00-start/src/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-layout'; 2 | -------------------------------------------------------------------------------- /00-start/src/pods/blog/blog.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import { Link } from 'gatsby'; 4 | import * as classes from './blog.styles'; 5 | 6 | export const Blog: React.FunctionComponent = () => { 7 | return ( 8 |
9 | Blog Page 10 |
11 | 12 | My post 13 | 14 |
15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /00-start/src/pods/blog/blog.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | `; 9 | 10 | export const posts = css` 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | margin-top: 3rem; 15 | width: 100%; 16 | `; 17 | 18 | export const postTitle = css` 19 | margin-top: 3rem; 20 | `; 21 | -------------------------------------------------------------------------------- /00-start/src/pods/blog/index.ts: -------------------------------------------------------------------------------- 1 | export * from './blog.component'; 2 | -------------------------------------------------------------------------------- /00-start/src/pods/home/home.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'gatsby'; 3 | import Typography from '@material-ui/core/Typography'; 4 | import { routes } from 'core/routes'; 5 | import * as classes from './home.styles'; 6 | 7 | export const Home: React.FunctionComponent = () => { 8 | return ( 9 |
10 | Welcome to this website 11 | (Image here) 12 | 13 | Check out our blog 14 | 15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /00-start/src/pods/home/home.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: space-between; 8 | align-items: center; 9 | `; 10 | 11 | export const imageContainer = css` 12 | width: 50%; 13 | `; 14 | -------------------------------------------------------------------------------- /00-start/src/pods/home/index.ts: -------------------------------------------------------------------------------- 1 | export * from './home.component'; 2 | -------------------------------------------------------------------------------- /00-start/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "target": "es6", 5 | "moduleResolution": "node", 6 | "outDir": "./dist/", 7 | "sourceMap": true, 8 | "noImplicitAny": false, 9 | "jsx": "react", 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "baseUrl": "src", 13 | "paths": { 14 | "common": ["common"], 15 | "common-app": ["common-app"], 16 | "core": ["core"], 17 | "layouts": ["layouts"], 18 | "pods": ["pods"], 19 | } 20 | }, 21 | "include": ["./src/**/*", "./global.types.d.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /01-hello-gatsby/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-gatsby"], 3 | "plugins": ["@emotion"] 4 | } 5 | -------------------------------------------------------------------------------- /01-hello-gatsby/.env.example: -------------------------------------------------------------------------------- 1 | CONTENTFUL_SPACE_ID="spaceId" 2 | CONTENTFUL_ACCESS_TOKEN="accessToken" 3 | -------------------------------------------------------------------------------- /01-hello-gatsby/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | -------------------------------------------------------------------------------- /01-hello-gatsby/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "endOfLine": "lf" 5 | } 6 | -------------------------------------------------------------------------------- /01-hello-gatsby/gatsby-config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | siteMetadata: { 5 | title: 'Gatsby by sample', 6 | description: 'Project to work with Gatsby', 7 | author: 'Lemoncode', 8 | }, 9 | plugins: [ 10 | 'gatsby-plugin-typescript', 11 | { 12 | resolve: 'gatsby-plugin-alias-imports', 13 | options: { 14 | alias: { 15 | common: path.resolve(__dirname, 'src/common'), 16 | 'common-app': path.resolve(__dirname, 'src/common-app'), 17 | core: path.resolve(__dirname, 'src/core'), 18 | layouts: path.resolve(__dirname, 'src/layouts'), 19 | pods: path.resolve(__dirname, 'src/pods'), 20 | }, 21 | }, 22 | }, 23 | { 24 | resolve: 'gatsby-plugin-manifest', 25 | options: { 26 | name: 'Gatsby by sample', 27 | short_name: 'Lemoncode-Gatsby', 28 | start_url: '/', 29 | background_color: '#f5f7fb', 30 | theme_color: '#d9d900', 31 | display: 'fullscreen', 32 | icon: 'src/core/images/favicon.png', 33 | }, 34 | }, 35 | 'gatsby-plugin-react-helmet', 36 | ], 37 | }; 38 | -------------------------------------------------------------------------------- /01-hello-gatsby/global.types.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png'; 2 | -------------------------------------------------------------------------------- /01-hello-gatsby/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-by-sample", 3 | "version": "1.0.0", 4 | "description": "Project with examples using Gatsby step by step", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "gatsby build", 8 | "start": "gatsby develop" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/Lemoncode/gatsby-by-sample.git" 13 | }, 14 | "keywords": [ 15 | "gatsby", 16 | "lemoncode", 17 | "step", 18 | "by", 19 | "step" 20 | ], 21 | "author": "Lemoncode", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/Lemoncode/gatsby-by-sample/issues" 25 | }, 26 | "homepage": "https://github.com/Lemoncode/gatsby-by-sample#readme", 27 | "dependencies": { 28 | "@emotion/css": "^11.1.3", 29 | "@material-ui/core": "^4.11.4", 30 | "@material-ui/icons": "^4.11.2", 31 | "gatsby": "^3.7.2", 32 | "normalize.css": "^8.0.1", 33 | "react": "^17.0.2", 34 | "react-dom": "^17.0.2", 35 | "react-helmet": "^6.1.0" 36 | }, 37 | "devDependencies": { 38 | "@emotion/babel-plugin": "^11.3.0", 39 | "@types/react": "^17.0.11", 40 | "@types/react-dom": "^17.0.8", 41 | "@types/react-helmet": "^6.1.1", 42 | "babel-preset-gatsby": "^1.7.1", 43 | "dotenv": "^10.0.0", 44 | "gatsby-plugin-alias-imports": "^1.0.5", 45 | "gatsby-plugin-manifest": "^3.7.1", 46 | "gatsby-plugin-react-helmet": "^4.7.1", 47 | "gatsby-plugin-typescript": "^3.7.1", 48 | "typescript": "^4.3.4" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/common-app/mock-posts/first-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/first-post' 3 | date: '2019-05-24' 4 | title: 'My first post' 5 | --- 6 | 7 | # This is a title 8 | 9 | Hello this is my first post using markdown. 10 | 11 | `This is inline code` 12 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/common-app/mock-posts/second-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/second-post' 3 | date: '2019-05-25' 4 | title: 'My second post' 5 | --- 6 | 7 | ## This is a subtitle 8 | 9 | Hello this is my second post using markdown. 10 | 11 | ### This is a list 12 | 13 | - With one 14 | - Two 15 | - And three items 16 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/common/components/app-bar/app-bar.business.ts: -------------------------------------------------------------------------------- 1 | import { routes } from 'core/routes'; 2 | 3 | export const getPageName = (pathname: string) => 4 | pathname === routes.home ? 'Home' : 'Blog'; 5 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/common/components/app-bar/app-bar.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import { navigate } from 'gatsby'; 4 | import Bar from '@material-ui/core/AppBar'; 5 | import Toolbar from '@material-ui/core/Toolbar'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import Menu from '@material-ui/core/Menu'; 8 | import MenuItem from '@material-ui/core/MenuItem'; 9 | import MenuIcon from '@material-ui/icons/Menu'; 10 | import IconButton from '@material-ui/core/IconButton'; 11 | import { getPageName } from './app-bar.business'; 12 | import { routes } from 'core/routes'; 13 | import * as classes from './app-bar.styles'; 14 | 15 | interface Props { 16 | className?: string; 17 | pathname: string; 18 | } 19 | 20 | export const AppBar: React.FunctionComponent = props => { 21 | const { pathname, className } = props; 22 | const [isOpen, setOpen] = React.useState(false); 23 | const [element, setElement] = React.useState(null); 24 | 25 | const navigateTo = route => () => { 26 | onCloseMenu(); 27 | navigate(route); 28 | }; 29 | 30 | const onOpenMenu = e => { 31 | setElement(e.currentTarget); 32 | setOpen(true); 33 | }; 34 | 35 | const onCloseMenu = () => { 36 | setElement(null); 37 | setOpen(false); 38 | }; 39 | 40 | return ( 41 | 42 | 43 | 48 | 49 | 50 | 51 | Home 52 | Blog 53 | 54 | 55 | {getPageName(pathname)} 56 | 57 | 58 | 59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/common/components/app-bar/app-bar.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | background-color: ${theme.palette.primary.main}; 6 | color: ${theme.palette.secondary.main}; 7 | `; 8 | 9 | export const title = css` 10 | font-size: ${theme.typography.h6.fontSize}; 11 | font-weight: ${theme.typography.h6.fontWeight}; 12 | color: ${theme.palette.secondary.main}; 13 | margin-left: 1rem; 14 | `; 15 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/common/components/app-bar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar.component'; 2 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/common/components/footer/footer.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import * as classes from './footer.styles'; 4 | 5 | interface Props { 6 | className?: string; 7 | } 8 | 9 | export const Footer: React.FunctionComponent = props => { 10 | const { className } = props; 11 | return ( 12 |
13 |
14 |
15 | Copyright 2019 Lemoncode. All Rights Reserved. 16 |
17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/common/components/footer/footer.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | padding: 1rem; 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | `; 10 | 11 | export const line = css` 12 | height: 1px; 13 | width: 90%; 14 | background-color: ${theme.palette.primary.main}; 15 | `; 16 | 17 | export const footer = css` 18 | padding-top: 1rem; 19 | font-size: ${theme.typography.subtitle2.fontSize}; 20 | color: ${theme.palette.secondary.main}; 21 | `; 22 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/common/components/footer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './footer.component'; 2 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/common/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar'; 2 | export * from './footer'; 3 | export * from './seo.component'; 4 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/common/components/seo.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import { useStaticQuery, graphql } from 'gatsby'; 4 | 5 | interface Props { 6 | title: string; 7 | description?: string; 8 | lang?: string; 9 | meta?: any[]; 10 | keywords?: string[]; 11 | } 12 | 13 | const query = graphql` 14 | query DefaultSEOQuery { 15 | site { 16 | siteMetadata { 17 | title 18 | description 19 | author 20 | } 21 | } 22 | } 23 | `; 24 | 25 | export const SEO: React.StatelessComponent = (props) => { 26 | const { description, lang, meta, keywords, title } = props; 27 | const data = useStaticQuery(query); 28 | const metaDescription = description || data.site.siteMetadata.description; 29 | return ( 30 | 0 72 | ? { 73 | name: 'keywords', 74 | content: keywords.join(', '), 75 | } 76 | : [] 77 | ) 78 | .concat(meta)} 79 | /> 80 | ); 81 | }; 82 | 83 | SEO.defaultProps = { 84 | lang: 'en', 85 | meta: [], 86 | keywords: [], 87 | }; 88 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/core/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/01-hello-gatsby/src/core/images/favicon.png -------------------------------------------------------------------------------- /01-hello-gatsby/src/core/images/home-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/01-hello-gatsby/src/core/images/home-logo.png -------------------------------------------------------------------------------- /01-hello-gatsby/src/core/routes.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | home: '/', 3 | blog: '/blog', 4 | }; 5 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/core/theme/index.ts: -------------------------------------------------------------------------------- 1 | export * from './theme'; 2 | export * from './theme-provider.component'; 3 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/core/theme/theme-provider.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StylesProvider } from '@material-ui/styles'; 3 | import ThemeProvider from '@material-ui/styles/ThemeProvider'; 4 | import { theme } from './theme'; 5 | 6 | export const ThemeProviderComponent = props => { 7 | const { children } = props; 8 | 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/core/theme/theme.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createMuiTheme, 3 | Theme as DefaultTheme, 4 | } from '@material-ui/core/styles'; 5 | 6 | const defaultTheme = createMuiTheme({ 7 | typography: { 8 | fontFamily: '"Open Sans", "Roboto", "Helvetica", "Arial", sans-serif', 9 | }, 10 | palette: { 11 | primary: { 12 | main: '#d9d900', 13 | }, 14 | secondary: { 15 | main: '#333326', 16 | }, 17 | }, 18 | }); 19 | 20 | type Theme = DefaultTheme; 21 | 22 | export const theme: Theme = { 23 | ...defaultTheme, 24 | }; 25 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/layouts/app-layout.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 99.9vh; 5 | display: grid; 6 | grid-template-columns: 1fr; 7 | grid-template-rows: auto 1fr auto; 8 | grid-template-areas: 9 | "header" 10 | "body" 11 | "footer"; 12 | grid-column-gap: 1rem; 13 | grid-row-gap: 0.5rem; 14 | ` 15 | 16 | export const header = css` 17 | grid-area: header; 18 | `; 19 | 20 | export const body = css` 21 | margin-left: 10%; 22 | margin-right: 10%; 23 | grid-area: body; 24 | `; 25 | 26 | export const footer = css` 27 | grid-area: footer; 28 | `; 29 | 30 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/layouts/app-layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProviderComponent } from 'core/theme'; 3 | import { AppBar, Footer } from 'common/components'; 4 | import * as classes from './app-layout.styles'; 5 | 6 | interface Props { 7 | seoComponent: React.ReactNode; 8 | pathname: string; 9 | } 10 | 11 | export const AppLayout: React.StatelessComponent = props => { 12 | const { 13 | seoComponent, 14 | children, 15 | pathname, 16 | } = props; 17 | return ( 18 | 19 | {seoComponent} 20 |
21 | 22 |
{children}
23 |
24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-layout'; 2 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { css } from '@emotion/css'; 3 | import { SEO } from 'common/components'; 4 | 5 | const root = css` 6 | background-color: tomato; 7 | color: white; 8 | font-size: 4rem; 9 | font-family: 'Open Sans'; 10 | padding: 2rem; 11 | `; 12 | 13 | const IndexPage = () => { 14 | return ( 15 | <> 16 | 26 |
Hello from Gatsby
27 | 28 | ); 29 | }; 30 | 31 | export default IndexPage; 32 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/pods/blog/blog.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import { Link } from 'gatsby'; 4 | import * as classes from './blog.styles'; 5 | 6 | export const Blog: React.FunctionComponent = () => { 7 | return ( 8 |
9 | Blog Page 10 |
11 | 12 | My post 13 | 14 |
15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/pods/blog/blog.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | `; 9 | 10 | export const posts = css` 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | margin-top: 3rem; 15 | width: 100%; 16 | `; 17 | 18 | export const postTitle = css` 19 | margin-top: 3rem; 20 | `; 21 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/pods/blog/index.ts: -------------------------------------------------------------------------------- 1 | export * from './blog.component'; 2 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/pods/home/home.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'gatsby'; 3 | import Typography from '@material-ui/core/Typography'; 4 | import { routes } from 'core/routes'; 5 | import * as classes from './home.styles'; 6 | 7 | export const Home: React.FunctionComponent = () => { 8 | return ( 9 |
10 | Welcome to this website 11 | (Image here) 12 | 13 | Check out our blog 14 | 15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/pods/home/home.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: space-between; 8 | align-items: center; 9 | `; 10 | 11 | export const imageContainer = css` 12 | width: 50%; 13 | `; 14 | -------------------------------------------------------------------------------- /01-hello-gatsby/src/pods/home/index.ts: -------------------------------------------------------------------------------- 1 | export * from './home.component'; 2 | -------------------------------------------------------------------------------- /01-hello-gatsby/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "target": "es6", 5 | "moduleResolution": "node", 6 | "outDir": "./dist/", 7 | "sourceMap": true, 8 | "noImplicitAny": false, 9 | "jsx": "react", 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "baseUrl": "src", 13 | "paths": { 14 | "common": ["common"], 15 | "common-app": ["common-app"], 16 | "core": ["core"], 17 | "layouts": ["layouts"], 18 | "pods": ["pods"], 19 | } 20 | }, 21 | "include": ["./src/**/*", "./global.types.d.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /02-navigation/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-gatsby"], 3 | "plugins": ["@emotion"] 4 | } 5 | -------------------------------------------------------------------------------- /02-navigation/.env.example: -------------------------------------------------------------------------------- 1 | CONTENTFUL_SPACE_ID="spaceId" 2 | CONTENTFUL_ACCESS_TOKEN="accessToken" 3 | -------------------------------------------------------------------------------- /02-navigation/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | -------------------------------------------------------------------------------- /02-navigation/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "endOfLine": "lf" 5 | } 6 | -------------------------------------------------------------------------------- /02-navigation/README.md: -------------------------------------------------------------------------------- 1 | # 02-navigation 2 | 3 | In this sample we will add some page navigation with Gatsby. 4 | 5 | We will start from previous example `01-hello-gatsby`: 6 | 7 | ```bash 8 | npm install 9 | ``` 10 | 11 | - First, we are going to create a second page: 12 | 13 | ### ./src/pages/blog.tsx 14 | 15 | ```javascript 16 | import React from 'react'; 17 | import { SEO } from 'common/components'; 18 | 19 | const BlogPage = () => { 20 | return ( 21 | <> 22 | 32 |
This is a blog page
33 | 34 | ); 35 | }; 36 | 37 | export default BlogPage; 38 | ``` 39 | 40 | - Run app and navigate to `/blog`: 41 | 42 | ```bash 43 | npm start 44 | ``` 45 | 46 | - We have to ways to navigate in Gatsby. Using `link` component: 47 | 48 | ### ./src/pages/index.ts 49 | 50 | ```diff 51 | import React from 'react'; 52 | + import { Link } from 'gatsby'; 53 | import { css } from '@emotion/css'; 54 | import { SEO } from 'common/components'; 55 | 56 | ... 57 | const IndexPage = () => ( 58 | <> 59 | ... 60 |
Hello from Gatsby
61 | + Navigate to blog 62 | 63 | ); 64 | ... 65 | 66 | ``` 67 | 68 | - Or programmatically: 69 | 70 | ### ./src/pages/blog.tsx 71 | 72 | ```diff 73 | import React from 'react'; 74 | + import { navigate } from 'gatsby'; 75 | import { SEO } from 'common/components'; 76 | 77 | const BlogPage = () => { 78 | + const handleNavigate = () => { 79 | + navigate('/'); 80 | + }; 81 | 82 | return ( 83 | <> 84 | 94 |
This is a blog page
95 | + 96 | 97 | ); 98 | }; 99 | 100 | ``` 101 | 102 | - Finally, since we are using layouts, we are going to refactor both pages and use it: 103 | 104 | ### ./src/pages/index.tsx 105 | 106 | ```diff 107 | import React from 'react'; 108 | - import { Link } from 'gatsby'; 109 | - import { css } from '@emotion/css'; 110 | + import { PageRendererProps } from 'gatsby'; 111 | import { SEO } from 'common/components'; 112 | + import { AppLayout } from 'layouts'; 113 | + import { Home } from 'pods/home'; 114 | 115 | - const root = css` 116 | - background-color: tomato; 117 | - color: white; 118 | - font-size: 4rem; 119 | - font-family: 'Open Sans'; 120 | - padding: 2rem; 121 | - `; 122 | 123 | - const IndexPage = () => { 124 | + const IndexPage: React.FunctionComponent = props => { 125 | + const { location } = props; 126 | return ( 127 | - <> 128 | + 141 | + } 142 | + > 143 | -
Hello from Gatsby
144 | - Navigate to blog 145 | + 146 | - 147 | +
148 | ); 149 | }; 150 | 151 | ... 152 | 153 | ``` 154 | 155 | - Update `blog` page: 156 | 157 | ### ./src/pages/blog.tsx 158 | 159 | ```diff 160 | import React from 'react'; 161 | - import { navigate } from 'gatsby'; 162 | + import { PageRendererProps } from 'gatsby'; 163 | import { SEO } from 'common/components'; 164 | + import { AppLayout } from 'layouts'; 165 | + import { Blog } from 'pods/blog'; 166 | 167 | - const BlogPage = () => { 168 | + const BlogPage: React.FunctionComponent = props => { 169 | + const { location } = props; 170 | - const handleNavigate = () => { 171 | - navigate('/'); 172 | - }; 173 | 174 | return ( 175 | - <> 176 | + 189 | + } 190 | + > 191 | -
This is a blog page
192 | - 193 | + 194 | - 195 | +
196 | ); 197 | }; 198 | 199 | ... 200 | 201 | ``` 202 | 203 | # About Basefactor + Lemoncode 204 | 205 | We are an innovating team of Javascript experts, passionate about turning your ideas into robust products. 206 | 207 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services. 208 | 209 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services. 210 | 211 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend 212 | -------------------------------------------------------------------------------- /02-navigation/gatsby-config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | siteMetadata: { 5 | title: 'Gatsby by sample', 6 | description: 'Project to work with Gatsby', 7 | author: 'Lemoncode', 8 | }, 9 | plugins: [ 10 | 'gatsby-plugin-typescript', 11 | { 12 | resolve: 'gatsby-plugin-alias-imports', 13 | options: { 14 | alias: { 15 | common: path.resolve(__dirname, 'src/common'), 16 | 'common-app': path.resolve(__dirname, 'src/common-app'), 17 | core: path.resolve(__dirname, 'src/core'), 18 | layouts: path.resolve(__dirname, 'src/layouts'), 19 | pods: path.resolve(__dirname, 'src/pods'), 20 | }, 21 | }, 22 | }, 23 | { 24 | resolve: 'gatsby-plugin-manifest', 25 | options: { 26 | name: 'Gatsby by sample', 27 | short_name: 'Lemoncode-Gatsby', 28 | start_url: '/', 29 | background_color: '#f5f7fb', 30 | theme_color: '#d9d900', 31 | display: 'fullscreen', 32 | icon: 'src/core/images/favicon.png', 33 | }, 34 | }, 35 | 'gatsby-plugin-react-helmet', 36 | ], 37 | }; 38 | -------------------------------------------------------------------------------- /02-navigation/global.types.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png'; 2 | -------------------------------------------------------------------------------- /02-navigation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-by-sample", 3 | "version": "1.0.0", 4 | "description": "Project with examples using Gatsby step by step", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "gatsby build", 8 | "start": "gatsby develop" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/Lemoncode/gatsby-by-sample.git" 13 | }, 14 | "keywords": [ 15 | "gatsby", 16 | "lemoncode", 17 | "step", 18 | "by", 19 | "step" 20 | ], 21 | "author": "Lemoncode", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/Lemoncode/gatsby-by-sample/issues" 25 | }, 26 | "homepage": "https://github.com/Lemoncode/gatsby-by-sample#readme", 27 | "dependencies": { 28 | "@emotion/css": "^11.1.3", 29 | "@material-ui/core": "^4.11.4", 30 | "@material-ui/icons": "^4.11.2", 31 | "gatsby": "^3.7.2", 32 | "normalize.css": "^8.0.1", 33 | "react": "^17.0.2", 34 | "react-dom": "^17.0.2", 35 | "react-helmet": "^6.1.0" 36 | }, 37 | "devDependencies": { 38 | "@emotion/babel-plugin": "^11.3.0", 39 | "@types/react": "^17.0.11", 40 | "@types/react-dom": "^17.0.8", 41 | "@types/react-helmet": "^6.1.1", 42 | "babel-preset-gatsby": "^1.7.1", 43 | "dotenv": "^10.0.0", 44 | "gatsby-plugin-alias-imports": "^1.0.5", 45 | "gatsby-plugin-manifest": "^3.7.1", 46 | "gatsby-plugin-react-helmet": "^4.7.1", 47 | "gatsby-plugin-typescript": "^3.7.1", 48 | "typescript": "^4.3.4" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /02-navigation/src/common-app/mock-posts/first-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/first-post' 3 | date: '2019-05-24' 4 | title: 'My first post' 5 | --- 6 | 7 | # This is a title 8 | 9 | Hello this is my first post using markdown. 10 | 11 | `This is inline code` 12 | -------------------------------------------------------------------------------- /02-navigation/src/common-app/mock-posts/second-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/second-post' 3 | date: '2019-05-25' 4 | title: 'My second post' 5 | --- 6 | 7 | ## This is a subtitle 8 | 9 | Hello this is my second post using markdown. 10 | 11 | ### This is a list 12 | 13 | - With one 14 | - Two 15 | - And three items 16 | -------------------------------------------------------------------------------- /02-navigation/src/common/components/app-bar/app-bar.business.ts: -------------------------------------------------------------------------------- 1 | import { routes } from 'core/routes'; 2 | 3 | export const getPageName = (pathname: string) => 4 | pathname === routes.home ? 'Home' : 'Blog'; 5 | -------------------------------------------------------------------------------- /02-navigation/src/common/components/app-bar/app-bar.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import { navigate } from 'gatsby'; 4 | import Bar from '@material-ui/core/AppBar'; 5 | import Toolbar from '@material-ui/core/Toolbar'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import Menu from '@material-ui/core/Menu'; 8 | import MenuItem from '@material-ui/core/MenuItem'; 9 | import MenuIcon from '@material-ui/icons/Menu'; 10 | import IconButton from '@material-ui/core/IconButton'; 11 | import { getPageName } from './app-bar.business'; 12 | import { routes } from 'core/routes'; 13 | import * as classes from './app-bar.styles'; 14 | 15 | interface Props { 16 | className?: string; 17 | pathname: string; 18 | } 19 | 20 | export const AppBar: React.FunctionComponent = props => { 21 | const { pathname, className } = props; 22 | const [isOpen, setOpen] = React.useState(false); 23 | const [element, setElement] = React.useState(null); 24 | 25 | const navigateTo = route => () => { 26 | onCloseMenu(); 27 | navigate(route); 28 | }; 29 | 30 | const onOpenMenu = e => { 31 | setElement(e.currentTarget); 32 | setOpen(true); 33 | }; 34 | 35 | const onCloseMenu = () => { 36 | setElement(null); 37 | setOpen(false); 38 | }; 39 | 40 | return ( 41 | 42 | 43 | 48 | 49 | 50 | 51 | Home 52 | Blog 53 | 54 | 55 | {getPageName(pathname)} 56 | 57 | 58 | 59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /02-navigation/src/common/components/app-bar/app-bar.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | background-color: ${theme.palette.primary.main}; 6 | color: ${theme.palette.secondary.main}; 7 | `; 8 | 9 | export const title = css` 10 | font-size: ${theme.typography.h6.fontSize}; 11 | font-weight: ${theme.typography.h6.fontWeight}; 12 | color: ${theme.palette.secondary.main}; 13 | margin-left: 1rem; 14 | `; 15 | -------------------------------------------------------------------------------- /02-navigation/src/common/components/app-bar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar.component'; 2 | -------------------------------------------------------------------------------- /02-navigation/src/common/components/footer/footer.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import * as classes from './footer.styles'; 4 | 5 | interface Props { 6 | className?: string; 7 | } 8 | 9 | export const Footer: React.FunctionComponent = props => { 10 | const { className } = props; 11 | return ( 12 |
13 |
14 |
15 | Copyright 2019 Lemoncode. All Rights Reserved. 16 |
17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /02-navigation/src/common/components/footer/footer.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | padding: 1rem; 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | `; 10 | 11 | export const line = css` 12 | height: 1px; 13 | width: 90%; 14 | background-color: ${theme.palette.primary.main}; 15 | `; 16 | 17 | export const footer = css` 18 | padding-top: 1rem; 19 | font-size: ${theme.typography.subtitle2.fontSize}; 20 | color: ${theme.palette.secondary.main}; 21 | `; 22 | -------------------------------------------------------------------------------- /02-navigation/src/common/components/footer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './footer.component'; 2 | -------------------------------------------------------------------------------- /02-navigation/src/common/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar'; 2 | export * from './footer'; 3 | export * from './seo.component'; 4 | -------------------------------------------------------------------------------- /02-navigation/src/common/components/seo.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import { useStaticQuery, graphql } from 'gatsby'; 4 | 5 | interface Props { 6 | title: string; 7 | description?: string; 8 | lang?: string; 9 | meta?: any[]; 10 | keywords?: string[]; 11 | } 12 | 13 | const query = graphql` 14 | query DefaultSEOQuery { 15 | site { 16 | siteMetadata { 17 | title 18 | description 19 | author 20 | } 21 | } 22 | } 23 | `; 24 | 25 | export const SEO: React.StatelessComponent = (props) => { 26 | const { description, lang, meta, keywords, title } = props; 27 | const data = useStaticQuery(query); 28 | const metaDescription = description || data.site.siteMetadata.description; 29 | return ( 30 | 0 72 | ? { 73 | name: 'keywords', 74 | content: keywords.join(', '), 75 | } 76 | : [] 77 | ) 78 | .concat(meta)} 79 | /> 80 | ); 81 | }; 82 | 83 | SEO.defaultProps = { 84 | lang: 'en', 85 | meta: [], 86 | keywords: [], 87 | }; 88 | -------------------------------------------------------------------------------- /02-navigation/src/core/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/02-navigation/src/core/images/favicon.png -------------------------------------------------------------------------------- /02-navigation/src/core/images/home-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/02-navigation/src/core/images/home-logo.png -------------------------------------------------------------------------------- /02-navigation/src/core/routes.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | home: '/', 3 | blog: '/blog', 4 | }; 5 | -------------------------------------------------------------------------------- /02-navigation/src/core/theme/index.ts: -------------------------------------------------------------------------------- 1 | export * from './theme'; 2 | export * from './theme-provider.component'; 3 | -------------------------------------------------------------------------------- /02-navigation/src/core/theme/theme-provider.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StylesProvider } from '@material-ui/styles'; 3 | import ThemeProvider from '@material-ui/styles/ThemeProvider'; 4 | import { theme } from './theme'; 5 | 6 | export const ThemeProviderComponent = props => { 7 | const { children } = props; 8 | 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /02-navigation/src/core/theme/theme.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createMuiTheme, 3 | Theme as DefaultTheme, 4 | } from '@material-ui/core/styles'; 5 | 6 | const defaultTheme = createMuiTheme({ 7 | typography: { 8 | fontFamily: '"Open Sans", "Roboto", "Helvetica", "Arial", sans-serif', 9 | }, 10 | palette: { 11 | primary: { 12 | main: '#d9d900', 13 | }, 14 | secondary: { 15 | main: '#333326', 16 | }, 17 | }, 18 | }); 19 | 20 | type Theme = DefaultTheme; 21 | 22 | export const theme: Theme = { 23 | ...defaultTheme, 24 | }; 25 | -------------------------------------------------------------------------------- /02-navigation/src/layouts/app-layout.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 99.9vh; 5 | display: grid; 6 | grid-template-columns: 1fr; 7 | grid-template-rows: auto 1fr auto; 8 | grid-template-areas: 9 | "header" 10 | "body" 11 | "footer"; 12 | grid-column-gap: 1rem; 13 | grid-row-gap: 0.5rem; 14 | ` 15 | 16 | export const header = css` 17 | grid-area: header; 18 | `; 19 | 20 | export const body = css` 21 | margin-left: 10%; 22 | margin-right: 10%; 23 | grid-area: body; 24 | `; 25 | 26 | export const footer = css` 27 | grid-area: footer; 28 | `; 29 | 30 | -------------------------------------------------------------------------------- /02-navigation/src/layouts/app-layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProviderComponent } from 'core/theme'; 3 | import { AppBar, Footer } from 'common/components'; 4 | import * as classes from './app-layout.styles'; 5 | 6 | interface Props { 7 | seoComponent: React.ReactNode; 8 | pathname: string; 9 | } 10 | 11 | export const AppLayout: React.StatelessComponent = props => { 12 | const { 13 | seoComponent, 14 | children, 15 | pathname, 16 | } = props; 17 | return ( 18 | 19 | {seoComponent} 20 |
21 | 22 |
{children}
23 |
24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /02-navigation/src/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-layout'; 2 | -------------------------------------------------------------------------------- /02-navigation/src/pages/blog.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PageRendererProps } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Blog } from 'pods/blog'; 6 | 7 | const BlogPage: React.FunctionComponent = (props) => { 8 | const { location } = props; 9 | 10 | return ( 11 | 24 | } 25 | > 26 | 27 | 28 | ); 29 | }; 30 | 31 | export default BlogPage; 32 | -------------------------------------------------------------------------------- /02-navigation/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PageRendererProps } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Home } from 'pods/home'; 6 | 7 | const IndexPage: React.FunctionComponent = (props) => { 8 | const { location } = props; 9 | return ( 10 | 23 | } 24 | > 25 | 26 | 27 | ); 28 | }; 29 | 30 | export default IndexPage; 31 | -------------------------------------------------------------------------------- /02-navigation/src/pods/blog/blog.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import { Link } from 'gatsby'; 4 | import * as classes from './blog.styles'; 5 | 6 | export const Blog: React.FunctionComponent = () => { 7 | return ( 8 |
9 | Blog Page 10 |
11 | 12 | My post 13 | 14 |
15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /02-navigation/src/pods/blog/blog.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | `; 9 | 10 | export const posts = css` 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | margin-top: 3rem; 15 | width: 100%; 16 | `; 17 | 18 | export const postTitle = css` 19 | margin-top: 3rem; 20 | `; 21 | -------------------------------------------------------------------------------- /02-navigation/src/pods/blog/index.ts: -------------------------------------------------------------------------------- 1 | export * from './blog.component'; 2 | -------------------------------------------------------------------------------- /02-navigation/src/pods/home/home.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'gatsby'; 3 | import Typography from '@material-ui/core/Typography'; 4 | import { routes } from 'core/routes'; 5 | import * as classes from './home.styles'; 6 | 7 | export const Home: React.FunctionComponent = () => { 8 | return ( 9 |
10 | Welcome to this website 11 | (Image here) 12 | 13 | Check out our blog 14 | 15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /02-navigation/src/pods/home/home.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: space-between; 8 | align-items: center; 9 | `; 10 | 11 | export const imageContainer = css` 12 | width: 50%; 13 | `; 14 | -------------------------------------------------------------------------------- /02-navigation/src/pods/home/index.ts: -------------------------------------------------------------------------------- 1 | export * from './home.component'; 2 | -------------------------------------------------------------------------------- /02-navigation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "target": "es6", 5 | "moduleResolution": "node", 6 | "outDir": "./dist/", 7 | "sourceMap": true, 8 | "noImplicitAny": false, 9 | "jsx": "react", 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "baseUrl": "src", 13 | "paths": { 14 | "common": ["common"], 15 | "common-app": ["common-app"], 16 | "core": ["core"], 17 | "layouts": ["layouts"], 18 | "pods": ["pods"], 19 | } 20 | }, 21 | "include": ["./src/**/*", "./global.types.d.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /03-handling-images/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-gatsby"], 3 | "plugins": ["@emotion"] 4 | } 5 | -------------------------------------------------------------------------------- /03-handling-images/.env.example: -------------------------------------------------------------------------------- 1 | CONTENTFUL_SPACE_ID="spaceId" 2 | CONTENTFUL_ACCESS_TOKEN="accessToken" 3 | -------------------------------------------------------------------------------- /03-handling-images/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | -------------------------------------------------------------------------------- /03-handling-images/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "endOfLine": "lf" 5 | } 6 | -------------------------------------------------------------------------------- /03-handling-images/README.md: -------------------------------------------------------------------------------- 1 | # 03-handling-images 2 | 3 | In this sample we will config all necessary plugins to work with images. 4 | 5 | We will start from previous example `02-navigation`: 6 | 7 | ```bash 8 | npm install 9 | ``` 10 | 11 | - Now, we want to add the `core/images/home-logo.png`: 12 | 13 | ### ./src/pods/home/home.component.tsx 14 | 15 | ```diff 16 | import React from 'react'; 17 | import { Link } from 'gatsby'; 18 | import Typography from '@material-ui/core/Typography'; 19 | import { routes } from 'core/routes'; 20 | + import logo from 'core/images/home-logo.png'; 21 | import * as classes from './home.styles'; 22 | 23 | export const Home: React.FunctionComponent = () => { 24 | return ( 25 |
26 | Welcome to this website 27 | - (Image here) 28 | + 29 | 30 | Check out our blog 31 | 32 |
33 | ); 34 | }; 35 | 36 | ``` 37 | 38 | - Issues related with approach: 39 | - The size needed for the design. 40 | - We don't need larger image sizes for smartphones or tables, only for desktop. 41 | - Render images is slow for initial page load. 42 | 43 | - So, we are going to use [gatsby-plugin-image](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-image) library along with `gatsby-transformer-sharp`, `gatsby-plugin-sharp` and `gatsby-source-filesystem`: 44 | 45 | ```bash 46 | npm i gatsby-plugin-image -P 47 | npm i gatsby-transformer-sharp gatsby-plugin-sharp gatsby-source-filesystem -D 48 | ``` 49 | 50 | - Update `gatsby-config`: 51 | 52 | ### ./gatsby-config.js 53 | 54 | ```diff 55 | const path = require('path'); 56 | 57 | module.exports = { 58 | ... 59 | plugins: [ 60 | ... 61 | 'gatsby-plugin-react-helmet', 62 | + 'gatsby-plugin-image', 63 | + 'gatsby-transformer-sharp', 64 | + 'gatsby-plugin-sharp', 65 | + { 66 | + resolve: 'gatsby-source-filesystem', 67 | + options: { 68 | + name: 'images', 69 | + path: path.resolve(__dirname, 'src/core/images'), 70 | + }, 71 | + }, 72 | ], 73 | }; 74 | 75 | ``` 76 | 77 | - Using `gatsby-image`: 78 | 79 | ### ./src/pods/home/home.component.tsx 80 | 81 | ```diff 82 | import React from 'react'; 83 | + import { StaticImage } from 'gatsby-plugin-image'; 84 | import { Link } from 'gatsby'; 85 | import Typography from '@material-ui/core/Typography'; 86 | import { routes } from 'core/routes'; 87 | - import logo from 'core/images/home-logo.png'; 88 | import * as classes from './home.styles'; 89 | 90 | export const Home: React.FunctionComponent = () => { 91 | return ( 92 |
93 | Welcome to this website 94 | - 95 | + 103 | 104 | Check out our blog 105 | 106 |
107 | ); 108 | }; 109 | 110 | ``` 111 | 112 | > [StaticImage vs GatsbyImage](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-image#deciding-which-component-to-use) 113 | 114 | - Or we could use another component for dynamic images: 115 | 116 | 117 | 118 | ### ./src/pods/home/home.component.tsx 119 | 120 | ```diff 121 | import React from 'react'; 122 | - import { StaticImage } from 'gatsby-plugin-image'; 123 | + import { GatsbyImage } from 'gatsby-plugin-image'; 124 | import { Link } from 'gatsby'; 125 | import Typography from '@material-ui/core/Typography'; 126 | import { routes } from 'core/routes'; 127 | import * as classes from './home.styles'; 128 | 129 | export const Home: React.FunctionComponent = () => { 130 | return ( 131 |
132 | Welcome to this website 133 | - 141 | 142 | Check out our blog 143 | 144 |
145 | ); 146 | }; 147 | 148 | ``` 149 | 150 | - How can we get image src? How it works?. We can take a look to: `http://localhost:8000/___graphql`: 151 | 152 | ```graphql 153 | query { 154 | homeLogo: file(relativePath: {eq: "home-logo.png"}) { 155 | childImageSharp { 156 | gatsbyImageData(layout:FIXED, placeholder: BLURRED) 157 | } 158 | } 159 | } 160 | 161 | ``` 162 | 163 | - Use it in `home` component: 164 | 165 | ### ./src/pods/home/home.component.tsx 166 | 167 | ```diff 168 | import React from 'react'; 169 | - import { GatsbyImage } from 'gatsby-plugin-image'; 170 | + import { GatsbyImage, getImage } from 'gatsby-plugin-image'; 171 | - import { Link } from 'gatsby'; 172 | + import { Link, useStaticQuery, graphql } from 'gatsby'; 173 | import Typography from '@material-ui/core/Typography'; 174 | import { routes } from 'core/routes'; 175 | import * as classes from './home.styles'; 176 | 177 | + const query = graphql` 178 | + query { 179 | + homeLogo: file(relativePath: { eq: "home-logo.png" }) { 180 | + childImageSharp { 181 | + gatsbyImageData(layout: FIXED, placeholder: BLURRED) 182 | + } 183 | + } 184 | + } 185 | + `; 186 | 187 | export const Home: React.FunctionComponent = () => { 188 | + const { homeLogo } = useStaticQuery(query); 189 | 190 | return ( 191 |
192 | Welcome to this website 193 | + 194 | 195 | Check out our blog 196 | 197 |
198 | ); 199 | }; 200 | 201 | ``` 202 | 203 | > [Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/) 204 | 205 | - It load an image resized to `400x400`. If we want to get image size by `screen size`, we have to use `CONSTRAINED` instead: 206 | 207 | > NOTE: Show `Network tab`, `disable cache` in chrome dev tools and `srcset` 208 | > [srcset](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images) 209 | 210 | ### ./src/pods/home/home.component.tsx 211 | 212 | ```diff 213 | ... 214 | 215 | const query = graphql` 216 | query { 217 | homeLogo: file(relativePath: { eq: "home-logo.png" }) { 218 | childImageSharp { 219 | - gatsbyImageData(layout: FIXED, placeholder: BLURRED) 220 | + gatsbyImageData(layout: CONSTRAINED, placeholder: BLURRED) 221 | } 222 | } 223 | } 224 | `; 225 | 226 | ``` 227 | 228 | # About Basefactor + Lemoncode 229 | 230 | We are an innovating team of Javascript experts, passionate about turning your ideas into robust products. 231 | 232 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services. 233 | 234 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services. 235 | 236 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend 237 | -------------------------------------------------------------------------------- /03-handling-images/gatsby-config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | siteMetadata: { 5 | title: 'Gatsby by sample', 6 | description: 'Project to work with Gatsby', 7 | author: 'Lemoncode', 8 | }, 9 | plugins: [ 10 | 'gatsby-plugin-typescript', 11 | { 12 | resolve: 'gatsby-plugin-alias-imports', 13 | options: { 14 | alias: { 15 | common: path.resolve(__dirname, 'src/common'), 16 | 'common-app': path.resolve(__dirname, 'src/common-app'), 17 | core: path.resolve(__dirname, 'src/core'), 18 | layouts: path.resolve(__dirname, 'src/layouts'), 19 | pods: path.resolve(__dirname, 'src/pods'), 20 | }, 21 | }, 22 | }, 23 | { 24 | resolve: 'gatsby-plugin-manifest', 25 | options: { 26 | name: 'Gatsby by sample', 27 | short_name: 'Lemoncode-Gatsby', 28 | start_url: '/', 29 | background_color: '#f5f7fb', 30 | theme_color: '#d9d900', 31 | display: 'fullscreen', 32 | icon: 'src/core/images/favicon.png', 33 | }, 34 | }, 35 | 'gatsby-plugin-react-helmet', 36 | 'gatsby-plugin-image', 37 | 'gatsby-transformer-sharp', 38 | 'gatsby-plugin-sharp', 39 | { 40 | resolve: 'gatsby-source-filesystem', 41 | options: { 42 | name: 'images', 43 | path: path.resolve(__dirname, 'src/core/images'), 44 | }, 45 | }, 46 | ], 47 | }; 48 | -------------------------------------------------------------------------------- /03-handling-images/global.types.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png'; 2 | -------------------------------------------------------------------------------- /03-handling-images/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-by-sample", 3 | "version": "1.0.0", 4 | "description": "Project with examples using Gatsby step by step", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "gatsby build", 8 | "start": "gatsby develop" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/Lemoncode/gatsby-by-sample.git" 13 | }, 14 | "keywords": [ 15 | "gatsby", 16 | "lemoncode", 17 | "step", 18 | "by", 19 | "step" 20 | ], 21 | "author": "Lemoncode", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/Lemoncode/gatsby-by-sample/issues" 25 | }, 26 | "homepage": "https://github.com/Lemoncode/gatsby-by-sample#readme", 27 | "dependencies": { 28 | "@emotion/css": "^11.1.3", 29 | "@material-ui/core": "^4.11.4", 30 | "@material-ui/icons": "^4.11.2", 31 | "gatsby": "^3.7.2", 32 | "gatsby-plugin-image": "^1.7.1", 33 | "normalize.css": "^8.0.1", 34 | "react": "^17.0.2", 35 | "react-dom": "^17.0.2", 36 | "react-helmet": "^6.1.0" 37 | }, 38 | "devDependencies": { 39 | "@emotion/babel-plugin": "^11.3.0", 40 | "@types/react": "^17.0.11", 41 | "@types/react-dom": "^17.0.8", 42 | "@types/react-helmet": "^6.1.1", 43 | "babel-preset-gatsby": "^1.7.1", 44 | "dotenv": "^10.0.0", 45 | "gatsby-plugin-alias-imports": "^1.0.5", 46 | "gatsby-plugin-manifest": "^3.7.1", 47 | "gatsby-plugin-react-helmet": "^4.7.1", 48 | "gatsby-plugin-sharp": "^3.7.1", 49 | "gatsby-plugin-typescript": "^3.7.1", 50 | "gatsby-source-filesystem": "^3.7.1", 51 | "gatsby-transformer-sharp": "^3.7.1", 52 | "typescript": "^4.3.4" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /03-handling-images/src/common-app/mock-posts/first-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/first-post' 3 | date: '2019-05-24' 4 | title: 'My first post' 5 | --- 6 | 7 | # This is a title 8 | 9 | Hello this is my first post using markdown. 10 | 11 | `This is inline code` 12 | -------------------------------------------------------------------------------- /03-handling-images/src/common-app/mock-posts/second-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/second-post' 3 | date: '2019-05-25' 4 | title: 'My second post' 5 | --- 6 | 7 | ## This is a subtitle 8 | 9 | Hello this is my second post using markdown. 10 | 11 | ### This is a list 12 | 13 | - With one 14 | - Two 15 | - And three items 16 | -------------------------------------------------------------------------------- /03-handling-images/src/common/components/app-bar/app-bar.business.ts: -------------------------------------------------------------------------------- 1 | import { routes } from 'core/routes'; 2 | 3 | export const getPageName = (pathname: string) => 4 | pathname === routes.home ? 'Home' : 'Blog'; 5 | -------------------------------------------------------------------------------- /03-handling-images/src/common/components/app-bar/app-bar.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import { navigate } from 'gatsby'; 4 | import Bar from '@material-ui/core/AppBar'; 5 | import Toolbar from '@material-ui/core/Toolbar'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import Menu from '@material-ui/core/Menu'; 8 | import MenuItem from '@material-ui/core/MenuItem'; 9 | import MenuIcon from '@material-ui/icons/Menu'; 10 | import IconButton from '@material-ui/core/IconButton'; 11 | import { getPageName } from './app-bar.business'; 12 | import { routes } from 'core/routes'; 13 | import * as classes from './app-bar.styles'; 14 | 15 | interface Props { 16 | className?: string; 17 | pathname: string; 18 | } 19 | 20 | export const AppBar: React.FunctionComponent = props => { 21 | const { pathname, className } = props; 22 | const [isOpen, setOpen] = React.useState(false); 23 | const [element, setElement] = React.useState(null); 24 | 25 | const navigateTo = route => () => { 26 | onCloseMenu(); 27 | navigate(route); 28 | }; 29 | 30 | const onOpenMenu = e => { 31 | setElement(e.currentTarget); 32 | setOpen(true); 33 | }; 34 | 35 | const onCloseMenu = () => { 36 | setElement(null); 37 | setOpen(false); 38 | }; 39 | 40 | return ( 41 | 42 | 43 | 48 | 49 | 50 | 51 | Home 52 | Blog 53 | 54 | 55 | {getPageName(pathname)} 56 | 57 | 58 | 59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /03-handling-images/src/common/components/app-bar/app-bar.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | background-color: ${theme.palette.primary.main}; 6 | color: ${theme.palette.secondary.main}; 7 | `; 8 | 9 | export const title = css` 10 | font-size: ${theme.typography.h6.fontSize}; 11 | font-weight: ${theme.typography.h6.fontWeight}; 12 | color: ${theme.palette.secondary.main}; 13 | margin-left: 1rem; 14 | `; 15 | -------------------------------------------------------------------------------- /03-handling-images/src/common/components/app-bar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar.component'; 2 | -------------------------------------------------------------------------------- /03-handling-images/src/common/components/footer/footer.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import * as classes from './footer.styles'; 4 | 5 | interface Props { 6 | className?: string; 7 | } 8 | 9 | export const Footer: React.FunctionComponent = props => { 10 | const { className } = props; 11 | return ( 12 |
13 |
14 |
15 | Copyright 2019 Lemoncode. All Rights Reserved. 16 |
17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /03-handling-images/src/common/components/footer/footer.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | padding: 1rem; 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | `; 10 | 11 | export const line = css` 12 | height: 1px; 13 | width: 90%; 14 | background-color: ${theme.palette.primary.main}; 15 | `; 16 | 17 | export const footer = css` 18 | padding-top: 1rem; 19 | font-size: ${theme.typography.subtitle2.fontSize}; 20 | color: ${theme.palette.secondary.main}; 21 | `; 22 | -------------------------------------------------------------------------------- /03-handling-images/src/common/components/footer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './footer.component'; 2 | -------------------------------------------------------------------------------- /03-handling-images/src/common/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar'; 2 | export * from './footer'; 3 | export * from './seo.component'; 4 | -------------------------------------------------------------------------------- /03-handling-images/src/common/components/seo.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import { useStaticQuery, graphql } from 'gatsby'; 4 | 5 | interface Props { 6 | title: string; 7 | description?: string; 8 | lang?: string; 9 | meta?: any[]; 10 | keywords?: string[]; 11 | } 12 | 13 | const query = graphql` 14 | query DefaultSEOQuery { 15 | site { 16 | siteMetadata { 17 | title 18 | description 19 | author 20 | } 21 | } 22 | } 23 | `; 24 | 25 | export const SEO: React.StatelessComponent = (props) => { 26 | const { description, lang, meta, keywords, title } = props; 27 | const data = useStaticQuery(query); 28 | const metaDescription = description || data.site.siteMetadata.description; 29 | return ( 30 | 0 72 | ? { 73 | name: 'keywords', 74 | content: keywords.join(', '), 75 | } 76 | : [] 77 | ) 78 | .concat(meta)} 79 | /> 80 | ); 81 | }; 82 | 83 | SEO.defaultProps = { 84 | lang: 'en', 85 | meta: [], 86 | keywords: [], 87 | }; 88 | -------------------------------------------------------------------------------- /03-handling-images/src/core/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/03-handling-images/src/core/images/favicon.png -------------------------------------------------------------------------------- /03-handling-images/src/core/images/home-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/03-handling-images/src/core/images/home-logo.png -------------------------------------------------------------------------------- /03-handling-images/src/core/routes.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | home: '/', 3 | blog: '/blog', 4 | }; 5 | -------------------------------------------------------------------------------- /03-handling-images/src/core/theme/index.ts: -------------------------------------------------------------------------------- 1 | export * from './theme'; 2 | export * from './theme-provider.component'; 3 | -------------------------------------------------------------------------------- /03-handling-images/src/core/theme/theme-provider.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StylesProvider } from '@material-ui/styles'; 3 | import ThemeProvider from '@material-ui/styles/ThemeProvider'; 4 | import { theme } from './theme'; 5 | 6 | export const ThemeProviderComponent = props => { 7 | const { children } = props; 8 | 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /03-handling-images/src/core/theme/theme.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createMuiTheme, 3 | Theme as DefaultTheme, 4 | } from '@material-ui/core/styles'; 5 | 6 | const defaultTheme = createMuiTheme({ 7 | typography: { 8 | fontFamily: '"Open Sans", "Roboto", "Helvetica", "Arial", sans-serif', 9 | }, 10 | palette: { 11 | primary: { 12 | main: '#d9d900', 13 | }, 14 | secondary: { 15 | main: '#333326', 16 | }, 17 | }, 18 | }); 19 | 20 | type Theme = DefaultTheme; 21 | 22 | export const theme: Theme = { 23 | ...defaultTheme, 24 | }; 25 | -------------------------------------------------------------------------------- /03-handling-images/src/layouts/app-layout.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 99.9vh; 5 | display: grid; 6 | grid-template-columns: 1fr; 7 | grid-template-rows: auto 1fr auto; 8 | grid-template-areas: 9 | "header" 10 | "body" 11 | "footer"; 12 | grid-column-gap: 1rem; 13 | grid-row-gap: 0.5rem; 14 | ` 15 | 16 | export const header = css` 17 | grid-area: header; 18 | `; 19 | 20 | export const body = css` 21 | margin-left: 10%; 22 | margin-right: 10%; 23 | grid-area: body; 24 | `; 25 | 26 | export const footer = css` 27 | grid-area: footer; 28 | `; 29 | 30 | -------------------------------------------------------------------------------- /03-handling-images/src/layouts/app-layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProviderComponent } from 'core/theme'; 3 | import { AppBar, Footer } from 'common/components'; 4 | import * as classes from './app-layout.styles'; 5 | 6 | interface Props { 7 | seoComponent: React.ReactNode; 8 | pathname: string; 9 | } 10 | 11 | export const AppLayout: React.StatelessComponent = props => { 12 | const { 13 | seoComponent, 14 | children, 15 | pathname, 16 | } = props; 17 | return ( 18 | 19 | {seoComponent} 20 |
21 | 22 |
{children}
23 |
24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /03-handling-images/src/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-layout'; 2 | -------------------------------------------------------------------------------- /03-handling-images/src/pages/blog.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PageRendererProps } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Blog } from 'pods/blog'; 6 | 7 | const BlogPage: React.FunctionComponent = (props) => { 8 | const { location } = props; 9 | 10 | return ( 11 | 24 | } 25 | > 26 | 27 | 28 | ); 29 | }; 30 | 31 | export default BlogPage; 32 | -------------------------------------------------------------------------------- /03-handling-images/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PageRendererProps } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Home } from 'pods/home'; 6 | 7 | const IndexPage: React.FunctionComponent = (props) => { 8 | const { location } = props; 9 | return ( 10 | 23 | } 24 | > 25 | 26 | 27 | ); 28 | }; 29 | 30 | export default IndexPage; 31 | -------------------------------------------------------------------------------- /03-handling-images/src/pods/blog/blog.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import { Link } from 'gatsby'; 4 | import * as classes from './blog.styles'; 5 | 6 | export const Blog: React.FunctionComponent = () => { 7 | return ( 8 |
9 | Blog Page 10 |
11 | 12 | My post 13 | 14 |
15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /03-handling-images/src/pods/blog/blog.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | `; 9 | 10 | export const posts = css` 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | margin-top: 3rem; 15 | width: 100%; 16 | `; 17 | 18 | export const postTitle = css` 19 | margin-top: 3rem; 20 | `; 21 | -------------------------------------------------------------------------------- /03-handling-images/src/pods/blog/index.ts: -------------------------------------------------------------------------------- 1 | export * from './blog.component'; 2 | -------------------------------------------------------------------------------- /03-handling-images/src/pods/home/home.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GatsbyImage, getImage } from 'gatsby-plugin-image'; 3 | import { Link, useStaticQuery, graphql } from 'gatsby'; 4 | import Typography from '@material-ui/core/Typography'; 5 | import { routes } from 'core/routes'; 6 | import * as classes from './home.styles'; 7 | 8 | const query = graphql` 9 | query { 10 | homeLogo: file(relativePath: { eq: "home-logo.png" }) { 11 | childImageSharp { 12 | gatsbyImageData(layout: CONSTRAINED, placeholder: BLURRED) 13 | } 14 | } 15 | } 16 | `; 17 | 18 | export const Home: React.FunctionComponent = () => { 19 | const { homeLogo } = useStaticQuery(query); 20 | return ( 21 |
22 | Welcome to this website 23 | 24 | 25 | Check out our blog 26 | 27 |
28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /03-handling-images/src/pods/home/home.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: space-between; 8 | align-items: center; 9 | `; 10 | 11 | export const imageContainer = css` 12 | width: 50%; 13 | `; 14 | -------------------------------------------------------------------------------- /03-handling-images/src/pods/home/index.ts: -------------------------------------------------------------------------------- 1 | export * from './home.component'; 2 | -------------------------------------------------------------------------------- /03-handling-images/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "target": "es6", 5 | "moduleResolution": "node", 6 | "outDir": "./dist/", 7 | "sourceMap": true, 8 | "noImplicitAny": false, 9 | "jsx": "react", 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "baseUrl": "src", 13 | "paths": { 14 | "common": ["common"], 15 | "common-app": ["common-app"], 16 | "core": ["core"], 17 | "layouts": ["layouts"], 18 | "pods": ["pods"], 19 | } 20 | }, 21 | "include": ["./src/**/*", "./global.types.d.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /04-blog-from-md/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-gatsby"], 3 | "plugins": ["@emotion"] 4 | } 5 | -------------------------------------------------------------------------------- /04-blog-from-md/.env.example: -------------------------------------------------------------------------------- 1 | CONTENTFUL_SPACE_ID="spaceId" 2 | CONTENTFUL_ACCESS_TOKEN="accessToken" 3 | -------------------------------------------------------------------------------- /04-blog-from-md/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | -------------------------------------------------------------------------------- /04-blog-from-md/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "endOfLine": "lf" 5 | } 6 | -------------------------------------------------------------------------------- /04-blog-from-md/README.md: -------------------------------------------------------------------------------- 1 | # 04-blog-from-md 2 | 3 | In this sample we will create a blog using local markdown files. 4 | 5 | We will start from previous example `03-handling-images`: 6 | 7 | ```bash 8 | npm install 9 | ``` 10 | 11 | - Right now, we have a "hardcoded" blog page. Take a look to `blog.component`: 12 | 13 | ### ./src/pods/blog/blog.component.tsx 14 | 15 | ```javascript 16 | import React from 'react'; 17 | import Typography from '@material-ui/core/Typography'; 18 | import { Link } from 'gatsby'; 19 | import * as classes from './blog.styles'; 20 | 21 | export const Blog: React.FunctionComponent = () => { 22 | return ( 23 |
24 | Blog Page 25 |
26 | 27 | My post 28 | 29 |
30 |
31 | ); 32 | }; 33 | 34 | ``` 35 | 36 | - Instead of that, we want to use `common-app/mock-posts` files to create and style the `blog/post` components using local files and then move to cloud approach. That is, using a progressive web design: 37 | 38 | ```bash 39 | npm i gatsby-transformer-remark -D 40 | ``` 41 | 42 | ### ./gatsby-config.js 43 | 44 | ```diff 45 | ... 46 | plugins: [ 47 | ... 48 | { 49 | resolve: 'gatsby-source-filesystem', 50 | options: { 51 | name: 'images', 52 | path: path.resolve(__dirname, 'src/core/images'), 53 | }, 54 | }, 55 | + 'gatsby-transformer-remark', 56 | + { 57 | + resolve: 'gatsby-source-filesystem', 58 | + options: { 59 | + name: 'posts', 60 | + path: path.resolve(__dirname, 'src/common-app/mock-posts'), 61 | + }, 62 | + }, 63 | ], 64 | }; 65 | 66 | ``` 67 | 68 | - Take a look graphql server: 69 | 70 | ```graphql 71 | query { 72 | postListQuery: allMarkdownRemark { 73 | nodes { 74 | frontmatter { 75 | title 76 | path 77 | } 78 | html 79 | } 80 | } 81 | } 82 | ``` 83 | 84 | > NOTE: with order 85 | ``` 86 | allMarkdownRemark( 87 | sort: { fields: frontmatter___date, order: ASC } 88 | ) 89 | ``` 90 | 91 | - Update `blog` component: 92 | 93 | ### ./src/pods/blog/blog.component.tsx 94 | 95 | ```diff 96 | import React from 'react'; 97 | import Typography from '@material-ui/core/Typography'; 98 | - import { Link } from 'gatsby'; 99 | + import { Link, useStaticQuery, graphql } from 'gatsby'; 100 | import * as classes from './blog.styles'; 101 | 102 | + const query = graphql` 103 | + query { 104 | + postListQuery: allMarkdownRemark( 105 | + sort: { fields: frontmatter___date, order: ASC } 106 | + ) { 107 | + nodes { 108 | + frontmatter { 109 | + title 110 | + path 111 | + } 112 | + } 113 | + } 114 | + } 115 | + `; 116 | 117 | export const Blog: React.FunctionComponent = () => { 118 | + const { postListQuery } = useStaticQuery(query); 119 | 120 | return ( 121 |
122 | Blog Page 123 |
124 | + {postListQuery.nodes.map(node => ( 125 | - 126 | - My post 127 | - 128 | + 133 | + {node.frontmatter.title} 134 | + 135 | + ))} 136 |
137 |
138 | ); 139 | 140 | ``` 141 | 142 | - Now, it's time create the post's page, dynamically. Let's create a `post.component`: 143 | 144 | ### ./src/pods/post/post.styles.ts 145 | 146 | ```javascript 147 | import { css } from '@emotion/css'; 148 | 149 | export const root = css` 150 | height: 100%; 151 | display: flex; 152 | flex-direction: column; 153 | justify-content: flex-start; 154 | & > :nth-child(n) { 155 | margin-top: 1rem; 156 | } 157 | `; 158 | 159 | export const body = css` 160 | display: flex; 161 | flex-direction: column; 162 | align-items: center; 163 | max-width: 100%; 164 | margin-top: 3rem; 165 | font-size: 2rem; 166 | `; 167 | 168 | ``` 169 | 170 | ### ./src/pods/post/post.component.tsx 171 | 172 | ```javascript 173 | import React from 'react'; 174 | import Typography from '@material-ui/core/Typography'; 175 | import * as classes from './post.styles'; 176 | 177 | interface Props { 178 | title: string; 179 | date: string; 180 | body: string; 181 | } 182 | 183 | export const Post: React.FunctionComponent = (props) => { 184 | const { title, date, body } = props; 185 | 186 | return ( 187 |
188 | {title} 189 | {date} 190 |
194 |
195 | ); 196 | }; 197 | 198 | ``` 199 | 200 | - Take a look to GraphQL server: 201 | 202 | ```graphql 203 | query { 204 | post: markdownRemark(frontmatter: { path: { eq: "/blog/first-post" } }) { 205 | frontmatter { 206 | title 207 | date 208 | } 209 | html 210 | } 211 | } 212 | ``` 213 | 214 | - To create a dynamic page, we have to overwrite [createPages](https://www.gatsbyjs.org/docs/node-apis/#createPages) method in `gatsby-node.js`: 215 | 216 | ### ./gatsby-node.js 217 | 218 | ```javascript 219 | const { resolve } = require('path'); 220 | 221 | const query = ` 222 | query { 223 | postListQuery: allMarkdownRemark { 224 | nodes { 225 | frontmatter { 226 | path 227 | } 228 | } 229 | } 230 | } 231 | `; 232 | 233 | exports.createPages = async ({ graphql, actions }) => { 234 | const { createPage } = actions; 235 | const { data } = await graphql(query); 236 | const { postListQuery } = data; 237 | 238 | postListQuery.nodes.forEach((node) => { 239 | const { path } = node.frontmatter; 240 | if (path) { 241 | createPage({ 242 | path, 243 | component: resolve(__dirname, 'src/pods/post/post.template.tsx'), 244 | context: { 245 | slug: path, 246 | }, 247 | }); 248 | } 249 | }); 250 | }; 251 | ``` 252 | 253 | - Create `post.template.tsx`: 254 | 255 | ### ./src/pods/post/post.template.tsx 256 | 257 | > NOTE: Difference between [Static Query and page query](https://www.gatsbyjs.org/docs/static-query/#how-staticquery-differs-from-page-query) 258 | 259 | ```javascript 260 | import React from 'react'; 261 | import { graphql } from 'gatsby'; 262 | import { SEO } from 'common/components'; 263 | import { AppLayout } from 'layouts'; 264 | import { Post } from './post.component'; 265 | 266 | export const query = graphql` 267 | query($slug: String) { 268 | post: markdownRemark(frontmatter: { path: { eq: $slug } }) { 269 | frontmatter { 270 | title 271 | date 272 | } 273 | html 274 | } 275 | } 276 | `; 277 | 278 | interface Props { 279 | data: { post }; 280 | pageContext: { 281 | slug: string; 282 | }; 283 | } 284 | 285 | const PostTemplate: React.FunctionComponent = props => { 286 | const { 287 | pageContext: { slug }, 288 | data: { 289 | post: { 290 | frontmatter: { title, date }, 291 | html, 292 | }, 293 | }, 294 | } = props; 295 | 296 | return ( 297 | 311 | } 312 | > 313 | 314 | 315 | ); 316 | }; 317 | 318 | export default PostTemplate; 319 | 320 | ``` 321 | 322 | # About Basefactor + Lemoncode 323 | 324 | We are an innovating team of Javascript experts, passionate about turning your ideas into robust products. 325 | 326 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services. 327 | 328 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services. 329 | 330 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend 331 | -------------------------------------------------------------------------------- /04-blog-from-md/gatsby-config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | siteMetadata: { 5 | title: 'Gatsby by sample', 6 | description: 'Project to work with Gatsby', 7 | author: 'Lemoncode', 8 | }, 9 | plugins: [ 10 | 'gatsby-plugin-typescript', 11 | { 12 | resolve: 'gatsby-plugin-alias-imports', 13 | options: { 14 | alias: { 15 | common: path.resolve(__dirname, 'src/common'), 16 | 'common-app': path.resolve(__dirname, 'src/common-app'), 17 | core: path.resolve(__dirname, 'src/core'), 18 | layouts: path.resolve(__dirname, 'src/layouts'), 19 | pods: path.resolve(__dirname, 'src/pods'), 20 | }, 21 | }, 22 | }, 23 | { 24 | resolve: 'gatsby-plugin-manifest', 25 | options: { 26 | name: 'Gatsby by sample', 27 | short_name: 'Lemoncode-Gatsby', 28 | start_url: '/', 29 | background_color: '#f5f7fb', 30 | theme_color: '#d9d900', 31 | display: 'fullscreen', 32 | icon: 'src/core/images/favicon.png', 33 | }, 34 | }, 35 | 'gatsby-plugin-react-helmet', 36 | 'gatsby-plugin-image', 37 | 'gatsby-transformer-sharp', 38 | 'gatsby-plugin-sharp', 39 | { 40 | resolve: 'gatsby-source-filesystem', 41 | options: { 42 | name: 'images', 43 | path: path.resolve(__dirname, 'src/core/images'), 44 | }, 45 | }, 46 | 'gatsby-transformer-remark', 47 | { 48 | resolve: 'gatsby-source-filesystem', 49 | options: { 50 | name: 'posts', 51 | path: path.resolve(__dirname, 'src/common-app/mock-posts'), 52 | }, 53 | }, 54 | ], 55 | }; 56 | -------------------------------------------------------------------------------- /04-blog-from-md/gatsby-node.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path'); 2 | 3 | const query = ` 4 | query { 5 | postListQuery: allMarkdownRemark { 6 | nodes { 7 | frontmatter { 8 | path 9 | } 10 | } 11 | } 12 | } 13 | `; 14 | 15 | exports.createPages = async ({ graphql, actions }) => { 16 | const { createPage } = actions; 17 | const { data } = await graphql(query); 18 | const { postListQuery } = data; 19 | 20 | postListQuery.nodes.forEach((node) => { 21 | const { path } = node.frontmatter; 22 | if (path) { 23 | createPage({ 24 | path, 25 | component: resolve(__dirname, 'src/pods/post/post.template.tsx'), 26 | context: { 27 | slug: path, 28 | }, 29 | }); 30 | } 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /04-blog-from-md/global.types.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png'; 2 | -------------------------------------------------------------------------------- /04-blog-from-md/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-by-sample", 3 | "version": "1.0.0", 4 | "description": "Project with examples using Gatsby step by step", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "gatsby build", 8 | "start": "gatsby develop" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/Lemoncode/gatsby-by-sample.git" 13 | }, 14 | "keywords": [ 15 | "gatsby", 16 | "lemoncode", 17 | "step", 18 | "by", 19 | "step" 20 | ], 21 | "author": "Lemoncode", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/Lemoncode/gatsby-by-sample/issues" 25 | }, 26 | "homepage": "https://github.com/Lemoncode/gatsby-by-sample#readme", 27 | "dependencies": { 28 | "@emotion/css": "^11.1.3", 29 | "@material-ui/core": "^4.11.4", 30 | "@material-ui/icons": "^4.11.2", 31 | "gatsby": "^3.7.2", 32 | "gatsby-plugin-image": "^1.7.1", 33 | "normalize.css": "^8.0.1", 34 | "react": "^17.0.2", 35 | "react-dom": "^17.0.2", 36 | "react-helmet": "^6.1.0" 37 | }, 38 | "devDependencies": { 39 | "@emotion/babel-plugin": "^11.3.0", 40 | "@types/react": "^17.0.11", 41 | "@types/react-dom": "^17.0.8", 42 | "@types/react-helmet": "^6.1.1", 43 | "babel-preset-gatsby": "^1.7.1", 44 | "dotenv": "^10.0.0", 45 | "gatsby-plugin-alias-imports": "^1.0.5", 46 | "gatsby-plugin-manifest": "^3.7.1", 47 | "gatsby-plugin-react-helmet": "^4.7.1", 48 | "gatsby-plugin-sharp": "^3.7.1", 49 | "gatsby-plugin-typescript": "^3.7.1", 50 | "gatsby-source-filesystem": "^3.7.1", 51 | "gatsby-transformer-remark": "^4.4.1", 52 | "gatsby-transformer-sharp": "^3.7.1", 53 | "typescript": "^4.3.4" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /04-blog-from-md/src/common-app/mock-posts/first-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/first-post' 3 | date: '2019-05-24' 4 | title: 'My first post' 5 | --- 6 | 7 | # This is a title 8 | 9 | Hello this is my first post using markdown. 10 | 11 | `This is inline code` 12 | -------------------------------------------------------------------------------- /04-blog-from-md/src/common-app/mock-posts/second-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/second-post' 3 | date: '2019-05-25' 4 | title: 'My second post' 5 | --- 6 | 7 | ## This is a subtitle 8 | 9 | Hello this is my second post using markdown. 10 | 11 | ### This is a list 12 | 13 | - With one 14 | - Two 15 | - And three items 16 | -------------------------------------------------------------------------------- /04-blog-from-md/src/common/components/app-bar/app-bar.business.ts: -------------------------------------------------------------------------------- 1 | import { routes } from 'core/routes'; 2 | 3 | export const getPageName = (pathname: string) => 4 | pathname === routes.home ? 'Home' : 'Blog'; 5 | -------------------------------------------------------------------------------- /04-blog-from-md/src/common/components/app-bar/app-bar.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import { navigate } from 'gatsby'; 4 | import Bar from '@material-ui/core/AppBar'; 5 | import Toolbar from '@material-ui/core/Toolbar'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import Menu from '@material-ui/core/Menu'; 8 | import MenuItem from '@material-ui/core/MenuItem'; 9 | import MenuIcon from '@material-ui/icons/Menu'; 10 | import IconButton from '@material-ui/core/IconButton'; 11 | import { getPageName } from './app-bar.business'; 12 | import { routes } from 'core/routes'; 13 | import * as classes from './app-bar.styles'; 14 | 15 | interface Props { 16 | className?: string; 17 | pathname: string; 18 | } 19 | 20 | export const AppBar: React.FunctionComponent = props => { 21 | const { pathname, className } = props; 22 | const [isOpen, setOpen] = React.useState(false); 23 | const [element, setElement] = React.useState(null); 24 | 25 | const navigateTo = route => () => { 26 | onCloseMenu(); 27 | navigate(route); 28 | }; 29 | 30 | const onOpenMenu = e => { 31 | setElement(e.currentTarget); 32 | setOpen(true); 33 | }; 34 | 35 | const onCloseMenu = () => { 36 | setElement(null); 37 | setOpen(false); 38 | }; 39 | 40 | return ( 41 | 42 | 43 | 48 | 49 | 50 | 51 | Home 52 | Blog 53 | 54 | 55 | {getPageName(pathname)} 56 | 57 | 58 | 59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /04-blog-from-md/src/common/components/app-bar/app-bar.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | background-color: ${theme.palette.primary.main}; 6 | color: ${theme.palette.secondary.main}; 7 | `; 8 | 9 | export const title = css` 10 | font-size: ${theme.typography.h6.fontSize}; 11 | font-weight: ${theme.typography.h6.fontWeight}; 12 | color: ${theme.palette.secondary.main}; 13 | margin-left: 1rem; 14 | `; 15 | -------------------------------------------------------------------------------- /04-blog-from-md/src/common/components/app-bar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar.component'; 2 | -------------------------------------------------------------------------------- /04-blog-from-md/src/common/components/footer/footer.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import * as classes from './footer.styles'; 4 | 5 | interface Props { 6 | className?: string; 7 | } 8 | 9 | export const Footer: React.FunctionComponent = props => { 10 | const { className } = props; 11 | return ( 12 |
13 |
14 |
15 | Copyright 2019 Lemoncode. All Rights Reserved. 16 |
17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /04-blog-from-md/src/common/components/footer/footer.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | padding: 1rem; 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | `; 10 | 11 | export const line = css` 12 | height: 1px; 13 | width: 90%; 14 | background-color: ${theme.palette.primary.main}; 15 | `; 16 | 17 | export const footer = css` 18 | padding-top: 1rem; 19 | font-size: ${theme.typography.subtitle2.fontSize}; 20 | color: ${theme.palette.secondary.main}; 21 | `; 22 | -------------------------------------------------------------------------------- /04-blog-from-md/src/common/components/footer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './footer.component'; 2 | -------------------------------------------------------------------------------- /04-blog-from-md/src/common/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar'; 2 | export * from './footer'; 3 | export * from './seo.component'; 4 | -------------------------------------------------------------------------------- /04-blog-from-md/src/common/components/seo.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import { useStaticQuery, graphql } from 'gatsby'; 4 | 5 | interface Props { 6 | title: string; 7 | description?: string; 8 | lang?: string; 9 | meta?: any[]; 10 | keywords?: string[]; 11 | } 12 | 13 | const query = graphql` 14 | query DefaultSEOQuery { 15 | site { 16 | siteMetadata { 17 | title 18 | description 19 | author 20 | } 21 | } 22 | } 23 | `; 24 | 25 | export const SEO: React.StatelessComponent = (props) => { 26 | const { description, lang, meta, keywords, title } = props; 27 | const data = useStaticQuery(query); 28 | const metaDescription = description || data.site.siteMetadata.description; 29 | return ( 30 | 0 72 | ? { 73 | name: 'keywords', 74 | content: keywords.join(', '), 75 | } 76 | : [] 77 | ) 78 | .concat(meta)} 79 | /> 80 | ); 81 | }; 82 | 83 | SEO.defaultProps = { 84 | lang: 'en', 85 | meta: [], 86 | keywords: [], 87 | }; 88 | -------------------------------------------------------------------------------- /04-blog-from-md/src/core/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/04-blog-from-md/src/core/images/favicon.png -------------------------------------------------------------------------------- /04-blog-from-md/src/core/images/home-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/04-blog-from-md/src/core/images/home-logo.png -------------------------------------------------------------------------------- /04-blog-from-md/src/core/routes.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | home: '/', 3 | blog: '/blog', 4 | }; 5 | -------------------------------------------------------------------------------- /04-blog-from-md/src/core/theme/index.ts: -------------------------------------------------------------------------------- 1 | export * from './theme'; 2 | export * from './theme-provider.component'; 3 | -------------------------------------------------------------------------------- /04-blog-from-md/src/core/theme/theme-provider.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StylesProvider } from '@material-ui/styles'; 3 | import ThemeProvider from '@material-ui/styles/ThemeProvider'; 4 | import { theme } from './theme'; 5 | 6 | export const ThemeProviderComponent = props => { 7 | const { children } = props; 8 | 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /04-blog-from-md/src/core/theme/theme.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createMuiTheme, 3 | Theme as DefaultTheme, 4 | } from '@material-ui/core/styles'; 5 | 6 | const defaultTheme = createMuiTheme({ 7 | typography: { 8 | fontFamily: '"Open Sans", "Roboto", "Helvetica", "Arial", sans-serif', 9 | }, 10 | palette: { 11 | primary: { 12 | main: '#d9d900', 13 | }, 14 | secondary: { 15 | main: '#333326', 16 | }, 17 | }, 18 | }); 19 | 20 | type Theme = DefaultTheme; 21 | 22 | export const theme: Theme = { 23 | ...defaultTheme, 24 | }; 25 | -------------------------------------------------------------------------------- /04-blog-from-md/src/layouts/app-layout.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 99.9vh; 5 | display: grid; 6 | grid-template-columns: 1fr; 7 | grid-template-rows: auto 1fr auto; 8 | grid-template-areas: 9 | "header" 10 | "body" 11 | "footer"; 12 | grid-column-gap: 1rem; 13 | grid-row-gap: 0.5rem; 14 | ` 15 | 16 | export const header = css` 17 | grid-area: header; 18 | `; 19 | 20 | export const body = css` 21 | margin-left: 10%; 22 | margin-right: 10%; 23 | grid-area: body; 24 | `; 25 | 26 | export const footer = css` 27 | grid-area: footer; 28 | `; 29 | 30 | -------------------------------------------------------------------------------- /04-blog-from-md/src/layouts/app-layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProviderComponent } from 'core/theme'; 3 | import { AppBar, Footer } from 'common/components'; 4 | import * as classes from './app-layout.styles'; 5 | 6 | interface Props { 7 | seoComponent: React.ReactNode; 8 | pathname: string; 9 | } 10 | 11 | export const AppLayout: React.StatelessComponent = props => { 12 | const { 13 | seoComponent, 14 | children, 15 | pathname, 16 | } = props; 17 | return ( 18 | 19 | {seoComponent} 20 |
21 | 22 |
{children}
23 |
24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /04-blog-from-md/src/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-layout'; 2 | -------------------------------------------------------------------------------- /04-blog-from-md/src/pages/blog.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PageRendererProps } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Blog } from 'pods/blog'; 6 | 7 | const BlogPage: React.FunctionComponent = (props) => { 8 | const { location } = props; 9 | 10 | return ( 11 | 24 | } 25 | > 26 | 27 | 28 | ); 29 | }; 30 | 31 | export default BlogPage; 32 | -------------------------------------------------------------------------------- /04-blog-from-md/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PageRendererProps } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Home } from 'pods/home'; 6 | 7 | const IndexPage: React.FunctionComponent = (props) => { 8 | const { location } = props; 9 | return ( 10 | 23 | } 24 | > 25 | 26 | 27 | ); 28 | }; 29 | 30 | export default IndexPage; 31 | -------------------------------------------------------------------------------- /04-blog-from-md/src/pods/blog/blog.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import { Link, useStaticQuery, graphql } from 'gatsby'; 4 | import * as classes from './blog.styles'; 5 | 6 | const query = graphql` 7 | query { 8 | postListQuery: allMarkdownRemark( 9 | sort: { fields: frontmatter___date, order: ASC } 10 | ) { 11 | nodes { 12 | frontmatter { 13 | title 14 | path 15 | } 16 | } 17 | } 18 | } 19 | `; 20 | 21 | export const Blog: React.FunctionComponent = () => { 22 | const { postListQuery } = useStaticQuery(query); 23 | 24 | return ( 25 |
26 | Blog Page 27 |
28 | {postListQuery.nodes.map((node) => ( 29 | 34 | {node.frontmatter.title} 35 | 36 | ))} 37 |
38 |
39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /04-blog-from-md/src/pods/blog/blog.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | `; 9 | 10 | export const posts = css` 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | margin-top: 3rem; 15 | width: 100%; 16 | `; 17 | 18 | export const postTitle = css` 19 | margin-top: 3rem; 20 | `; 21 | -------------------------------------------------------------------------------- /04-blog-from-md/src/pods/blog/index.ts: -------------------------------------------------------------------------------- 1 | export * from './blog.component'; 2 | -------------------------------------------------------------------------------- /04-blog-from-md/src/pods/home/home.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GatsbyImage, getImage } from 'gatsby-plugin-image'; 3 | import { Link, useStaticQuery, graphql } from 'gatsby'; 4 | import Typography from '@material-ui/core/Typography'; 5 | import { routes } from 'core/routes'; 6 | import * as classes from './home.styles'; 7 | 8 | const query = graphql` 9 | query { 10 | homeLogo: file(relativePath: { eq: "home-logo.png" }) { 11 | childImageSharp { 12 | gatsbyImageData(layout: CONSTRAINED, placeholder: BLURRED) 13 | } 14 | } 15 | } 16 | `; 17 | 18 | export const Home: React.FunctionComponent = () => { 19 | const { homeLogo } = useStaticQuery(query); 20 | return ( 21 |
22 | Welcome to this website 23 | 24 | 25 | Check out our blog 26 | 27 |
28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /04-blog-from-md/src/pods/home/home.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: space-between; 8 | align-items: center; 9 | `; 10 | 11 | export const imageContainer = css` 12 | width: 50%; 13 | `; 14 | -------------------------------------------------------------------------------- /04-blog-from-md/src/pods/home/index.ts: -------------------------------------------------------------------------------- 1 | export * from './home.component'; 2 | -------------------------------------------------------------------------------- /04-blog-from-md/src/pods/post/post.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import * as classes from './post.styles'; 4 | 5 | interface Props { 6 | title: string; 7 | date: string; 8 | body: string; 9 | } 10 | 11 | export const Post: React.FunctionComponent = (props) => { 12 | const { title, date, body } = props; 13 | 14 | return ( 15 |
16 | {title} 17 | {date} 18 |
22 |
23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /04-blog-from-md/src/pods/post/post.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: flex-start; 8 | & > :nth-child(n) { 9 | margin-top: 1rem; 10 | } 11 | `; 12 | 13 | export const body = css` 14 | display: flex; 15 | flex-direction: column; 16 | align-items: center; 17 | max-width: 100%; 18 | margin-top: 3rem; 19 | font-size: 2rem; 20 | `; 21 | -------------------------------------------------------------------------------- /04-blog-from-md/src/pods/post/post.template.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { graphql } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Post } from './post.component'; 6 | 7 | export const query = graphql` 8 | query($slug: String) { 9 | post: markdownRemark(frontmatter: { path: { eq: $slug } }) { 10 | frontmatter { 11 | title 12 | date 13 | } 14 | html 15 | } 16 | } 17 | `; 18 | 19 | interface Props { 20 | data: { post }; 21 | pageContext: { 22 | slug: string; 23 | }; 24 | } 25 | 26 | const PostTemplate: React.FunctionComponent = props => { 27 | const { 28 | pageContext: { slug }, 29 | data: { 30 | post: { 31 | frontmatter: { title, date }, 32 | html, 33 | }, 34 | }, 35 | } = props; 36 | 37 | return ( 38 | 52 | } 53 | > 54 | 55 | 56 | ); 57 | }; 58 | 59 | export default PostTemplate; 60 | -------------------------------------------------------------------------------- /04-blog-from-md/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "target": "es6", 5 | "moduleResolution": "node", 6 | "outDir": "./dist/", 7 | "sourceMap": true, 8 | "noImplicitAny": false, 9 | "jsx": "react", 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "baseUrl": "src", 13 | "paths": { 14 | "common": ["common"], 15 | "common-app": ["common-app"], 16 | "core": ["core"], 17 | "layouts": ["layouts"], 18 | "pods": ["pods"], 19 | } 20 | }, 21 | "include": ["./src/**/*", "./global.types.d.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /05-blog-from-contentful/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-gatsby"], 3 | "plugins": ["@emotion"] 4 | } 5 | -------------------------------------------------------------------------------- /05-blog-from-contentful/.env.example: -------------------------------------------------------------------------------- 1 | CONTENTFUL_SPACE_ID="spaceId" 2 | CONTENTFUL_ACCESS_TOKEN="accessToken" 3 | -------------------------------------------------------------------------------- /05-blog-from-contentful/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | -------------------------------------------------------------------------------- /05-blog-from-contentful/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "endOfLine": "lf" 5 | } 6 | -------------------------------------------------------------------------------- /05-blog-from-contentful/gatsby-browser.js: -------------------------------------------------------------------------------- 1 | require('prismjs/themes/prism-tomorrow.css'); 2 | -------------------------------------------------------------------------------- /05-blog-from-contentful/gatsby-config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | siteMetadata: { 6 | title: 'Gatsby by sample', 7 | description: 'Project to work with Gatsby', 8 | author: 'Lemoncode', 9 | }, 10 | plugins: [ 11 | 'gatsby-plugin-typescript', 12 | { 13 | resolve: 'gatsby-plugin-alias-imports', 14 | options: { 15 | alias: { 16 | common: path.resolve(__dirname, 'src/common'), 17 | 'common-app': path.resolve(__dirname, 'src/common-app'), 18 | core: path.resolve(__dirname, 'src/core'), 19 | layouts: path.resolve(__dirname, 'src/layouts'), 20 | pods: path.resolve(__dirname, 'src/pods'), 21 | }, 22 | }, 23 | }, 24 | { 25 | resolve: 'gatsby-plugin-manifest', 26 | options: { 27 | name: 'Gatsby by sample', 28 | short_name: 'Lemoncode-Gatsby', 29 | start_url: '/', 30 | background_color: '#f5f7fb', 31 | theme_color: '#d9d900', 32 | display: 'fullscreen', 33 | icon: 'src/core/images/favicon.png', 34 | }, 35 | }, 36 | 'gatsby-plugin-react-helmet', 37 | 'gatsby-plugin-image', 38 | 'gatsby-transformer-sharp', 39 | 'gatsby-plugin-sharp', 40 | { 41 | resolve: 'gatsby-source-filesystem', 42 | options: { 43 | name: 'images', 44 | path: path.resolve(__dirname, 'src/core/images'), 45 | }, 46 | }, 47 | { 48 | resolve: 'gatsby-transformer-remark', 49 | options: { 50 | plugins: [ 51 | { 52 | resolve: 'gatsby-remark-images-contentful', 53 | }, 54 | { 55 | resolve: 'gatsby-remark-prismjs', 56 | }, 57 | ], 58 | }, 59 | }, 60 | { 61 | resolve: 'gatsby-source-filesystem', 62 | options: { 63 | name: 'posts', 64 | path: path.resolve(__dirname, 'src/common-app/mock-posts'), 65 | }, 66 | }, 67 | { 68 | resolve: 'gatsby-source-contentful', 69 | options: { 70 | spaceId: process.env.CONTENTFUL_SPACE_ID, 71 | accessToken: process.env.CONTENTFUL_ACCESS_TOKEN, 72 | }, 73 | }, 74 | ], 75 | }; 76 | -------------------------------------------------------------------------------- /05-blog-from-contentful/gatsby-node.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path'); 2 | 3 | const query = ` 4 | query { 5 | postListQuery: allContentfulPost { 6 | nodes { 7 | path 8 | } 9 | } 10 | } 11 | `; 12 | 13 | exports.createPages = async ({ graphql, actions }) => { 14 | const { createPage } = actions; 15 | const { data } = await graphql(query); 16 | const { postListQuery } = data; 17 | 18 | postListQuery.nodes.forEach((node) => { 19 | const { path } = node; 20 | if (path) { 21 | createPage({ 22 | path, 23 | component: resolve(__dirname, 'src/pods/post/post.template.tsx'), 24 | context: { 25 | slug: path, 26 | }, 27 | }); 28 | } 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /05-blog-from-contentful/global.types.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png'; 2 | -------------------------------------------------------------------------------- /05-blog-from-contentful/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-by-sample", 3 | "version": "1.0.0", 4 | "description": "Project with examples using Gatsby step by step", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "gatsby build", 8 | "start": "gatsby develop" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/Lemoncode/gatsby-by-sample.git" 13 | }, 14 | "keywords": [ 15 | "gatsby", 16 | "lemoncode", 17 | "step", 18 | "by", 19 | "step" 20 | ], 21 | "author": "Lemoncode", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/Lemoncode/gatsby-by-sample/issues" 25 | }, 26 | "homepage": "https://github.com/Lemoncode/gatsby-by-sample#readme", 27 | "dependencies": { 28 | "@emotion/css": "^11.1.3", 29 | "@material-ui/core": "^4.11.4", 30 | "@material-ui/icons": "^4.11.2", 31 | "gatsby": "^3.7.2", 32 | "gatsby-plugin-image": "^1.7.1", 33 | "normalize.css": "^8.0.1", 34 | "prismjs": "^1.23.0", 35 | "react": "^17.0.2", 36 | "react-dom": "^17.0.2", 37 | "react-helmet": "^6.1.0" 38 | }, 39 | "devDependencies": { 40 | "@emotion/babel-plugin": "^11.3.0", 41 | "@types/react": "^17.0.11", 42 | "@types/react-dom": "^17.0.8", 43 | "@types/react-helmet": "^6.1.1", 44 | "babel-preset-gatsby": "^1.7.1", 45 | "dotenv": "^10.0.0", 46 | "gatsby-plugin-alias-imports": "^1.0.5", 47 | "gatsby-plugin-manifest": "^3.7.1", 48 | "gatsby-plugin-react-helmet": "^4.7.1", 49 | "gatsby-plugin-sharp": "^3.7.1", 50 | "gatsby-plugin-typescript": "^3.7.1", 51 | "gatsby-remark-images-contentful": "^4.4.1", 52 | "gatsby-remark-prismjs": "^5.4.1", 53 | "gatsby-source-contentful": "^5.7.1", 54 | "gatsby-source-filesystem": "^3.7.1", 55 | "gatsby-transformer-remark": "^4.4.1", 56 | "gatsby-transformer-sharp": "^3.7.1", 57 | "typescript": "^4.3.4" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/01-add-space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/01-add-space.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/02-add-content-type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/02-add-content-type.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/03-create-post-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/03-create-post-model.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/04-add-fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/04-add-fields.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/05-required-validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/05-required-validation.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/06-custom-validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/06-custom-validation.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/07-save-changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/07-save-changes.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/08-add-content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/08-add-content.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/09-publishing-post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/09-publishing-post.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/10-add-media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/10-add-media.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/11-add-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/11-add-logo.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/12-link-existing-media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/12-link-existing-media.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/13-add-media-to-content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/13-add-media-to-content.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/14-access-to-api-key-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/14-access-to-api-key-settings.png -------------------------------------------------------------------------------- /05-blog-from-contentful/readme-resources/15-copy-space-id-access-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/readme-resources/15-copy-space-id-access-token.png -------------------------------------------------------------------------------- /05-blog-from-contentful/src/common-app/mock-posts/first-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/first-post' 3 | date: '2019-05-24' 4 | title: 'My first post' 5 | --- 6 | 7 | # This is a title 8 | 9 | Hello this is my first post using markdown. 10 | 11 | `This is inline code` 12 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/common-app/mock-posts/second-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/second-post' 3 | date: '2019-05-25' 4 | title: 'My second post' 5 | --- 6 | 7 | ## This is a subtitle 8 | 9 | Hello this is my second post using markdown. 10 | 11 | ### This is a list 12 | 13 | - With one 14 | - Two 15 | - And three items 16 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/common/components/app-bar/app-bar.business.ts: -------------------------------------------------------------------------------- 1 | import { routes } from 'core/routes'; 2 | 3 | export const getPageName = (pathname: string) => 4 | pathname === routes.home ? 'Home' : 'Blog'; 5 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/common/components/app-bar/app-bar.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import { navigate } from 'gatsby'; 4 | import Bar from '@material-ui/core/AppBar'; 5 | import Toolbar from '@material-ui/core/Toolbar'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import Menu from '@material-ui/core/Menu'; 8 | import MenuItem from '@material-ui/core/MenuItem'; 9 | import MenuIcon from '@material-ui/icons/Menu'; 10 | import IconButton from '@material-ui/core/IconButton'; 11 | import { getPageName } from './app-bar.business'; 12 | import { routes } from 'core/routes'; 13 | import * as classes from './app-bar.styles'; 14 | 15 | interface Props { 16 | className?: string; 17 | pathname: string; 18 | } 19 | 20 | export const AppBar: React.FunctionComponent = props => { 21 | const { pathname, className } = props; 22 | const [isOpen, setOpen] = React.useState(false); 23 | const [element, setElement] = React.useState(null); 24 | 25 | const navigateTo = route => () => { 26 | onCloseMenu(); 27 | navigate(route); 28 | }; 29 | 30 | const onOpenMenu = e => { 31 | setElement(e.currentTarget); 32 | setOpen(true); 33 | }; 34 | 35 | const onCloseMenu = () => { 36 | setElement(null); 37 | setOpen(false); 38 | }; 39 | 40 | return ( 41 | 42 | 43 | 48 | 49 | 50 | 51 | Home 52 | Blog 53 | 54 | 55 | {getPageName(pathname)} 56 | 57 | 58 | 59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/common/components/app-bar/app-bar.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | background-color: ${theme.palette.primary.main}; 6 | color: ${theme.palette.secondary.main}; 7 | `; 8 | 9 | export const title = css` 10 | font-size: ${theme.typography.h6.fontSize}; 11 | font-weight: ${theme.typography.h6.fontWeight}; 12 | color: ${theme.palette.secondary.main}; 13 | margin-left: 1rem; 14 | `; 15 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/common/components/app-bar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar.component'; 2 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/common/components/footer/footer.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import * as classes from './footer.styles'; 4 | 5 | interface Props { 6 | className?: string; 7 | } 8 | 9 | export const Footer: React.FunctionComponent = props => { 10 | const { className } = props; 11 | return ( 12 |
13 |
14 |
15 | Copyright 2019 Lemoncode. All Rights Reserved. 16 |
17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/common/components/footer/footer.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | padding: 1rem; 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | `; 10 | 11 | export const line = css` 12 | height: 1px; 13 | width: 90%; 14 | background-color: ${theme.palette.primary.main}; 15 | `; 16 | 17 | export const footer = css` 18 | padding-top: 1rem; 19 | font-size: ${theme.typography.subtitle2.fontSize}; 20 | color: ${theme.palette.secondary.main}; 21 | `; 22 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/common/components/footer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './footer.component'; 2 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/common/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar'; 2 | export * from './footer'; 3 | export * from './seo.component'; 4 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/common/components/seo.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import { useStaticQuery, graphql } from 'gatsby'; 4 | 5 | interface Props { 6 | title: string; 7 | description?: string; 8 | lang?: string; 9 | meta?: any[]; 10 | keywords?: string[]; 11 | } 12 | 13 | const query = graphql` 14 | query DefaultSEOQuery { 15 | site { 16 | siteMetadata { 17 | title 18 | description 19 | author 20 | } 21 | } 22 | } 23 | `; 24 | 25 | export const SEO: React.StatelessComponent = (props) => { 26 | const { description, lang, meta, keywords, title } = props; 27 | const data = useStaticQuery(query); 28 | const metaDescription = description || data.site.siteMetadata.description; 29 | return ( 30 | 0 72 | ? { 73 | name: 'keywords', 74 | content: keywords.join(', '), 75 | } 76 | : [] 77 | ) 78 | .concat(meta)} 79 | /> 80 | ); 81 | }; 82 | 83 | SEO.defaultProps = { 84 | lang: 'en', 85 | meta: [], 86 | keywords: [], 87 | }; 88 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/core/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/src/core/images/favicon.png -------------------------------------------------------------------------------- /05-blog-from-contentful/src/core/images/home-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/05-blog-from-contentful/src/core/images/home-logo.png -------------------------------------------------------------------------------- /05-blog-from-contentful/src/core/routes.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | home: '/', 3 | blog: '/blog', 4 | }; 5 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/core/theme/index.ts: -------------------------------------------------------------------------------- 1 | export * from './theme'; 2 | export * from './theme-provider.component'; 3 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/core/theme/theme-provider.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StylesProvider } from '@material-ui/styles'; 3 | import ThemeProvider from '@material-ui/styles/ThemeProvider'; 4 | import { theme } from './theme'; 5 | 6 | export const ThemeProviderComponent = props => { 7 | const { children } = props; 8 | 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/core/theme/theme.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createMuiTheme, 3 | Theme as DefaultTheme, 4 | } from '@material-ui/core/styles'; 5 | 6 | const defaultTheme = createMuiTheme({ 7 | typography: { 8 | fontFamily: '"Open Sans", "Roboto", "Helvetica", "Arial", sans-serif', 9 | }, 10 | palette: { 11 | primary: { 12 | main: '#d9d900', 13 | }, 14 | secondary: { 15 | main: '#333326', 16 | }, 17 | }, 18 | }); 19 | 20 | type Theme = DefaultTheme; 21 | 22 | export const theme: Theme = { 23 | ...defaultTheme, 24 | }; 25 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/layouts/app-layout.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 99.9vh; 5 | display: grid; 6 | grid-template-columns: 1fr; 7 | grid-template-rows: auto 1fr auto; 8 | grid-template-areas: 9 | "header" 10 | "body" 11 | "footer"; 12 | grid-column-gap: 1rem; 13 | grid-row-gap: 0.5rem; 14 | ` 15 | 16 | export const header = css` 17 | grid-area: header; 18 | `; 19 | 20 | export const body = css` 21 | margin-left: 10%; 22 | margin-right: 10%; 23 | grid-area: body; 24 | `; 25 | 26 | export const footer = css` 27 | grid-area: footer; 28 | `; 29 | 30 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/layouts/app-layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProviderComponent } from 'core/theme'; 3 | import { AppBar, Footer } from 'common/components'; 4 | import * as classes from './app-layout.styles'; 5 | 6 | interface Props { 7 | seoComponent: React.ReactNode; 8 | pathname: string; 9 | } 10 | 11 | export const AppLayout: React.StatelessComponent = props => { 12 | const { 13 | seoComponent, 14 | children, 15 | pathname, 16 | } = props; 17 | return ( 18 | 19 | {seoComponent} 20 |
21 | 22 |
{children}
23 |
24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-layout'; 2 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/pages/blog.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PageRendererProps } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Blog } from 'pods/blog'; 6 | 7 | const BlogPage: React.FunctionComponent = (props) => { 8 | const { location } = props; 9 | 10 | return ( 11 | 24 | } 25 | > 26 | 27 | 28 | ); 29 | }; 30 | 31 | export default BlogPage; 32 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PageRendererProps } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Home } from 'pods/home'; 6 | 7 | const IndexPage: React.FunctionComponent = (props) => { 8 | const { location } = props; 9 | return ( 10 | 23 | } 24 | > 25 | 26 | 27 | ); 28 | }; 29 | 30 | export default IndexPage; 31 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/pods/blog/blog.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import { Link, useStaticQuery, graphql } from 'gatsby'; 4 | import * as classes from './blog.styles'; 5 | 6 | const query = graphql` 7 | query { 8 | postListQuery: allContentfulPost(sort: { fields: date, order: ASC }) { 9 | nodes { 10 | title 11 | path 12 | } 13 | } 14 | } 15 | `; 16 | 17 | export const Blog: React.FunctionComponent = () => { 18 | const { postListQuery } = useStaticQuery(query); 19 | 20 | return ( 21 |
22 | Blog Page 23 |
24 | {postListQuery.nodes.map((node) => ( 25 | 30 | {node.title} 31 | 32 | ))} 33 |
34 |
35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/pods/blog/blog.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | `; 9 | 10 | export const posts = css` 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | margin-top: 3rem; 15 | width: 100%; 16 | `; 17 | 18 | export const postTitle = css` 19 | margin-top: 3rem; 20 | `; 21 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/pods/blog/index.ts: -------------------------------------------------------------------------------- 1 | export * from './blog.component'; 2 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/pods/home/home.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GatsbyImage, getImage } from 'gatsby-plugin-image'; 3 | import { Link, useStaticQuery, graphql } from 'gatsby'; 4 | import Typography from '@material-ui/core/Typography'; 5 | import { routes } from 'core/routes'; 6 | import * as classes from './home.styles'; 7 | 8 | const query = graphql` 9 | query { 10 | homeLogo: file(relativePath: { eq: "home-logo.png" }) { 11 | childImageSharp { 12 | gatsbyImageData(layout: CONSTRAINED, placeholder: BLURRED) 13 | } 14 | } 15 | } 16 | `; 17 | 18 | export const Home: React.FunctionComponent = () => { 19 | const { homeLogo } = useStaticQuery(query); 20 | return ( 21 |
22 | Welcome to this website 23 | 24 | 25 | Check out our blog 26 | 27 |
28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/pods/home/home.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: space-between; 8 | align-items: center; 9 | `; 10 | 11 | export const imageContainer = css` 12 | width: 50%; 13 | `; 14 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/pods/home/index.ts: -------------------------------------------------------------------------------- 1 | export * from './home.component'; 2 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/pods/post/post.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import * as classes from './post.styles'; 4 | 5 | interface Props { 6 | title: string; 7 | date: string; 8 | body: string; 9 | } 10 | 11 | export const Post: React.FunctionComponent = (props) => { 12 | const { title, date, body } = props; 13 | 14 | return ( 15 |
16 | {title} 17 | {date} 18 |
22 |
23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/pods/post/post.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: flex-start; 8 | & > :nth-child(n) { 9 | margin-top: 1rem; 10 | } 11 | `; 12 | 13 | export const body = css` 14 | display: flex; 15 | flex-direction: column; 16 | align-items: center; 17 | max-width: 100%; 18 | margin-top: 3rem; 19 | font-size: 2rem; 20 | `; 21 | -------------------------------------------------------------------------------- /05-blog-from-contentful/src/pods/post/post.template.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { graphql } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Post } from './post.component'; 6 | 7 | export const query = graphql` 8 | query ($slug: String) { 9 | post: contentfulPost(path: { eq: $slug }) { 10 | title 11 | date 12 | body { 13 | childMarkdownRemark { 14 | html 15 | } 16 | } 17 | } 18 | } 19 | `; 20 | 21 | interface Props { 22 | data: { post }; 23 | pageContext: { 24 | slug: string; 25 | }; 26 | } 27 | 28 | const PostTemplate: React.FunctionComponent = (props) => { 29 | const { 30 | pageContext: { slug }, 31 | data: { 32 | post: { 33 | title, 34 | date, 35 | body: { 36 | childMarkdownRemark: { html }, 37 | }, 38 | }, 39 | }, 40 | } = props; 41 | 42 | return ( 43 | 57 | } 58 | > 59 | 60 | 61 | ); 62 | }; 63 | 64 | export default PostTemplate; 65 | -------------------------------------------------------------------------------- /05-blog-from-contentful/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "target": "es6", 5 | "moduleResolution": "node", 6 | "outDir": "./dist/", 7 | "sourceMap": true, 8 | "noImplicitAny": false, 9 | "jsx": "react", 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "baseUrl": "src", 13 | "paths": { 14 | "common": ["common"], 15 | "common-app": ["common-app"], 16 | "core": ["core"], 17 | "layouts": ["layouts"], 18 | "pods": ["pods"], 19 | } 20 | }, 21 | "include": ["./src/**/*", "./global.types.d.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-gatsby"], 3 | "plugins": ["@emotion"] 4 | } 5 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/.env.example: -------------------------------------------------------------------------------- 1 | CONTENTFUL_SPACE_ID="spaceId" 2 | CONTENTFUL_ACCESS_TOKEN="accessToken" 3 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "endOfLine": "lf" 5 | } 6 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/README.md: -------------------------------------------------------------------------------- 1 | # 06-deploy-to-netlify 2 | 3 | In this sample we will deploy this app to Netlify. 4 | 5 | We will start from previous example `05-blog-from-contentful`: 6 | 7 | ```bash 8 | npm install 9 | ``` 10 | 11 | - First, we need to upload this app to a repository. 12 | 13 | ```bash 14 | git init 15 | git remote add origin https://github.com/... 16 | git add . 17 | git commit -m "initial commit" 18 | git push -u origin master 19 | ``` 20 | 21 | - Create an account in [Netlify](https://www.netlify.com/). 22 | 23 | - Connect Netlify with repository: 24 | 25 | ![16-connect-netlify-with-repo](readme-resources/16-connect-netlify-with-repo.png) 26 | 27 | - Select repository: 28 | 29 | ![17-select-repository](readme-resources/17-select-repository.png) 30 | 31 | - Show advance settings, add env variables and deploy site: 32 | 33 | ![18-show-advance-settings](readme-resources/18-show-advance-settings.png) 34 | 35 | ![19-add-env-variables](readme-resources/19-add-env-variables.png) 36 | 37 | 38 | - Now site is deployed. We can see that we can access to `https` site, so we have SSL for free. 39 | 40 | - Some other features: 41 | - Set up a custom domain. 42 | - Collect data from a `form` element. 43 | - Add `build hooks`. 44 | 45 | - We can see an example how to configure a `build hook` to run a deploy when some content change in Contentful: 46 | 47 | ![20-access-to-site](./readme-resources/20-access-to-site.png) 48 | 49 | ![21-access-to-site-settings](./readme-resources/21-access-to-site-settings.png) 50 | 51 | - Add `build hook`: 52 | 53 | ![22-add-build-hook](./readme-resources/22-add-build-hook.png) 54 | 55 | ![23-add-contentful-hook](./readme-resources/23-add-contentful-hook.png) 56 | 57 | - Configure web hook in Contentful: 58 | 59 | ![24-add-webhook-in-contentful](./readme-resources/24-add-webhook-in-contentful.png) 60 | 61 | ![25-create-contentful-web-hook](./readme-resources/25-create-contentful-web-hook.png) 62 | 63 | - Finally we can update the `triggers`: 64 | 65 | ![26-update-triggers](./readme-resources/26-update-triggers.png) 66 | 67 | # About Basefactor + Lemoncode 68 | 69 | We are an innovating team of Javascript experts, passionate about turning your ideas into robust products. 70 | 71 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services. 72 | 73 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services. 74 | 75 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend 76 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/gatsby-browser.js: -------------------------------------------------------------------------------- 1 | require('prismjs/themes/prism-tomorrow.css'); 2 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/gatsby-config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | siteMetadata: { 6 | title: 'Gatsby by sample', 7 | description: 'Project to work with Gatsby', 8 | author: 'Lemoncode', 9 | }, 10 | plugins: [ 11 | 'gatsby-plugin-typescript', 12 | { 13 | resolve: 'gatsby-plugin-alias-imports', 14 | options: { 15 | alias: { 16 | common: path.resolve(__dirname, 'src/common'), 17 | 'common-app': path.resolve(__dirname, 'src/common-app'), 18 | core: path.resolve(__dirname, 'src/core'), 19 | layouts: path.resolve(__dirname, 'src/layouts'), 20 | pods: path.resolve(__dirname, 'src/pods'), 21 | }, 22 | }, 23 | }, 24 | { 25 | resolve: 'gatsby-plugin-manifest', 26 | options: { 27 | name: 'Gatsby by sample', 28 | short_name: 'Lemoncode-Gatsby', 29 | start_url: '/', 30 | background_color: '#f5f7fb', 31 | theme_color: '#d9d900', 32 | display: 'fullscreen', 33 | icon: 'src/core/images/favicon.png', 34 | }, 35 | }, 36 | 'gatsby-plugin-react-helmet', 37 | 'gatsby-plugin-image', 38 | 'gatsby-transformer-sharp', 39 | 'gatsby-plugin-sharp', 40 | { 41 | resolve: 'gatsby-source-filesystem', 42 | options: { 43 | name: 'images', 44 | path: path.resolve(__dirname, 'src/core/images'), 45 | }, 46 | }, 47 | { 48 | resolve: 'gatsby-transformer-remark', 49 | options: { 50 | plugins: [ 51 | { 52 | resolve: 'gatsby-remark-images-contentful', 53 | }, 54 | { 55 | resolve: 'gatsby-remark-prismjs', 56 | }, 57 | ], 58 | }, 59 | }, 60 | { 61 | resolve: 'gatsby-source-filesystem', 62 | options: { 63 | name: 'posts', 64 | path: path.resolve(__dirname, 'src/common-app/mock-posts'), 65 | }, 66 | }, 67 | { 68 | resolve: 'gatsby-source-contentful', 69 | options: { 70 | spaceId: process.env.CONTENTFUL_SPACE_ID, 71 | accessToken: process.env.CONTENTFUL_ACCESS_TOKEN, 72 | }, 73 | }, 74 | ], 75 | }; 76 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/gatsby-node.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path'); 2 | 3 | const query = ` 4 | query { 5 | postListQuery: allContentfulPost { 6 | nodes { 7 | path 8 | } 9 | } 10 | } 11 | `; 12 | 13 | exports.createPages = async ({ graphql, actions }) => { 14 | const { createPage } = actions; 15 | const { data } = await graphql(query); 16 | const { postListQuery } = data; 17 | 18 | postListQuery.nodes.forEach((node) => { 19 | const { path } = node; 20 | if (path) { 21 | createPage({ 22 | path, 23 | component: resolve(__dirname, 'src/pods/post/post.template.tsx'), 24 | context: { 25 | slug: path, 26 | }, 27 | }); 28 | } 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/global.types.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png'; 2 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-by-sample", 3 | "version": "1.0.0", 4 | "description": "Project with examples using Gatsby step by step", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "gatsby build", 8 | "start": "gatsby develop" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/Lemoncode/gatsby-by-sample.git" 13 | }, 14 | "keywords": [ 15 | "gatsby", 16 | "lemoncode", 17 | "step", 18 | "by", 19 | "step" 20 | ], 21 | "author": "Lemoncode", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/Lemoncode/gatsby-by-sample/issues" 25 | }, 26 | "homepage": "https://github.com/Lemoncode/gatsby-by-sample#readme", 27 | "dependencies": { 28 | "@emotion/css": "^11.1.3", 29 | "@material-ui/core": "^4.11.4", 30 | "@material-ui/icons": "^4.11.2", 31 | "gatsby": "^3.7.2", 32 | "gatsby-plugin-image": "^1.7.1", 33 | "normalize.css": "^8.0.1", 34 | "prismjs": "^1.23.0", 35 | "react": "^17.0.2", 36 | "react-dom": "^17.0.2", 37 | "react-helmet": "^6.1.0" 38 | }, 39 | "devDependencies": { 40 | "@emotion/babel-plugin": "^11.3.0", 41 | "@types/react": "^17.0.11", 42 | "@types/react-dom": "^17.0.8", 43 | "@types/react-helmet": "^6.1.1", 44 | "babel-preset-gatsby": "^1.7.1", 45 | "dotenv": "^10.0.0", 46 | "gatsby-plugin-alias-imports": "^1.0.5", 47 | "gatsby-plugin-manifest": "^3.7.1", 48 | "gatsby-plugin-react-helmet": "^4.7.1", 49 | "gatsby-plugin-sharp": "^3.7.1", 50 | "gatsby-plugin-typescript": "^3.7.1", 51 | "gatsby-remark-images-contentful": "^4.4.1", 52 | "gatsby-remark-prismjs": "^5.4.1", 53 | "gatsby-source-contentful": "^5.7.1", 54 | "gatsby-source-filesystem": "^3.7.1", 55 | "gatsby-transformer-remark": "^4.4.1", 56 | "gatsby-transformer-sharp": "^3.7.1", 57 | "typescript": "^4.3.4" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/01-add-space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/01-add-space.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/02-add-content-type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/02-add-content-type.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/03-create-post-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/03-create-post-model.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/04-add-fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/04-add-fields.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/05-required-validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/05-required-validation.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/06-custom-validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/06-custom-validation.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/07-save-changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/07-save-changes.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/08-add-content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/08-add-content.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/09-publishing-post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/09-publishing-post.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/10-add-media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/10-add-media.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/11-add-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/11-add-logo.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/12-link-existing-media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/12-link-existing-media.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/13-add-media-to-content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/13-add-media-to-content.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/14-access-to-api-key-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/14-access-to-api-key-settings.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/15-copy-space-id-access-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/15-copy-space-id-access-token.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/16-connect-netlify-with-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/16-connect-netlify-with-repo.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/17-select-repository.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/17-select-repository.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/18-show-advance-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/18-show-advance-settings.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/19-add-env-variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/19-add-env-variables.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/20-access-to-site.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/20-access-to-site.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/21-access-to-site-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/21-access-to-site-settings.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/22-add-build-hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/22-add-build-hook.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/23-add-contentful-hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/23-add-contentful-hook.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/24-add-webhook-in-contentful.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/24-add-webhook-in-contentful.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/25-create-contentful-web-hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/25-create-contentful-web-hook.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/readme-resources/26-update-triggers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/readme-resources/26-update-triggers.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/common-app/mock-posts/first-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/first-post' 3 | date: '2019-05-24' 4 | title: 'My first post' 5 | --- 6 | 7 | # This is a title 8 | 9 | Hello this is my first post using markdown. 10 | 11 | `This is inline code` 12 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/common-app/mock-posts/second-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: '/blog/second-post' 3 | date: '2019-05-25' 4 | title: 'My second post' 5 | --- 6 | 7 | ## This is a subtitle 8 | 9 | Hello this is my second post using markdown. 10 | 11 | ### This is a list 12 | 13 | - With one 14 | - Two 15 | - And three items 16 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/common/components/app-bar/app-bar.business.ts: -------------------------------------------------------------------------------- 1 | import { routes } from 'core/routes'; 2 | 3 | export const getPageName = (pathname: string) => 4 | pathname === routes.home ? 'Home' : 'Blog'; 5 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/common/components/app-bar/app-bar.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import { navigate } from 'gatsby'; 4 | import Bar from '@material-ui/core/AppBar'; 5 | import Toolbar from '@material-ui/core/Toolbar'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import Menu from '@material-ui/core/Menu'; 8 | import MenuItem from '@material-ui/core/MenuItem'; 9 | import MenuIcon from '@material-ui/icons/Menu'; 10 | import IconButton from '@material-ui/core/IconButton'; 11 | import { getPageName } from './app-bar.business'; 12 | import { routes } from 'core/routes'; 13 | import * as classes from './app-bar.styles'; 14 | 15 | interface Props { 16 | className?: string; 17 | pathname: string; 18 | } 19 | 20 | export const AppBar: React.FunctionComponent = props => { 21 | const { pathname, className } = props; 22 | const [isOpen, setOpen] = React.useState(false); 23 | const [element, setElement] = React.useState(null); 24 | 25 | const navigateTo = route => () => { 26 | onCloseMenu(); 27 | navigate(route); 28 | }; 29 | 30 | const onOpenMenu = e => { 31 | setElement(e.currentTarget); 32 | setOpen(true); 33 | }; 34 | 35 | const onCloseMenu = () => { 36 | setElement(null); 37 | setOpen(false); 38 | }; 39 | 40 | return ( 41 | 42 | 43 | 48 | 49 | 50 | 51 | Home 52 | Blog 53 | 54 | 55 | {getPageName(pathname)} 56 | 57 | 58 | 59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/common/components/app-bar/app-bar.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | background-color: ${theme.palette.primary.main}; 6 | color: ${theme.palette.secondary.main}; 7 | `; 8 | 9 | export const title = css` 10 | font-size: ${theme.typography.h6.fontSize}; 11 | font-weight: ${theme.typography.h6.fontWeight}; 12 | color: ${theme.palette.secondary.main}; 13 | margin-left: 1rem; 14 | `; 15 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/common/components/app-bar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar.component'; 2 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/common/components/footer/footer.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cx } from '@emotion/css'; 3 | import * as classes from './footer.styles'; 4 | 5 | interface Props { 6 | className?: string; 7 | } 8 | 9 | export const Footer: React.FunctionComponent = props => { 10 | const { className } = props; 11 | return ( 12 |
13 |
14 |
15 | Copyright 2019 Lemoncode. All Rights Reserved. 16 |
17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/common/components/footer/footer.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | import { theme } from 'core/theme'; 3 | 4 | export const root = css` 5 | padding: 1rem; 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | `; 10 | 11 | export const line = css` 12 | height: 1px; 13 | width: 90%; 14 | background-color: ${theme.palette.primary.main}; 15 | `; 16 | 17 | export const footer = css` 18 | padding-top: 1rem; 19 | font-size: ${theme.typography.subtitle2.fontSize}; 20 | color: ${theme.palette.secondary.main}; 21 | `; 22 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/common/components/footer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './footer.component'; 2 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/common/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-bar'; 2 | export * from './footer'; 3 | export * from './seo.component'; 4 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/common/components/seo.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import { useStaticQuery, graphql } from 'gatsby'; 4 | 5 | interface Props { 6 | title: string; 7 | description?: string; 8 | lang?: string; 9 | meta?: any[]; 10 | keywords?: string[]; 11 | } 12 | 13 | const query = graphql` 14 | query DefaultSEOQuery { 15 | site { 16 | siteMetadata { 17 | title 18 | description 19 | author 20 | } 21 | } 22 | } 23 | `; 24 | 25 | export const SEO: React.StatelessComponent = (props) => { 26 | const { description, lang, meta, keywords, title } = props; 27 | const data = useStaticQuery(query); 28 | const metaDescription = description || data.site.siteMetadata.description; 29 | return ( 30 | 0 72 | ? { 73 | name: 'keywords', 74 | content: keywords.join(', '), 75 | } 76 | : [] 77 | ) 78 | .concat(meta)} 79 | /> 80 | ); 81 | }; 82 | 83 | SEO.defaultProps = { 84 | lang: 'en', 85 | meta: [], 86 | keywords: [], 87 | }; 88 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/core/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/src/core/images/favicon.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/core/images/home-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/gatsby-by-sample/d80ab18037e90e7c9258ed9a6ad78dec1b189a27/06-deploy-to-netlify/src/core/images/home-logo.png -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/core/routes.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | home: '/', 3 | blog: '/blog', 4 | }; 5 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/core/theme/index.ts: -------------------------------------------------------------------------------- 1 | export * from './theme'; 2 | export * from './theme-provider.component'; 3 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/core/theme/theme-provider.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StylesProvider } from '@material-ui/styles'; 3 | import ThemeProvider from '@material-ui/styles/ThemeProvider'; 4 | import { theme } from './theme'; 5 | 6 | export const ThemeProviderComponent = props => { 7 | const { children } = props; 8 | 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/core/theme/theme.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createMuiTheme, 3 | Theme as DefaultTheme, 4 | } from '@material-ui/core/styles'; 5 | 6 | const defaultTheme = createMuiTheme({ 7 | typography: { 8 | fontFamily: '"Open Sans", "Roboto", "Helvetica", "Arial", sans-serif', 9 | }, 10 | palette: { 11 | primary: { 12 | main: '#d9d900', 13 | }, 14 | secondary: { 15 | main: '#333326', 16 | }, 17 | }, 18 | }); 19 | 20 | type Theme = DefaultTheme; 21 | 22 | export const theme: Theme = { 23 | ...defaultTheme, 24 | }; 25 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/layouts/app-layout.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 99.9vh; 5 | display: grid; 6 | grid-template-columns: 1fr; 7 | grid-template-rows: auto 1fr auto; 8 | grid-template-areas: 9 | "header" 10 | "body" 11 | "footer"; 12 | grid-column-gap: 1rem; 13 | grid-row-gap: 0.5rem; 14 | ` 15 | 16 | export const header = css` 17 | grid-area: header; 18 | `; 19 | 20 | export const body = css` 21 | margin-left: 10%; 22 | margin-right: 10%; 23 | grid-area: body; 24 | `; 25 | 26 | export const footer = css` 27 | grid-area: footer; 28 | `; 29 | 30 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/layouts/app-layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProviderComponent } from 'core/theme'; 3 | import { AppBar, Footer } from 'common/components'; 4 | import * as classes from './app-layout.styles'; 5 | 6 | interface Props { 7 | seoComponent: React.ReactNode; 8 | pathname: string; 9 | } 10 | 11 | export const AppLayout: React.StatelessComponent = props => { 12 | const { 13 | seoComponent, 14 | children, 15 | pathname, 16 | } = props; 17 | return ( 18 | 19 | {seoComponent} 20 |
21 | 22 |
{children}
23 |
24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-layout'; 2 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/pages/blog.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PageRendererProps } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Blog } from 'pods/blog'; 6 | 7 | const BlogPage: React.FunctionComponent = (props) => { 8 | const { location } = props; 9 | 10 | return ( 11 | 24 | } 25 | > 26 | 27 | 28 | ); 29 | }; 30 | 31 | export default BlogPage; 32 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PageRendererProps } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Home } from 'pods/home'; 6 | 7 | const IndexPage: React.FunctionComponent = (props) => { 8 | const { location } = props; 9 | return ( 10 | 23 | } 24 | > 25 | 26 | 27 | ); 28 | }; 29 | 30 | export default IndexPage; 31 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/pods/blog/blog.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import { Link, useStaticQuery, graphql } from 'gatsby'; 4 | import * as classes from './blog.styles'; 5 | 6 | const query = graphql` 7 | query { 8 | postListQuery: allContentfulPost(sort: { fields: date, order: ASC }) { 9 | nodes { 10 | title 11 | path 12 | } 13 | } 14 | } 15 | `; 16 | 17 | export const Blog: React.FunctionComponent = () => { 18 | const { postListQuery } = useStaticQuery(query); 19 | 20 | return ( 21 |
22 | Blog Page 23 |
24 | {postListQuery.nodes.map((node) => ( 25 | 30 | {node.title} 31 | 32 | ))} 33 |
34 |
35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/pods/blog/blog.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | `; 9 | 10 | export const posts = css` 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | margin-top: 3rem; 15 | width: 100%; 16 | `; 17 | 18 | export const postTitle = css` 19 | margin-top: 3rem; 20 | `; 21 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/pods/blog/index.ts: -------------------------------------------------------------------------------- 1 | export * from './blog.component'; 2 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/pods/home/home.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GatsbyImage, getImage } from 'gatsby-plugin-image'; 3 | import { Link, useStaticQuery, graphql } from 'gatsby'; 4 | import Typography from '@material-ui/core/Typography'; 5 | import { routes } from 'core/routes'; 6 | import * as classes from './home.styles'; 7 | 8 | const query = graphql` 9 | query { 10 | homeLogo: file(relativePath: { eq: "home-logo.png" }) { 11 | childImageSharp { 12 | gatsbyImageData(layout: CONSTRAINED, placeholder: BLURRED) 13 | } 14 | } 15 | } 16 | `; 17 | 18 | export const Home: React.FunctionComponent = () => { 19 | const { homeLogo } = useStaticQuery(query); 20 | return ( 21 |
22 | Welcome to this website 23 | 24 | 25 | Check out our blog 26 | 27 |
28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/pods/home/home.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: space-between; 8 | align-items: center; 9 | `; 10 | 11 | export const imageContainer = css` 12 | width: 50%; 13 | `; 14 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/pods/home/index.ts: -------------------------------------------------------------------------------- 1 | export * from './home.component'; 2 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/pods/post/post.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import * as classes from './post.styles'; 4 | 5 | interface Props { 6 | title: string; 7 | date: string; 8 | body: string; 9 | } 10 | 11 | export const Post: React.FunctionComponent = (props) => { 12 | const { title, date, body } = props; 13 | 14 | return ( 15 |
16 | {title} 17 | {date} 18 |
22 |
23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/pods/post/post.styles.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css'; 2 | 3 | export const root = css` 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: flex-start; 8 | & > :nth-child(n) { 9 | margin-top: 1rem; 10 | } 11 | `; 12 | 13 | export const body = css` 14 | display: flex; 15 | flex-direction: column; 16 | align-items: center; 17 | max-width: 100%; 18 | margin-top: 3rem; 19 | font-size: 2rem; 20 | `; 21 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/src/pods/post/post.template.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { graphql } from 'gatsby'; 3 | import { SEO } from 'common/components'; 4 | import { AppLayout } from 'layouts'; 5 | import { Post } from './post.component'; 6 | 7 | export const query = graphql` 8 | query ($slug: String) { 9 | post: contentfulPost(path: { eq: $slug }) { 10 | title 11 | date 12 | body { 13 | childMarkdownRemark { 14 | html 15 | } 16 | } 17 | } 18 | } 19 | `; 20 | 21 | interface Props { 22 | data: { post }; 23 | pageContext: { 24 | slug: string; 25 | }; 26 | } 27 | 28 | const PostTemplate: React.FunctionComponent = (props) => { 29 | const { 30 | pageContext: { slug }, 31 | data: { 32 | post: { 33 | title, 34 | date, 35 | body: { 36 | childMarkdownRemark: { html }, 37 | }, 38 | }, 39 | }, 40 | } = props; 41 | 42 | return ( 43 | 57 | } 58 | > 59 | 60 | 61 | ); 62 | }; 63 | 64 | export default PostTemplate; 65 | -------------------------------------------------------------------------------- /06-deploy-to-netlify/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "target": "es6", 5 | "moduleResolution": "node", 6 | "outDir": "./dist/", 7 | "sourceMap": true, 8 | "noImplicitAny": false, 9 | "jsx": "react", 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "baseUrl": "src", 13 | "paths": { 14 | "common": ["common"], 15 | "common-app": ["common-app"], 16 | "core": ["core"], 17 | "layouts": ["layouts"], 18 | "pods": ["pods"], 19 | } 20 | }, 21 | "include": ["./src/**/*", "./global.types.d.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Lemoncode 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gatsby by sample 2 | 3 | Learn Gatsby by sample, each of the samples contains a `readme.md` file that indicates the purpose of the sample plus an step by step guide to reproduce it. 4 | 5 | # Demos 6 | 7 | ### 00 Start 8 | 9 | Usually, we are going to start with Gatsby installing the `gatsby-cli` to create initial project with some [Gatsby starter](https://www.gatsbyjs.org/docs/starters/). But this sample is the starter sample for next one. 10 | 11 | ### 01-hello-gatsby 12 | 13 | In this sample we are going to start working with Gatsby. 14 | 15 | ### 02-navigation 16 | 17 | In this sample we will add some page navigation with Gatsby. 18 | 19 | ### 03-handling-images 20 | 21 | In this sample we will config all necessary plugins to work with images. 22 | 23 | ### 04-blog-from-md 24 | 25 | In this sample we will create a blog using local markdown files. 26 | 27 | ### 05-blog-from-contentful 28 | 29 | In this sample we will create a blog using Contentful to store data. 30 | 31 | ### 06-deploy-to-netlify 32 | 33 | In this sample we will deploy this app to Netlify. 34 | 35 | # About Basefactor + Lemoncode 36 | 37 | We are an innovating team of Javascript experts, passionate about turning your ideas into robust products. 38 | 39 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services. 40 | 41 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services. 42 | 43 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend 44 | --------------------------------------------------------------------------------