From 7acb4f035e5c1f52ed92ecd1c39f79e5a536c0ac Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Thu, 19 Oct 2017 15:27:59 +0300 Subject: [PATCH] Add organization data to schema (will be used on footer and about page too. --- src/components/Page/Page.js | 45 ++++++++++++++++++++++++++++--------- src/config.js | 22 +++++++++++++++++- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/components/Page/Page.js b/src/components/Page/Page.js index ed51674f..cf52f9de 100644 --- a/src/components/Page/Page.js +++ b/src/components/Page/Page.js @@ -83,6 +83,7 @@ class PageComponent extends Component { const { pathname, search = '' } = history.location; const pathWithSearch = `${pathname}${search}`; + const canonicalRootURL = config.canonicalRootURL; const siteTitle = config.siteTitle; const schemaTitle = intl.formatMessage({ id: 'Page.schemaTitle' }, { siteTitle }); const schemaDescription = intl.formatMessage({ id: 'Page.schemaDescription' }); @@ -91,7 +92,7 @@ class PageComponent extends Component { const facebookImgs = facebookImages || [ { name: 'facebook', - url: `${config.canonicalRootURL}${facebookImage}`, + url: `${canonicalRootURL}${facebookImage}`, width: 1200, height: 630, }, @@ -99,7 +100,7 @@ class PageComponent extends Component { const twitterImgs = twitterImages || [ { name: 'twitter', - url: `${config.canonicalRootURL}${twitterImage}`, + url: `${canonicalRootURL}${twitterImage}`, width: 600, height: 314, }, @@ -123,17 +124,41 @@ class PageComponent extends Component { // eslint-disable-next-line react/no-array-index-key const metaTags = metaToHead.map((metaProps, i) => ); + const facebookPage = config.siteFacebookPage; + const twitterPage = + config.siteTwitterHandle && config.siteTwitterHandle.charAt(0) === '@' + ? `https://twitter.com/${config.siteTwitterHandle.substring(1)}` + : null; + const instagramPage = config.siteInstagramPage; + const sameOrganizationAs = [facebookPage, twitterPage, instagramPage].filter(v => v != null); + // Schema for search engines (helps them to understand what this page is about) // http://schema.org // We are using JSON-LD format - const schemaJSONString = - schema || - JSON.stringify({ + const schemaFromProps = Array.isArray(schema) ? schema : [schema]; + const schemaArrayJSONString = JSON.stringify([ + ...schemaFromProps, + { '@context': 'http://schema.org', - '@type': 'WebPage', + '@type': 'Organization', + '@id': `${canonicalRootURL}#organization`, + url: canonicalRootURL, + name: siteTitle, + sameAs: sameOrganizationAs, + logo: `${canonicalRootURL}/static/webapp-icon-192x192.png`, + address: config.address, + }, + { + '@context': 'http://schema.org', + '@type': 'WebSite', + url: canonicalRootURL, description: schemaDescription, name: schemaTitle, - }); + publisher: { + '@id': `${canonicalRootURL}#organization`, + }, + }, + ]); return (
@@ -147,7 +172,7 @@ class PageComponent extends Component { {metaTags} - + {authInfoError ? (
@@ -165,7 +190,7 @@ class PageComponent extends Component { } } -const { any, arrayOf, bool, func, number, shape, string } = PropTypes; +const { any, array, arrayOf, bool, func, number, object, oneOfType, shape, string } = PropTypes; PageComponent.defaultProps = { className: null, @@ -214,7 +239,7 @@ PageComponent.propTypes = { }) ), published: string, // article:published_time - schema: string, // http://schema.org + schema: oneOfType([object, array]), // http://schema.org tags: string, // article:tag title: string.isRequired, // page title twitterHandle: string, // twitter handle diff --git a/src/config.js b/src/config.js index e6acdfa8..d9be9791 100644 --- a/src/config.js +++ b/src/config.js @@ -187,15 +187,27 @@ const stripeSupportedCountries = [ }, ]; +// Address information is used in SEO schema for Organization (http://schema.org/PostalAddress) +const addressCountry = 'FI'; +const addressRegion = 'Helsinki'; +const postalCode = '00100'; +const streetAddress = 'Bulevardi 14'; + // Canonical root url is needed in social media sharing and SEO optimization purposes. const canonicalRootURL = process.env.REACT_APP_CANONICAL_ROOT_URL || 'http://localhost:3000'; // Site title is needed in meta tags (bots and social media sharing reads those) const siteTitle = 'Saunatime'; -// Twitter handle is needed in meta tags (twitter:site) +// Twitter handle is needed in meta tags (twitter:site). Start it with '@' character const siteTwitterHandle = '@sharetribe'; +// Instagram page is used in SEO schema (http://schema.org/Organization) +const siteInstagramPage = null; + +// Facebook page is used in SEO schema (http://schema.org/Organization) +const siteFacebookPage = null; + // Facebook counts shares with app or page associated by this id // Currently it is unset, but you can read more about fb:app_id from // https://developers.facebook.com/docs/sharing/webmasters#basic @@ -212,7 +224,15 @@ const config = { currencyConfig, stripe: { publishableKey: stripePublishableKey, supportedCountries: stripeSupportedCountries }, canonicalRootURL, + address: { + addressCountry, + addressRegion, + postalCode, + streetAddress, + }, siteTitle, + siteFacebookPage, + siteInstagramPage, siteTwitterHandle, facebookAppId, sentryDsn,