Skip to content
Permalink
Browse files

feat(FAQJsonLd): add new json ld component for FAQ pages

  • Loading branch information
ifiokjr committed Jan 9, 2020
1 parent aa447a7 commit 2c0e8a09ac72954a3ad6166652b584fabf647f92
@@ -44,6 +44,7 @@ This codebase was initially forked from the brilliant [next-seo](https://github.
- [Blog](#blog)
- [Book](#book)
- [Speakable](#speakable)
- [FAQ](#faq)
- [Course](#course)
- [Corporate Contact (Deprecated)](#corporate-contact-deprecated)
- [Local Business](#local-business)
@@ -52,7 +53,7 @@ This codebase was initially forked from the brilliant [next-seo](https://github.
- [Social Profile (Deprecated)](#social-profile-deprecated)
- [JsonLd](#jsonld)
- [API Docs](#api-docs)
- [FAQ](#faq)
- [FAQ](#faq-1)
- [Why did you choose `gatsby-plugin-next-seo` as the project name?](#why-did-you-choose-gatsby-plugin-next-seo-as-the-project-name)
- [Contributors](#contributors)

@@ -639,6 +640,7 @@ Google has excellent documentation on JSON-LD -> [HERE](https://developers.googl
- [Book](#book)
- [Speakable](#speakable)
- [Course](#course)
- [FAQ](#faq)
- [Corporate Contact (Deprecated)](#corporate-contact-deprecated)
- [Local Business](#local-business)
- [Logo](#logo)
@@ -882,7 +884,7 @@ export default () => (

The speakable schema.org property identifies sections within an article or webpage that are best suited for audio playback using text-to-speech (TTS).

Adding markup allows search engines and other applications to identify content to read aloud on Google Assistant-enabled devices using TTS. Webpages with speakable structured data can use the Google Assistant to distribute the content through new channels and reach a wider base of users.
Adding markup allows search engines and other applications to identify content to read aloud on voice assistant-enabled devices using TTS. Webpages with speakable structured data can use voice assistants to distribute the content through new channels and reach a wider base of users.

```tsx
import React from 'react';
@@ -896,6 +898,36 @@ export default () => (
);
```

### FAQ

A Frequently Asked Question (FAQ) page contains a list of questions and answers pertaining to a particular topic. Properly marked up FAQ pages may be eligible to have a rich result on Search and voice assistants.

```tsx
import React from 'react';
import { FAQJsonLd } from 'gatsby-plugin-next-seo';
export default () => (
<>
<FAQJsonLd
mainEntity={[
{ question: 'What?', answer: 'Stand' },
{ question: 'How?', answer: 'Effort' },
{ question: 'Why?', answer: 'Peace' },
]}
/>
<h1>What?</h1>
<p>Stand</p>
<h1>How?</h1>
<p>Effort</p>
<h1>Why?</h1>
<p>Peace</p>
</>
);
```

### Course

```tsx
@@ -27,7 +27,7 @@ const JsonLd = () => (
datePublished='2015-02-05T08:00:00+08:00'
dateModified='2015-02-05T09:00:00+08:00'
authorName='Jane Blogs'
publisherName='Gary Meehan'
publisherName='Ifiok Jr.'
publisherLogo='https://www.example.com/photos/logo.jpg'
description='This is a mighty good description of this article.'
/>
@@ -160,7 +160,7 @@ const JsonLd = () => (
datePublished='2015-02-05T08:00:00+08:00'
dateModified='2015-02-05T09:00:00+08:00'
authorName='Jane Blogs'
publisherName='Gary Meehan'
publisherName='Ifiok Jr.'
publisherLogo='https://www.example.com/photos/logo.jpg'
description='This is a mighty good description of this news article.'
body='This is article body of news article'
@@ -28,7 +28,7 @@ exports[`ArticleJsonLd 1`] = `
},
"publisher": {
"@type": "Organization",
"name": "Gary Meehan",
"name": "Ifiok Jr.",
"logo": {
"@type": "ImageObject",
"url": "https://www.example.com/photos/logo.jpg"
@@ -274,6 +274,51 @@ exports[`CourseJsonLd 1`] = `
</html>
`;

exports[`FAQJsonLd 1`] = `
<html>
<head>
<script
data-rh="true"
type="application/ld+json"
>
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"acceptedAnswer": {
"@type": "Answer",
"text": "Stand"
},
"name": "What?"
},
{
"@type": "Question",
"acceptedAnswer": {
"@type": "Answer",
"text": "Effort"
},
"name": "How?"
},
{
"@type": "Question",
"acceptedAnswer": {
"@type": "Answer",
"text": "Peace"
},
"name": "Why?"
}
]
}
</script>
</head>
<body>
<div />
</body>
</html>
`;

exports[`LocalBusinessJsonLd 1`] = `
<html>
<head>
@@ -366,7 +411,7 @@ exports[`NewsArticleJsonLd 1`] = `
},
"publisher": {
"@type": "Organization",
"name": "Gary Meehan",
"name": "Ifiok Jr.",
"logo": {
"@type": "ImageObject",
"url": "https://www.example.com/photos/logo.jpg"
@@ -11,13 +11,14 @@ import {
BookJsonLd,
BreadcrumbJsonLd,
CourseJsonLd,
FAQJsonLd,
LocalBusinessJsonLd,
LogoJsonLd,
NewsArticleJsonLd,
ProductJsonLd,
SpeakableJsonLd,
} from '../..';
import schemas from '../../../e2e/schema';
import { SpeakableJsonLd } from '../speakable';

const render = (ui: ReactElement) => testRender(ui, { wrapper: HelmetProvider });

@@ -33,23 +34,21 @@ afterAll(() => {

test('ArticleJsonLd', () => {
render(
<>
<ArticleJsonLd
url='https://example.com/article'
headline='Article headline'
images={[
'https://example.com/photos/1x1/photo.jpg',
'https://example.com/photos/4x3/photo.jpg',
'https://example.com/photos/16x9/photo.jpg',
]}
datePublished='2015-02-05T08:00:00+08:00'
dateModified='2015-02-05T09:00:00+08:00'
authorName='Jane Blogs'
publisherName='Gary Meehan'
publisherLogo='https://www.example.com/photos/logo.jpg'
description='This is a mighty good description of this article.'
/>
</>,
<ArticleJsonLd
url='https://example.com/article'
headline='Article headline'
images={[
'https://example.com/photos/1x1/photo.jpg',
'https://example.com/photos/4x3/photo.jpg',
'https://example.com/photos/16x9/photo.jpg',
]}
datePublished='2015-02-05T08:00:00+08:00'
dateModified='2015-02-05T09:00:00+08:00'
authorName='Jane Blogs'
publisherName='Ifiok Jr.'
publisherLogo='https://www.example.com/photos/logo.jpg'
description='This is a mighty good description of this article.'
/>,
);
const jsonLD = JSON.parse(document.querySelector('script')?.innerHTML ?? '{}');
assertSchema(schemas)('Article', '1.0.0')(jsonLD);
@@ -72,7 +71,7 @@ test('NewsArticleJsonLd', () => {
datePublished='2015-02-05T08:00:00+08:00'
dateModified='2015-02-05T09:00:00+08:00'
authorName='Jane Blogs'
publisherName='Gary Meehan'
publisherName='Ifiok Jr.'
publisherLogo='https://www.example.com/photos/logo.jpg'
description='This is a mighty good description of this news article.'
body='This is article body of news article'
@@ -305,3 +304,17 @@ test('ProductJsonLd', () => {
assertSchema(schemas)('Product', '1.0.0')(jsonLD);
expect(document.documentElement).toMatchSnapshot();
});

test('FAQJsonLd', () => {
render(
<FAQJsonLd
mainEntity={[
{ question: 'What?', answer: 'Stand' },
{ question: 'How?', answer: 'Effort' },
{ question: 'Why?', answer: 'Peace' },
]}
/>,
);
JSON.parse(document.querySelector('script')?.innerHTML!);
expect(document.documentElement).toMatchSnapshot();
});
@@ -296,7 +296,7 @@ export interface NewsArticleJsonLdProps
* datePublished='2015-02-05T08:00:00+08:00'
* dateModified='2015-02-05T09:00:00+08:00'
* authorName='Jane Blogs'
* publisherName='Gary Meehan'
* publisherName='Ifiok Jr.'
* publisherLogo='https://www.example.com/photos/logo.jpg'
* description='This is a mighty good description of this article.'
* body='This is all text for this news article'
@@ -1,7 +1,7 @@
import React, { FC } from 'react';
import {
Book,
BookFormatType as RawBookFormatType,
BookFormatType as SchemaBookFormatType,
Date,
ReadAction,
Text,
@@ -15,8 +15,8 @@ import { Overrides } from '../utils/shared-types';
import { JsonLd } from './jsonld';

export type BookFormatType = 'AudiobookFormat' | 'EBook' | 'GraphicNovel' | 'Hardcover' | 'Paperback';
const getBookFormat = (type?: BookFormatType): RawBookFormatType | undefined =>
type ? (`https://schema.org/${type}` as RawBookFormatType) : undefined;
const getBookFormat = (type?: BookFormatType): SchemaBookFormatType | undefined =>
type ? (`https://schema.org/${type}` as SchemaBookFormatType) : undefined;

interface Person {
/**
@@ -0,0 +1,90 @@
import React, { FC } from 'react';
import { FAQPage, Question as SchemaQuestion, WithContext } from 'schema-dts';

import { DeferSeoProps } from '../types';
import { Overrides } from '../utils/shared-types';
import { JsonLd } from './jsonld';

/**
* The FAQPage JSON LD Component props.
*
* @public
*/
export interface FAQJsonLdProps extends DeferSeoProps, Overrides<FAQPage> {
/**
* An array of Question elements which comprise the list of answered questions
* that this FAQPage is about.
*/
mainEntity: Question[];
}

interface Question {
/**
* The full text of the question. For example, "How long does it take to
* process a refund?".
*/
question: string;

/**
* The answer to the question. There must be one answer per question.
*
* @remarks
*
* The answer may contain HTML content such as links and lists. Valid HTML
* tags include: <h1> through <h6>, <br>, <ol>, <ul>, <li>, <a>, <p>, <div>,
* <b>, <strong>, <i>, and <em>.
*/
answer: string;
}

const transformMainEntity = (questions: Question[]): SchemaQuestion[] =>
questions.map<SchemaQuestion>(({ question, answer }) => ({
'@type': 'Question',
acceptedAnswer: { '@type': 'Answer', text: answer },
name: question,
}));

/**
* A Frequently Asked Question (FAQ) page contains a list of questions and
* answers pertaining to a particular topic.
*
* @remarks
*
* Properly marked up FAQ pages may be eligible to have a rich result on Search
* and voice assistants.
*
* ```tsx
* import React from 'react';
* import { FAQJsonLd } from 'gatsby-plugin-next-seo';
*
* export default () => (
* <>
* <FAQJsonLd mainEntity={[{ question: 'What?', answer: 'Stand' }, { question:
* 'How?', answer: 'Effort' }, { question: 'Why?', answer: 'Peace' },
* ]}
* />
*
* <h1>What?</h1>
* <p>Stand</p>
*
* <h1>How?</h1>
* <p>Effort</p>
*
* <h1>Why?</h1>
* <p>Peace</p>
* </>
* );
* ```
*
* @public
*/
export const FAQJsonLd: FC<FAQJsonLdProps> = ({ mainEntity, overrides = {}, defer = false }) => {
const json: WithContext<FAQPage> = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: transformMainEntity(mainEntity),
...overrides,
};

return <JsonLd defer={defer} json={json} />;
};
@@ -4,6 +4,7 @@ export * from './book';
export * from './breadcrumb';
export * from './corporate-contact';
export * from './course';
export * from './faq';
export * from './jsonld';
export * from './local-business';
export * from './logo';

0 comments on commit 2c0e8a0

Please sign in to comment.
You can’t perform that action at this time.