From 0e9d7ac04fdf9a7d30cca8c7ccf7565d3955502f Mon Sep 17 00:00:00 2001 From: Alec Brunelle Date: Fri, 25 Sep 2020 13:17:35 -0400 Subject: [PATCH] feat(minimal-blog): Add Canonical URL support (#494) Co-authored-by: LekoArts --- cypress/e2e/minimal-blog.ts | 4 ++++ .../posts/curses-and-counter-courses/index.mdx | 1 + themes/gatsby-theme-minimal-blog-core/gatsby-node.js | 3 +++ .../src/templates/post-query.tsx | 1 + themes/gatsby-theme-minimal-blog/README.md | 3 ++- .../gatsby-theme-minimal-blog/src/components/post.tsx | 2 ++ .../gatsby-theme-minimal-blog/src/components/seo.tsx | 11 ++++++++++- 7 files changed, 23 insertions(+), 2 deletions(-) diff --git a/cypress/e2e/minimal-blog.ts b/cypress/e2e/minimal-blog.ts index 8fdee6f7c..ea62e4db0 100644 --- a/cypress/e2e/minimal-blog.ts +++ b/cypress/e2e/minimal-blog.ts @@ -122,4 +122,8 @@ describe(`gatsby-theme-minimal-blog`, () => { .should(`have.css`, `color`, `rgb(203, 213, 224)`) .should(`have.css`, `background`, `rgb(26, 32, 44) none repeat scroll 0% 0% / auto padding-box border-box`) }) + it(`should accept canonical url in frontmatter and set in head`, () => { + cy.visit(`/curses-counter-curses-and-more`).waitForRouteChange() + cy.get(`head link[rel='canonical']`).should(`have.attr`, `href`, `https://random-blog-about-curses.com`) + }) }) diff --git a/examples/minimal-blog/content/posts/curses-and-counter-courses/index.mdx b/examples/minimal-blog/content/posts/curses-and-counter-courses/index.mdx index 8adbe6ac1..a2f09f1dd 100644 --- a/examples/minimal-blog/content/posts/curses-and-counter-courses/index.mdx +++ b/examples/minimal-blog/content/posts/curses-and-counter-courses/index.mdx @@ -2,6 +2,7 @@ title: "Curses and Counter-curses (Bewitch Your Friends and Befuddle Your Enemies with the Latest Revenges: Hair Loss, Jelly-Legs, Tongue-Tying, and Much, Much More)" date: 2019-10-25 slug: "/curses-counter-curses-and-more" +canonicalUrl: "https://random-blog-about-curses.com" --- Thestral dirigible plums, Viktor Krum hexed memory charm Animagus Invisibility Cloak three-headed Dog. Half-Blood Prince Invisibility Cloak cauldron cakes, hiya Harry! Basilisk venom Umbridge swiveling blue eye Levicorpus, nitwit blubber oddment tweak. Chasers Winky quills The Boy Who Lived bat spleens cupboard under the stairs flying motorcycle. Sirius Black Holyhead Harpies, you’ve got dirt on your nose. Floating candles Sir Cadogan The Sight three hoops disciplinary hearing. Grindlewald pig’s tail Sorcerer's Stone biting teacup. Side-along dragon-scale suits Filch 20 points, Mr. Potter. diff --git a/themes/gatsby-theme-minimal-blog-core/gatsby-node.js b/themes/gatsby-theme-minimal-blog-core/gatsby-node.js index eb6dfddd2..ec13214a2 100644 --- a/themes/gatsby-theme-minimal-blog-core/gatsby-node.js +++ b/themes/gatsby-theme-minimal-blog-core/gatsby-node.js @@ -60,6 +60,7 @@ exports.createSchemaCustomization = ({ actions, schema }, themeOptions) => { tags: [PostTag] banner: File @fileByRelativePath description: String + canonicalUrl: String } type PostTag { @@ -86,6 +87,7 @@ exports.createSchemaCustomization = ({ actions, schema }, themeOptions) => { tags: [PostTag] banner: File @fileByRelativePath description: String + canonicalUrl: String } type MdxPage implements Node & Page { @@ -195,6 +197,7 @@ exports.onCreateNode = ({ node, actions, getNode, createNodeId, createContentDig tags: modifiedTags, banner: node.frontmatter.banner, description: node.frontmatter.description, + canonicalUrl: node.frontmatter.canonicalUrl, } const mdxPostId = createNodeId(`${node.id} >>> MdxPost`) diff --git a/themes/gatsby-theme-minimal-blog-core/src/templates/post-query.tsx b/themes/gatsby-theme-minimal-blog-core/src/templates/post-query.tsx index d11c882eb..4723d644c 100644 --- a/themes/gatsby-theme-minimal-blog-core/src/templates/post-query.tsx +++ b/themes/gatsby-theme-minimal-blog-core/src/templates/post-query.tsx @@ -14,6 +14,7 @@ export const query = graphql` slug } description + canonicalUrl body excerpt timeToRead diff --git a/themes/gatsby-theme-minimal-blog/README.md b/themes/gatsby-theme-minimal-blog/README.md index 99a43eae1..05c021393 100644 --- a/themes/gatsby-theme-minimal-blog/README.md +++ b/themes/gatsby-theme-minimal-blog/README.md @@ -261,10 +261,11 @@ tags: - Tutorial - Dark Arts banner: ./defence-against-the-dark-arts.jpg +canonicalUrl: https://random-blog-about-curses.com/curses-counter-curses-and-more --- ``` -**The fields `description` and `banner` are optional!** If no description is provided, an excerpt of the blog post will be used. If no banner is provided, the default `siteImage` (from `siteMetadata`) is used. +**The fields `description`, `banner` and `canonicalUrl` are optional!** If no description is provided, an excerpt of the blog post will be used. If no banner is provided, the default `siteImage` (from `siteMetadata`) is used. If no `canonicalUrl` is provided, it will not be included in the header. The `date` field has to be written in the format `YYYY-MM-DD`! diff --git a/themes/gatsby-theme-minimal-blog/src/components/post.tsx b/themes/gatsby-theme-minimal-blog/src/components/post.tsx index c259ca439..191f4a72c 100644 --- a/themes/gatsby-theme-minimal-blog/src/components/post.tsx +++ b/themes/gatsby-theme-minimal-blog/src/components/post.tsx @@ -17,6 +17,7 @@ type PostProps = { slug: string }[] description?: string + canonicalUrl?: string body: string excerpt: string timeToRead?: number @@ -41,6 +42,7 @@ const Post = ({ data: { post } }: PostProps) => ( description={post.description ? post.description : post.excerpt} image={post.banner ? post.banner.childImageSharp.resize.src : undefined} pathname={post.slug} + canonicalUrl={post.canonicalUrl} /> {post.title}

diff --git a/themes/gatsby-theme-minimal-blog/src/components/seo.tsx b/themes/gatsby-theme-minimal-blog/src/components/seo.tsx index 2884b7142..ca6b65985 100644 --- a/themes/gatsby-theme-minimal-blog/src/components/seo.tsx +++ b/themes/gatsby-theme-minimal-blog/src/components/seo.tsx @@ -9,9 +9,17 @@ type SEOProps = { pathname?: string image?: string children?: React.ReactNode + canonicalUrl?: string } -const SEO = ({ title = ``, description = ``, pathname = ``, image = ``, children = null }: SEOProps) => { +const SEO = ({ + title = ``, + description = ``, + pathname = ``, + image = ``, + children = null, + canonicalUrl = ``, +}: SEOProps) => { const site = useSiteMetadata() const { @@ -52,6 +60,7 @@ const SEO = ({ title = ``, description = ``, pathname = ``, image = ``, children + {canonicalUrl ? : null} {children} )