Skip to content

Commit 74f653d

Browse files
authored
refactor(theme-{classic,common}): change how site/page/search metadata is handled (#6925)
1 parent 74e37e8 commit 74f653d

36 files changed

Lines changed: 802 additions & 619 deletions

File tree

packages/docusaurus-module-type-aliases/src/index.d.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,6 @@ declare module '@theme/Layout' {
9292

9393
export interface Props {
9494
readonly children?: ReactNode;
95-
readonly title?: string;
96-
readonly description?: string;
9795
}
9896
export default function Layout(props: Props): JSX.Element;
9997
}
@@ -117,6 +115,10 @@ declare module '@theme/Root' {
117115
export default function Root({children}: Props): JSX.Element;
118116
}
119117

118+
declare module '@theme/SiteMetadata' {
119+
export default function SiteMetadata(): JSX.Element;
120+
}
121+
120122
declare module '@docusaurus/constants' {
121123
export const DEFAULT_PLUGIN_ID: 'default';
122124
}

packages/docusaurus-theme-classic/src/theme-classic.d.ts

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -364,31 +364,17 @@ declare module '@theme/Layout' {
364364

365365
export interface Props {
366366
readonly children?: ReactNode;
367-
readonly title?: string;
368367
readonly noFooter?: boolean;
369-
readonly description?: string;
370-
readonly image?: string;
371-
readonly keywords?: string | string[];
372-
readonly permalink?: string;
373368
readonly wrapperClassName?: string;
374-
readonly pageClassName?: string;
375-
readonly searchMetadata?: {
376-
readonly version?: string;
377-
readonly tag?: string;
378-
};
369+
370+
// Not really layout-related, but kept for convenience/retro-compatibility
371+
readonly title?: string;
372+
readonly description?: string;
379373
}
380374

381375
export default function Layout(props: Props): JSX.Element;
382376
}
383377

384-
declare module '@theme/LayoutHead' {
385-
import type {Props as LayoutProps} from '@theme/Layout';
386-
387-
export interface Props extends Omit<LayoutProps, 'children'> {}
388-
389-
export default function LayoutHead(props: Props): JSX.Element;
390-
}
391-
392378
declare module '@theme/LayoutProviders' {
393379
import type {ReactNode} from 'react';
394380

@@ -480,7 +466,7 @@ declare module '@theme/Navbar/Content' {
480466

481467
declare module '@theme/Navbar/Layout' {
482468
export interface Props {
483-
children: React.ReactNode;
469+
readonly children: React.ReactNode;
484470
}
485471

486472
export default function NavbarLayout(props: Props): JSX.Element;
@@ -927,17 +913,3 @@ declare module '@theme/prism-include-languages' {
927913
PrismObject: typeof PrismNamespace,
928914
): void;
929915
}
930-
931-
declare module '@theme/Seo' {
932-
import type {ReactNode} from 'react';
933-
934-
export interface Props {
935-
readonly title?: string;
936-
readonly description?: string;
937-
readonly keywords?: readonly string[] | string;
938-
readonly image?: string;
939-
readonly children?: ReactNode;
940-
}
941-
942-
export default function Seo(props: Props): JSX.Element;
943-
}

packages/docusaurus-theme-classic/src/theme/BlogArchivePage/index.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Layout from '@theme/Layout';
1010
import Link from '@docusaurus/Link';
1111
import type {ArchiveBlogPost, Props} from '@theme/BlogArchivePage';
1212
import {translate} from '@docusaurus/Translate';
13+
import {PageMetadata} from '@docusaurus/theme-common';
1314

1415
type YearProp = {
1516
year: string;
@@ -75,14 +76,17 @@ export default function BlogArchive({archive}: Props): JSX.Element {
7576
});
7677
const years = listPostsByYears(archive.blogPosts);
7778
return (
78-
<Layout title={title} description={description}>
79-
<header className="hero hero--primary">
80-
<div className="container">
81-
<h1 className="hero__title">{title}</h1>
82-
<p className="hero__subtitle">{description}</p>
83-
</div>
84-
</header>
85-
<main>{years.length > 0 && <YearsSection years={years} />}</main>
86-
</Layout>
79+
<>
80+
<PageMetadata title={title} description={description} />
81+
<Layout>
82+
<header className="hero hero--primary">
83+
<div className="container">
84+
<h1 className="hero__title">{title}</h1>
85+
<p className="hero__subtitle">{description}</p>
86+
</div>
87+
</header>
88+
<main>{years.length > 0 && <YearsSection years={years} />}</main>
89+
</Layout>
90+
</>
8791
);
8892
}

packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,34 @@ import BlogLayout from '@theme/BlogLayout';
1212
import BlogPostItem from '@theme/BlogPostItem';
1313
import BlogListPaginator from '@theme/BlogListPaginator';
1414
import type {Props} from '@theme/BlogListPage';
15-
import {ThemeClassNames} from '@docusaurus/theme-common';
15+
import {
16+
PageMetadata,
17+
HtmlClassNameProvider,
18+
ThemeClassNames,
19+
} from '@docusaurus/theme-common';
20+
import SearchMetadata from '@theme/SearchMetadata';
21+
import clsx from 'clsx';
1622

17-
export default function BlogListPage(props: Props): JSX.Element {
18-
const {metadata, items, sidebar} = props;
23+
function BlogListPageMetadata(props: Props): JSX.Element {
24+
const {metadata} = props;
1925
const {
2026
siteConfig: {title: siteTitle},
2127
} = useDocusaurusContext();
2228
const {blogDescription, blogTitle, permalink} = metadata;
2329
const isBlogOnlyMode = permalink === '/';
2430
const title = isBlogOnlyMode ? siteTitle : blogTitle;
31+
return (
32+
<>
33+
<PageMetadata title={title} description={blogDescription} />
34+
<SearchMetadata tag="blog_posts_list" />
35+
</>
36+
);
37+
}
2538

39+
function BlogListPageContent(props: Props): JSX.Element {
40+
const {metadata, items, sidebar} = props;
2641
return (
27-
<BlogLayout
28-
title={title}
29-
description={blogDescription}
30-
wrapperClassName={ThemeClassNames.wrapper.blogPages}
31-
pageClassName={ThemeClassNames.page.blogListPage}
32-
searchMetadata={{
33-
// assign unique search tag to exclude this page from search results!
34-
tag: 'blog_posts_list',
35-
}}
36-
sidebar={sidebar}>
42+
<BlogLayout sidebar={sidebar}>
3743
{items.map(({content: BlogPostContent}) => (
3844
<BlogPostItem
3945
key={BlogPostContent.metadata.permalink}
@@ -48,3 +54,16 @@ export default function BlogListPage(props: Props): JSX.Element {
4854
</BlogLayout>
4955
);
5056
}
57+
58+
export default function BlogListPage(props: Props): JSX.Element {
59+
return (
60+
<HtmlClassNameProvider
61+
className={clsx(
62+
ThemeClassNames.wrapper.blogPages,
63+
ThemeClassNames.page.blogListPage,
64+
)}>
65+
<BlogListPageMetadata {...props} />
66+
<BlogListPageContent {...props} />
67+
</HtmlClassNameProvider>
68+
);
69+
}

packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,63 @@
66
*/
77

88
import React from 'react';
9-
import Seo from '@theme/Seo';
109
import BlogLayout from '@theme/BlogLayout';
1110
import BlogPostItem from '@theme/BlogPostItem';
1211
import BlogPostPaginator from '@theme/BlogPostPaginator';
1312
import type {Props} from '@theme/BlogPostPage';
14-
import {ThemeClassNames} from '@docusaurus/theme-common';
13+
import {
14+
PageMetadata,
15+
HtmlClassNameProvider,
16+
ThemeClassNames,
17+
} from '@docusaurus/theme-common';
1518
import TOC from '@theme/TOC';
19+
import clsx from 'clsx';
1620

17-
export default function BlogPostPage(props: Props): JSX.Element {
21+
function BlogPostPageMetadata(props: Props): JSX.Element {
22+
const {content: BlogPostContents} = props;
23+
const {assets, metadata} = BlogPostContents;
24+
const {title, description, date, tags, authors, frontMatter} = metadata;
25+
const {keywords} = frontMatter;
26+
const image = assets.image ?? frontMatter.image;
27+
return (
28+
<PageMetadata
29+
title={title}
30+
description={description}
31+
keywords={keywords}
32+
image={image}>
33+
<meta property="og:type" content="article" />
34+
<meta property="article:published_time" content={date} />
35+
{/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */}
36+
{authors.some((author) => author.url) && (
37+
<meta
38+
property="article:author"
39+
content={authors
40+
.map((author) => author.url)
41+
.filter(Boolean)
42+
.join(',')}
43+
/>
44+
)}
45+
{tags.length > 0 && (
46+
<meta
47+
property="article:tag"
48+
content={tags.map((tag) => tag.label).join(',')}
49+
/>
50+
)}
51+
</PageMetadata>
52+
);
53+
}
54+
55+
function BlogPostPageContent(props: Props): JSX.Element {
1856
const {content: BlogPostContents, sidebar} = props;
1957
const {assets, metadata} = BlogPostContents;
20-
const {
21-
title,
22-
description,
23-
nextItem,
24-
prevItem,
25-
date,
26-
tags,
27-
authors,
28-
frontMatter,
29-
} = metadata;
58+
const {nextItem, prevItem, frontMatter} = metadata;
3059
const {
3160
hide_table_of_contents: hideTableOfContents,
32-
keywords,
3361
toc_min_heading_level: tocMinHeadingLevel,
3462
toc_max_heading_level: tocMaxHeadingLevel,
3563
} = frontMatter;
36-
37-
const image = assets.image ?? frontMatter.image;
38-
3964
return (
4065
<BlogLayout
41-
wrapperClassName={ThemeClassNames.wrapper.blogPages}
42-
pageClassName={ThemeClassNames.page.blogPostPage}
4366
sidebar={sidebar}
4467
toc={
4568
!hideTableOfContents &&
@@ -52,35 +75,6 @@ export default function BlogPostPage(props: Props): JSX.Element {
5275
/>
5376
) : undefined
5477
}>
55-
<Seo
56-
// TODO refactor needed: it's a bit annoying but Seo MUST be inside
57-
// BlogLayout, otherwise default image (set by BlogLayout) would shadow
58-
// the custom blog post image
59-
title={title}
60-
description={description}
61-
keywords={keywords}
62-
image={image}>
63-
<meta property="og:type" content="article" />
64-
<meta property="article:published_time" content={date} />
65-
66-
{/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */}
67-
{authors.some((author) => author.url) && (
68-
<meta
69-
property="article:author"
70-
content={authors
71-
.map((author) => author.url)
72-
.filter(Boolean)
73-
.join(',')}
74-
/>
75-
)}
76-
{tags.length > 0 && (
77-
<meta
78-
property="article:tag"
79-
content={tags.map((tag) => tag.label).join(',')}
80-
/>
81-
)}
82-
</Seo>
83-
8478
<BlogPostItem
8579
frontMatter={frontMatter}
8680
assets={assets}
@@ -95,3 +89,16 @@ export default function BlogPostPage(props: Props): JSX.Element {
9589
</BlogLayout>
9690
);
9791
}
92+
93+
export default function BlogPostPage(props: Props): JSX.Element {
94+
return (
95+
<HtmlClassNameProvider
96+
className={clsx(
97+
ThemeClassNames.wrapper.blogPages,
98+
ThemeClassNames.page.blogPostPage,
99+
)}>
100+
<BlogPostPageMetadata {...props} />
101+
<BlogPostPageContent {...props} />
102+
</HtmlClassNameProvider>
103+
);
104+
}

packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.tsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,29 @@ import BlogLayout from '@theme/BlogLayout';
1111
import TagsListByLetter from '@theme/TagsListByLetter';
1212
import type {Props} from '@theme/BlogTagsListPage';
1313
import {
14+
PageMetadata,
15+
HtmlClassNameProvider,
1416
ThemeClassNames,
1517
translateTagsPageTitle,
1618
} from '@docusaurus/theme-common';
19+
import SearchMetadata from '../SearchMetadata';
20+
import clsx from 'clsx';
1721

1822
export default function BlogTagsListPage(props: Props): JSX.Element {
1923
const {tags, sidebar} = props;
2024
const title = translateTagsPageTitle();
2125
return (
22-
<BlogLayout
23-
title={title}
24-
wrapperClassName={ThemeClassNames.wrapper.blogPages}
25-
pageClassName={ThemeClassNames.page.blogTagsListPage}
26-
searchMetadata={{
27-
// assign unique search tag to exclude this page from search results!
28-
tag: 'blog_tags_list',
29-
}}
30-
sidebar={sidebar}>
31-
<h1>{title}</h1>
32-
<TagsListByLetter tags={Object.values(tags)} />
33-
</BlogLayout>
26+
<HtmlClassNameProvider
27+
className={clsx(
28+
ThemeClassNames.wrapper.blogPages,
29+
ThemeClassNames.page.blogTagsListPage,
30+
)}>
31+
<PageMetadata title={title} />
32+
<SearchMetadata tag="blog_tags_list" />
33+
<BlogLayout sidebar={sidebar}>
34+
<h1>{title}</h1>
35+
<TagsListByLetter tags={Object.values(tags)} />
36+
</BlogLayout>
37+
</HtmlClassNameProvider>
3438
);
3539
}

0 commit comments

Comments
 (0)