@docusaurus/theme-classic 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/lib/theme/BlogArchivePage/index.js +9 -1
  2. package/lib/theme/BlogLayout/index.js +1 -3
  3. package/lib/theme/BlogListPage/StructuredData/index.d.ts +10 -0
  4. package/lib/theme/BlogListPage/StructuredData/index.js +19 -0
  5. package/lib/theme/BlogListPage/index.js +2 -0
  6. package/lib/theme/BlogPostItem/Container/index.js +1 -26
  7. package/lib/theme/BlogPostItem/Content/index.js +1 -2
  8. package/lib/theme/BlogPostItem/Footer/index.js +61 -31
  9. package/lib/theme/BlogPostItem/Header/Author/index.js +5 -18
  10. package/lib/theme/BlogPostItem/Header/Info/index.js +15 -9
  11. package/lib/theme/BlogPostItem/Header/Title/index.js +2 -8
  12. package/lib/theme/{BlogPostItem/Footer/styles.module.css → BlogPostPage/StructuredData/index.d.ts} +2 -4
  13. package/lib/theme/BlogPostPage/StructuredData/index.js +19 -0
  14. package/lib/theme/BlogPostPage/index.js +3 -0
  15. package/lib/theme/DocCard/index.js +19 -12
  16. package/lib/theme/DocItem/Footer/index.js +17 -42
  17. package/lib/theme/DocSidebarItem/Category/index.js +3 -1
  18. package/lib/theme/EditMetaRow/index.d.ts +3 -0
  19. package/lib/theme/EditMetaRow/index.js +31 -0
  20. package/lib/theme/LastUpdated/index.d.ts +1 -1
  21. package/lib/theme/LastUpdated/index.js +17 -16
  22. package/lib/theme/MDXComponents/Img/index.js +1 -0
  23. package/{src/theme/BlogPostItem/Footer/styles.module.css → lib/theme/MDXComponents/Li.d.ts} +3 -4
  24. package/lib/theme/MDXComponents/Li.js +13 -0
  25. package/lib/theme/MDXComponents/index.js +2 -0
  26. package/lib/theme/NavbarItem/DropdownNavbarItem/index.js +3 -0
  27. package/package.json +14 -15
  28. package/src/theme/BlogArchivePage/index.tsx +11 -1
  29. package/src/theme/BlogLayout/index.tsx +1 -3
  30. package/src/theme/BlogListPage/StructuredData/index.tsx +22 -0
  31. package/src/theme/BlogListPage/index.tsx +2 -0
  32. package/src/theme/BlogPostItem/Container/index.tsx +1 -26
  33. package/src/theme/BlogPostItem/Content/index.tsx +1 -2
  34. package/src/theme/BlogPostItem/Footer/index.tsx +62 -32
  35. package/src/theme/BlogPostItem/Header/Author/index.tsx +5 -18
  36. package/src/theme/BlogPostItem/Header/Info/index.tsx +24 -9
  37. package/src/theme/BlogPostItem/Header/Title/index.tsx +2 -8
  38. package/src/theme/BlogPostPage/StructuredData/index.tsx +21 -0
  39. package/src/theme/BlogPostPage/index.tsx +3 -0
  40. package/src/theme/DocCard/index.tsx +20 -12
  41. package/src/theme/DocItem/Footer/index.tsx +19 -52
  42. package/src/theme/DocSidebarItem/Category/index.tsx +3 -1
  43. package/src/theme/EditMetaRow/index.tsx +34 -0
  44. package/src/theme/LastUpdated/index.tsx +18 -13
  45. package/src/theme/MDXComponents/Img/index.tsx +1 -0
  46. package/src/theme/MDXComponents/Li.tsx +17 -0
  47. package/src/theme/MDXComponents/index.tsx +2 -0
  48. package/src/theme/NavbarItem/DropdownNavbarItem/index.tsx +3 -0
  49. package/src/theme-classic.d.ts +18 -1
  50. package/lib/theme/{DocItem/Footer → EditMetaRow}/styles.module.css +2 -2
  51. package/src/theme/{DocItem/Footer → EditMetaRow}/styles.module.css +2 -2
@@ -8,9 +8,17 @@ import React from 'react';
8
8
  import Link from '@docusaurus/Link';
9
9
  import {translate} from '@docusaurus/Translate';
10
10
  import {PageMetadata} from '@docusaurus/theme-common';
11
+ import {useDateTimeFormat} from '@docusaurus/theme-common/internal';
11
12
  import Layout from '@theme/Layout';
12
13
  import Heading from '@theme/Heading';
13
14
  function Year({year, posts}) {
15
+ const dateTimeFormat = useDateTimeFormat({
16
+ day: 'numeric',
17
+ month: 'long',
18
+ timeZone: 'UTC',
19
+ });
20
+ const formatDate = (lastUpdated) =>
21
+ dateTimeFormat.format(new Date(lastUpdated));
14
22
  return (
15
23
  <>
16
24
  <Heading as="h3" id={year}>
@@ -20,7 +28,7 @@ function Year({year, posts}) {
20
28
  {posts.map((post) => (
21
29
  <li key={post.metadata.date}>
22
30
  <Link to={post.metadata.permalink}>
23
- {post.metadata.formattedDate} - {post.metadata.title}
31
+ {formatDate(post.metadata.date)} - {post.metadata.title}
24
32
  </Link>
25
33
  </li>
26
34
  ))}
@@ -20,9 +20,7 @@ export default function BlogLayout(props) {
20
20
  className={clsx('col', {
21
21
  'col--7': hasSidebar,
22
22
  'col--9 col--offset-1': !hasSidebar,
23
- })}
24
- itemScope
25
- itemType="https://schema.org/Blog">
23
+ })}>
26
24
  {children}
27
25
  </main>
28
26
  {toc && <div className="col col--2">{toc}</div>}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ /// <reference types="@docusaurus/plugin-content-blog" />
8
+ /// <reference types="react" />
9
+ import type { Props } from '@theme/BlogListPage/StructuredData';
10
+ export default function BlogListPageStructuredData(props: Props): JSX.Element;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import React from 'react';
8
+ import Head from '@docusaurus/Head';
9
+ import {useBlogListPageStructuredData} from '@docusaurus/theme-common';
10
+ export default function BlogListPageStructuredData(props) {
11
+ const structuredData = useBlogListPageStructuredData(props);
12
+ return (
13
+ <Head>
14
+ <script type="application/ld+json">
15
+ {JSON.stringify(structuredData)}
16
+ </script>
17
+ </Head>
18
+ );
19
+ }
@@ -16,6 +16,7 @@ import BlogLayout from '@theme/BlogLayout';
16
16
  import BlogListPaginator from '@theme/BlogListPaginator';
17
17
  import SearchMetadata from '@theme/SearchMetadata';
18
18
  import BlogPostItems from '@theme/BlogPostItems';
19
+ import BlogListPageStructuredData from '@theme/BlogListPage/StructuredData';
19
20
  function BlogListPageMetadata(props) {
20
21
  const {metadata} = props;
21
22
  const {
@@ -48,6 +49,7 @@ export default function BlogListPage(props) {
48
49
  ThemeClassNames.page.blogListPage,
49
50
  )}>
50
51
  <BlogListPageMetadata {...props} />
52
+ <BlogListPageStructuredData {...props} />
51
53
  <BlogListPageContent {...props} />
52
54
  </HtmlClassNameProvider>
53
55
  );
@@ -5,31 +5,6 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import React from 'react';
8
- import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
9
- import {useBlogPost} from '@docusaurus/theme-common/internal';
10
8
  export default function BlogPostItemContainer({children, className}) {
11
- const {
12
- frontMatter,
13
- assets,
14
- metadata: {description},
15
- } = useBlogPost();
16
- const {withBaseUrl} = useBaseUrlUtils();
17
- const image = assets.image ?? frontMatter.image;
18
- const keywords = frontMatter.keywords ?? [];
19
- return (
20
- <article
21
- className={className}
22
- itemProp="blogPost"
23
- itemScope
24
- itemType="https://schema.org/BlogPosting">
25
- {description && <meta itemProp="description" content={description} />}
26
- {image && (
27
- <link itemProp="image" href={withBaseUrl(image, {absolute: true})} />
28
- )}
29
- {keywords.length > 0 && (
30
- <meta itemProp="keywords" content={keywords.join(',')} />
31
- )}
32
- {children}
33
- </article>
34
- );
9
+ return <article className={className}>{children}</article>;
35
10
  }
@@ -15,8 +15,7 @@ export default function BlogPostItemContent({children, className}) {
15
15
  <div
16
16
  // This ID is used for the feed generation to locate the main content
17
17
  id={isBlogPostPage ? blogPostContainerID : undefined}
18
- className={clsx('markdown', className)}
19
- itemProp="articleBody">
18
+ className={clsx('markdown', className)}>
20
19
  <MDXContent>{children}</MDXContent>
21
20
  </div>
22
21
  );
@@ -7,13 +7,20 @@
7
7
  import React from 'react';
8
8
  import clsx from 'clsx';
9
9
  import {useBlogPost} from '@docusaurus/theme-common/internal';
10
- import EditThisPage from '@theme/EditThisPage';
10
+ import {ThemeClassNames} from '@docusaurus/theme-common';
11
+ import EditMetaRow from '@theme/EditMetaRow';
11
12
  import TagsListInline from '@theme/TagsListInline';
12
13
  import ReadMoreLink from '@theme/BlogPostItem/Footer/ReadMoreLink';
13
- import styles from './styles.module.css';
14
14
  export default function BlogPostItemFooter() {
15
15
  const {metadata, isBlogPostPage} = useBlogPost();
16
- const {tags, title, editUrl, hasTruncateMarker} = metadata;
16
+ const {
17
+ tags,
18
+ title,
19
+ editUrl,
20
+ hasTruncateMarker,
21
+ lastUpdatedBy,
22
+ lastUpdatedAt,
23
+ } = metadata;
17
24
  // A post is truncated if it's in the "list view" and it has a truncate marker
18
25
  const truncatedPost = !isBlogPostPage && hasTruncateMarker;
19
26
  const tagsExists = tags.length > 0;
@@ -21,32 +28,55 @@ export default function BlogPostItemFooter() {
21
28
  if (!renderFooter) {
22
29
  return null;
23
30
  }
24
- return (
25
- <footer
26
- className={clsx(
27
- 'row docusaurus-mt-lg',
28
- isBlogPostPage && styles.blogPostFooterDetailsFull,
29
- )}>
30
- {tagsExists && (
31
- <div className={clsx('col', {'col--9': truncatedPost})}>
32
- <TagsListInline tags={tags} />
33
- </div>
34
- )}
35
-
36
- {isBlogPostPage && editUrl && (
37
- <div className="col margin-top--sm">
38
- <EditThisPage editUrl={editUrl} />
39
- </div>
40
- )}
41
-
42
- {truncatedPost && (
43
- <div
44
- className={clsx('col text--right', {
45
- 'col--3': tagsExists,
46
- })}>
47
- <ReadMoreLink blogPostTitle={title} to={metadata.permalink} />
48
- </div>
49
- )}
50
- </footer>
51
- );
31
+ // BlogPost footer - details view
32
+ if (isBlogPostPage) {
33
+ const canDisplayEditMetaRow = !!(editUrl || lastUpdatedAt || lastUpdatedBy);
34
+ return (
35
+ <footer className="docusaurus-mt-lg">
36
+ {tagsExists && (
37
+ <div
38
+ className={clsx(
39
+ 'row',
40
+ 'margin-top--sm',
41
+ ThemeClassNames.blog.blogFooterEditMetaRow,
42
+ )}>
43
+ <div className="col">
44
+ <TagsListInline tags={tags} />
45
+ </div>
46
+ </div>
47
+ )}
48
+ {canDisplayEditMetaRow && (
49
+ <EditMetaRow
50
+ className={clsx(
51
+ 'margin-top--sm',
52
+ ThemeClassNames.blog.blogFooterEditMetaRow,
53
+ )}
54
+ editUrl={editUrl}
55
+ lastUpdatedAt={lastUpdatedAt}
56
+ lastUpdatedBy={lastUpdatedBy}
57
+ />
58
+ )}
59
+ </footer>
60
+ );
61
+ }
62
+ // BlogPost footer - list view
63
+ else {
64
+ return (
65
+ <footer className="row docusaurus-mt-lg">
66
+ {tagsExists && (
67
+ <div className={clsx('col', {'col--9': truncatedPost})}>
68
+ <TagsListInline tags={tags} />
69
+ </div>
70
+ )}
71
+ {truncatedPost && (
72
+ <div
73
+ className={clsx('col text--right', {
74
+ 'col--3': tagsExists,
75
+ })}>
76
+ <ReadMoreLink blogPostTitle={title} to={metadata.permalink} />
77
+ </div>
78
+ )}
79
+ </footer>
80
+ );
81
+ }
52
82
  }
@@ -20,31 +20,18 @@ export default function BlogPostItemHeaderAuthor({author, className}) {
20
20
  <div className={clsx('avatar margin-bottom--sm', className)}>
21
21
  {imageURL && (
22
22
  <MaybeLink href={link} className="avatar__photo-link">
23
- <img
24
- className="avatar__photo"
25
- src={imageURL}
26
- alt={name}
27
- itemProp="image"
28
- />
23
+ <img className="avatar__photo" src={imageURL} alt={name} />
29
24
  </MaybeLink>
30
25
  )}
31
26
 
32
27
  {name && (
33
- <div
34
- className="avatar__intro"
35
- itemProp="author"
36
- itemScope
37
- itemType="https://schema.org/Person">
28
+ <div className="avatar__intro">
38
29
  <div className="avatar__name">
39
- <MaybeLink href={link} itemProp="url">
40
- <span itemProp="name">{name}</span>
30
+ <MaybeLink href={link}>
31
+ <span>{name}</span>
41
32
  </MaybeLink>
42
33
  </div>
43
- {title && (
44
- <small className="avatar__subtitle" itemProp="description">
45
- {title}
46
- </small>
47
- )}
34
+ {title && <small className="avatar__subtitle">{title}</small>}
48
35
  </div>
49
36
  )}
50
37
  </div>
@@ -8,7 +8,10 @@ import React from 'react';
8
8
  import clsx from 'clsx';
9
9
  import {translate} from '@docusaurus/Translate';
10
10
  import {usePluralForm} from '@docusaurus/theme-common';
11
- import {useBlogPost} from '@docusaurus/theme-common/internal';
11
+ import {
12
+ useBlogPost,
13
+ useDateTimeFormat,
14
+ } from '@docusaurus/theme-common/internal';
12
15
  import styles from './styles.module.css';
13
16
  // Very simple pluralization: probably good enough for now
14
17
  function useReadingTimePlural() {
@@ -33,22 +36,25 @@ function ReadingTime({readingTime}) {
33
36
  const readingTimePlural = useReadingTimePlural();
34
37
  return <>{readingTimePlural(readingTime)}</>;
35
38
  }
36
- function Date({date, formattedDate}) {
37
- return (
38
- <time dateTime={date} itemProp="datePublished">
39
- {formattedDate}
40
- </time>
41
- );
39
+ function DateTime({date, formattedDate}) {
40
+ return <time dateTime={date}>{formattedDate}</time>;
42
41
  }
43
42
  function Spacer() {
44
43
  return <>{' · '}</>;
45
44
  }
46
45
  export default function BlogPostItemHeaderInfo({className}) {
47
46
  const {metadata} = useBlogPost();
48
- const {date, formattedDate, readingTime} = metadata;
47
+ const {date, readingTime} = metadata;
48
+ const dateTimeFormat = useDateTimeFormat({
49
+ day: 'numeric',
50
+ month: 'long',
51
+ year: 'numeric',
52
+ timeZone: 'UTC',
53
+ });
54
+ const formatDate = (blogDate) => dateTimeFormat.format(new Date(blogDate));
49
55
  return (
50
56
  <div className={clsx(styles.container, 'margin-vert--md', className)}>
51
- <Date date={date} formattedDate={formattedDate} />
57
+ <DateTime date={date} formattedDate={formatDate(date)} />
52
58
  {typeof readingTime !== 'undefined' && (
53
59
  <>
54
60
  <Spacer />
@@ -14,14 +14,8 @@ export default function BlogPostItemHeaderTitle({className}) {
14
14
  const {permalink, title} = metadata;
15
15
  const TitleHeading = isBlogPostPage ? 'h1' : 'h2';
16
16
  return (
17
- <TitleHeading className={clsx(styles.title, className)} itemProp="headline">
18
- {isBlogPostPage ? (
19
- title
20
- ) : (
21
- <Link itemProp="url" to={permalink}>
22
- {title}
23
- </Link>
24
- )}
17
+ <TitleHeading className={clsx(styles.title, className)}>
18
+ {isBlogPostPage ? title : <Link to={permalink}>{title}</Link>}
25
19
  </TitleHeading>
26
20
  );
27
21
  }
@@ -4,7 +4,5 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
-
8
- .blogPostFooterDetailsFull {
9
- flex-direction: column;
10
- }
7
+ /// <reference types="react" />
8
+ export default function BlogPostStructuredData(): JSX.Element;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import React from 'react';
8
+ import Head from '@docusaurus/Head';
9
+ import {useBlogPostStructuredData} from '@docusaurus/theme-common';
10
+ export default function BlogPostStructuredData() {
11
+ const structuredData = useBlogPostStructuredData();
12
+ return (
13
+ <Head>
14
+ <script type="application/ld+json">
15
+ {JSON.stringify(structuredData)}
16
+ </script>
17
+ </Head>
18
+ );
19
+ }
@@ -12,6 +12,7 @@ import BlogLayout from '@theme/BlogLayout';
12
12
  import BlogPostItem from '@theme/BlogPostItem';
13
13
  import BlogPostPaginator from '@theme/BlogPostPaginator';
14
14
  import BlogPostPageMetadata from '@theme/BlogPostPage/Metadata';
15
+ import BlogPostPageStructuredData from '@theme/BlogPostPage/StructuredData';
15
16
  import TOC from '@theme/TOC';
16
17
  import Unlisted from '@theme/Unlisted';
17
18
  function BlogPostPageContent({sidebar, children}) {
@@ -35,6 +36,7 @@ function BlogPostPageContent({sidebar, children}) {
35
36
  ) : undefined
36
37
  }>
37
38
  {unlisted && <Unlisted />}
39
+
38
40
  <BlogPostItem>{children}</BlogPostItem>
39
41
 
40
42
  {(nextItem || prevItem) && (
@@ -53,6 +55,7 @@ export default function BlogPostPage(props) {
53
55
  ThemeClassNames.page.blogPostPage,
54
56
  )}>
55
57
  <BlogPostPageMetadata />
58
+ <BlogPostPageStructuredData />
56
59
  <BlogPostPageContent sidebar={props.sidebar}>
57
60
  <BlogPostContent />
58
61
  </BlogPostPageContent>
@@ -11,10 +11,27 @@ import {
11
11
  findFirstSidebarItemLink,
12
12
  useDocById,
13
13
  } from '@docusaurus/theme-common/internal';
14
+ import {usePluralForm} from '@docusaurus/theme-common';
14
15
  import isInternalUrl from '@docusaurus/isInternalUrl';
15
16
  import {translate} from '@docusaurus/Translate';
16
17
  import Heading from '@theme/Heading';
17
18
  import styles from './styles.module.css';
19
+ function useCategoryItemsPlural() {
20
+ const {selectMessage} = usePluralForm();
21
+ return (count) =>
22
+ selectMessage(
23
+ count,
24
+ translate(
25
+ {
26
+ message: '{count} items',
27
+ id: 'theme.docs.DocCard.categoryDescription.plurals',
28
+ description:
29
+ 'The default description for a category card in the generated index about how many items this category includes',
30
+ },
31
+ {count},
32
+ ),
33
+ );
34
+ }
18
35
  function CardContainer({href, children}) {
19
36
  return (
20
37
  <Link
@@ -45,6 +62,7 @@ function CardLayout({href, icon, title, description}) {
45
62
  }
46
63
  function CardCategory({item}) {
47
64
  const href = findFirstSidebarItemLink(item);
65
+ const categoryItemsPlural = useCategoryItemsPlural();
48
66
  // Unexpected: categories that don't have a link have been filtered upfront
49
67
  if (!href) {
50
68
  return null;
@@ -54,18 +72,7 @@ function CardCategory({item}) {
54
72
  href={href}
55
73
  icon="🗃️"
56
74
  title={item.label}
57
- description={
58
- item.description ??
59
- translate(
60
- {
61
- message: '{count} items',
62
- id: 'theme.docs.DocCard.categoryDescription',
63
- description:
64
- 'The default description for a category card in the generated index about how many items this category includes',
65
- },
66
- {count: item.items.length},
67
- )
68
- }
75
+ description={item.description ?? categoryItemsPlural(item.items.length)}
69
76
  />
70
77
  );
71
78
  }
@@ -8,49 +8,11 @@ import React from 'react';
8
8
  import clsx from 'clsx';
9
9
  import {ThemeClassNames} from '@docusaurus/theme-common';
10
10
  import {useDoc} from '@docusaurus/theme-common/internal';
11
- import LastUpdated from '@theme/LastUpdated';
12
- import EditThisPage from '@theme/EditThisPage';
13
11
  import TagsListInline from '@theme/TagsListInline';
14
- import styles from './styles.module.css';
15
- function TagsRow(props) {
16
- return (
17
- <div
18
- className={clsx(
19
- ThemeClassNames.docs.docFooterTagsRow,
20
- 'row margin-bottom--sm',
21
- )}>
22
- <div className="col">
23
- <TagsListInline {...props} />
24
- </div>
25
- </div>
26
- );
27
- }
28
- function EditMetaRow({
29
- editUrl,
30
- lastUpdatedAt,
31
- lastUpdatedBy,
32
- formattedLastUpdatedAt,
33
- }) {
34
- return (
35
- <div className={clsx(ThemeClassNames.docs.docFooterEditMetaRow, 'row')}>
36
- <div className="col">{editUrl && <EditThisPage editUrl={editUrl} />}</div>
37
-
38
- <div className={clsx('col', styles.lastUpdated)}>
39
- {(lastUpdatedAt || lastUpdatedBy) && (
40
- <LastUpdated
41
- lastUpdatedAt={lastUpdatedAt}
42
- formattedLastUpdatedAt={formattedLastUpdatedAt}
43
- lastUpdatedBy={lastUpdatedBy}
44
- />
45
- )}
46
- </div>
47
- </div>
48
- );
49
- }
12
+ import EditMetaRow from '@theme/EditMetaRow';
50
13
  export default function DocItemFooter() {
51
14
  const {metadata} = useDoc();
52
- const {editUrl, lastUpdatedAt, formattedLastUpdatedAt, lastUpdatedBy, tags} =
53
- metadata;
15
+ const {editUrl, lastUpdatedAt, lastUpdatedBy, tags} = metadata;
54
16
  const canDisplayTagsRow = tags.length > 0;
55
17
  const canDisplayEditMetaRow = !!(editUrl || lastUpdatedAt || lastUpdatedBy);
56
18
  const canDisplayFooter = canDisplayTagsRow || canDisplayEditMetaRow;
@@ -60,13 +22,26 @@ export default function DocItemFooter() {
60
22
  return (
61
23
  <footer
62
24
  className={clsx(ThemeClassNames.docs.docFooter, 'docusaurus-mt-lg')}>
63
- {canDisplayTagsRow && <TagsRow tags={tags} />}
25
+ {canDisplayTagsRow && (
26
+ <div
27
+ className={clsx(
28
+ 'row margin-top--sm',
29
+ ThemeClassNames.docs.docFooterTagsRow,
30
+ )}>
31
+ <div className="col">
32
+ <TagsListInline tags={tags} />
33
+ </div>
34
+ </div>
35
+ )}
64
36
  {canDisplayEditMetaRow && (
65
37
  <EditMetaRow
38
+ className={clsx(
39
+ 'margin-top--sm',
40
+ ThemeClassNames.docs.docFooterEditMetaRow,
41
+ )}
66
42
  editUrl={editUrl}
67
43
  lastUpdatedAt={lastUpdatedAt}
68
44
  lastUpdatedBy={lastUpdatedBy}
69
- formattedLastUpdatedAt={formattedLastUpdatedAt}
70
45
  />
71
46
  )}
72
47
  </footer>
@@ -78,6 +78,7 @@ function CollapseButton({collapsed, categoryLabel, onClick}) {
78
78
  {label: categoryLabel},
79
79
  )
80
80
  }
81
+ aria-expanded={!collapsed}
81
82
  type="button"
82
83
  className="clean-btn menu__caret"
83
84
  onClick={onClick}
@@ -165,7 +166,8 @@ export default function DocSidebarItemCategory({
165
166
  }
166
167
  }
167
168
  aria-current={isCurrentPage ? 'page' : undefined}
168
- aria-expanded={collapsible ? !collapsed : undefined}
169
+ role={collapsible && !href ? 'button' : undefined}
170
+ aria-expanded={collapsible && !href ? !collapsed : undefined}
169
171
  href={collapsible ? hrefWithSSRFallback ?? '#' : hrefWithSSRFallback}
170
172
  {...props}>
171
173
  {label}
@@ -0,0 +1,3 @@
1
+ /// <reference types="react" />
2
+ import type { Props } from '@theme/EditMetaRow';
3
+ export default function EditMetaRow({ className, editUrl, lastUpdatedAt, lastUpdatedBy, }: Props): JSX.Element;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import React from 'react';
8
+ import clsx from 'clsx';
9
+ import EditThisPage from '@theme/EditThisPage';
10
+ import LastUpdated from '@theme/LastUpdated';
11
+ import styles from './styles.module.css';
12
+ export default function EditMetaRow({
13
+ className,
14
+ editUrl,
15
+ lastUpdatedAt,
16
+ lastUpdatedBy,
17
+ }) {
18
+ return (
19
+ <div className={clsx('row', className)}>
20
+ <div className="col">{editUrl && <EditThisPage editUrl={editUrl} />}</div>
21
+ <div className={clsx('col', styles.lastUpdated)}>
22
+ {(lastUpdatedAt || lastUpdatedBy) && (
23
+ <LastUpdated
24
+ lastUpdatedAt={lastUpdatedAt}
25
+ lastUpdatedBy={lastUpdatedBy}
26
+ />
27
+ )}
28
+ </div>
29
+ </div>
30
+ );
31
+ }
@@ -6,4 +6,4 @@
6
6
  */
7
7
  /// <reference types="react" />
8
8
  import type { Props } from '@theme/LastUpdated';
9
- export default function LastUpdated({ lastUpdatedAt, formattedLastUpdatedAt, lastUpdatedBy, }: Props): JSX.Element;
9
+ export default function LastUpdated({ lastUpdatedAt, lastUpdatedBy, }: Props): JSX.Element;
@@ -7,7 +7,16 @@
7
7
  import React from 'react';
8
8
  import Translate from '@docusaurus/Translate';
9
9
  import {ThemeClassNames} from '@docusaurus/theme-common';
10
- function LastUpdatedAtDate({lastUpdatedAt, formattedLastUpdatedAt}) {
10
+ import {useDateTimeFormat} from '@docusaurus/theme-common/internal';
11
+ function LastUpdatedAtDate({lastUpdatedAt}) {
12
+ const atDate = new Date(lastUpdatedAt);
13
+ const dateTimeFormat = useDateTimeFormat({
14
+ day: 'numeric',
15
+ month: 'short',
16
+ year: 'numeric',
17
+ timeZone: 'UTC',
18
+ });
19
+ const formattedLastUpdatedAt = dateTimeFormat.format(atDate);
11
20
  return (
12
21
  <Translate
13
22
  id="theme.lastUpdated.atDate"
@@ -15,7 +24,7 @@ function LastUpdatedAtDate({lastUpdatedAt, formattedLastUpdatedAt}) {
15
24
  values={{
16
25
  date: (
17
26
  <b>
18
- <time dateTime={new Date(lastUpdatedAt * 1000).toISOString()}>
27
+ <time dateTime={atDate.toISOString()} itemProp="dateModified">
19
28
  {formattedLastUpdatedAt}
20
29
  </time>
21
30
  </b>
@@ -37,26 +46,18 @@ function LastUpdatedByUser({lastUpdatedBy}) {
37
46
  </Translate>
38
47
  );
39
48
  }
40
- export default function LastUpdated({
41
- lastUpdatedAt,
42
- formattedLastUpdatedAt,
43
- lastUpdatedBy,
44
- }) {
49
+ export default function LastUpdated({lastUpdatedAt, lastUpdatedBy}) {
45
50
  return (
46
51
  <span className={ThemeClassNames.common.lastUpdated}>
47
52
  <Translate
48
53
  id="theme.lastUpdated.lastUpdatedAtBy"
49
54
  description="The sentence used to display when a page has been last updated, and by who"
50
55
  values={{
51
- atDate:
52
- lastUpdatedAt && formattedLastUpdatedAt ? (
53
- <LastUpdatedAtDate
54
- lastUpdatedAt={lastUpdatedAt}
55
- formattedLastUpdatedAt={formattedLastUpdatedAt}
56
- />
57
- ) : (
58
- ''
59
- ),
56
+ atDate: lastUpdatedAt ? (
57
+ <LastUpdatedAtDate lastUpdatedAt={lastUpdatedAt} />
58
+ ) : (
59
+ ''
60
+ ),
60
61
  byUser: lastUpdatedBy ? (
61
62
  <LastUpdatedByUser lastUpdatedBy={lastUpdatedBy} />
62
63
  ) : (
@@ -14,6 +14,7 @@ export default function MDXImg(props) {
14
14
  return (
15
15
  // eslint-disable-next-line jsx-a11y/alt-text
16
16
  <img
17
+ decoding="async"
17
18
  loading="lazy"
18
19
  {...props}
19
20
  className={transformImgClassName(props.className)}