From 41449e19dae97d627d676e97dabac46256afeba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lorber?= Date: Fri, 8 Jul 2022 13:28:53 +0200 Subject: [PATCH] refactor(theme): split BlogPostItem into smaller theme subcomponents (#7716) --- .../__snapshots__/translations.test.ts.snap | 4 +- .../src/__tests__/blogUtils.test.ts | 2 +- .../src/__tests__/index.test.ts | 12 +- .../src/__tests__/translations.test.ts | 2 +- .../src/blogUtils.ts | 2 +- .../src/index.ts | 38 ++-- .../src/plugin-content-blog.d.ts | 36 ++-- .../src/theme-classic.d.ts | 98 +++++++++-- .../src/theme/BlogListPage/index.tsx | 13 +- .../theme/BlogPostItem/Container/index.tsx | 32 ++++ .../src/theme/BlogPostItem/Content/index.tsx | 29 ++++ .../Footer/ReadMoreLink/index.tsx | 44 +++++ .../src/theme/BlogPostItem/Footer/index.tsx | 60 +++++++ .../BlogPostItem/Footer/styles.module.css | 10 ++ .../Header/Author}/index.tsx | 11 +- .../Header/Authors}/index.tsx | 18 +- .../Header/Authors}/styles.module.css | 0 .../theme/BlogPostItem/Header/Info/index.tsx | 71 ++++++++ .../Header/Info/styles.module.css | 10 ++ .../theme/BlogPostItem/Header/Title/index.tsx | 33 ++++ .../{ => Header/Title}/styles.module.css | 12 +- .../src/theme/BlogPostItem/Header/index.tsx | 21 +++ .../src/theme/BlogPostItem/index.tsx | 164 ++---------------- .../src/theme/BlogPostItems/index.tsx | 30 ++++ .../src/theme/BlogPostPage/Metadata/index.tsx | 44 +++++ .../src/theme/BlogPostPage/index.tsx | 89 ++++------ .../src/theme/BlogTagsPostsPage/index.tsx | 77 ++++---- .../src/contexts/blogPost.tsx | 80 +++++++++ .../src/contexts/doc.tsx | 3 +- .../docusaurus-theme-common/src/internal.ts | 6 + .../Header/Author}/index.tsx | 10 +- .../Header/Author}/styles.module.css | 0 .../Header/Authors}/index.tsx | 15 +- .../Header/Authors}/styles.module.css | 0 .../theme/ChangelogItem/Header/index.tsx | 35 ++++ .../ChangelogItem/Header/styles.module.css | 10 ++ .../changelog/theme/ChangelogItem/index.tsx | 69 +------- .../theme/ChangelogItem/styles.module.css | 16 +- .../theme/ChangelogList/Header/index.tsx | 81 +++++++++ .../{ => Header}/styles.module.css | 12 -- .../changelog/theme/ChangelogList/index.tsx | 78 +-------- .../changelog/theme/ChangelogPage/index.tsx | 135 ++++++-------- .../src/plugins/changelog/theme/types.d.ts | 12 +- 43 files changed, 931 insertions(+), 593 deletions(-) create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostItem/Container/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostItem/Content/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostItem/Footer/ReadMoreLink/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostItem/Footer/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostItem/Footer/styles.module.css rename packages/docusaurus-theme-classic/src/theme/{BlogPostAuthor => BlogPostItem/Header/Author}/index.tsx (83%) rename packages/docusaurus-theme-classic/src/theme/{BlogPostAuthors => BlogPostItem/Header/Authors}/index.tsx (74%) rename packages/docusaurus-theme-classic/src/theme/{BlogPostAuthors => BlogPostItem/Header/Authors}/styles.module.css (100%) create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Info/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Info/styles.module.css create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Title/index.tsx rename packages/docusaurus-theme-classic/src/theme/BlogPostItem/{ => Header/Title}/styles.module.css (72%) create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostItems/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostPage/Metadata/index.tsx create mode 100644 packages/docusaurus-theme-common/src/contexts/blogPost.tsx rename website/src/plugins/changelog/theme/{ChangelogAuthor => ChangelogItem/Header/Author}/index.tsx (81%) rename website/src/plugins/changelog/theme/{ChangelogAuthor => ChangelogItem/Header/Author}/styles.module.css (100%) rename website/src/plugins/changelog/theme/{ChangelogAuthors => ChangelogItem/Header/Authors}/index.tsx (81%) rename website/src/plugins/changelog/theme/{ChangelogAuthors => ChangelogItem/Header/Authors}/styles.module.css (100%) create mode 100644 website/src/plugins/changelog/theme/ChangelogItem/Header/index.tsx create mode 100644 website/src/plugins/changelog/theme/ChangelogItem/Header/styles.module.css create mode 100644 website/src/plugins/changelog/theme/ChangelogList/Header/index.tsx rename website/src/plugins/changelog/theme/ChangelogList/{ => Header}/styles.module.css (67%) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/translations.test.ts.snap b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/translations.test.ts.snap index 729181bd219d..6c2eb19ac42e 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/translations.test.ts.snap +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/translations.test.ts.snap @@ -52,11 +52,11 @@ exports[`translateContent falls back when translation is incomplete 1`] = ` "description": "/blog/2021/06/19/hello", "formattedDate": "June 19, 2021", "frontMatter": {}, + "hasTruncateMarker": true, "permalink": "/blog/2021/06/19/hello", "source": "/blog/2021/06/19/hello", "tags": [], "title": "Hello", - "truncated": true, }, }, ], @@ -96,11 +96,11 @@ exports[`translateContent returns translated loaded 1`] = ` "description": "/blog/2021/06/19/hello", "formattedDate": "June 19, 2021", "frontMatter": {}, + "hasTruncateMarker": true, "permalink": "/blog/2021/06/19/hello", "source": "/blog/2021/06/19/hello", "tags": [], "title": "Hello", - "truncated": true, }, }, ], diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/blogUtils.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/blogUtils.test.ts index 2adc44ca9831..3ec0e9d9f90a 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/blogUtils.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/blogUtils.test.ts @@ -210,7 +210,7 @@ describe('linkify', () => { permalink: '/blog/2019/01/01/date-matter', title: 'date-matter', }, - truncated: false, + hasTruncateMarker: false, frontMatter: {}, authors: [], formattedDate: '', diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts index 5107dc2f88df..64998082ff53 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts @@ -171,7 +171,7 @@ describe('blog plugin', () => { permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash', title: 'Happy 1st Birthday Slash! (translated)', }, - truncated: false, + hasTruncateMarker: false, }); expect( @@ -214,7 +214,7 @@ describe('blog plugin', () => { permalink: '/blog/date-matter', title: 'date-matter', }, - truncated: false, + hasTruncateMarker: false, }); expect({ @@ -251,7 +251,7 @@ describe('blog plugin', () => { permalink: '/blog/tags/complex', }, ], - truncated: false, + hasTruncateMarker: false, }); expect({ @@ -288,7 +288,7 @@ describe('blog plugin', () => { title: 'Simple Slug', }, tags: [], - truncated: false, + hasTruncateMarker: false, }); expect({ @@ -313,7 +313,7 @@ describe('blog plugin', () => { permalink: '/blog/date-matter', title: 'date-matter', }, - truncated: false, + hasTruncateMarker: false, }); }); @@ -470,7 +470,7 @@ describe('blog plugin', () => { tags: [], prevItem: undefined, nextItem: undefined, - truncated: false, + hasTruncateMarker: false, }); }); diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/translations.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/translations.test.ts index 5056da948215..0ec7471cfa35 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/translations.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/translations.test.ts @@ -32,7 +32,7 @@ const sampleBlogPosts: BlogPost[] = [ formattedDate: 'June 19, 2021', tags: [], title: 'Hello', - truncated: true, + hasTruncateMarker: true, authors: [], frontMatter: {}, }, diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index ee6742c7bff5..17e0dbb0bffc 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -323,7 +323,7 @@ async function processBlogSourceFile( defaultReadingTime, }) : undefined, - truncated: truncateMarker.test(content), + hasTruncateMarker: truncateMarker.test(content), authors, frontMatter, }, diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index c96ea7a84f34..315c87e6a792 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -195,6 +195,21 @@ export default async function pluginContentBlog( ? blogPosts : blogPosts.slice(0, options.blogSidebarCount); + function blogPostItemsModule(items: string[]) { + return items.map((postId) => { + const blogPostMetadata = blogItemsToMetadata[postId]!; + return { + content: { + __import: true, + path: blogPostMetadata.source, + query: { + truncated: true, + }, + }, + }; + }); + } + if (archiveBasePath && blogPosts.length) { const archiveUrl = normalizeUrl([ baseUrl, @@ -275,15 +290,7 @@ export default async function pluginContentBlog( exact: true, modules: { sidebar: aliasedSource(sidebarProp), - items: items.map((postID) => ({ - content: { - __import: true, - path: blogItemsToMetadata[postID]!.source, - query: { - truncated: true, - }, - }, - })), + items: blogPostItemsModule(items), metadata: aliasedSource(pageMetadataPath), }, }); @@ -344,18 +351,7 @@ export default async function pluginContentBlog( exact: true, modules: { sidebar: aliasedSource(sidebarProp), - items: items.map((postID) => { - const blogPostMetadata = blogItemsToMetadata[postID]!; - return { - content: { - __import: true, - path: blogPostMetadata.source, - query: { - truncated: true, - }, - }, - }; - }), + items: blogPostItemsModule(items), tag: aliasedSource(tagPropPath), listMetadata: aliasedSource(listMetadataPath), }, diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index ae87a218105b..63d31d5f58c7 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -6,6 +6,7 @@ */ declare module '@docusaurus/plugin-content-blog' { + import type {LoadedMDXContent} from '@docusaurus/mdx-loader'; import type {MDXOptions} from '@docusaurus/mdx-loader'; import type {FrontMatterTag, Tag} from '@docusaurus/utils'; import type {Plugin, LoadContext} from '@docusaurus/types'; @@ -201,7 +202,7 @@ declare module '@docusaurus/plugin-content-blog' { /** * Whether the truncate marker exists in the post's content. */ - readonly truncated?: boolean; + readonly hasTruncateMarker: boolean; /** * Used in pagination. Generated after the other metadata, so not readonly. * Content is just a subset of another post's metadata. @@ -462,6 +463,20 @@ declare module '@docusaurus/plugin-content-blog' { items: string[]; }; + type PropBlogPostMetadata = Overwrite< + BlogPostMetadata, + { + /** The publish date of the post. Serialized from the `Date` object. */ + date: string; + } + >; + + export type PropBlogPostContent = LoadedMDXContent< + BlogPostFrontMatter, + PropBlogPostMetadata, + Assets + >; + export default function pluginContentBlog( context: LoadContext, options: PluginOptions, @@ -469,26 +484,15 @@ declare module '@docusaurus/plugin-content-blog' { } declare module '@theme/BlogPostPage' { - import type {LoadedMDXContent} from '@docusaurus/mdx-loader'; import type { BlogPostFrontMatter, - BlogPostMetadata, - Assets, BlogSidebar, + PropBlogPostContent, } from '@docusaurus/plugin-content-blog'; - import type {Overwrite} from 'utility-types'; export type FrontMatter = BlogPostFrontMatter; - export type Metadata = Overwrite< - BlogPostMetadata, - { - /** The publish date of the post. Serialized from the `Date` object. */ - date: string; - } - >; - - export type Content = LoadedMDXContent; + export type Content = PropBlogPostContent; export interface Props { /** Blog sidebar. */ @@ -500,6 +504,10 @@ declare module '@theme/BlogPostPage' { export default function BlogPostPage(props: Props): JSX.Element; } +declare module '@theme/BlogPostPage/Metadata' { + export default function BlogPostPageMetadata(): JSX.Element; +} + declare module '@theme/BlogListPage' { import type {Content} from '@theme/BlogPostPage'; import type { diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 494579be0d2d..a3dd2cb5d873 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -95,41 +95,103 @@ declare module '@theme/BlogSidebar' { } declare module '@theme/BlogPostItem' { - import type {FrontMatter, Metadata} from '@theme/BlogPostPage'; - import type {Assets} from '@docusaurus/plugin-content-blog'; + import type {ReactNode} from 'react'; export interface Props { - readonly frontMatter: FrontMatter; - readonly assets: Assets; - readonly metadata: Metadata; - readonly truncated?: string | boolean; - readonly isBlogPostPage?: boolean; - readonly children: JSX.Element; + children: ReactNode; + className?: string; + } + + export default function BlogPostItem(props: Props): JSX.Element; +} + +declare module '@theme/BlogPostItems' { + import type {ComponentType, ReactNode} from 'react'; + import type {PropBlogPostContent} from '@docusaurus/plugin-content-blog'; + + export interface Props { + items: readonly {content: PropBlogPostContent}[]; + component?: ComponentType<{children: ReactNode}>; } export default function BlogPostItem(props: Props): JSX.Element; } -declare module '@theme/BlogPostAuthor' { - import type {Metadata} from '@theme/BlogPostPage'; +declare module '@theme/BlogPostItem/Container' { + import type {ReactNode} from 'react'; + + export interface Props { + children: ReactNode; + className?: string; + } + + export default function BlogPostItemContainer(props: Props): JSX.Element; +} + +declare module '@theme/BlogPostItem/Header' { + export default function BlogPostItemHeader(): JSX.Element; +} + +declare module '@theme/BlogPostItem/Header/Title' { + export interface Props { + className?: string; + } + + export default function BlogPostItemHeaderTitle(props: Props): JSX.Element; +} + +declare module '@theme/BlogPostItem/Header/Info' { + export interface Props { + className?: string; + } + + export default function BlogPostItemHeaderInfo(): JSX.Element; +} + +declare module '@theme/BlogPostItem/Header/Author' { + import type {PropBlogPostContent} from '@docusaurus/plugin-content-blog'; export interface Props { - readonly author: Metadata['authors'][number]; + readonly author: PropBlogPostContent['metadata']['authors'][number]; + readonly className?: string; } - export default function BlogPostAuthor(props: Props): JSX.Element; + export default function BlogPostItemHeaderAuthor(props: Props): JSX.Element; } -declare module '@theme/BlogPostAuthors' { - import type {Metadata} from '@theme/BlogPostPage'; - import type {Assets} from '@docusaurus/plugin-content-blog'; +declare module '@theme/BlogPostItem/Header/Authors' { + export interface Props { + readonly className?: string; + } + + export default function BlogPostItemHeaderAuthors(props: Props): JSX.Element; +} + +declare module '@theme/BlogPostItem/Content' { + import type {ReactNode} from 'react'; export interface Props { - readonly authors: Metadata['authors']; - readonly assets: Assets; + children: ReactNode; + className?: string; } - export default function BlogPostAuthors(props: Props): JSX.Element; + export default function BlogPostItemContent(props: Props): JSX.Element; +} + +declare module '@theme/BlogPostItem/Footer' { + export default function BlogPostItemFooter(): JSX.Element | null; +} + +declare module '@theme/BlogPostItem/Footer/ReadMoreLink' { + import type {Props as LinkProps} from '@docusaurus/Link'; + + export type Props = LinkProps & { + blogPostTitle: string; + }; + + export default function BlogPostItemFooterReadMoreLink( + props: Props, + ): JSX.Element | null; } declare module '@theme/BlogPostPaginator' { diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx index 72270058d99d..4bb30e8cbf40 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx @@ -15,10 +15,10 @@ import { ThemeClassNames, } from '@docusaurus/theme-common'; import BlogLayout from '@theme/BlogLayout'; -import BlogPostItem from '@theme/BlogPostItem'; import BlogListPaginator from '@theme/BlogListPaginator'; import SearchMetadata from '@theme/SearchMetadata'; import type {Props} from '@theme/BlogListPage'; +import BlogPostItems from '@theme/BlogPostItems'; function BlogListPageMetadata(props: Props): JSX.Element { const {metadata} = props; @@ -40,16 +40,7 @@ function BlogListPageContent(props: Props): JSX.Element { const {metadata, items, sidebar} = props; return ( - {items.map(({content: BlogPostContent}) => ( - - - - ))} + ); diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Container/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Container/index.tsx new file mode 100644 index 000000000000..ab9d2a986933 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Container/index.tsx @@ -0,0 +1,32 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; +import {useBlogPost} from '@docusaurus/theme-common/internal'; +import type {Props} from '@theme/BlogPostItem/Container'; + +export default function BlogPostItemContainer({ + children, + className, +}: Props): JSX.Element { + const {frontMatter, assets} = useBlogPost(); + const {withBaseUrl} = useBaseUrlUtils(); + const image = assets.image ?? frontMatter.image; + return ( +
+ {image && ( + + )} + {children} +
+ ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Content/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Content/index.tsx new file mode 100644 index 000000000000..592df27d24f6 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Content/index.tsx @@ -0,0 +1,29 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import clsx from 'clsx'; +import {blogPostContainerID} from '@docusaurus/utils-common'; +import {useBlogPost} from '@docusaurus/theme-common/internal'; +import MDXContent from '@theme/MDXContent'; +import type {Props} from '@theme/BlogPostItem/Content'; + +export default function BlogPostItemContent({ + children, + className, +}: Props): JSX.Element { + const {isBlogPostPage} = useBlogPost(); + return ( +
+ {children} +
+ ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Footer/ReadMoreLink/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Footer/ReadMoreLink/index.tsx new file mode 100644 index 000000000000..75f725cc75cc --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Footer/ReadMoreLink/index.tsx @@ -0,0 +1,44 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import Translate, {translate} from '@docusaurus/Translate'; +import Link from '@docusaurus/Link'; +import type {Props} from '@theme/BlogPostItem/Footer/ReadMoreLink'; + +function ReadMoreLabel() { + return ( + + + Read More + + + ); +} + +export default function BlogPostItemFooterReadMoreLink( + props: Props, +): JSX.Element { + const {blogPostTitle, ...linkProps} = props; + return ( + + + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Footer/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Footer/index.tsx new file mode 100644 index 000000000000..314faacf2d98 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Footer/index.tsx @@ -0,0 +1,60 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import clsx from 'clsx'; +import {useBlogPost} from '@docusaurus/theme-common/internal'; +import EditThisPage from '@theme/EditThisPage'; +import TagsListInline from '@theme/TagsListInline'; +import ReadMoreLink from '@theme/BlogPostItem/Footer/ReadMoreLink'; + +import styles from './styles.module.css'; + +export default function BlogPostItemFooter(): JSX.Element | null { + const {metadata, isBlogPostPage} = useBlogPost(); + const {tags, title, editUrl, hasTruncateMarker} = metadata; + + // A post is truncated if it's in the "list view" and it has a truncate marker + const truncatedPost = !isBlogPostPage && hasTruncateMarker; + + const tagsExists = tags.length > 0; + + const renderFooter = tagsExists || truncatedPost || editUrl; + + if (!renderFooter) { + return null; + } + + return ( +
+ {tagsExists && ( +
+ +
+ )} + + {isBlogPostPage && editUrl && ( +
+ +
+ )} + + {truncatedPost && ( +
+ +
+ )} +
+ ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Footer/styles.module.css b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Footer/styles.module.css new file mode 100644 index 000000000000..f9272fb53b69 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Footer/styles.module.css @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +.blogPostFooterDetailsFull { + flex-direction: column; +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostAuthor/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx similarity index 83% rename from packages/docusaurus-theme-classic/src/theme/BlogPostAuthor/index.tsx rename to packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx index 8dce39bd3e06..92ace43f3220 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostAuthor/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx @@ -6,8 +6,10 @@ */ import React from 'react'; +import clsx from 'clsx'; import Link, {type Props as LinkProps} from '@docusaurus/Link'; -import type {Props} from '@theme/BlogPostAuthor'; + +import type {Props} from '@theme/BlogPostItem/Header/Author'; function MaybeLink(props: LinkProps): JSX.Element { if (props.href) { @@ -16,11 +18,14 @@ function MaybeLink(props: LinkProps): JSX.Element { return <>{props.children}; } -export default function BlogPostAuthor({author}: Props): JSX.Element { +export default function BlogPostItemHeaderAuthor({ + author, + className, +}: Props): JSX.Element { const {name, title, url, imageURL, email} = author; const link = url || (email && `mailto:${email}`) || undefined; return ( -
+
{imageURL && ( {name} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostAuthors/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Authors/index.tsx similarity index 74% rename from packages/docusaurus-theme-classic/src/theme/BlogPostAuthors/index.tsx rename to packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Authors/index.tsx index 99386d4ae900..d1ed5bb11cdd 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostAuthors/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Authors/index.tsx @@ -7,16 +7,19 @@ import React from 'react'; import clsx from 'clsx'; -import BlogPostAuthor from '@theme/BlogPostAuthor'; -import type {Props} from '@theme/BlogPostAuthors'; - +import {useBlogPost} from '@docusaurus/theme-common/internal'; +import BlogPostItemHeaderAuthor from '@theme/BlogPostItem/Header/Author'; +import type {Props} from '@theme/BlogPostItem/Header/Authors'; import styles from './styles.module.css'; // Component responsible for the authors layout -export default function BlogPostAuthors({ - authors, - assets, +export default function BlogPostItemHeaderAuthors({ + className, }: Props): JSX.Element | null { + const { + metadata: {authors}, + assets, + } = useBlogPost(); const authorsCount = authors.length; if (authorsCount === 0) { return null; @@ -27,6 +30,7 @@ export default function BlogPostAuthors({ className={clsx( 'margin-top--md margin-bottom--sm', imageOnly ? styles.imageOnlyAuthorRow : 'row', + className, )}> {authors.map((author, idx) => (
- { + const readingTime = Math.ceil(readingTimeFloat); + return selectMessage( + readingTime, + translate( + { + id: 'theme.blog.post.readingTime.plurals', + description: + 'Pluralized label for "{readingTime} min read". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)', + message: 'One min read|{readingTime} min read', + }, + {readingTime}, + ), + ); + }; +} + +function ReadingTime({readingTime}: {readingTime: number}) { + const readingTimePlural = useReadingTimePlural(); + return <>{readingTimePlural(readingTime)}; +} + +function Date({date, formattedDate}: {date: string; formattedDate: string}) { + return ( + + ); +} + +function Spacer() { + return <>{' · '}; +} + +export default function BlogPostItemHeaderInfo({ + className, +}: Props): JSX.Element { + const {metadata} = useBlogPost(); + const {date, formattedDate, readingTime} = metadata; + + return ( +
+ + {typeof readingTime !== 'undefined' && ( + <> + + + + )} +
+ ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Info/styles.module.css b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Info/styles.module.css new file mode 100644 index 000000000000..796b0f1713b9 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Info/styles.module.css @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +.container { + font-size: 0.9rem; +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Title/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Title/index.tsx new file mode 100644 index 000000000000..ea40c2ec752f --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Title/index.tsx @@ -0,0 +1,33 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import clsx from 'clsx'; +import Link from '@docusaurus/Link'; +import {useBlogPost} from '@docusaurus/theme-common/internal'; +import type {Props} from '@theme/BlogPostItem/Header/Title'; + +import styles from './styles.module.css'; + +export default function BlogPostItemHeaderTitle({ + className, +}: Props): JSX.Element { + const {metadata, isBlogPostPage} = useBlogPost(); + const {permalink, title} = metadata; + const TitleHeading = isBlogPostPage ? 'h1' : 'h2'; + return ( + + {isBlogPostPage ? ( + title + ) : ( + + {title} + + )} + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/styles.module.css b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Title/styles.module.css similarity index 72% rename from packages/docusaurus-theme-classic/src/theme/BlogPostItem/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Title/styles.module.css index 824f0de78af7..5ffc89f2432b 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Title/styles.module.css @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -.blogPostTitle { +.title { font-size: 3rem; } @@ -13,15 +13,7 @@ Blog post title should be smaller on smaller devices **/ @media (max-width: 576px) { - .blogPostTitle { + .title { font-size: 2rem; } } - -.blogPostData { - font-size: 0.9rem; -} - -.blogPostDetailsFull { - flex-direction: column; -} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/index.tsx new file mode 100644 index 000000000000..77acbaf563af --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/index.tsx @@ -0,0 +1,21 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import BlogPostItemHeaderTitle from '@theme/BlogPostItem/Header/Title'; +import BlogPostItemHeaderInfo from '@theme/BlogPostItem/Header/Info'; +import BlogPostItemHeaderAuthors from '@theme/BlogPostItem/Header/Authors'; + +export default function BlogPostItemHeader(): JSX.Element { + return ( +
+ + + +
+ ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/index.tsx index de8fdb5a18b6..4dfb1073c0d8 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/index.tsx @@ -7,155 +7,29 @@ import React from 'react'; import clsx from 'clsx'; -import Translate, {translate} from '@docusaurus/Translate'; -import Link from '@docusaurus/Link'; -import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; -import {usePluralForm} from '@docusaurus/theme-common'; -import {blogPostContainerID} from '@docusaurus/utils-common'; -import MDXContent from '@theme/MDXContent'; -import EditThisPage from '@theme/EditThisPage'; -import TagsListInline from '@theme/TagsListInline'; -import BlogPostAuthors from '@theme/BlogPostAuthors'; +import {useBlogPost} from '@docusaurus/theme-common/internal'; +import BlogPostItemContainer from '@theme/BlogPostItem/Container'; +import BlogPostItemHeader from '@theme/BlogPostItem/Header'; +import BlogPostItemContent from '@theme/BlogPostItem/Content'; +import BlogPostItemFooter from '@theme/BlogPostItem/Footer'; import type {Props} from '@theme/BlogPostItem'; -import styles from './styles.module.css'; - -// Very simple pluralization: probably good enough for now -function useReadingTimePlural() { - const {selectMessage} = usePluralForm(); - return (readingTimeFloat: number) => { - const readingTime = Math.ceil(readingTimeFloat); - return selectMessage( - readingTime, - translate( - { - id: 'theme.blog.post.readingTime.plurals', - description: - 'Pluralized label for "{readingTime} min read". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)', - message: 'One min read|{readingTime} min read', - }, - {readingTime}, - ), - ); - }; +// apply a bottom margin in list view +function useContainerClassName() { + const {isBlogPostPage} = useBlogPost(); + return !isBlogPostPage ? 'margin-bottom--xl' : undefined; } -export default function BlogPostItem(props: Props): JSX.Element { - const readingTimePlural = useReadingTimePlural(); - const {withBaseUrl} = useBaseUrlUtils(); - const { - children, - frontMatter, - assets, - metadata, - truncated, - isBlogPostPage = false, - } = props; - const { - date, - formattedDate, - permalink, - tags, - readingTime, - title, - editUrl, - authors, - } = metadata; - - const image = assets.image ?? frontMatter.image; - const truncatedPost = !isBlogPostPage && truncated; - const tagsExists = tags.length > 0; - const TitleHeading = isBlogPostPage ? 'h1' : 'h2'; - +export default function BlogPostItem({ + children, + className, +}: Props): JSX.Element { + const containerClassName = useContainerClassName(); return ( -
-
- - {isBlogPostPage ? ( - title - ) : ( - - {title} - - )} - -
- - - {typeof readingTime !== 'undefined' && ( - <> - {' · '} - {readingTimePlural(readingTime)} - - )} -
- -
- - {image && ( - - )} - -
- {children} -
- - {(tagsExists || truncated || editUrl) && ( -
- {tagsExists && ( -
- -
- )} - - {isBlogPostPage && editUrl && ( -
- -
- )} - - {truncatedPost && ( -
- - - - Read More - - - -
- )} -
- )} -
+ + + {children} + + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItems/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItems/index.tsx new file mode 100644 index 000000000000..652a1a6a3fbf --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItems/index.tsx @@ -0,0 +1,30 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import {BlogPostProvider} from '@docusaurus/theme-common/internal'; +import BlogPostItem from '@theme/BlogPostItem'; +import type {Props} from '@theme/BlogPostItems'; + +export default function BlogPostItems({ + items, + component: BlogPostItemComponent = BlogPostItem, +}: Props): JSX.Element { + return ( + <> + {items.map(({content: BlogPostContent}) => ( + + + + + + ))} + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/Metadata/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/Metadata/index.tsx new file mode 100644 index 000000000000..515e104baa8e --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/Metadata/index.tsx @@ -0,0 +1,44 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import {PageMetadata} from '@docusaurus/theme-common'; +import {useBlogPost} from '@docusaurus/theme-common/internal'; + +export default function BlogPostPageMetadata(): JSX.Element { + const {assets, metadata} = useBlogPost(); + const {title, description, date, tags, authors, frontMatter} = metadata; + + const {keywords} = frontMatter; + const image = assets.image ?? frontMatter.image; + return ( + + + + {/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */} + {authors.some((author) => author.url) && ( + author.url) + .filter(Boolean) + .join(',')} + /> + )} + {tags.length > 0 && ( + tag.label).join(',')} + /> + )} + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx index d09c589f305c..2dec817ef470 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx @@ -5,56 +5,26 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, {type ReactNode} from 'react'; import clsx from 'clsx'; -import { - PageMetadata, - HtmlClassNameProvider, - ThemeClassNames, -} from '@docusaurus/theme-common'; +import {HtmlClassNameProvider, ThemeClassNames} from '@docusaurus/theme-common'; +import {BlogPostProvider, useBlogPost} from '@docusaurus/theme-common/internal'; import BlogLayout from '@theme/BlogLayout'; import BlogPostItem from '@theme/BlogPostItem'; import BlogPostPaginator from '@theme/BlogPostPaginator'; +import BlogPostPageMetadata from '@theme/BlogPostPage/Metadata'; import TOC from '@theme/TOC'; import type {Props} from '@theme/BlogPostPage'; +import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; -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 ( - - - - {/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */} - {authors.some((author) => author.url) && ( - author.url) - .filter(Boolean) - .join(',')} - /> - )} - {tags.length > 0 && ( - tag.label).join(',')} - /> - )} - - ); -} - -function BlogPostPageContent(props: Props): JSX.Element { - const {content: BlogPostContents, sidebar} = props; - const {assets, metadata} = BlogPostContents; +function BlogPostPageContent({ + sidebar, + children, +}: { + sidebar: BlogSidebar; + children: ReactNode; +}): JSX.Element { + const {metadata, toc} = useBlogPost(); const {nextItem, prevItem, frontMatter} = metadata; const { hide_table_of_contents: hideTableOfContents, @@ -65,21 +35,15 @@ function BlogPostPageContent(props: Props): JSX.Element { 0 ? ( + !hideTableOfContents && toc.length > 0 ? ( ) : undefined }> - - - + {children} {(nextItem || prevItem) && ( @@ -89,14 +53,19 @@ function BlogPostPageContent(props: Props): JSX.Element { } export default function BlogPostPage(props: Props): JSX.Element { + const BlogPostContent = props.content; return ( - - - - + + + + + + + + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx index 312bc945e107..1ace6a9b3c3c 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx @@ -16,10 +16,10 @@ import { } from '@docusaurus/theme-common'; import Link from '@docusaurus/Link'; import BlogLayout from '@theme/BlogLayout'; -import BlogPostItem from '@theme/BlogPostItem'; import BlogListPaginator from '@theme/BlogListPaginator'; import SearchMetadata from '@theme/SearchMetadata'; import type {Props} from '@theme/BlogTagsPostsPage'; +import BlogPostItems from '@theme/BlogPostItems'; // Very simple pluralization: probably good enough for now function useBlogPostsPlural() { @@ -39,14 +39,9 @@ function useBlogPostsPlural() { ); } -export default function BlogTagsPostsPage({ - tag, - items, - sidebar, - listMetadata, -}: Props): JSX.Element { +function useBlogTagsPostsPageTitle(tag: Props['tag']): string { const blogPostsPlural = useBlogPostsPlural(); - const title = translate( + return translate( { id: 'theme.blog.tagTitle', description: 'The title of the page for a blog tag', @@ -54,40 +49,52 @@ export default function BlogTagsPostsPage({ }, {nPosts: blogPostsPlural(tag.count), tagName: tag.label}, ); +} +function BlogTagsPostsPageMetadata({tag}: Props): JSX.Element { + const title = useBlogTagsPostsPageTitle(tag); + return ( + <> + + + + ); +} + +function BlogTagsPostsPageContent({ + tag, + items, + sidebar, + listMetadata, +}: Props): JSX.Element { + const title = useBlogTagsPostsPageTitle(tag); + return ( + +
+

{title}

+ + + + View All Tags + + +
+ + +
+ ); +} +export default function BlogTagsPostsPage(props: Props): JSX.Element { return ( - - - -
-

{title}

- - - - View All Tags - - -
- - {items.map(({content: BlogPostContent}) => ( - - - - ))} - -
+ +
); } diff --git a/packages/docusaurus-theme-common/src/contexts/blogPost.tsx b/packages/docusaurus-theme-common/src/contexts/blogPost.tsx new file mode 100644 index 000000000000..e31d1f2ff2b5 --- /dev/null +++ b/packages/docusaurus-theme-common/src/contexts/blogPost.tsx @@ -0,0 +1,80 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, {useMemo, type ReactNode, useContext} from 'react'; +import {ReactContextError} from '../utils/reactUtils'; + +import type {PropBlogPostContent} from '@docusaurus/plugin-content-blog'; + +/** + * The React context value returned by the `useBlogPost()` hook. + * It contains useful data related to the currently browsed blog post. + */ +export type BlogPostContextValue = Pick< + PropBlogPostContent, + 'metadata' | 'frontMatter' | 'assets' | 'toc' +> & { + readonly isBlogPostPage: boolean; +}; + +const Context = React.createContext(null); + +/** + * Note: we don't use `PropBlogPostContent` as context value on purpose. Metadata is + * currently stored inside the MDX component, but we may want to change that in + * the future. + */ +function useContextValue({ + content, + isBlogPostPage, +}: { + content: PropBlogPostContent; + isBlogPostPage: boolean; +}): BlogPostContextValue { + return useMemo( + () => ({ + metadata: content.metadata, + frontMatter: content.frontMatter, + assets: content.assets, + toc: content.toc, + isBlogPostPage, + }), + [content, isBlogPostPage], + ); +} + +/** + * This is a very thin layer around the `content` received from the MDX loader. + * It provides metadata about the blog post to the children tree. + */ +export function BlogPostProvider({ + children, + content, + isBlogPostPage = false, +}: { + children: ReactNode; + content: PropBlogPostContent; + isBlogPostPage?: boolean; +}): JSX.Element { + const contextValue = useContextValue({content, isBlogPostPage}); + return {children}; +} + +/** + * Returns the data of the currently browsed blog post. Gives access to + * front matter, metadata, TOC, etc. + * When swizzling a low-level component (e.g. the "Edit this page" link) + * and you need some extra metadata, you don't have to drill the props + * all the way through the component tree: simply use this hook instead. + */ +export function useBlogPost(): BlogPostContextValue { + const blogPost = useContext(Context); + if (blogPost === null) { + throw new ReactContextError('BlogPostProvider'); + } + return blogPost; +} diff --git a/packages/docusaurus-theme-common/src/contexts/doc.tsx b/packages/docusaurus-theme-common/src/contexts/doc.tsx index 39b9a6c2805e..64cc738d5669 100644 --- a/packages/docusaurus-theme-common/src/contexts/doc.tsx +++ b/packages/docusaurus-theme-common/src/contexts/doc.tsx @@ -42,8 +42,7 @@ function useContextValue(content: PropDocContent): DocContextValue { /** * This is a very thin layer around the `content` received from the MDX loader. - * It provides the component to be rendered and other metadata about the doc to - * the children. + * It provides metadata about the doc to the children tree. */ export function DocProvider({ children, diff --git a/packages/docusaurus-theme-common/src/internal.ts b/packages/docusaurus-theme-common/src/internal.ts index 8c2d4b9fd128..79812c968634 100644 --- a/packages/docusaurus-theme-common/src/internal.ts +++ b/packages/docusaurus-theme-common/src/internal.ts @@ -24,7 +24,13 @@ export { } from './contexts/docSidebarItemsExpandedState'; export {DocsVersionProvider, useDocsVersion} from './contexts/docsVersion'; export {DocsSidebarProvider, useDocsSidebar} from './contexts/docsSidebar'; + export {DocProvider, useDoc, type DocContextValue} from './contexts/doc'; +export { + BlogPostProvider, + useBlogPost, + type BlogPostContextValue, +} from './contexts/blogPost'; export { useDocsPreferredVersionByPluginId, diff --git a/website/src/plugins/changelog/theme/ChangelogAuthor/index.tsx b/website/src/plugins/changelog/theme/ChangelogItem/Header/Author/index.tsx similarity index 81% rename from website/src/plugins/changelog/theme/ChangelogAuthor/index.tsx rename to website/src/plugins/changelog/theme/ChangelogItem/Header/Author/index.tsx index c22cc2a4c829..185505530d24 100644 --- a/website/src/plugins/changelog/theme/ChangelogAuthor/index.tsx +++ b/website/src/plugins/changelog/theme/ChangelogItem/Header/Author/index.tsx @@ -6,15 +6,19 @@ */ import React from 'react'; +import clsx from 'clsx'; import Link from '@docusaurus/Link'; -import type {Props} from '@theme/BlogPostAuthor'; +import type {Props} from '@theme/BlogPostItem/Header/Author'; import styles from './styles.module.css'; -export default function ChangelogAuthor({author}: Props): JSX.Element { +export default function ChangelogAuthor({ + author, + className, +}: Props): JSX.Element { const {name, url, imageURL} = author; return ( -
+
{imageURL && ( {filteredAuthors.map((author, idx) => (
- + ); +} + +export default function ChangelogItemHeader(): JSX.Element { + return ( +
+ + + +
+ ); +} diff --git a/website/src/plugins/changelog/theme/ChangelogItem/Header/styles.module.css b/website/src/plugins/changelog/theme/ChangelogItem/Header/styles.module.css new file mode 100644 index 000000000000..5942100bdba1 --- /dev/null +++ b/website/src/plugins/changelog/theme/ChangelogItem/Header/styles.module.css @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +.changelogItemTitleList { + font-size: 2rem; +} diff --git a/website/src/plugins/changelog/theme/ChangelogItem/index.tsx b/website/src/plugins/changelog/theme/ChangelogItem/index.tsx index fee206306260..87d7b865e20c 100644 --- a/website/src/plugins/changelog/theme/ChangelogItem/index.tsx +++ b/website/src/plugins/changelog/theme/ChangelogItem/index.tsx @@ -6,71 +6,18 @@ */ import React from 'react'; -import clsx from 'clsx'; -import {MDXProvider} from '@mdx-js/react'; -import Link from '@docusaurus/Link'; -import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; -import {blogPostContainerID} from '@docusaurus/utils-common'; -import MDXComponents from '@theme/MDXComponents'; -import ChangelogAuthors from '@theme/ChangelogAuthors'; +import ChangelogItemHeader from '@theme/ChangelogItem/Header'; import type {Props} from '@theme/BlogPostItem'; +import BlogPostItemContainer from '@theme/BlogPostItem/Container'; +import BlogPostItemContent from '@theme/BlogPostItem/Content'; import styles from './styles.module.css'; -export default function ChangelogItem(props: Props): JSX.Element { - const {withBaseUrl} = useBaseUrlUtils(); - const { - children, - frontMatter, - assets, - metadata, - isBlogPostPage = false, - } = props; - const {date, formattedDate, permalink, title, authors} = metadata; - - const image = assets.image ?? frontMatter.image; - - const TitleHeading = isBlogPostPage ? 'h1' : 'h2'; - +export default function ChangelogItem({children}: Props): JSX.Element { return ( -
-
- - {isBlogPostPage ? ( - title - ) : ( - - {title} - - )} - -
- -
- -
- - {image && ( - - )} - -
- {children} -
-
+ + + {children} + ); } diff --git a/website/src/plugins/changelog/theme/ChangelogItem/styles.module.css b/website/src/plugins/changelog/theme/ChangelogItem/styles.module.css index b3e827c96fa2..33fa6c8fda1d 100644 --- a/website/src/plugins/changelog/theme/ChangelogItem/styles.module.css +++ b/website/src/plugins/changelog/theme/ChangelogItem/styles.module.css @@ -5,18 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -.blogPostTitle { - font-size: 2rem; -} - -.blogPostPageTitle { - font-size: 3rem; -} - -.blogPostData { - font-size: 0.9rem; -} - -.blogPostDetailsFull { - flex-direction: column; +.changelogItemContainer { + margin-bottom: 1rem; } diff --git a/website/src/plugins/changelog/theme/ChangelogList/Header/index.tsx b/website/src/plugins/changelog/theme/ChangelogList/Header/index.tsx new file mode 100644 index 000000000000..768c441b49c0 --- /dev/null +++ b/website/src/plugins/changelog/theme/ChangelogList/Header/index.tsx @@ -0,0 +1,81 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import Translate from '@docusaurus/Translate'; +import Link from '@docusaurus/Link'; +import styles from './styles.module.css'; + +function TwitterLink() { + return ( + + Twitter + + + + + ); +} + +function RssLink() { + return ( + + + RSS feeds + + + + + + ); +} + +export default function ChangelogListHeader({ + blogTitle, +}: { + blogTitle: string; +}): JSX.Element { + return ( +
+

{blogTitle}

+

+ , + rssLink: , + }}> + { + 'Subscribe through {rssLink} or follow us on {twitterLink} to stay up-to-date with new releases!' + } + +

+
+ ); +} diff --git a/website/src/plugins/changelog/theme/ChangelogList/styles.module.css b/website/src/plugins/changelog/theme/ChangelogList/Header/styles.module.css similarity index 67% rename from website/src/plugins/changelog/theme/ChangelogList/styles.module.css rename to website/src/plugins/changelog/theme/ChangelogList/Header/styles.module.css index 1537857cf302..ce866c313a35 100644 --- a/website/src/plugins/changelog/theme/ChangelogList/styles.module.css +++ b/website/src/plugins/changelog/theme/ChangelogList/Header/styles.module.css @@ -5,18 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -.blogPostTitle { - font-size: 2rem; -} - -.blogPostData { - font-size: 0.9rem; -} - -.blogPostDetailsFull { - flex-direction: column; -} - .rss, .rss:hover { color: #f26522; diff --git a/website/src/plugins/changelog/theme/ChangelogList/index.tsx b/website/src/plugins/changelog/theme/ChangelogList/index.tsx index 17aceebb6f01..d2315c32e256 100644 --- a/website/src/plugins/changelog/theme/ChangelogList/index.tsx +++ b/website/src/plugins/changelog/theme/ChangelogList/index.tsx @@ -7,8 +7,6 @@ import React from 'react'; import clsx from 'clsx'; -import Translate from '@docusaurus/Translate'; -import Link from '@docusaurus/Link'; import { PageMetadata, HtmlClassNameProvider, @@ -16,12 +14,12 @@ import { } from '@docusaurus/theme-common'; import BlogLayout from '@theme/BlogLayout'; import BlogListPaginator from '@theme/BlogListPaginator'; +import BlogPostItems from '@theme/BlogPostItems'; import SearchMetadata from '@theme/SearchMetadata'; import ChangelogItem from '@theme/ChangelogItem'; +import ChangelogListHeader from '@theme/ChangelogList/Header'; import type {Props} from '@theme/BlogListPage'; -import styles from './styles.module.css'; - function ChangelogListMetadata(props: Props): JSX.Element { const {metadata} = props; const {blogTitle, blogDescription} = metadata; @@ -36,78 +34,10 @@ function ChangelogListMetadata(props: Props): JSX.Element { function ChangelogListContent(props: Props): JSX.Element { const {metadata, items, sidebar} = props; const {blogTitle} = metadata; - return ( -
-

{blogTitle}

-

- - Twitter - - - - - ), - rssLink: ( - - - - RSS feeds - - - - - - - ), - }}> - { - 'Subscribe through {rssLink} or follow us on {twitterLink} to stay up-to-date with new releases!' - } - -

-
- {items.map(({content: BlogPostContent}) => ( - - - - ))} + +
); diff --git a/website/src/plugins/changelog/theme/ChangelogPage/index.tsx b/website/src/plugins/changelog/theme/ChangelogPage/index.tsx index 5f4a128d7759..0eed63471d1b 100644 --- a/website/src/plugins/changelog/theme/ChangelogPage/index.tsx +++ b/website/src/plugins/changelog/theme/ChangelogPage/index.tsx @@ -5,103 +5,65 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, {type ReactNode} from 'react'; import clsx from 'clsx'; import Translate from '@docusaurus/Translate'; import Link from '@docusaurus/Link'; -import { - PageMetadata, - HtmlClassNameProvider, - ThemeClassNames, -} from '@docusaurus/theme-common'; +import {HtmlClassNameProvider, ThemeClassNames} from '@docusaurus/theme-common'; +import {BlogPostProvider, useBlogPost} from '@docusaurus/theme-common/internal'; +import BlogPostPageMetadata from '@theme/BlogPostPage/Metadata'; import BlogLayout from '@theme/BlogLayout'; import ChangelogItem from '@theme/ChangelogItem'; import ChangelogPaginator from '@theme/ChangelogPaginator'; import TOC from '@theme/TOC'; import type {Props} from '@theme/BlogPostPage'; +import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; -function ChangelogPageMetadata(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; +function BackToIndexLink() { + const {metadata} = useBlogPost(); + // @ts-expect-error: we injected this + const {listPageLink} = metadata; return ( - - - - - {authors.some((author) => author.url) && ( - author.url) - .filter(Boolean) - .join(',')} - /> - )} - {tags.length > 0 && ( - tag.label).join(',')} - /> - )} - + + ← Back to index page + ); } -function ChangelogPageContent(props: Props): JSX.Element { - const {content: BlogPostContents, sidebar} = props; - const {assets, metadata} = BlogPostContents; - const { - nextItem, - prevItem, - frontMatter, - // @ts-expect-error: we injected this - listPageLink, - } = metadata; +function ChangelogPageContent({ + sidebar, + children, +}: { + sidebar: BlogSidebar; + children: ReactNode; +}): JSX.Element { + const {metadata, toc} = useBlogPost(); + const {nextItem, prevItem, frontMatter} = metadata; const { hide_table_of_contents: hideTableOfContents, toc_min_heading_level: tocMinHeadingLevel, toc_max_heading_level: tocMaxHeadingLevel, } = frontMatter; - return ( - <> - - 0 ? ( - - ) : undefined - }> - - ← Back to index page - + 0 ? ( + + ) : undefined + }> + - - - + {children} - {(nextItem || prevItem) && ( - - )} - - + {(nextItem || prevItem) && ( + + )} + ); } @@ -109,14 +71,19 @@ function ChangelogPageContent(props: Props): JSX.Element { // own ChangelogItem. We don't want to apply the swizzled item to the actual // blog. export default function ChangelogPage(props: Props): JSX.Element { + const ChangelogContent = props.content; return ( - - - - + + + + + + + + ); } diff --git a/website/src/plugins/changelog/theme/types.d.ts b/website/src/plugins/changelog/theme/types.d.ts index ab13c75aa745..4ac3776e3404 100644 --- a/website/src/plugins/changelog/theme/types.d.ts +++ b/website/src/plugins/changelog/theme/types.d.ts @@ -5,10 +5,16 @@ * LICENSE file in the root directory of this source tree. */ -declare module '@theme/ChangelogItem'; -declare module '@theme/ChangelogAuthors'; -declare module '@theme/ChangelogAuthor'; declare module '@theme/ChangelogPaginator'; + +declare module '@theme/ChangelogItem'; +declare module '@theme/ChangelogItem/Header'; +declare module '@theme/ChangelogItem/Header/Author'; +declare module '@theme/ChangelogItem/Header/Authors'; + +declare module '@theme/ChangelogList'; +declare module '@theme/ChangelogList/Header'; + declare module '@theme/IconExpand' { import type {ComponentProps} from 'react';