Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(theme-{classic,common}): refactor site/page/search metadata + apply className on html element #6925

Merged
merged 15 commits into from
Mar 18, 2022
Merged
6 changes: 4 additions & 2 deletions packages/docusaurus-module-type-aliases/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ declare module '@theme/Layout' {

export interface Props {
readonly children?: ReactNode;
readonly title?: string;
readonly description?: string;
}
export default function Layout(props: Props): JSX.Element;
}
Expand All @@ -117,6 +115,10 @@ declare module '@theme/Root' {
export default function Root({children}: Props): JSX.Element;
}

declare module '@theme/SiteMetadata' {
export default function SiteMetadata(): JSX.Element;
}

declare module '@docusaurus/constants' {
export const DEFAULT_PLUGIN_ID: 'default';
}
Expand Down
38 changes: 5 additions & 33 deletions packages/docusaurus-theme-classic/src/theme-classic.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,31 +364,17 @@ declare module '@theme/Layout' {

export interface Props {
readonly children?: ReactNode;
readonly title?: string;
readonly noFooter?: boolean;
readonly description?: string;
readonly image?: string;
readonly keywords?: string | string[];
readonly permalink?: string;
readonly wrapperClassName?: string;
readonly pageClassName?: string;
readonly searchMetadata?: {
readonly version?: string;
readonly tag?: string;
};

// Not really layout-related, but kept for convenience/retro-compatibility
readonly title?: string;
readonly description?: string;
}

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

declare module '@theme/LayoutHead' {
import type {Props as LayoutProps} from '@theme/Layout';

export interface Props extends Omit<LayoutProps, 'children'> {}

export default function LayoutHead(props: Props): JSX.Element;
}

declare module '@theme/LayoutProviders' {
import type {ReactNode} from 'react';

Expand Down Expand Up @@ -480,7 +466,7 @@ declare module '@theme/Navbar/Content' {

declare module '@theme/Navbar/Layout' {
export interface Props {
children: React.ReactNode;
readonly children: React.ReactNode;
}

export default function NavbarLayout(props: Props): JSX.Element;
Expand Down Expand Up @@ -927,17 +913,3 @@ declare module '@theme/prism-include-languages' {
PrismObject: typeof PrismNamespace,
): void;
}

declare module '@theme/Seo' {
import type {ReactNode} from 'react';

export interface Props {
readonly title?: string;
readonly description?: string;
readonly keywords?: readonly string[] | string;
readonly image?: string;
readonly children?: ReactNode;
}

export default function Seo(props: Props): JSX.Element;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import type {ArchiveBlogPost, Props} from '@theme/BlogArchivePage';
import {translate} from '@docusaurus/Translate';
import {PageMetadata} from '@docusaurus/theme-common';

type YearProp = {
year: string;
Expand Down Expand Up @@ -75,14 +76,17 @@ export default function BlogArchive({archive}: Props): JSX.Element {
});
const years = listPostsByYears(archive.blogPosts);
return (
<Layout title={title} description={description}>
<header className="hero hero--primary">
<div className="container">
<h1 className="hero__title">{title}</h1>
<p className="hero__subtitle">{description}</p>
</div>
</header>
<main>{years.length > 0 && <YearsSection years={years} />}</main>
</Layout>
<>
<PageMetadata title={title} description={description} />
<Layout>
<header className="hero hero--primary">
<div className="container">
<h1 className="hero__title">{title}</h1>
<p className="hero__subtitle">{description}</p>
</div>
</header>
<main>{years.length > 0 && <YearsSection years={years} />}</main>
</Layout>
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,34 @@ import BlogLayout from '@theme/BlogLayout';
import BlogPostItem from '@theme/BlogPostItem';
import BlogListPaginator from '@theme/BlogListPaginator';
import type {Props} from '@theme/BlogListPage';
import {ThemeClassNames} from '@docusaurus/theme-common';
import {
PageMetadata,
HtmlClassNameProvider,
ThemeClassNames,
} from '@docusaurus/theme-common';
import SearchMetadata from '@theme/SearchMetadata';
import clsx from 'clsx';

export default function BlogListPage(props: Props): JSX.Element {
const {metadata, items, sidebar} = props;
function BlogListPageMetadata(props: Props): JSX.Element {
const {metadata} = props;
const {
siteConfig: {title: siteTitle},
} = useDocusaurusContext();
const {blogDescription, blogTitle, permalink} = metadata;
const isBlogOnlyMode = permalink === '/';
const title = isBlogOnlyMode ? siteTitle : blogTitle;
return (
<>
<PageMetadata title={title} description={blogDescription} />
<SearchMetadata tag="blog_posts_list" />
</>
);
}

function BlogListPageContent(props: Props): JSX.Element {
const {metadata, items, sidebar} = props;
return (
<BlogLayout
title={title}
description={blogDescription}
wrapperClassName={ThemeClassNames.wrapper.blogPages}
pageClassName={ThemeClassNames.page.blogListPage}
searchMetadata={{
// assign unique search tag to exclude this page from search results!
tag: 'blog_posts_list',
}}
sidebar={sidebar}>
<BlogLayout sidebar={sidebar}>
{items.map(({content: BlogPostContent}) => (
<BlogPostItem
key={BlogPostContent.metadata.permalink}
Expand All @@ -48,3 +54,16 @@ export default function BlogListPage(props: Props): JSX.Element {
</BlogLayout>
);
}

export default function BlogListPage(props: Props): JSX.Element {
return (
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.blogPages,
ThemeClassNames.page.blogListPage,
)}>
<BlogListPageMetadata {...props} />
<BlogListPageContent {...props} />
</HtmlClassNameProvider>
);
}
103 changes: 55 additions & 48 deletions packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,63 @@
*/

import React from 'react';
import Seo from '@theme/Seo';
import BlogLayout from '@theme/BlogLayout';
import BlogPostItem from '@theme/BlogPostItem';
import BlogPostPaginator from '@theme/BlogPostPaginator';
import type {Props} from '@theme/BlogPostPage';
import {ThemeClassNames} from '@docusaurus/theme-common';
import {
PageMetadata,
HtmlClassNameProvider,
ThemeClassNames,
} from '@docusaurus/theme-common';
import TOC from '@theme/TOC';
import clsx from 'clsx';

export default function BlogPostPage(props: Props): JSX.Element {
function BlogPostPageMetadata(props: Props): JSX.Element {
const {content: BlogPostContents} = props;
const {assets, metadata} = BlogPostContents;
const {title, description, date, tags, authors, frontMatter} = metadata;
const {keywords} = frontMatter;
const image = assets.image ?? frontMatter.image;
return (
<PageMetadata
title={title}
description={description}
keywords={keywords}
image={image}>
<meta property="og:type" content="article" />
<meta property="article:published_time" content={date} />
{/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */}
{authors.some((author) => author.url) && (
<meta
property="article:author"
content={authors
.map((author) => author.url)
.filter(Boolean)
.join(',')}
/>
)}
{tags.length > 0 && (
<meta
property="article:tag"
content={tags.map((tag) => tag.label).join(',')}
/>
)}
</PageMetadata>
);
}

function BlogPostPageContent(props: Props): JSX.Element {
const {content: BlogPostContents, sidebar} = props;
const {assets, metadata} = BlogPostContents;
const {
title,
description,
nextItem,
prevItem,
date,
tags,
authors,
frontMatter,
} = metadata;
const {nextItem, prevItem, frontMatter} = metadata;
const {
hide_table_of_contents: hideTableOfContents,
keywords,
toc_min_heading_level: tocMinHeadingLevel,
toc_max_heading_level: tocMaxHeadingLevel,
} = frontMatter;

const image = assets.image ?? frontMatter.image;

return (
<BlogLayout
wrapperClassName={ThemeClassNames.wrapper.blogPages}
pageClassName={ThemeClassNames.page.blogPostPage}
sidebar={sidebar}
toc={
!hideTableOfContents &&
Expand All @@ -52,35 +75,6 @@ export default function BlogPostPage(props: Props): JSX.Element {
/>
) : undefined
}>
<Seo
// TODO refactor needed: it's a bit annoying but Seo MUST be inside
// BlogLayout, otherwise default image (set by BlogLayout) would shadow
// the custom blog post image
title={title}
description={description}
keywords={keywords}
image={image}>
<meta property="og:type" content="article" />
<meta property="article:published_time" content={date} />

{/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */}
{authors.some((author) => author.url) && (
<meta
property="article:author"
content={authors
.map((author) => author.url)
.filter(Boolean)
.join(',')}
/>
)}
{tags.length > 0 && (
<meta
property="article:tag"
content={tags.map((tag) => tag.label).join(',')}
/>
)}
</Seo>

<BlogPostItem
frontMatter={frontMatter}
assets={assets}
Expand All @@ -95,3 +89,16 @@ export default function BlogPostPage(props: Props): JSX.Element {
</BlogLayout>
);
}

export default function BlogPostPage(props: Props): JSX.Element {
return (
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.blogPages,
ThemeClassNames.page.blogPostPage,
)}>
<BlogPostPageMetadata {...props} />
<BlogPostPageContent {...props} />
</HtmlClassNameProvider>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,29 @@ import BlogLayout from '@theme/BlogLayout';
import TagsListByLetter from '@theme/TagsListByLetter';
import type {Props} from '@theme/BlogTagsListPage';
import {
PageMetadata,
HtmlClassNameProvider,
ThemeClassNames,
translateTagsPageTitle,
} from '@docusaurus/theme-common';
import SearchMetadata from '../SearchMetadata';
import clsx from 'clsx';

export default function BlogTagsListPage(props: Props): JSX.Element {
const {tags, sidebar} = props;
const title = translateTagsPageTitle();
return (
<BlogLayout
title={title}
wrapperClassName={ThemeClassNames.wrapper.blogPages}
pageClassName={ThemeClassNames.page.blogTagsListPage}
searchMetadata={{
// assign unique search tag to exclude this page from search results!
tag: 'blog_tags_list',
}}
sidebar={sidebar}>
<h1>{title}</h1>
<TagsListByLetter tags={Object.values(tags)} />
</BlogLayout>
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.blogPages,
ThemeClassNames.page.blogTagsListPage,
)}>
<PageMetadata title={title} />
<SearchMetadata tag="blog_tags_list" />
<BlogLayout sidebar={sidebar}>
<h1>{title}</h1>
<TagsListByLetter tags={Object.values(tags)} />
</BlogLayout>
</HtmlClassNameProvider>
);
}
Loading