Skip to content

Commit 0193a4d

Browse files
committed
feat(json-ld): add WebSite component
1 parent 0030c7c commit 0193a4d

13 files changed

+236
-2
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ package-lock.json
1010
lib
1111
notes.md
1212
.DS_Store
13+
.idea

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ If you are using **`pages`** directory then `NextSeo` is **exactly what you need
6161
- [dangerouslySetAllPagesToNoFollow](#dangerouslysetallpagestonofollow)
6262
- [robotsProps](#robotsprops)
6363
- [Twitter](#twitter)
64-
- [facebook](#facebook)
64+
- [Facebook](#facebook)
6565
- [Canonical URL](#canonical-url)
6666
- [Alternate](#alternate)
6767
- [Additional Meta Tags](#additional-meta-tags)

cypress/e2e/webSiteJsonLd.spec.js

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { assertSchema } from '@cypress/schema-tools';
2+
import schemas from '../schemas';
3+
4+
describe('WebPage JSON-LD', () => {
5+
it('matches schema', () => {
6+
cy.visit('http://localhost:3000/jsonld/webSite');
7+
cy.get('head script[type="application/ld+json"]').then(tags => {
8+
const jsonLD = JSON.parse(tags[0].innerHTML);
9+
assertSchema(schemas)('WebSite', '1.0.0')(jsonLD);
10+
});
11+
});
12+
13+
it('renders with all props', () => {
14+
cy.visit('http://localhost:3000/jsonld/webSite');
15+
cy.get('head script[type="application/ld+json"]').then(tags => {
16+
const jsonLD = JSON.parse(tags[0].innerHTML);
17+
expect(jsonLD).to.deep.equal({
18+
'@context': 'https://schema.org',
19+
'@type': 'WebSite',
20+
name: 'Example',
21+
alternateName: ['Example Org', 'Example Organization'],
22+
url: 'https://example.org',
23+
publisher: {
24+
'@id': 'https://example.org/#organization',
25+
},
26+
});
27+
});
28+
});
29+
30+
it('renders without a publisher', () => {
31+
cy.visit('http://localhost:3000/jsonld/webSite/withoutPublisher');
32+
cy.get('head script[type="application/ld+json"]').then(tags => {
33+
const jsonLD = JSON.parse(tags[0].innerHTML);
34+
expect(jsonLD).to.deep.equal({
35+
'@context': 'https://schema.org',
36+
'@type': 'WebSite',
37+
name: 'Example',
38+
alternateName: ['Example Org', 'Example Organization'],
39+
url: 'https://example.org',
40+
});
41+
});
42+
});
43+
44+
it('renders with a single alternate name', () => {
45+
cy.visit('http://localhost:3000/jsonld/webSite/withSingleAlternateName');
46+
cy.get('head script[type="application/ld+json"]').then(tags => {
47+
const jsonLD = JSON.parse(tags[0].innerHTML);
48+
expect(jsonLD).to.deep.equal({
49+
'@context': 'https://schema.org',
50+
'@type': 'WebSite',
51+
name: 'Example',
52+
alternateName: 'Example Organization',
53+
url: 'https://example.org',
54+
});
55+
});
56+
});
57+
});

cypress/schemas/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import howToVersions from './how-to-schema';
2929
import imageVersions from './image-schema';
3030
import campgroundVersions from './campground-schema';
3131
import parkVersions from './park-schema';
32-
32+
import webSiteVersions from './web-site-schema';
3333

3434
const schemas = combineSchemas(
3535
articleVersions,
@@ -61,5 +61,6 @@ const schemas = combineSchemas(
6161
imageVersions,
6262
campgroundVersions,
6363
parkVersions,
64+
webSiteVersions,
6465
);
6566
export default schemas;

cypress/schemas/web-site-schema.js

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { versionSchemas } from '@cypress/schema-tools';
2+
3+
const webSite100 = {
4+
version: {
5+
major: 1,
6+
minor: 0,
7+
patch: 0,
8+
},
9+
schema: {
10+
type: 'object',
11+
title: 'WebSite',
12+
description: 'An example schema describing JSON-LD for type: WebSite',
13+
properties: {
14+
'@context': {
15+
type: 'string',
16+
description: 'Schema.org context',
17+
},
18+
'@type': {
19+
type: 'string',
20+
description: 'JSON-LD type: WebSite',
21+
},
22+
name: {
23+
type: 'string',
24+
description: 'The site name',
25+
},
26+
url: {
27+
type: 'string',
28+
description: 'The URL of the website',
29+
},
30+
alternateName: {
31+
type: 'array',
32+
items: {
33+
type: 'string',
34+
},
35+
description: 'One or multiple alternate names for the site',
36+
},
37+
publisher: {
38+
type: 'object',
39+
properties: {
40+
'@id': {
41+
type: 'string',
42+
description: 'Id of the publisher node',
43+
},
44+
},
45+
},
46+
},
47+
},
48+
example: {
49+
'@context': 'https://schema.org',
50+
'@type': 'WebSite',
51+
url: 'https://example.org',
52+
name: 'Example',
53+
alternateName: ['Example Org', 'Example Organization'],
54+
publisher: {
55+
'@id': 'https://example.org/#organization',
56+
},
57+
},
58+
};
59+
60+
const webSiteVersions = versionSchemas(webSite100);
61+
export default webSiteVersions;

e2e/pages/jsonld/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const allJsonLDPages = [
3131
'videoGame',
3232
'webPage',
3333
'webPage2',
34+
'webSite',
3435
];
3536

3637
const Home = () => (

e2e/pages/jsonld/webSite/index.tsx

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
import { WebSiteJsonLd } from '../../../..';
3+
4+
function WebPage() {
5+
return (
6+
<>
7+
<h1>WebSite</h1>
8+
<WebSiteJsonLd
9+
name="Example"
10+
alternateName={['Example Org', 'Example Organization']}
11+
url="https://example.org"
12+
publisher={{
13+
id: 'https://example.org/#organization',
14+
}}
15+
/>
16+
</>
17+
);
18+
}
19+
20+
export default WebPage;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
import { WebSiteJsonLd } from '../../../..';
3+
4+
function WebPage() {
5+
return (
6+
<>
7+
<h1>WebSite</h1>
8+
<WebSiteJsonLd
9+
name="Example"
10+
alternateName="Example Organization"
11+
url="https://example.org"
12+
/>
13+
</>
14+
);
15+
}
16+
17+
export default WebPage;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
import { WebSiteJsonLd } from '../../../..';
3+
4+
function WebPage() {
5+
return (
6+
<>
7+
<h1>WebSite</h1>
8+
<WebSiteJsonLd
9+
name="Example"
10+
alternateName={['Example Org', 'Example Organization']}
11+
url="https://example.org"
12+
/>
13+
</>
14+
);
15+
}
16+
17+
export default WebPage;

src/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,5 @@ export {
7272
CampgroundJsonLdProps,
7373
} from './jsonld/campground';
7474
export { default as ParkJsonLd, ParkJsonLdProps } from './jsonld/park';
75+
export { default as WebSiteJsonLd, WebSiteJsonLdProps } from './jsonld/webSite';
7576
export { DefaultSeoProps, NextSeoProps } from './types';

src/jsonld/webSite.tsx

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React from 'react';
2+
3+
import { JsonLd, JsonLdProps } from './jsonld';
4+
import { WebSitePublisher } from '../types';
5+
import { setWebSitePublisher } from '../utils/schema/setWebSitePublisher';
6+
7+
export interface WebSiteJsonLdProps extends JsonLdProps {
8+
name: string;
9+
url?: string;
10+
alternateName?: string | string[];
11+
publisher?: WebSitePublisher;
12+
}
13+
14+
function WebSiteJsonLd({
15+
type = 'WebSite',
16+
keyOverride,
17+
publisher,
18+
...rest
19+
}: WebSiteJsonLdProps) {
20+
const data = {
21+
...rest,
22+
publisher: setWebSitePublisher(publisher),
23+
};
24+
25+
return (
26+
<JsonLd
27+
type={type}
28+
keyOverride={keyOverride}
29+
{...data}
30+
scriptKey="WebSite"
31+
/>
32+
);
33+
}
34+
35+
export default WebSiteJsonLd;

src/types.ts

+12
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export type StepDetails = {
5454
export interface Person {
5555
name: string;
5656
}
57+
5758
export interface Answer {
5859
text: string;
5960
dateCreated?: string;
@@ -79,10 +80,12 @@ export interface Instruction {
7980
url?: string;
8081
image?: string;
8182
}
83+
8284
export interface Performer {
8385
type?: 'Person' | 'PerformingGroup';
8486
name: string;
8587
}
88+
8689
export interface Place {
8790
name: string;
8891
address: Address;
@@ -123,6 +126,7 @@ export interface ContactPoint {
123126
availableLanguage?: string | string[];
124127
contactOption?: string | string[];
125128
}
129+
126130
export interface CreativeWork {
127131
author: string;
128132
about: string;
@@ -143,16 +147,19 @@ export interface Question {
143147
questionName: string;
144148
acceptedAnswerText: string;
145149
}
150+
146151
export interface Provider {
147152
type?: 'Organization' | 'Person';
148153
name: string;
149154
url?: string;
150155
}
156+
151157
export interface ItemListElements {
152158
item: string;
153159
name: string;
154160
position: number;
155161
}
162+
156163
export interface OpenGraphMedia {
157164
url: string;
158165
width?: number | null;
@@ -395,6 +402,10 @@ export type Publisher = {
395402
name: string;
396403
};
397404

405+
export type WebSitePublisher = {
406+
id: string;
407+
};
408+
398409
export type ReviewedBy = {
399410
type?: string;
400411
name: string;
@@ -511,4 +522,5 @@ export interface DefaultSeoProps {
511522
additionalLinkTags?: ReadonlyArray<LinkTag>;
512523
children?: never;
513524
}
525+
514526
export interface BuildTagsParams extends DefaultSeoProps, NextSeoProps {}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { WebSitePublisher } from '../../types';
2+
3+
export function setWebSitePublisher(publisher?: WebSitePublisher) {
4+
if (publisher) {
5+
return {
6+
'@id': publisher.id,
7+
};
8+
}
9+
10+
return undefined;
11+
}

0 commit comments

Comments
 (0)