SEO
This commit is contained in:
parent
bfdd36ff9c
commit
9873372623
7 changed files with 172 additions and 73 deletions
45
src/components/SEO/SEO.stories.svelte
Normal file
45
src/components/SEO/SEO.stories.svelte
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<script>
|
||||||
|
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import componentDocs from './stories/docs/component.md?raw';
|
||||||
|
// @ts-ignore
|
||||||
|
import quickitDocs from './stories/docs/quickit.md?raw';
|
||||||
|
|
||||||
|
import SEO from './SEO.svelte';
|
||||||
|
|
||||||
|
import {
|
||||||
|
withComponentDocs,
|
||||||
|
withStoryDocs,
|
||||||
|
} from '$lib/docs/utils/withParams.js';
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Utilities/SEO',
|
||||||
|
component: SEO,
|
||||||
|
...withComponentDocs(componentDocs),
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Meta {...meta} />
|
||||||
|
|
||||||
|
<Template let:args>
|
||||||
|
<SEO {...args} />
|
||||||
|
<div>Nothing to see here. 😎</div>
|
||||||
|
</Template>
|
||||||
|
|
||||||
|
<Story
|
||||||
|
name="Default"
|
||||||
|
args={{
|
||||||
|
url: new URL('https://graphics.reuters.com/hello-world/'),
|
||||||
|
publishTime: new Date('2020-09-15').toISOString(),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Story
|
||||||
|
name="🚀 QUICKIT"
|
||||||
|
args={{
|
||||||
|
url: new URL('https://graphics.reuters.com/hello-world/'),
|
||||||
|
publishTime: new Date('2020-09-15').toISOString(),
|
||||||
|
}}
|
||||||
|
{...withStoryDocs(quickitDocs)}
|
||||||
|
/>
|
||||||
|
|
@ -1,45 +1,76 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import pkg from '$pkg';
|
|
||||||
import { page } from '$app/stores';
|
|
||||||
import { get } from 'lodash-es';
|
|
||||||
import urljoin from 'proper-url-join';
|
|
||||||
import { browser } from '$app/env';
|
|
||||||
import analytics from './analytics';
|
import analytics from './analytics';
|
||||||
import publisherTags from './publisherTags';
|
import publisherTags from './publisherTags';
|
||||||
import { assets } from '$app/paths';
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL) object for the page.
|
||||||
|
* @required
|
||||||
|
* @type {URL}
|
||||||
|
*/
|
||||||
|
export let url: URL | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SEO title
|
||||||
|
* @required
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
export let seoTitle;
|
export let seoTitle;
|
||||||
|
/**
|
||||||
|
* SEO description
|
||||||
|
* @required
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
export let seoDescription;
|
export let seoDescription;
|
||||||
|
/**
|
||||||
|
* Share title
|
||||||
|
* @required
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
export let shareTitle;
|
export let shareTitle;
|
||||||
|
/**
|
||||||
|
* Share description
|
||||||
|
* @required
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
export let shareDescription;
|
export let shareDescription;
|
||||||
|
/**
|
||||||
|
* Share image path. **Must be an absolute path.**
|
||||||
|
* @required
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
export let shareImgPath;
|
export let shareImgPath;
|
||||||
|
/**
|
||||||
|
* [HTML lang attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang). **Two-letter code only.**
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
export let lang = 'en';
|
export let lang = 'en';
|
||||||
export let hostname = 'graphics.reuters.com';
|
/**
|
||||||
|
* Publish time as an [ISO string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
export let publishTime: string = '';
|
||||||
|
/**
|
||||||
|
* Updated time as an [ISO string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
export let updateTime: string = '';
|
||||||
|
|
||||||
|
interface GraphicAuthor {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
const parseUrl = (url) => {
|
/**
|
||||||
try {
|
* Array of authors for the piece. Each author object must have `name` and `url` attributes.
|
||||||
return new URL(url);
|
*/
|
||||||
} catch {
|
export let authors: GraphicAuthor[] = [];
|
||||||
return {};
|
/**
|
||||||
}
|
* Whether to inject Google Analytics code for this page.
|
||||||
};
|
*/
|
||||||
|
export let includeAnalytics: boolean = false;
|
||||||
const url = get(pkg, 'homepage')
|
|
||||||
? urljoin(parseUrl(pkg.homepage).origin, $page.url.pathname, {
|
|
||||||
trailingSlash: true,
|
|
||||||
})
|
|
||||||
: get(pkg, 'reuters.preview')
|
|
||||||
? urljoin(parseUrl(pkg.reuters.preview).origin, $page.url.pathname, {
|
|
||||||
trailingSlash: true,
|
|
||||||
})
|
|
||||||
: $page.host
|
|
||||||
? urljoin('https://' + $page.host, $page.url.pathname, {
|
|
||||||
trailingSlash: true,
|
|
||||||
})
|
|
||||||
: `https://${hostname}`;
|
|
||||||
|
|
||||||
// Only fire analytics on prod sites
|
// Only fire analytics on prod sites
|
||||||
if (browser && window.location.host === 'graphics.reuters.com') {
|
if (typeof window !== 'undefined' && includeAnalytics) {
|
||||||
analytics(url, seoTitle);
|
analytics(url, seoTitle);
|
||||||
publisherTags();
|
publisherTags();
|
||||||
}
|
}
|
||||||
|
|
@ -62,27 +93,27 @@
|
||||||
'@context': 'http://schema.org',
|
'@context': 'http://schema.org',
|
||||||
'@type': 'NewsArticle',
|
'@type': 'NewsArticle',
|
||||||
headline: seoTitle,
|
headline: seoTitle,
|
||||||
url,
|
url: url.href,
|
||||||
mainEntityOfPage: {
|
mainEntityOfPage: {
|
||||||
'@type': 'WebPage',
|
'@type': 'WebPage',
|
||||||
'@id': url,
|
'@id': url.href,
|
||||||
},
|
},
|
||||||
thumbnailUrl: urljoin(assets, shareImgPath),
|
thumbnailUrl: shareImgPath,
|
||||||
image: [
|
image: [
|
||||||
{
|
{
|
||||||
'@context': 'http://schema.org',
|
'@context': 'http://schema.org',
|
||||||
'@type': 'ImageObject',
|
'@type': 'ImageObject',
|
||||||
url: urljoin(assets, shareImgPath),
|
url: shareImgPath,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
publisher: { '@id': 'https://www.reuters.com/#publisher' },
|
publisher: { '@id': 'https://www.reuters.com/#publisher' },
|
||||||
copyrightHolder: { '@id': 'https://www.reuters.com/#publisher' },
|
copyrightHolder: { '@id': 'https://www.reuters.com/#publisher' },
|
||||||
sourceOrganization: { '@id': 'https://www.reuters.com/#publisher' },
|
sourceOrganization: { '@id': 'https://www.reuters.com/#publisher' },
|
||||||
copyrightYear: new Date().getFullYear(),
|
copyrightYear: new Date().getFullYear(),
|
||||||
dateCreated: get(pkg, 'reuters.graphic.published'),
|
dateCreated: publishTime,
|
||||||
datePublished: get(pkg, 'reuters.graphic.published'),
|
datePublished: publishTime,
|
||||||
dateModified: get(pkg, 'reuters.graphic.updated'),
|
dateModified: updateTime,
|
||||||
author: get(pkg, 'reuters.graphic.authors', []).map(({ name, url }) => ({
|
author: authors.map(({ name, url }) => ({
|
||||||
'@type': 'Person',
|
'@type': 'Person',
|
||||||
name,
|
name,
|
||||||
url,
|
url,
|
||||||
|
|
@ -98,7 +129,7 @@
|
||||||
<html lang="{lang}"></html>
|
<html lang="{lang}"></html>
|
||||||
<title>{seoTitle}</title>
|
<title>{seoTitle}</title>
|
||||||
<meta name="description" content="{seoDescription}" />
|
<meta name="description" content="{seoDescription}" />
|
||||||
<link rel="canonical" href="{url}" />
|
<link rel="canonical" href="{url.href}" />
|
||||||
<link
|
<link
|
||||||
rel="shortcut icon"
|
rel="shortcut icon"
|
||||||
type="image/x-icon"
|
type="image/x-icon"
|
||||||
|
|
@ -123,7 +154,7 @@
|
||||||
sizes="96x96"
|
sizes="96x96"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<meta property="og:url" content="{url}" />
|
<meta property="og:url" content="{url.href}" />
|
||||||
<meta property="og:type" content="article" />
|
<meta property="og:type" content="article" />
|
||||||
<meta property="og:title" content="{shareTitle}" itemprop="name" />
|
<meta property="og:title" content="{shareTitle}" itemprop="name" />
|
||||||
<meta
|
<meta
|
||||||
|
|
@ -133,7 +164,7 @@
|
||||||
/>
|
/>
|
||||||
<meta
|
<meta
|
||||||
property="og:image"
|
property="og:image"
|
||||||
content="{urljoin(assets, shareImgPath)}"
|
content="{shareImgPath}"
|
||||||
itemprop="image"
|
itemprop="image"
|
||||||
/>
|
/>
|
||||||
<meta property="og:site_name" content="Reuters" />
|
<meta property="og:site_name" content="Reuters" />
|
||||||
|
|
@ -141,10 +172,10 @@
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
<meta name="twitter:site" content="@ReutersGraphics" />
|
<meta name="twitter:site" content="@ReutersGraphics" />
|
||||||
<meta name="twitter:creator" content="@ReutersGraphics" />
|
<meta name="twitter:creator" content="@ReutersGraphics" />
|
||||||
<meta name="twitter:domain" content="{`https://${hostname}`}" />
|
<meta name="twitter:domain" content="{url.origin}" />
|
||||||
<meta name="twitter:title" content="{shareTitle}" />
|
<meta name="twitter:title" content="{shareTitle}" />
|
||||||
<meta name="twitter:description" content="{shareDescription}" />
|
<meta name="twitter:description" content="{shareDescription}" />
|
||||||
<meta name="twitter:image:src" content="{urljoin(assets, shareImgPath)}" />
|
<meta name="twitter:image:src" content="{shareImgPath}" />
|
||||||
|
|
||||||
<meta property="fb:app_id" content="319194411438328" />
|
<meta property="fb:app_id" content="319194411438328" />
|
||||||
<meta property="fb:admins" content="616167736" />
|
<meta property="fb:admins" content="616167736" />
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
const attachScript = function (i, s, o, g, r, a, m) {
|
const attachScript = function (i, s, o, g, r, a, m) {
|
||||||
i.GoogleAnalyticsObject = r;
|
i.GoogleAnalyticsObject = r;
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
---
|
|
||||||
title: SEO
|
|
||||||
description: Add SEO to the page.
|
|
||||||
slug: seo
|
|
||||||
---
|
|
||||||
|
|
||||||
<section>
|
|
||||||
|
|
||||||
## {title}
|
|
||||||
|
|
||||||
{description}
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
```svelte
|
|
||||||
<script>
|
|
||||||
import { SEO } from '@reuters-graphics/graphics-svelte-components';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<SEO
|
|
||||||
seoTitle="{'A title for Google'}"
|
|
||||||
seoDescription="{'A description for Google'}"
|
|
||||||
shareTitle="{'A title for Twitter/Facebook'}"
|
|
||||||
shareDecription="{'A description for Twitter/Facebook'}"
|
|
||||||
shareImgPath="{'images/share.jpg'}"
|
|
||||||
lang="{'en'}"
|
|
||||||
hostname="{'graphics.reuters.com'}"
|
|
||||||
/>
|
|
||||||
```
|
|
||||||
24
src/components/SEO/stories/docs/component.md
Normal file
24
src/components/SEO/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
The `SEO` component adds essential metadata to published pages.
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { SEO } from '@reuters-graphics/graphics-components';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SEO
|
||||||
|
url={new URL('https://graphics.reuters.com/world-coronavirus-tracker-and-maps/')}
|
||||||
|
seoTitle="A title for Google"
|
||||||
|
seoDescription="A description for Google"
|
||||||
|
shareTitle="A title for Twitter/Facebook"
|
||||||
|
shareDecription="A description for Twitter/Facebook"
|
||||||
|
shareImgPath="https://graphics.reuters.com/world-coronavirus-tracker-and-maps/assets/images/share.jpg"
|
||||||
|
lang="en"
|
||||||
|
publishTime="2020-09-15T00:00:00.000Z"
|
||||||
|
updateTime="2021-01-10T12:30:00.000Z"
|
||||||
|
authors={[
|
||||||
|
{ name: 'Jane Doe', url: 'https://twitter.com/JaneDoe' },
|
||||||
|
{ name: 'John Doe', url: 'https://twitter.com/JohnDoe' },
|
||||||
|
]}
|
||||||
|
includeAnalytics={true}
|
||||||
|
/>
|
||||||
|
```
|
||||||
27
src/components/SEO/stories/docs/quickit.md
Normal file
27
src/components/SEO/stories/docs/quickit.md
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
Add the `SEO` component to any page specified in the `pages/` directory of the Graphics Kit.
|
||||||
|
|
||||||
|
Most of the fields are filled in by other sources, like your Google Doc and package.json. (Usually, the fastest way is to copy the `SEO` component from one of the pages already in your project.)
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { SEO } from '@reuters-graphics/graphics-components';
|
||||||
|
import pkg from '$pkg';
|
||||||
|
import content from '$locales/en/content.json';
|
||||||
|
import { assets } from '$app/paths';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SEO
|
||||||
|
lang="en"
|
||||||
|
url={$page.url}
|
||||||
|
seoTitle="{content.SEOTitle}"
|
||||||
|
seoDescription="{content.SEODescription}"
|
||||||
|
shareTitle="{content.ShareTitle}"
|
||||||
|
shareDescription="{content.ShareDescription}"
|
||||||
|
shareImgPath="{`${assets}/${content.ShareImgPath}`}"
|
||||||
|
publishTime={pkg?.reuters?.graphic?.published}
|
||||||
|
updateTime={pkg?.reuters?.graphic?.updated}
|
||||||
|
authors={pkg?.reuters?.graphic?.authors}
|
||||||
|
includeAnalytics={true}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
@ -13,7 +13,7 @@ export { default as NoteText } from './components/NoteText/NoteText.svelte';
|
||||||
export { default as PymChild } from './components/PymChild/PymChild.svelte';
|
export { default as PymChild } from './components/PymChild/PymChild.svelte';
|
||||||
export { default as ReutersLogo } from './components/ReutersLogo/ReutersLogo.svelte';
|
export { default as ReutersLogo } from './components/ReutersLogo/ReutersLogo.svelte';
|
||||||
// export { default as Scroller } from './Scroller/index.svelte';
|
// export { default as Scroller } from './Scroller/index.svelte';
|
||||||
// export { default as SEO } from './SEO/index.svelte';
|
export { default as SEO } from './components/SEO/SEO.svelte';
|
||||||
export { default as Sharer } from './components/Sharer/Sharer.svelte';
|
export { default as Sharer } from './components/Sharer/Sharer.svelte';
|
||||||
export {
|
export {
|
||||||
default as SiteFooter,
|
default as SiteFooter,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue