0.4.0 (#20)
* Improve reusability of Meta component * Rename page prop to fields on pages / posts * Update config.yml with meta object for each item
This commit is contained in:
parent
7bd0b54b6d
commit
7b4780c581
12 changed files with 186 additions and 158 deletions
|
|
@ -1,3 +1,9 @@
|
|||
## 0.4.0 - 2018-06-06
|
||||
|
||||
* Improve reusability of <Meta /> component
|
||||
* Rename `page` prop to `fields` on pages / posts
|
||||
* Update config.yml with meta object for each item
|
||||
|
||||
## 0.3.1 - 2018-06-01
|
||||
|
||||
* Encode CMS image urls
|
||||
|
|
|
|||
10
cms/cms.js
10
cms/cms.js
|
|
@ -25,19 +25,19 @@ const posts = getDocuments('posts')
|
|||
|
||||
// Preview Templates
|
||||
CMS.registerPreviewTemplate('home-page', ({ entry }) => (
|
||||
<Home page={entry.toJS().data} />
|
||||
<Home fields={entry.toJS().data} />
|
||||
))
|
||||
CMS.registerPreviewTemplate('about-page', ({ entry }) => (
|
||||
<About page={entry.toJS().data} />
|
||||
<About fields={entry.toJS().data} />
|
||||
))
|
||||
CMS.registerPreviewTemplate('contact-page', ({ entry }) => (
|
||||
<Contact page={entry.toJS().data} siteTitle={globalSettings.siteTitle} />
|
||||
<Contact fields={entry.toJS().data} siteTitle={globalSettings.siteTitle} />
|
||||
))
|
||||
CMS.registerPreviewTemplate('blog-page', ({ entry }) => (
|
||||
<Blog page={entry.toJS().data} posts={posts} />
|
||||
<Blog fields={entry.toJS().data} posts={posts} />
|
||||
))
|
||||
CMS.registerPreviewTemplate('posts', ({ entry }) => (
|
||||
<SinglePost singlePost={entry.toJS().data} />
|
||||
<SinglePost fields={entry.toJS().data} />
|
||||
))
|
||||
|
||||
// Return to home when user logging out
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
---
|
||||
title: Updates
|
||||
meta:
|
||||
description: Desc
|
||||
title: Test Updates Title
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
---
|
||||
title: Hello World!
|
||||
status: Featured / Published
|
||||
categories:
|
||||
- category: News
|
||||
date: '2018-03-27'
|
||||
postFeaturedImage: /images/uploads/tim-marshall-155597.jpg
|
||||
excerpt: >-
|
||||
|
|
@ -10,6 +8,13 @@ excerpt: >-
|
|||
interdum arcu vitae augue pulvinar sodales. Sed non dui diam. Quisque lectus
|
||||
est, lobortis ac efficitur vitae, posuere a mauris. Phasellus ac dui
|
||||
pellentesque, lacinia risus ut, imperdiet eros.
|
||||
categories:
|
||||
- category: News
|
||||
meta:
|
||||
canonicalLink: ''
|
||||
description: ''
|
||||
noindex: false
|
||||
title: ''
|
||||
---
|
||||
|
||||
Paragraphs are separated by a blank line.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,14 @@ collections: # A list of collections the CMS should be able to edit
|
|||
- {label: Address, name: address, widget: text}
|
||||
- {label: Phone, name: phone, widget: string}
|
||||
- {label: Email Address, name: email, widget: string}
|
||||
- label: "Meta"
|
||||
name: "meta"
|
||||
widget: "object"
|
||||
fields:
|
||||
- {label: Title, name: title, widget: string, required: false}
|
||||
- {label: Description, name: description, widget: text, required: false, pattern: [".{,300}", "Must not exceed 300 characters"]}
|
||||
- {label: Canonical Link, name: canonicalLink, widget: string, required: false}
|
||||
- {label: Noindex, name: noindex, widget: boolean, required: false}
|
||||
|
||||
- file: "content/pages/blog.md"
|
||||
label: "Blog Page"
|
||||
|
|
@ -33,6 +41,14 @@ collections: # A list of collections the CMS should be able to edit
|
|||
- {label: Title, name: title, widget: string}
|
||||
- {label: Featured Image, name: featuredImage, widget: image}
|
||||
- {label: Subtitle, name: subtitle, widget: markdown}
|
||||
- label: "Meta"
|
||||
name: "meta"
|
||||
widget: "object"
|
||||
fields:
|
||||
- {label: Title, name: title, widget: string, required: false}
|
||||
- {label: Description, name: description, widget: text, required: false, pattern: [".{,300}", "Must not exceed 300 characters"]}
|
||||
- {label: Canonical Link, name: canonicalLink, widget: string, required: false}
|
||||
- {label: Noindex, name: noindex, widget: boolean, required: false}
|
||||
|
||||
- file: "content/pages/about.md"
|
||||
label: "About Page"
|
||||
|
|
@ -43,6 +59,14 @@ collections: # A list of collections the CMS should be able to edit
|
|||
- {label: Subtitle, name: subtitle, widget: markdown}
|
||||
- {label: Section 1, name: section1, widget: markdown}
|
||||
- {label: Section 2, name: section2, widget: markdown}
|
||||
- label: "Meta"
|
||||
name: "meta"
|
||||
widget: "object"
|
||||
fields:
|
||||
- {label: Title, name: title, widget: string, required: false}
|
||||
- {label: Description, name: description, widget: text, required: false, pattern: [".{,300}", "Must not exceed 300 characters"]}
|
||||
- {label: Canonical Link, name: canonicalLink, widget: string, required: false}
|
||||
- {label: Noindex, name: noindex, widget: boolean, required: false}
|
||||
|
||||
- file: "content/pages/home.md"
|
||||
label: "Home Page"
|
||||
|
|
@ -52,6 +76,14 @@ collections: # A list of collections the CMS should be able to edit
|
|||
- {label: Featured Image, name: featuredImage, widget: image}
|
||||
- {label: Subtitle, name: subtitle, widget: markdown}
|
||||
- {label: Body, name: body, widget: markdown}
|
||||
- label: "Meta"
|
||||
name: "meta"
|
||||
widget: "object"
|
||||
fields:
|
||||
- {label: Title, name: title, widget: string, required: false}
|
||||
- {label: Description, name: description, widget: text, required: false, pattern: [".{,300}", "Must not exceed 300 characters"]}
|
||||
- {label: Canonical Link, name: canonicalLink, widget: string, required: false}
|
||||
- {label: Noindex, name: noindex, widget: boolean, required: false}
|
||||
|
||||
|
||||
- name: posts
|
||||
|
|
@ -83,6 +115,14 @@ collections: # A list of collections the CMS should be able to edit
|
|||
collection: "postCategories"
|
||||
searchFields: ["title"]
|
||||
valueField: "title"
|
||||
- label: "Meta"
|
||||
name: "meta"
|
||||
widget: "object"
|
||||
fields:
|
||||
- {label: Title, name: title, widget: string, required: false}
|
||||
- {label: Description, name: description, widget: text, required: false, pattern: [".{,300}", "Must not exceed 300 characters"]}
|
||||
- {label: Canonical Link, name: canonicalLink, widget: string, required: false}
|
||||
- {label: Noindex, name: noindex, widget: boolean, required: false}
|
||||
|
||||
|
||||
- name: postCategories
|
||||
|
|
@ -94,6 +134,14 @@ collections: # A list of collections the CMS should be able to edit
|
|||
create: true # Allow users to create new documents in this collection
|
||||
fields: # The fields each document in this collection have
|
||||
- {label: Title, name: title, widget: string}
|
||||
- label: "Meta"
|
||||
name: "meta"
|
||||
widget: "object"
|
||||
fields:
|
||||
- {label: Title, name: title, widget: string, required: false}
|
||||
- {label: Description, name: description, widget: text, required: false, pattern: [".{,300}", "Must not exceed 300 characters"]}
|
||||
- {label: Canonical Link, name: canonicalLink, widget: string, required: false}
|
||||
- {label: Noindex, name: noindex, widget: boolean, required: false}
|
||||
|
||||
|
||||
- name: "settings"
|
||||
|
|
|
|||
149
src/App.js
149
src/App.js
|
|
@ -1,7 +1,6 @@
|
|||
import React, { Component } from 'react'
|
||||
import React, { Component, Fragment } from 'react'
|
||||
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
|
||||
import Helmet from 'react-helmet'
|
||||
import _findIndex from 'lodash/findIndex'
|
||||
|
||||
import ScrollToTop from './components/ScrollToTop'
|
||||
import Meta from './components/Meta'
|
||||
|
|
@ -19,6 +18,18 @@ import data from './data.json'
|
|||
import { slugify } from './util/url'
|
||||
import { documentHasTerm, getCollectionTerms } from './util/collection'
|
||||
|
||||
const RouteWithMeta = ({ component: Component, ...props }) => (
|
||||
<Route
|
||||
{...props}
|
||||
render={routeProps => (
|
||||
<Fragment>
|
||||
<Meta {...props} />
|
||||
<Component {...routeProps} {...props} />
|
||||
</Fragment>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
data
|
||||
|
|
@ -28,7 +39,7 @@ class App extends Component {
|
|||
this.state.data[collection] &&
|
||||
this.state.data[collection].filter(page => page.name === name)[0]
|
||||
|
||||
getDocuments = collection => this.state.data[collection]
|
||||
getDocuments = collection => this.state.data[collection] || []
|
||||
|
||||
render () {
|
||||
const globalSettings = this.getDocument('settings', 'global')
|
||||
|
|
@ -59,9 +70,7 @@ class App extends Component {
|
|||
titleTemplate={`${siteTitle} | %s`}
|
||||
/>
|
||||
<Meta
|
||||
title={siteTitle}
|
||||
url={siteUrl}
|
||||
description={siteDescription}
|
||||
headerScripts={headerScripts}
|
||||
absoluteImageUrl={
|
||||
socialMediaCard &&
|
||||
socialMediaCard.image &&
|
||||
|
|
@ -73,95 +82,75 @@ class App extends Component {
|
|||
twitterSiteAccount={
|
||||
socialMediaCard && socialMediaCard.twitterSiteAccount
|
||||
}
|
||||
headerScripts={headerScripts}
|
||||
/>
|
||||
|
||||
<Nav />
|
||||
|
||||
<Switch>
|
||||
<Route
|
||||
<RouteWithMeta
|
||||
path='/'
|
||||
exact
|
||||
render={props => (
|
||||
<Home page={this.getDocument('pages', 'home')} {...props} />
|
||||
)}
|
||||
component={Home}
|
||||
description={siteDescription}
|
||||
fields={this.getDocument('pages', 'home')}
|
||||
/>
|
||||
<Route
|
||||
<RouteWithMeta
|
||||
path='/about/'
|
||||
exact
|
||||
render={props => (
|
||||
<About page={this.getDocument('pages', 'about')} {...props} />
|
||||
)}
|
||||
component={About}
|
||||
fields={this.getDocument('pages', 'about')}
|
||||
/>
|
||||
<Route
|
||||
<RouteWithMeta
|
||||
path='/contact/'
|
||||
exact
|
||||
render={props => (
|
||||
<Contact
|
||||
page={this.getDocument('pages', 'contact')}
|
||||
siteTitle={siteTitle}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
component={Contact}
|
||||
fields={this.getDocument('pages', 'contact')}
|
||||
siteTitle={siteTitle}
|
||||
/>
|
||||
|
||||
{/* Blog Routes */}
|
||||
<Route
|
||||
<RouteWithMeta
|
||||
path='/blog/'
|
||||
exact
|
||||
render={props => (
|
||||
<Blog
|
||||
page={this.getDocument('pages', 'blog')}
|
||||
posts={posts}
|
||||
postCategories={postCategories}
|
||||
{...props}
|
||||
component={Blog}
|
||||
fields={this.getDocument('pages', 'blog')}
|
||||
posts={posts}
|
||||
postCategories={postCategories}
|
||||
/>
|
||||
|
||||
{posts.map((post, index) => {
|
||||
const path = slugify(`/blog/${post.title}`)
|
||||
const nextPost = posts[index - 1]
|
||||
const prevPost = posts[index + 1]
|
||||
return (
|
||||
<RouteWithMeta
|
||||
key={path}
|
||||
path={path}
|
||||
exact
|
||||
component={SinglePost}
|
||||
fields={post}
|
||||
nextPostURL={nextPost && slugify(`/blog/${nextPost.title}/`)}
|
||||
prevPostURL={prevPost && slugify(`/blog/${prevPost.title}/`)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path='/blog/category/:slug/'
|
||||
render={props => {
|
||||
// help needed
|
||||
const slug = props.match.params.slug
|
||||
const categoryPosts = posts.filter(post =>
|
||||
documentHasTerm(post, 'categories', slug)
|
||||
)
|
||||
if (!categoryPosts.length) return <NoMatch siteUrl={siteUrl} />
|
||||
return (
|
||||
<Blog
|
||||
page={this.getDocument('pages', 'blog')}
|
||||
posts={categoryPosts}
|
||||
postCategories={postCategories}
|
||||
showFeatured={false}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path='/blog/:slug/'
|
||||
render={props => {
|
||||
const slug = props.match.params.slug
|
||||
const singlePostID = _findIndex(
|
||||
posts,
|
||||
item => slugify(item.title) === slug
|
||||
)
|
||||
const singlePost = posts[singlePostID]
|
||||
const nextPost = posts[singlePostID - 1]
|
||||
const prevPost = posts[singlePostID + 1]
|
||||
if (!singlePost) return <NoMatch siteUrl={siteUrl} />
|
||||
return (
|
||||
<SinglePost
|
||||
singlePost={singlePost}
|
||||
nextPostURL={
|
||||
nextPost && `/blog/${slugify(nextPost.title)}/`
|
||||
}
|
||||
prevPostURL={
|
||||
prevPost && `/blog/${slugify(prevPost.title)}/`
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
||||
{postCategories.map(postCategory => {
|
||||
const slug = slugify(postCategory.title)
|
||||
const path = slugify(`/blog/category/${slug}`)
|
||||
const categoryPosts = posts.filter(post =>
|
||||
documentHasTerm(post, 'categories', slug)
|
||||
)
|
||||
return (
|
||||
<RouteWithMeta
|
||||
key={path}
|
||||
path={path}
|
||||
exact
|
||||
component={Blog}
|
||||
fields={this.getDocument('pages', 'blog')}
|
||||
posts={categoryPosts}
|
||||
postCategories={postCategories}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
||||
<Route render={() => <NoMatch siteUrl={siteUrl} />} />
|
||||
</Switch>
|
||||
|
|
|
|||
|
|
@ -1,58 +1,56 @@
|
|||
import React from 'react'
|
||||
import Helmet from 'react-helmet'
|
||||
import _get from 'lodash/get'
|
||||
import _pickBy from 'lodash/pickBy'
|
||||
|
||||
const Meta = ({
|
||||
title,
|
||||
url,
|
||||
description,
|
||||
absoluteImageUrl,
|
||||
twitterSiteAccount,
|
||||
twitterCreatorAccount,
|
||||
headerScripts
|
||||
}) => {
|
||||
const onlyTruthyValues = obj => _pickBy(obj, item => item)
|
||||
|
||||
const Meta = props => {
|
||||
const {
|
||||
title,
|
||||
url,
|
||||
description,
|
||||
absoluteImageUrl,
|
||||
twitterSiteAccount,
|
||||
twitterCreatorAccount,
|
||||
headerScripts,
|
||||
noindex,
|
||||
canonicalLink
|
||||
// overwrite { title, description } if in fields or fields.meta
|
||||
} = {
|
||||
...props,
|
||||
...onlyTruthyValues(_get(props, 'fields')),
|
||||
...onlyTruthyValues(_get(props, 'fields.meta'))
|
||||
}
|
||||
|
||||
// write headerScripts
|
||||
const headerScriptsElement = document.head.querySelector('#headerScripts')
|
||||
if (headerScripts && headerScriptsElement) {
|
||||
headerScriptsElement.outerHTML = headerScripts
|
||||
}
|
||||
|
||||
const runChecks = () => {
|
||||
if (process.env.NODE_ENV !== 'development') return
|
||||
|
||||
// Site Url Check
|
||||
if (url === 'https://netlify-cms-react-starter.netlify.com') {
|
||||
console.warn(`Site url may need updating (${url})`)
|
||||
}
|
||||
|
||||
// Title
|
||||
if (!title) {
|
||||
console.error(`Missing Site Title`)
|
||||
}
|
||||
|
||||
// Description
|
||||
if (!description) {
|
||||
console.error(`Missing Site Description`)
|
||||
}
|
||||
}
|
||||
|
||||
runChecks()
|
||||
|
||||
return (
|
||||
<Helmet>
|
||||
<meta name='description' content={description} />
|
||||
<meta property='og:title' content={title} />
|
||||
<meta property='og:type' content='website' />
|
||||
<meta property='og:url' content={url} />
|
||||
<meta property='og:description' content={description} />
|
||||
{title && <title>{title}</title>}
|
||||
{title && <meta property='og:title' content={title} />}
|
||||
{description && <meta name='description' content={description} />}
|
||||
{description && <meta property='og:description' content={description} />}
|
||||
{url && <meta property='og:type' content='website' />}
|
||||
{url && <meta property='og:url' content={url} />}
|
||||
{absoluteImageUrl && (
|
||||
<meta name='twitter:card' content='summary_large_image' />
|
||||
)}
|
||||
{absoluteImageUrl && (
|
||||
<meta property='og:image' content={absoluteImageUrl} />
|
||||
)}
|
||||
<meta name='twitter:card' content='summary_large_image' />
|
||||
{twitterSiteAccount && (
|
||||
<meta name='twitter:site' content={twitterSiteAccount} />
|
||||
)}
|
||||
{twitterCreatorAccount && (
|
||||
<meta name='twitter:creator' content={twitterCreatorAccount} />
|
||||
)}
|
||||
{noindex && <meta name='robots' content='noindex' />}
|
||||
{canonicalLink && <link rel='canonical' href={canonicalLink} />}
|
||||
</Helmet>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,14 @@
|
|||
import React from 'react'
|
||||
import Helmet from 'react-helmet'
|
||||
|
||||
import PageHeader from '../components/PageHeader'
|
||||
import LazyImage from '../components/LazyImage'
|
||||
import Content from '../components/Content.js'
|
||||
import './About.css'
|
||||
|
||||
export default ({ page }) => {
|
||||
const { title, subtitle, featuredImage, section1, section2 } = page
|
||||
export default ({ fields }) => {
|
||||
const { title, subtitle, featuredImage, section1, section2 } = fields
|
||||
return (
|
||||
<div className='About'>
|
||||
<Helmet>
|
||||
<title>{title}</title>
|
||||
</Helmet>
|
||||
<PageHeader
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react'
|
||||
import Helmet from 'react-helmet'
|
||||
import _sortBy from 'lodash/sortBy'
|
||||
|
||||
import PageHeader from '../components/PageHeader'
|
||||
|
|
@ -9,20 +8,16 @@ import PostSection from '../components/PostSection'
|
|||
import './Blog.css'
|
||||
|
||||
export default ({
|
||||
page,
|
||||
fields,
|
||||
posts = [],
|
||||
postCategories = [],
|
||||
showFeatured = true
|
||||
}) => {
|
||||
const { title, subtitle, featuredImage } = page
|
||||
const { title, subtitle, featuredImage } = fields
|
||||
posts = _sortBy(posts, ['date']).reverse()
|
||||
|
||||
return (
|
||||
<main className='Blog'>
|
||||
<Helmet>
|
||||
<title>{title}</title>
|
||||
</Helmet>
|
||||
|
||||
<PageHeader
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
|
|
@ -32,6 +27,7 @@ export default ({
|
|||
{!!postCategories.length && (
|
||||
<PostCategoriesNav categories={postCategories} />
|
||||
)}
|
||||
|
||||
{!!posts.length && <PostSection posts={posts} />}
|
||||
</main>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react'
|
||||
import Helmet from 'react-helmet'
|
||||
import { MapPin, Smartphone, Mail } from 'react-feather'
|
||||
|
||||
import PageHeader from '../components/PageHeader'
|
||||
|
|
@ -7,14 +6,10 @@ import EnquiryFormSimpleAjax from '../components/EnquiryFormSimpleAjax'
|
|||
import Content from '../components/Content'
|
||||
import './Contact.css'
|
||||
|
||||
export default ({ page }) => {
|
||||
const { title, subtitle, featuredImage, address, phone, email } = page
|
||||
export default ({ fields }) => {
|
||||
const { body, title, subtitle, featuredImage, address, phone, email } = fields
|
||||
return (
|
||||
<div className='Contact'>
|
||||
<Helmet>
|
||||
<title>{page.title}</title>
|
||||
</Helmet>
|
||||
|
||||
<PageHeader
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
|
|
@ -24,7 +19,7 @@ export default ({ page }) => {
|
|||
<div className='section Contact--Section1'>
|
||||
<div className='container Contact--Section1--Container'>
|
||||
<div>
|
||||
<Content source={page.body} />
|
||||
<Content source={body} />
|
||||
|
||||
<div className='Contact--Details'>
|
||||
{address && (
|
||||
|
|
|
|||
|
|
@ -1,23 +1,20 @@
|
|||
import React from 'react'
|
||||
import Helmet from 'react-helmet'
|
||||
|
||||
import Content from '../components/Content'
|
||||
import PageHeader from '../components/PageHeader'
|
||||
import './Home.css'
|
||||
|
||||
export default ({ page }) => {
|
||||
const { title, subtitle, featuredImage, body } = page
|
||||
export default ({ fields }) => {
|
||||
const { title, subtitle, featuredImage, body } = fields
|
||||
return (
|
||||
<main className='Home'>
|
||||
<Helmet>
|
||||
<title>{title}</title>
|
||||
</Helmet>
|
||||
<PageHeader
|
||||
large
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
backgroundImage={featuredImage}
|
||||
/>
|
||||
|
||||
<div className='section'>
|
||||
<div className='container'>
|
||||
<Content source={body} />
|
||||
|
|
|
|||
|
|
@ -1,22 +1,16 @@
|
|||
import React from 'react'
|
||||
import Helmet from 'react-helmet'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { ChevronLeft } from 'react-feather'
|
||||
|
||||
import Content from '../components/Content'
|
||||
import BackgroundImage from '../components/BackgroundImage'
|
||||
import { dateFormatted } from '../util/date'
|
||||
|
||||
import './SinglePost.css'
|
||||
|
||||
export default ({ singlePost, nextPostURL, prevPostURL }) => {
|
||||
const { title, date, postFeaturedImage, body, categories = [] } = singlePost
|
||||
export default ({ fields, nextPostURL, prevPostURL }) => {
|
||||
const { title, date, postFeaturedImage, body, categories = [] } = fields
|
||||
return (
|
||||
<article className='SinglePost section light'>
|
||||
<Helmet>
|
||||
<title>{title}</title>
|
||||
</Helmet>
|
||||
|
||||
{postFeaturedImage && (
|
||||
<BackgroundImage
|
||||
className='SinglePost--BackgroundImage'
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue