diff --git a/components/article/ArticlePage.tsx b/components/article/ArticlePage.tsx new file mode 100644 index 0000000000..ac4cdfaec2 --- /dev/null +++ b/components/article/ArticlePage.tsx @@ -0,0 +1,152 @@ +import cx from 'classnames' + +import { DefaultLayout } from 'components/DefaultLayout' +import { ArticleVersionPicker } from 'components/article/ArticleVersionPicker' +import { Breadcrumbs } from 'components/Breadcrumbs' +import { ArticleTitle } from 'components/article/ArticleTitle' +import { useArticleContext } from 'components/context/ArticleContext' +import { InfoIcon } from '@primer/octicons-react' +import { useTranslation } from 'components/hooks/useTranslation' +import { LearningTrackNav } from './LearningTrackNav' + +export const ArticlePage = () => { + const { + title, + intro, + renderedPage, + contributor, + permissions, + includesPlatformSpecificContent, + defaultPlatform, + product, + miniTocItems, + currentLearningTrack, + } = useArticleContext() + const { t } = useTranslation('pages') + return ( + +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ {title} + + {contributor && ( +
+

+ + + + {t('contributor_callout')} {contributor.name}. +

+
+ )} + + {intro && ( +
+

{intro}

+
+ )} + + {permissions && ( +
+ )} + + {includesPlatformSpecificContent && ( + + )} + + {product && ( +
+ )} +
+ +
+
+ {miniTocItems.length > 1 && ( + <> +

+ + {t('miniToc')} + +

+
    + {miniTocItems.map((item) => { + return ( +
  • + ) + })} +
+ + )} +
+
+ +
+
+
+
+ + {currentLearningTrack?.trackName ? ( +
+ +
+ ) : null} +
+
+
+ ) +} diff --git a/components/article/ArticleTitle.tsx b/components/article/ArticleTitle.tsx index 52a0789c59..c9a74e8efe 100644 --- a/components/article/ArticleTitle.tsx +++ b/components/article/ArticleTitle.tsx @@ -7,7 +7,7 @@ type Props = { export const ArticleTitle = ({ children }: Props) => { return (
-

{children}

+

{children}

+ + {prevGuide && ( + <> + {t('prevGuide')} + + {prevGuide.title} + + + )} + + + + {nextGuide && ( + <> + {t('nextGuide')} + + {nextGuide.title} + + + )} + +
+ ) +} diff --git a/components/context/ArticleContext.tsx b/components/context/ArticleContext.tsx new file mode 100644 index 0000000000..c3f898bf16 --- /dev/null +++ b/components/context/ArticleContext.tsx @@ -0,0 +1,61 @@ +import { createContext, useContext } from 'react' + +export type LearningTrack = { + trackName?: string + prevGuide?: { href: string; title: string } + nextGuide?: { href: string; title: string } +} + +export type MiniTocItem = { + indentationLevel: number + platform: string + contents: string +} + +export type ArticleContextT = { + title: string + intro: string + renderedPage: string + miniTocItems: Array + contributor: { name: string; URL: string } | null + permissions?: string + includesPlatformSpecificContent: boolean + defaultPlatform?: string + product?: string + currentLearningTrack?: LearningTrack +} + +export const ArticleContext = createContext(null) + +export const useArticleContext = (): ArticleContextT => { + const context = useContext(ArticleContext) + + if (!context) { + throw new Error('"useArticleContext" may only be used inside "ArticleContext.Provider"') + } + + return context +} + +export const getArticleContextFromRequest = (req: any): ArticleContextT => { + const page = req.context.page + return { + title: page.titlePlainText, + intro: page.introPlainText, + renderedPage: req.context.renderedPage || '', + miniTocItems: + (req.context.miniTocItems || []).map((item: any) => { + return { + indentationLevel: item.indentationLevel || 0, + platform: item.platform || '', + contents: item.contents || '', + } + }) || [], + contributor: page.contributor || null, + permissions: page.permissions || '', + includesPlatformSpecificContent: page.includesPlatformSpecificContent || false, + defaultPlatform: page.defaultPlatform || '', + product: page.product || '', + currentLearningTrack: req.context.currentLearningTrack, + } +} diff --git a/data/ui.yml b/data/ui.yml index a904b4c35d..75b548ee5e 100644 --- a/data/ui.yml +++ b/data/ui.yml @@ -40,6 +40,7 @@ toc: pages: article_version: 'Article version:' miniToc: In this article + contributor_callout: This article is contributed and maintained by errors: oops: Ooops! something_went_wrong: It looks like something went wrong. @@ -176,7 +177,6 @@ product_sublanding: learning_track_nav: prevGuide: Previous guide nextGuide: Next guide -contributor_callout: This article is contributed and maintained by toggle_images: off: Images are off, click to show on: Images are on, click to hide diff --git a/includes/article.html b/includes/article.html index 0277c0bb50..c06e41412d 100644 --- a/includes/article.html +++ b/includes/article.html @@ -22,7 +22,7 @@ {% if page.contributor %}
-

{% octicon "info" %}{% data ui.contributor_callout %} {{ page.contributor.name }}.

+

{% octicon "info" %}{% data ui.pages.contributor_callout %} {{ page.contributor.name }}.

{% endif %} diff --git a/lib/page.js b/lib/page.js index 71ea17141a..5344462355 100644 --- a/lib/page.js +++ b/lib/page.js @@ -158,6 +158,7 @@ class Page { this.intro = await renderContent(this.rawIntro, context) this.introPlainText = await renderContent(this.rawIntro, context, { textOnly: true }) this.title = await renderContent(this.rawTitle, context, { textOnly: true, encodeEntities: true }) + this.titlePlainText = await renderContent(this.rawTitle, context, { textOnly: true }) this.shortTitle = await renderContent(this.shortTitle, context, { textOnly: true, encodeEntities: true }) this.product_video = await renderContent(this.raw_product_video, context, { textOnly: true }) diff --git a/middleware/render-page.js b/middleware/render-page.js index 43e4ed4caf..4f44511e0d 100644 --- a/middleware/render-page.js +++ b/middleware/render-page.js @@ -171,7 +171,7 @@ module.exports = async function renderPage (req, res, next) { } // Create string for tag - context.page.fullTitle = context.page.title + context.page.fullTitle = context.page.titlePlainText // add localized ` - GitHub Docs` suffix to <title> tag (except for the homepage) if (!patterns.homepagePath.test(req.path)) { @@ -194,6 +194,8 @@ module.exports = async function renderPage (req, res, next) { // Hand rendering over to NextJS when appropriate if (renderWithNextjs) { + req.context.renderedPage = context.renderedPage + req.context.miniTocItems = context.miniTocItems return nextHandleRequest(req, res) } diff --git a/pages/[versionId]/[productId]/index.tsx b/pages/[versionId]/[productId]/index.tsx index 2ba64d13b6..c0af21516e 100644 --- a/pages/[versionId]/[productId]/index.tsx +++ b/pages/[versionId]/[productId]/index.tsx @@ -5,12 +5,20 @@ import { MainContext, getMainContextFromRequest, } from 'components/context/MainContext' + import { getProductLandingContextFromRequest, ProductLandingContextT, ProductLandingContext, } from 'components/context/ProductLandingContext' +import { + getArticleContextFromRequest, + ArticleContextT, + ArticleContext, +} from 'components/context/ArticleContext' +import { ArticlePage } from 'components/article/ArticlePage' + import { ProductLanding } from 'components/landing/ProductLanding' import { TocLanding } from 'components/landing/TocLanding' import { @@ -23,8 +31,14 @@ type Props = { mainContext: MainContextT productLandingContext: ProductLandingContextT tocLandingContext: TocLandingContextT + articleContext: ArticleContextT } -const GlobalPage = ({ mainContext, productLandingContext, tocLandingContext }: Props) => { +const GlobalPage = ({ + mainContext, + productLandingContext, + tocLandingContext, + articleContext, +}: Props) => { const { currentLayoutName, page, relativePath } = mainContext let content @@ -49,7 +63,11 @@ const GlobalPage = ({ mainContext, productLandingContext, tocLandingContext }: P </TocLandingContext.Provider> ) } else { - content = <p>article / fallback rendering</p> + content = ( + <ArticleContext.Provider value={articleContext}> + <ArticlePage /> + </ArticleContext.Provider> + ) } return <MainContext.Provider value={mainContext}>{content}</MainContext.Provider> @@ -65,6 +83,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) => mainContext: getMainContextFromRequest(req), productLandingContext: getProductLandingContextFromRequest(req), tocLandingContext: getTocLandingContextFromRequest(req), + articleContext: getArticleContextFromRequest(req), }, } }