Merge pull request #510 from sharetribe/add-organization-to-schema

Add organization to schema
This commit is contained in:
Vesa Luusua 2017-10-19 17:41:29 +03:00 committed by GitHub
commit cc8c685c32
10 changed files with 130 additions and 83 deletions

View file

@ -21,6 +21,15 @@ const preventDefault = e => {
e.preventDefault();
};
const twitterPageURL = siteTwitterHandle => {
if (siteTwitterHandle && siteTwitterHandle.charAt(0) === '@') {
return `https://twitter.com/${siteTwitterHandle.substring(1)}`;
} else if (siteTwitterHandle) {
return `https://twitter.com/${siteTwitterHandle}`;
}
return null;
};
class PageComponent extends Component {
componentDidMount() {
this.historyUnlisten = this.props.history.listen(() => scrollToTop());
@ -83,6 +92,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 +101,7 @@ class PageComponent extends Component {
const facebookImgs = facebookImages || [
{
name: 'facebook',
url: `${config.canonicalRootURL}${facebookImage}`,
url: `${canonicalRootURL}${facebookImage}`,
width: 1200,
height: 630,
},
@ -99,7 +109,7 @@ class PageComponent extends Component {
const twitterImgs = twitterImages || [
{
name: 'twitter',
url: `${config.canonicalRootURL}${twitterImage}`,
url: `${canonicalRootURL}${twitterImage}`,
width: 600,
height: 314,
},
@ -123,17 +133,42 @@ class PageComponent extends Component {
// eslint-disable-next-line react/no-array-index-key
const metaTags = metaToHead.map((metaProps, i) => <meta key={i} {...metaProps} />);
const facebookPage = config.siteFacebookPage;
const twitterPage = twitterPageURL(config.siteTwitterHandle);
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({
// Schema attribute can be either single schema object or an array of objects
// This makes it possible to include several different items from the same page.
// E.g. Product, Place, Video
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 (
<div className={classes}>
@ -147,7 +182,7 @@ class PageComponent extends Component {
<meta httpEquiv="Content-Type" content="text/html; charset=UTF-8" />
<meta httpEquiv="Content-Language" content={intl.locale} />
{metaTags}
<script type="application/ld+json">{schemaJSONString}</script>
<script type="application/ld+json">{schemaArrayJSONString}</script>
</Helmet>
{authInfoError ? (
<div style={{ color: 'red' }}>
@ -165,7 +200,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 +249,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

View file

@ -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,

View file

@ -210,13 +210,11 @@ export const AuthenticationPageComponent = props => {
logoutError={logoutError}
title={schemaTitle}
scrollingDisabled={scrollingDisabled}
schema={`
{
"@context": "http://schema.org",
"@type": "WebPage",
"name": "${schemaTitle}",
}
`}
schema={{
'@context': 'http://schema.org',
'@type': 'WebPage',
name: schemaTitle,
}}
>
<TopbarContainer className={topbarClasses} />
<div className={css.root}>

View file

@ -4,13 +4,13 @@ exports[`AuthenticationPageComponent matches snapshot 1`] = `
<Page
authInfoError={null}
logoutError={null}
schema="
{
\\"@context\\": \\"http://schema.org\\",
\\"@type\\": \\"WebPage\\",
\\"name\\": \\"AuthenticationPage.schemaTitleLogin\\",
}
"
schema={
Object {
"@context": "http://schema.org",
"@type": "WebPage",
"name": "AuthenticationPage.schemaTitleLogin",
}
}
scrollingDisabled={false}
title="AuthenticationPage.schemaTitleLogin"
>

View file

@ -36,17 +36,13 @@ export const LandingPageComponent = props => {
title={schemaTitle}
facebookImages={[{ url: facebookImage, width: 1200, height: 630 }]}
twitterImages={[{ url: twitterImage, width: 600, height: 314 }]}
schema={`
{
"@context": "http://schema.org",
"@type": "WebPage",
"description": "${schemaDescription}",
"name": "${schemaTitle}",
"image": [
"${schemaImage}"
]
}
`}
schema={{
'@context': 'http://schema.org',
'@type': 'WebPage',
description: schemaDescription,
name: schemaTitle,
image: [schemaImage],
}}
>
<TopbarContainer />
<div className={css.heroContainer}>

View file

@ -15,17 +15,17 @@ exports[`LandingPage matches snapshot 1`] = `
]
}
logoutError={null}
schema="
{
\\"@context\\": \\"http://schema.org\\",
\\"@type\\": \\"WebPage\\",
\\"description\\": \\"LandingPage.schemaDescription\\",
\\"name\\": \\"LandingPage.schemaTitle\\",
\\"image\\": [
\\"http://localhost:3000/saunatimeFacebook-1200x630.jpg\\"
]
}
"
schema={
Object {
"@context": "http://schema.org",
"@type": "WebPage",
"description": "LandingPage.schemaDescription",
"image": Array [
"http://localhost:3000/saunatimeFacebook-1200x630.jpg",
],
"name": "LandingPage.schemaTitle",
}
}
scrollingDisabled={false}
title="LandingPage.schemaTitle"
twitterImages={

View file

@ -335,15 +335,13 @@ export class ListingPageComponent extends Component {
facebookImages={facebookImages}
twitterImages={twitterImages}
canonicalPath={canonicalPath}
schema={`
{
"@context": "http://schema.org",
"@type": "ItemPage",
"description": "${description}",
"name": "${schemaTitle}",
"image": ${schemaImages}
}
`}
schema={{
'@context': 'http://schema.org',
'@type': 'ItemPage',
description: description,
name: schemaTitle,
image: schemaImages,
}}
>
{topbar}
<div className={listingClasses}>

View file

@ -9,15 +9,15 @@ exports[`ListingPage matches snapshot 1`] = `
description="listing1 description"
facebookImages={Array []}
logoutError={null}
schema="
{
\\"@context\\": \\"http://schema.org\\",
\\"@type\\": \\"ItemPage\\",
\\"description\\": \\"listing1 description\\",
\\"name\\": \\"ListingPage.schemaTitle\\",
\\"image\\": []
}
"
schema={
Object {
"@context": "http://schema.org",
"@type": "ItemPage",
"description": "listing1 description",
"image": "[]",
"name": "ListingPage.schemaTitle",
}
}
scrollingDisabled={false}
title="ListingPage.schemaTitle"
twitterImages={Array []}

View file

@ -276,15 +276,13 @@ export class SearchPageComponent extends Component {
scrollingDisabled={scrollingDisabled}
description={schemaDescription}
title={schemaTitle}
schema={`
{
"@context": "http://schema.org",
"@type": "SearchResultsPage",
"description": "${schemaDescription}",
"name": "${schemaTitle}",
"mainEntity": [${schemaMainEntity}]
}
`}
schema={{
'@context': 'http://schema.org',
'@type': 'SearchResultsPage',
description: schemaDescription,
name: schemaTitle,
mainEntity: [schemaMainEntity],
}}
>
<TopbarContainer className={css.topbar} />
<div className={css.container}>

View file

@ -5,15 +5,17 @@ exports[`SearchPageComponent matches snapshot 1`] = `
authInfoError={null}
description="SearchPage.schemaDescription"
logoutError={null}
schema="
{
\\"@context\\": \\"http://schema.org\\",
\\"@type\\": \\"SearchResultsPage\\",
\\"description\\": \\"SearchPage.schemaDescription\\",
\\"name\\": \\"SearchPage.schemaTitle\\",
\\"mainEntity\\": [{\\"@type\\":\\"ItemList\\",\\"name\\":\\"SearchPage.schemaMapSearch\\",\\"itemListOrder\\":\\"http://schema.org/ItemListOrderAscending\\",\\"itemListElement\\":[]}]
}
"
schema={
Object {
"@context": "http://schema.org",
"@type": "SearchResultsPage",
"description": "SearchPage.schemaDescription",
"mainEntity": Array [
"{\\"@type\\":\\"ItemList\\",\\"name\\":\\"SearchPage.schemaMapSearch\\",\\"itemListOrder\\":\\"http://schema.org/ItemListOrderAscending\\",\\"itemListElement\\":[]}",
],
"name": "SearchPage.schemaTitle",
}
}
scrollingDisabled={false}
title="SearchPage.schemaTitle"
>