1
0
mirror of synced 2025-12-20 02:19:14 -05:00

Change product landing page introLinks to a general map of link titles to links (#26360)

* Add cta button to frontmatter and context

* Add cta to Page

* Render cta button in product landing hero

* Handle external links

* Add simple unit test for cta link

* Address feedback

Co-authored-by: Peter Bengtsson <mail@peterbe.com>

* Actually push condition update

* Show it's an external link

* Refactor FullLink so we use Link once

Co-authored-by: Peter Bengtsson <mail@peterbe.com>

* Custom link can also be null

* Rename 'cta' to 'custom' and make it the last introLink

* Update tests with 'cta' to 'custom' change

* Filter once

* Revert "Filter once"

This reverts commit a3f9a8a06b505d77fed47ca96a66c187c86c3c91.

* Update introLinks to a map of titles and URLs

* No more custom introLink in LandingHero

* Simplify introLinks processing

* introLinks can also be null

Co-authored-by: Peter Bengtsson <mail@peterbe.com>

* more concise

Co-authored-by: Peter Bengtsson <mail@peterbe.com>

* No longer necessary with the a plain introLinks map

Co-authored-by: Peter Bengtsson <mail@peterbe.com>

* '.entries()` is simpler

Co-authored-by: Peter Bengtsson <mail@peterbe.com>

* 'link' could be false depending on what version you're on

* Update test for new introLinks

Co-authored-by: Peter Bengtsson <mail@peterbe.com>
This commit is contained in:
Robert Sese
2022-04-01 11:01:37 -05:00
committed by GitHub
parent 0a0c83db5b
commit 33c05d81ce
6 changed files with 61 additions and 44 deletions

View File

@@ -38,11 +38,7 @@ export type ProductLandingContextT = {
intro: string intro: string
beta_product: boolean beta_product: boolean
product: Product product: Product
introLinks: { introLinks: Record<string, string> | null
quickstart?: string
reference?: string
overview?: string
} | null
product_video?: string product_video?: string
featuredLinks: Record<string, Array<FeaturedLink>> featuredLinks: Record<string, Array<FeaturedLink>>
productCodeExamples: Array<CodeExample> productCodeExamples: Array<CodeExample>
@@ -128,13 +124,7 @@ export const getProductLandingContextFromRequest = (req: any): ProductLandingCon
}) })
), ),
introLinks: page.introLinks introLinks: page.introLinks || null,
? {
quickstart: page.introLinks.quickstart,
reference: page.introLinks.reference,
overview: page.introLinks.overview,
}
: null,
featuredLinks: getFeaturedLinksFromReq(req), featuredLinks: getFeaturedLinksFromReq(req),

View File

@@ -1,8 +1,9 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import cx from 'classnames' import cx from 'classnames'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useMainContext } from 'components/context/MainContext' import { LinkExternalIcon } from '@primer/octicons-react'
import { useMainContext } from 'components/context/MainContext'
import { Link } from 'components/Link' import { Link } from 'components/Link'
import { useProductLandingContext } from 'components/context/ProductLandingContext' import { useProductLandingContext } from 'components/context/ProductLandingContext'
import { useTranslation } from 'components/hooks/useTranslation' import { useTranslation } from 'components/hooks/useTranslation'
@@ -46,7 +47,7 @@ export const LandingHero = () => {
href={link} href={link}
className={cx('btn btn-large f4 mt-3 mr-3 ', i === 0 && 'btn-primary')} className={cx('btn btn-large f4 mt-3 mr-3 ', i === 0 && 'btn-primary')}
> >
{t(key)} {t(key) || key}
</FullLink> </FullLink>
) )
})} })}
@@ -72,7 +73,8 @@ export const LandingHero = () => {
) )
} }
// Fully Qualified Link - it includes the version and locale in the path // Fully Qualified Link - it includes the version and locale in the path if
// the href is not an external link.
type Props = { type Props = {
href: string href: string
children: React.ReactNode children: React.ReactNode
@@ -81,13 +83,24 @@ type Props = {
export const FullLink = ({ href, children, className }: Props) => { export const FullLink = ({ href, children, className }: Props) => {
const router = useRouter() const router = useRouter()
const { currentVersion } = useVersion() const { currentVersion } = useVersion()
const locale = router.locale || 'en'
const fullyQualifiedHref = `/${locale}${ const isExternal = href.startsWith('https')
currentVersion !== 'free-pro-team@latest' ? `/${currentVersion}` : '' let linkHref = href
}${href}` if (!isExternal) {
const locale = router.locale || 'en'
linkHref = `/${locale}${
currentVersion !== 'free-pro-team@latest' ? `/${currentVersion}` : ''
}${href}`
}
return ( return (
<Link href={fullyQualifiedHref} className={className}> <Link href={linkHref} className={className}>
{children} {children}{' '}
{isExternal && (
<span className="ml-1">
<LinkExternalIcon size="small" />
</span>
)}
</Link> </Link>
) )
} }

View File

@@ -77,11 +77,6 @@ export const schema = {
}, },
introLinks: { introLinks: {
type: 'object', type: 'object',
properties: {
quickstart: { type: 'string' },
reference: { type: 'string' },
overview: { type: 'string' },
},
}, },
authors: { authors: {
type: 'array', type: 'array',

View File

@@ -90,12 +90,7 @@ class Page {
this.rawLearningTracks = this.learningTracks this.rawLearningTracks = this.learningTracks
this.rawIncludeGuides = this.includeGuides this.rawIncludeGuides = this.includeGuides
this.raw_product_video = this.product_video this.raw_product_video = this.product_video
this.rawIntroLinks = this.introLinks
if (this.introLinks) {
this.introLinks.rawQuickstart = this.introLinks.quickstart
this.introLinks.rawReference = this.introLinks.reference
this.introLinks.rawOverview = this.introLinks.overview
}
// Is this the Homepage or a Product, Category, Topic, or Article? // Is this the Homepage or a Product, Category, Topic, or Article?
this.documentType = getDocumentType(this.relativePath) this.documentType = getDocumentType(this.relativePath)
@@ -209,18 +204,6 @@ class Page {
this.product_video = await renderContent(this.raw_product_video, context, { textOnly: true }) this.product_video = await renderContent(this.raw_product_video, context, { textOnly: true })
if (this.introLinks) {
this.introLinks.quickstart = await renderContent(this.introLinks.rawQuickstart, context, {
textOnly: true,
})
this.introLinks.reference = await renderContent(this.introLinks.rawReference, context, {
textOnly: true,
})
this.introLinks.overview = await renderContent(this.introLinks.rawOverview, context, {
textOnly: true,
})
}
context.relativePath = this.relativePath context.relativePath = this.relativePath
const html = await renderContentCacheByContext('markdown')(this.markdown, context) const html = await renderContentCacheByContext('markdown')(this.markdown, context)
@@ -260,6 +243,18 @@ class Page {
this.learningTracks = learningTracks this.learningTracks = learningTracks
} }
// introLinks may contain Liquid and need to have versioning processed.
if (this.rawIntroLinks) {
const introLinks = {}
for (const [rawKey, value] of Object.entries(this.rawIntroLinks)) {
introLinks[rawKey] = await renderContent(value, context, {
textOnly: true,
})
}
this.introLinks = introLinks
}
if (this.rawIncludeGuides) { if (this.rawIncludeGuides) {
this.allTopics = [] this.allTopics = []
this.includeGuides = await getLinkData(this.rawIncludeGuides, context) this.includeGuides = await getLinkData(this.rawIncludeGuides, context)

View File

@@ -0,0 +1,9 @@
---
title: Article with introLinks
versions:
ghec: '*'
ghes: '*'
introLinks:
overview: 'https://github.com'
custom link!: 'https://github.com/features'
---

View File

@@ -609,6 +609,21 @@ describe('Page class', () => {
}) })
}) })
describe('introLinks', () => {
it('includes the links specified in the introLinks frontmatter', async () => {
const page = await Page.init({
relativePath: 'article-with-introLinks.md',
basePath: path.join(__dirname, '../fixtures'),
languageCode: 'en',
})
expect(page.introLinks).toStrictEqual({
overview: 'https://github.com',
'custom link!': 'https://github.com/features',
})
})
})
describe('Page.parseFrontmatter()', () => { describe('Page.parseFrontmatter()', () => {
it('throws an error on bad input', () => { it('throws an error on bad input', () => {
const markdown = null const markdown = null