Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.0] Add basic Contentful source support #1084

Merged
merged 8 commits into from
Jun 3, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions examples/using-contentful/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"env": {
"browser": true
},
"globals": {
"graphql": false
}
}
6 changes: 6 additions & 0 deletions examples/using-contentful/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Using Contentful

https://gatsby-using-contentful.netlify.com

Example site that demonstrates how to build Gatsby sites
that pull data from the [Contentful CMS API](https://www.contentful.com/).
14 changes: 14 additions & 0 deletions examples/using-contentful/gatsby-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
siteMetadata: {
title: `Gatsby with Contentful`,
},
plugins: [
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: `ubriaw6jfhm1`,
accessToken: `e481b0f7c5572374474b29f81a91e8ac487bb27d70a6f14dd12142837d8e980a`,
},
},
],
}
58 changes: 58 additions & 0 deletions examples/using-contentful/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const _ = require(`lodash`)
const Promise = require(`bluebird`)
const path = require(`path`)
const slash = require(`slash`)

// Implement the Gatsby API “createPages”. This is
// called after the Gatsby bootstrap is finished so you have
// access to any information necessary to programatically
// create pages.
exports.createPages = ({ graphql, boundActionCreators }) => {
const { createPage } = boundActionCreators
return new Promise((resolve, reject) => {
// The “graphql” function allows us to run arbitrary
// queries against the local Contentful graphql schema. Think of
// it like the site has a built-in database constructed
// from the fetched data that you can run queries against.
graphql(
`
{
allContentfulProduct(limit: 1000) {
edges {
node {
id
}
}
}
}
`
).then(result => {
if (result.errors) {
reject(result.errors)
}

// Create Product pages
const productTemplate = path.resolve(`./src/templates/product.js`)
// We want to create a detailed page for each
// product node. We'll just use the Contentful sys id for the slug.
_.each(result.data.allContentfulProduct.edges, edge => {
// Gatsby uses Redux to manage its internal state.
// Plugins and sites can use functions like "createPage"
// to interact with Gatsby.
createPage({
// Each page is required to have a `path` as well
// as a template component. The `context` is
// optional but is often necessary so the template
// can query data specific to each page.
path: `/node/${edge.node.id}/`,
component: slash(productTemplate),
context: {
id: edge.node.id,
},
})
})

resolve()
})
})
}
29 changes: 29 additions & 0 deletions examples/using-contentful/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "gatsby-example-using-contentful",
"private": true,
"description": "Gatsby example site using the Contentful source plugin",
"version": "1.0.0",
"author": "Marcus Ericsson <mericsson@gmail.com> (mericsson.com)",
"dependencies": {
"gatsby": "canary",
"gatsby-link": "canary",
"gatsby-plugin-google-analytics": "canary",
"gatsby-plugin-offline": "canary",
"gatsby-source-contentful": "canary",
"lodash": "^4.16.4",
"react-typography": "^0.15.0",
"slash": "^1.0.0",
"typography": "^0.15.8",
"typography-breakpoint-constants": "^0.14.0"
},
"keywords": [
"gatsby"
],
"license": "MIT",
"main": "n/a",
"scripts": {
"dev": "gatsby develop",
"build": "gatsby build",
"start": "gatsby serve-build"
}
}
69 changes: 69 additions & 0 deletions examples/using-contentful/src/html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from "react"
import { TypographyStyle } from "react-typography"
import * as PropTypes from "prop-types"
import typography from "./utils/typography"

let stylesStr
if (process.env.NODE_ENV === `production`) {
try {
stylesStr = require(`!raw-loader!../public/styles.css`)
} catch (e) {
console.log(e)
}
}

const propTypes = {
headComponents: PropTypes.node.isRequired,
body: PropTypes.node.isRequired,
postBodyComponents: PropTypes.node.isRequired,
}

class Html extends React.Component {
render() {
const {
headComponents,
body,
postBodyComponents,
} = this.props
let css
if (process.env.NODE_ENV === `production`) {
css = (
<style
id="gatsby-inlined-css"
dangerouslySetInnerHTML={{ __html: stylesStr }}
/>
)
}

return (
<html op="news" lang="en">
<head>
{headComponents}

<meta name="referrer" content="origin" />
<meta charSet="utf-8" />
<meta name="description" content="Gatsby example site using Contentful" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>Gatsby Contentful</title>
<TypographyStyle typography={typography} />
{css}
</head>
<body>
<div
id="___gatsby"
dangerouslySetInnerHTML={{ __html: body }}
/>
{postBodyComponents}
</body>
</html>
)
}
}

Html.propTypes = propTypes

export default Html
52 changes: 52 additions & 0 deletions examples/using-contentful/src/layouts/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from "react"
import Link from "gatsby-link"
import * as PropTypes from "prop-types"

