Skip to content

Commit

Permalink
[EUI+] Implement document content styles (#7872)
Browse files Browse the repository at this point in the history
  • Loading branch information
mgadewoll authored Jul 11, 2024
1 parent 1a25e9b commit e39a488
Show file tree
Hide file tree
Showing 15 changed files with 521 additions and 23 deletions.
1 change: 1 addition & 0 deletions packages/docusaurus-theme/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"dependencies": {
"@docusaurus/core": "^3.4.0",
"@docusaurus/module-type-aliases": "^3.4.0",
"@docusaurus/theme-common": "^3.4.0",
"@docusaurus/utils-validation": "^3.4.0",
"@elastic/datemath": "^5.0.3",
"@elastic/eui": "94.5.0",
Expand Down
72 changes: 53 additions & 19 deletions packages/docusaurus-theme/src/theme/DocItem/Content/index.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,67 @@
import OriginalContent from '@theme-init/DocItem/Content';
import type ContentType from '@theme-init/DocItem/Content';
import type { WrapperProps } from '@docusaurus/types';
import { useEuiMemoizedStyles, UseEuiTheme } from '@elastic/eui';
import { ReactNode } from 'react';
import clsx from 'clsx';
import { css } from '@emotion/react';
import { ThemeClassNames } from '@docusaurus/theme-common';
import { useDoc } from '@docusaurus/theme-common/internal';
import MDXContent from '@theme-original/MDXContent';
import {
EuiHorizontalRule,
useEuiMemoizedStyles,
UseEuiTheme,
} from '@elastic/eui';
import { Props } from '@theme-original/DocItem/Content';

type Props = WrapperProps<typeof ContentType>;
import Heading from '../../MDXComponents/Heading';

/**
Title can be declared inside md content or declared through
front matter and added manually. To make both cases consistent,
the added title is added under the same div.markdown block
See https://github.com/facebook/docusaurus/pull/4882#issuecomment-853021120
We render a "synthetic title" if:
- user doesn't ask to hide it with front matter
- the markdown content does not already contain a top-level h1 heading
*/
function useSyntheticTitle(): string | null {
const { metadata, frontMatter, contentTitle } = useDoc();
const shouldRender =
!frontMatter.hide_title && typeof contentTitle === 'undefined';
if (!shouldRender) {
return null;
}
return metadata.title;
}

const getContentStyles = ({ euiTheme }: UseEuiTheme) => {
return {
content: css`
// required specificity to apply styles
.markdown header > h1 {
--ifm-h1-font-size: 2.86rem;
--ifm-h1-vertical-rhythm-bottom: 1.486;
header: css`
// required specificity to override docusaurus styles
& > h1.euiTitle {
--ifm-h1-font-size: var(--eui-font-size-xxl);
--ifm-h1-vertical-rhythm-bottom: 1.2;
line-height: 2.8rem;
}
`,
};
};

/* OriginalContent holds the document title and markdown content
NOTE: ejecting this results in an error due to using useDoc() hook outside of DocProvider
https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-theme-classic/src/theme/DocItem/Content/index.tsx */
const Content = (props: Props): JSX.Element => {
export default function DocItemContent({ children }: Props): JSX.Element {
const syntheticTitle = useSyntheticTitle();
const styles = useEuiMemoizedStyles(getContentStyles);

return (
<div css={styles.content}>
<OriginalContent {...props} />
<div className={clsx(ThemeClassNames.docs.docMarkdown, 'markdown')}>
{syntheticTitle && (
<>
<header css={styles.header}>
<Heading as="h1">{syntheticTitle}</Heading>
</header>
<EuiHorizontalRule />
</>
)}
<MDXContent>{children}</MDXContent>
</div>
);
};

export default Content;
}
51 changes: 51 additions & 0 deletions packages/docusaurus-theme/src/theme/DocItem/Footer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import clsx from 'clsx';
import { ThemeClassNames } from '@docusaurus/theme-common';
import { useDoc } from '@docusaurus/theme-common/internal';
import TagsListInline from '@theme-original/TagsListInline';

import EditMetaRow from '@theme-original/EditMetaRow';

export default function DocItemFooter(): JSX.Element | null {
const { metadata } = useDoc();
const { editUrl, lastUpdatedAt, lastUpdatedBy, tags } = metadata;

const canDisplayTagsRow = tags.length > 0;
const canDisplayEditMetaRow = !!(editUrl || lastUpdatedAt || lastUpdatedBy);

const canDisplayFooter = canDisplayTagsRow || canDisplayEditMetaRow;

if (!canDisplayFooter) {
return null;
}

return (
<footer
className={clsx(ThemeClassNames.docs.docFooter, 'docusaurus-mt-lg')}
>
{canDisplayTagsRow && (
<div
className={clsx(
'row margin-top--sm',
ThemeClassNames.docs.docFooterTagsRow
)}
>
<div className="col">
<TagsListInline tags={tags} />
</div>
</div>
)}
{canDisplayEditMetaRow && (
<EditMetaRow
className={clsx(
'margin-top--sm',
ThemeClassNames.docs.docFooterEditMetaRow
)}
editUrl={editUrl}
lastUpdatedAt={lastUpdatedAt}
lastUpdatedBy={lastUpdatedBy}
/>
)}
</footer>
);
}
82 changes: 82 additions & 0 deletions packages/docusaurus-theme/src/theme/DocItem/Layout/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React from 'react';
import { css } from '@emotion/react';
import { useWindowSize } from '@docusaurus/theme-common';
import { useDoc } from '@docusaurus/theme-common/internal';
import DocItemPaginator from '@theme-original/DocItem/Paginator';
import DocVersionBanner from '@theme-original/DocVersionBanner';
import DocVersionBadge from '@theme-original/DocVersionBadge';
import DocBreadcrumbs from '@theme-original/DocBreadcrumbs';
import Unlisted from '@theme-original/Unlisted';
import * as Props from '@theme-original/DocItem/Layout';
import { EuiHorizontalRule } from '@elastic/eui';

import DocItemContent from '../Content';
import DocItemTOCMobile from '../TOC/Mobile';
import DocItemTOCDesktop from '../TOC/Desktop';
import DocItemFooter from '../Footer';

// converted from css modules to emotion
const layoutStyles = {
docItemContainer: css`
& header + *,
& article > *:first-child {
margin-top: 0;
}
`,
docItemCol: css`
@media (min-width: 997px) {
max-width: 75% !important;
}
`,
};

/**
* Decide if the toc should be rendered, on mobile or desktop viewports
*/
function useDocTOC() {
const { frontMatter, toc } = useDoc();
const windowSize = useWindowSize();

const hidden = frontMatter.hide_table_of_contents ?? false;
const canRender = !hidden && toc.length > 0;

const mobile = canRender ? <DocItemTOCMobile /> : undefined;

const desktop =
canRender && (windowSize === 'desktop' || windowSize === 'ssr') ? (
<DocItemTOCDesktop />
) : undefined;

return {
hidden,
mobile,
desktop,
};
}

export default function DocItemLayout({ children }: typeof Props): JSX.Element {
const docTOC = useDocTOC();
const {
metadata: { unlisted },
} = useDoc();
return (
<div className="row">
<div className="col" css={layoutStyles.docItemCol}>
{unlisted && <Unlisted />}
<DocVersionBanner />
<div css={layoutStyles.docItemContainer}>
<article>
<DocBreadcrumbs />
<DocVersionBadge />
{docTOC.mobile}
<DocItemContent>{children}</DocItemContent>
<DocItemFooter />
</article>
<EuiHorizontalRule margin="xl" />
<DocItemPaginator />
</div>
</div>
{docTOC.desktop && <div className="col col--3">{docTOC.desktop}</div>}
</div>
);
}
15 changes: 15 additions & 0 deletions packages/docusaurus-theme/src/theme/DocItem/Metadata/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import {PageMetadata} from '@docusaurus/theme-common';
import {useDoc} from '@docusaurus/theme-common/internal';

export default function DocItemMetadata(): JSX.Element {
const {metadata, frontMatter, assets} = useDoc();
return (
<PageMetadata
title={metadata.title}
description={metadata.description}
keywords={frontMatter.keywords}
image={assets.image ?? frontMatter.image}
/>
);
}
12 changes: 12 additions & 0 deletions packages/docusaurus-theme/src/theme/DocItem/Paginator/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import { useDoc } from '@docusaurus/theme-common/internal';
import DocPaginator from '@theme-original/DocPaginator';

/**
* This extra component is needed, because <DocPaginator> should remain generic.
* DocPaginator is used in non-docs contexts too: generated-index pages...
*/
export default function DocItemPaginator(): JSX.Element {
const { metadata } = useDoc();
return <DocPaginator previous={metadata.previous} next={metadata.next} />;
}
17 changes: 17 additions & 0 deletions packages/docusaurus-theme/src/theme/DocItem/TOC/Desktop/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { ThemeClassNames } from '@docusaurus/theme-common';
import { useDoc } from '@docusaurus/theme-common/internal';

import TOC from '@theme-original/TOC';

export default function DocItemTOCDesktop(): JSX.Element {
const { toc, frontMatter } = useDoc();
return (
<TOC
toc={toc}
minHeadingLevel={frontMatter.toc_min_heading_level}
maxHeadingLevel={frontMatter.toc_max_heading_level}
className={ThemeClassNames.docs.docTocDesktop}
/>
);
}
33 changes: 33 additions & 0 deletions packages/docusaurus-theme/src/theme/DocItem/TOC/Mobile/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { css } from '@emotion/react';
import { ThemeClassNames } from '@docusaurus/theme-common';
import { useDoc } from '@docusaurus/theme-common/internal';

import TOCCollapsible from '@theme-original/TOCCollapsible';

// converted from css modules to emotion
const tocStyles = {
tocMobile: css`
@media (min-width: 997px) {
/* Prevent hydration FOUC, as the mobile TOC needs to be server-rendered */
display: none;
}
@media print {
display: none;
}
`,
};

export default function DocItemTOCMobile(): JSX.Element {
const { toc, frontMatter } = useDoc();
return (
<TOCCollapsible
toc={toc}
minHeadingLevel={frontMatter.toc_min_heading_level}
maxHeadingLevel={frontMatter.toc_max_heading_level}
className={ThemeClassNames.docs.docTocMobile}
css={tocStyles.tocMobile}
/>
);
}
22 changes: 22 additions & 0 deletions packages/docusaurus-theme/src/theme/DocItem/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { HtmlClassNameProvider } from '@docusaurus/theme-common';
import { DocProvider } from '@docusaurus/theme-common/internal';
import DocItemMetadata from '@theme-original/DocItem/Metadata';
import type { Props } from '@theme/DocItem';

import DocItemLayout from './Layout';

export default function DocItem(props: Props): JSX.Element {
const docHtmlClassName = `docs-doc-id-${props.content.metadata.id}`;
const MDXComponent = props.content;
return (
<DocProvider content={props.content}>
<HtmlClassNameProvider className={docHtmlClassName}>
<DocItemMetadata />
<DocItemLayout>
<MDXComponent />
</DocItemLayout>
</HtmlClassNameProvider>
</DocProvider>
);
}
55 changes: 55 additions & 0 deletions packages/docusaurus-theme/src/theme/DocPaginator/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { css } from '@emotion/react';
import Translate, { translate } from '@docusaurus/Translate';
import PaginatorNavLink from '@theme-original/PaginatorNavLink';
import type { Props } from '@theme-original/DocPaginator';

const styles = {
pagination: css`
// docusaurus reset, we add spacing via the
// horizontal rule in Layout instead
margin-top: 0;
`,
};

export default function DocPaginator(props: Props): JSX.Element {
const { previous, next } = props;
return (
<nav
className="pagination-nav docusaurus-mt-lg"
css={styles.pagination}
aria-label={translate({
id: 'theme.docs.paginator.navAriaLabel',
message: 'Docs pages',
description: 'The ARIA label for the docs pagination',
})}
>
{previous && (
<PaginatorNavLink
{...previous}
subLabel={
<Translate
id="theme.docs.paginator.previous"
description="The label used to navigate to the previous doc"
>
Previous
</Translate>
}
/>
)}
{next && (
<PaginatorNavLink
{...next}
subLabel={
<Translate
id="theme.docs.paginator.next"
description="The label used to navigate to the next doc"
>
Next
</Translate>
}
isNext
/>
)}
</nav>
);
}
Loading

0 comments on commit e39a488

Please sign in to comment.