import { rhythm } from "../utils/typography"

const propTypes = {
children: PropTypes.func.isRequired,
}

class DefaultLayout extends React.Component {
render() {
return (
<div
style={{
margin: `0 auto`,
marginTop: rhythm(1.5),
marginBottom: rhythm(1.5),
maxWidth: 650,
paddingLeft: rhythm(3 / 4),
paddingRight: rhythm(3 / 4),
}}
>
<Link style={{ textDecoration: `none` }} to="/">
<h3 style={{ color: `tomato`, marginBottom: rhythm(1.5) }}>
Example of using Contentful as a data source for a Gatsby site
</h3>
</Link>
{this.props.children()}
<hr />
<p>
The src for this website is at
{` `}
<a href="https://github.com/gatsbyjs/gatsby/tree/1.0/examples/using-contentful">
https://github.com/gatsbyjs/gatsby/tree/1.0/examples/using-contentful
</a>
</p>
<p>
The Contentful site that is providing the data for this site is at
{` `}
<a href="https://discovery.contentful.com/entries/by-content-type/2PqfXUJwE8qSYKuM0U6w8M?delivery_access_token=e481b0f7c5572374474b29f81a91e8ac487bb27d70a6f14dd12142837d8e980a&space_id=ubriaw6jfhm1">
https://discovery.contentful.com/entries/by-content-type/2PqfXUJwE8qSYKuM0U6w8M?delivery_access_token=e481b0f7c5572374474b29f81a91e8ac487bb27d70a6f14dd12142837d8e980a&space_id=ubriaw6jfhm1
</a>
</p>
</div>
)
}
}

DefaultLayout.propTypes = propTypes

export default DefaultLayout
66 changes: 66 additions & 0 deletions examples/using-contentful/src/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from "react"
import Link from "gatsby-link"
import * as PropTypes from "prop-types"

const propTypes = {
data: PropTypes.object.isRequired,
}

class IndexPage extends React.Component {
render() {
const productEdges = this.props.data.allContentfulProduct.edges
const assetEdges = this.props.data.allContentfulAsset.edges
// Not ideal, but brute force method works..
const imageUrls = productEdges.map(productEdge => {
const assetEdge = assetEdges.find(assetEdge => assetEdge.node.id === productEdge.node.image[0].sys.id)
return assetEdge && assetEdge.node.file.url
})
return (
<div>
{productEdges.map((productEdge, i) => {
const product = productEdge.node
return (
<div key={product.id}>
<Link to={`/node/${product.id}/`}>
<h4>{product.productName}</h4>
<img src={imageUrls[i]}/>
</Link>
</div>
)
})}
</div>
)
}
}

IndexPage.propTypes = propTypes

export default IndexPage

export const pageQuery = graphql`
query PageQuery {
allContentfulProduct {
edges {
node {
id
productName
image {
sys {
id
}
}
}
}
}
allContentfulAsset {
edges {
node {
id
file {
url
}
}
}
}
}
`
78 changes: 78 additions & 0 deletions examples/using-contentful/src/templates/product.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from "react"
import * as PropTypes from "prop-types"

import { rhythm } from "../utils/typography"

const propTypes = {
data: PropTypes.object.isRequired,
}

class ProductTemplate extends React.Component {
render() {
const product = this.props.data.contentfulProduct
const {
productName,
productDescription,
price,
} = product
const assetEdges = this.props.data.allContentfulAsset.edges
// Not ideal, but brute force method works..
const assetEdge = assetEdges.find(assetEdge => assetEdge.node.id === product.image[0].sys.id)
const imageUrl = assetEdge && assetEdge.node.file.url
return (
<div>
<div style={{ display: `flex`, marginBottom: rhythm(1 / 2) }}>
<div style={{ height: rhythm(2), width: rhythm(2) }}>
<img
style={{
height: `auto`,
width: `auto`,
maxWidth: rhythm(2),
maxHeight: rhythm(2),
marginRight: rhythm(1 / 2),
}}
src={imageUrl}
/>
</div>
<div style={{ display: `flex`, flexDirection: `column` }}>
<h4 style={{ marginBottom: 0 }}>{productName}</h4>
</div>
</div>
<h1>{productName}</h1>
<div>
<span>Price: ${price}</span>
<div dangerouslySetInnerHTML={{ __html: productDescription }} />
</div>
</div>
)
}
}

ProductTemplate.propTypes = propTypes

export default ProductTemplate

export const pageQuery = graphql`
query productQuery($id: String!) {
contentfulProduct(id: { eq: $id }) {
productName
productDescription
price
image {
sys {
id
}
}
}
allContentfulAsset {
edges {
node {
id
file {
url
}
}
}
}
}
`
Loading