only include productGroups in main context on homepage (#31640)
This commit is contained in:
@@ -13,13 +13,6 @@ export type ProductT = {
|
|||||||
versions?: Array<string>
|
versions?: Array<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProductGroupT = {
|
|
||||||
name: string
|
|
||||||
icon: string
|
|
||||||
octicon: string
|
|
||||||
children: Array<ProductT>
|
|
||||||
}
|
|
||||||
|
|
||||||
type VersionItem = {
|
type VersionItem = {
|
||||||
// free-pro-team@latest, enterprise-cloud@latest, enterprise-server@3.3 ...
|
// free-pro-team@latest, enterprise-cloud@latest, enterprise-server@3.3 ...
|
||||||
version: string
|
version: string
|
||||||
@@ -77,7 +70,6 @@ export type MainContextT = {
|
|||||||
article?: BreadcrumbT
|
article?: BreadcrumbT
|
||||||
}
|
}
|
||||||
activeProducts: Array<ProductT>
|
activeProducts: Array<ProductT>
|
||||||
productGroups: Array<ProductGroupT>
|
|
||||||
communityRedirect: {
|
communityRedirect: {
|
||||||
name: string
|
name: string
|
||||||
href: string
|
href: string
|
||||||
@@ -137,7 +129,6 @@ export const getMainContext = async (req: any, res: any): Promise<MainContextT>
|
|||||||
return {
|
return {
|
||||||
breadcrumbs: req.context.breadcrumbs || {},
|
breadcrumbs: req.context.breadcrumbs || {},
|
||||||
activeProducts: req.context.activeProducts,
|
activeProducts: req.context.activeProducts,
|
||||||
productGroups: req.context.productGroups,
|
|
||||||
communityRedirect: req.context.page?.communityRedirect || {},
|
communityRedirect: req.context.page?.communityRedirect || {},
|
||||||
currentProduct: req.context.productMap[req.context.currentProduct] || null,
|
currentProduct: req.context.productMap[req.context.currentProduct] || null,
|
||||||
currentLayoutName: req.context.currentLayoutName,
|
currentLayoutName: req.context.currentLayoutName,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { ProductT, ProductGroupT, useMainContext } from 'components/context/MainContext'
|
import { ProductT, useMainContext } from 'components/context/MainContext'
|
||||||
|
import type { ProductGroupT } from 'components/homepage/ProductSelections'
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
import { useMainContext } from 'components/context/MainContext'
|
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { ProductT } from 'components/context/MainContext'
|
||||||
import { ProductSelectionCard } from './ProductSelectionCard'
|
import { ProductSelectionCard } from './ProductSelectionCard'
|
||||||
|
|
||||||
export const ProductSelections = () => {
|
export type ProductGroupT = {
|
||||||
const { productGroups } = useMainContext()
|
name: string
|
||||||
|
icon: string
|
||||||
|
octicon: string
|
||||||
|
children: Array<ProductT>
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
productGroups: Array<ProductGroupT>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProductSelections = ({ productGroups }: Props) => {
|
||||||
return (
|
return (
|
||||||
<section className="container-xl pb-lg-4 mt-6 px-3 px-md-6" data-testid="product">
|
<section className="container-xl pb-lg-4 mt-6 px-3 px-md-6" data-testid="product">
|
||||||
<div className="">
|
<div className="">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import languages from '../lib/languages.js'
|
import languages from '../lib/languages.js'
|
||||||
import enterpriseServerReleases from '../lib/enterprise-server-releases.js'
|
import enterpriseServerReleases from '../lib/enterprise-server-releases.js'
|
||||||
import { allVersions } from '../lib/all-versions.js'
|
import { allVersions } from '../lib/all-versions.js'
|
||||||
import { productMap, getProductGroups } from '../lib/all-products.js'
|
import { productMap } from '../lib/all-products.js'
|
||||||
import pathUtils from '../lib/path-utils.js'
|
import pathUtils from '../lib/path-utils.js'
|
||||||
import productNames from '../lib/product-names.js'
|
import productNames from '../lib/product-names.js'
|
||||||
import warmServer from '../lib/warm-server.js'
|
import warmServer from '../lib/warm-server.js'
|
||||||
@@ -39,7 +39,6 @@ export default async function contextualize(req, res, next) {
|
|||||||
req.context.currentProduct = getProductStringFromPath(req.pagePath)
|
req.context.currentProduct = getProductStringFromPath(req.pagePath)
|
||||||
req.context.currentCategory = getCategoryStringFromPath(req.pagePath)
|
req.context.currentCategory = getCategoryStringFromPath(req.pagePath)
|
||||||
req.context.productMap = productMap
|
req.context.productMap = productMap
|
||||||
req.context.productGroups = getProductGroups(pageMap, req.language)
|
|
||||||
req.context.activeProducts = activeProducts
|
req.context.activeProducts = activeProducts
|
||||||
req.context.allVersions = allVersions
|
req.context.allVersions = allVersions
|
||||||
req.context.currentPathWithoutLanguage = getPathWithoutLanguage(req.pagePath)
|
req.context.currentPathWithoutLanguage = getPathWithoutLanguage(req.pagePath)
|
||||||
|
|||||||
32
middleware/contextualizers/product-groups.js
Normal file
32
middleware/contextualizers/product-groups.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { getProductGroups } from '../../lib/all-products.js'
|
||||||
|
import warmServer from '../../lib/warm-server.js'
|
||||||
|
import { languageKeys } from '../../lib/languages.js'
|
||||||
|
import { allVersionKeys } from '../../lib/all-versions.js'
|
||||||
|
|
||||||
|
const isHomepage = (path) => {
|
||||||
|
const split = path.split('/')
|
||||||
|
// E.g. `/foo` but not `foo/bar` or `foo/`
|
||||||
|
if (split.length === 2 && split[1] && !split[0]) {
|
||||||
|
return languageKeys.includes(split[1])
|
||||||
|
}
|
||||||
|
// E.g. `/foo/possiblyproductname` but not `foo/possiblyproductname` or
|
||||||
|
// `/foo/something/`
|
||||||
|
if (split.length === 3 && !split[0] && split[2]) {
|
||||||
|
return allVersionKeys.includes(split[2])
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function productGroups(req, res, next) {
|
||||||
|
// It's important to use `req.pathPage` instead of `req.path` because
|
||||||
|
// the request could be the client-side routing from Next where the URL
|
||||||
|
// might be something like `/_next/data/foo/bar.json` which is translated,
|
||||||
|
// in another middleware, to what it would equate to if it wasn't
|
||||||
|
// client-side routing.
|
||||||
|
if (isHomepage(req.pagePath)) {
|
||||||
|
const { pages } = await warmServer()
|
||||||
|
req.context.productGroups = getProductGroups(pages, req.language)
|
||||||
|
}
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
@@ -45,6 +45,7 @@ import genericToc from './contextualizers/generic-toc.js'
|
|||||||
import breadcrumbs from './contextualizers/breadcrumbs.js'
|
import breadcrumbs from './contextualizers/breadcrumbs.js'
|
||||||
import features from './contextualizers/features.js'
|
import features from './contextualizers/features.js'
|
||||||
import productExamples from './contextualizers/product-examples.js'
|
import productExamples from './contextualizers/product-examples.js'
|
||||||
|
import productGroups from './contextualizers/product-groups.js'
|
||||||
import featuredLinks from './featured-links.js'
|
import featuredLinks from './featured-links.js'
|
||||||
import learningTrack from './learning-track.js'
|
import learningTrack from './learning-track.js'
|
||||||
import next from './next.js'
|
import next from './next.js'
|
||||||
@@ -267,6 +268,7 @@ export default function (app) {
|
|||||||
app.use(asyncMiddleware(instrument(breadcrumbs, './contextualizers/breadcrumbs')))
|
app.use(asyncMiddleware(instrument(breadcrumbs, './contextualizers/breadcrumbs')))
|
||||||
app.use(instrument(features, './contextualizers/features'))
|
app.use(instrument(features, './contextualizers/features'))
|
||||||
app.use(asyncMiddleware(instrument(productExamples, './contextualizers/product-examples')))
|
app.use(asyncMiddleware(instrument(productExamples, './contextualizers/product-examples')))
|
||||||
|
app.use(asyncMiddleware(instrument(productGroups, './contextualizers/product-groups')))
|
||||||
|
|
||||||
app.use(asyncMiddleware(instrument(featuredLinks, './featured-links')))
|
app.use(asyncMiddleware(instrument(featuredLinks, './featured-links')))
|
||||||
app.use(asyncMiddleware(instrument(learningTrack, './learning-track')))
|
app.use(asyncMiddleware(instrument(learningTrack, './learning-track')))
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { GetServerSideProps } from 'next'
|
import React from 'react'
|
||||||
|
import type { GetServerSideProps } from 'next'
|
||||||
|
|
||||||
import { MainContextT, MainContext, getMainContext } from 'components/context/MainContext'
|
import { MainContextT, MainContext, getMainContext } from 'components/context/MainContext'
|
||||||
|
|
||||||
import React from 'react'
|
|
||||||
import { DefaultLayout } from 'components/DefaultLayout'
|
import { DefaultLayout } from 'components/DefaultLayout'
|
||||||
import { useTranslation } from 'components/hooks/useTranslation'
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
import { ArticleList } from 'components/landing/ArticleList'
|
import { ArticleList } from 'components/landing/ArticleList'
|
||||||
import { HomePageHero } from 'components/homepage/HomePageHero'
|
import { HomePageHero } from 'components/homepage/HomePageHero'
|
||||||
|
import type { ProductGroupT } from 'components/homepage/ProductSelections'
|
||||||
import { ProductSelections } from 'components/homepage/ProductSelections'
|
import { ProductSelections } from 'components/homepage/ProductSelections'
|
||||||
|
|
||||||
type FeaturedLink = {
|
type FeaturedLink = {
|
||||||
@@ -19,13 +20,23 @@ type Props = {
|
|||||||
mainContext: MainContextT
|
mainContext: MainContextT
|
||||||
popularLinks: Array<FeaturedLink>
|
popularLinks: Array<FeaturedLink>
|
||||||
gettingStartedLinks: Array<FeaturedLink>
|
gettingStartedLinks: Array<FeaturedLink>
|
||||||
|
productGroups: Array<ProductGroupT>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MainHomePage({ mainContext, gettingStartedLinks, popularLinks }: Props) {
|
export default function MainHomePage({
|
||||||
|
mainContext,
|
||||||
|
gettingStartedLinks,
|
||||||
|
popularLinks,
|
||||||
|
productGroups,
|
||||||
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<MainContext.Provider value={mainContext}>
|
<MainContext.Provider value={mainContext}>
|
||||||
<DefaultLayout>
|
<DefaultLayout>
|
||||||
<HomePage gettingStartedLinks={gettingStartedLinks} popularLinks={popularLinks} />
|
<HomePage
|
||||||
|
gettingStartedLinks={gettingStartedLinks}
|
||||||
|
popularLinks={popularLinks}
|
||||||
|
productGroups={productGroups}
|
||||||
|
/>
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
</MainContext.Provider>
|
</MainContext.Provider>
|
||||||
)
|
)
|
||||||
@@ -34,15 +45,16 @@ export default function MainHomePage({ mainContext, gettingStartedLinks, popular
|
|||||||
type HomePageProps = {
|
type HomePageProps = {
|
||||||
popularLinks: Array<FeaturedLink>
|
popularLinks: Array<FeaturedLink>
|
||||||
gettingStartedLinks: Array<FeaturedLink>
|
gettingStartedLinks: Array<FeaturedLink>
|
||||||
|
productGroups: Array<ProductGroupT>
|
||||||
}
|
}
|
||||||
function HomePage(props: HomePageProps) {
|
function HomePage(props: HomePageProps) {
|
||||||
const { gettingStartedLinks, popularLinks } = props
|
const { gettingStartedLinks, popularLinks, productGroups } = props
|
||||||
const { t } = useTranslation(['toc'])
|
const { t } = useTranslation(['toc'])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<HomePageHero />
|
<HomePageHero />
|
||||||
<ProductSelections />
|
<ProductSelections productGroups={productGroups} />
|
||||||
<div className="mt-6 px-3 px-md-6 container-xl">
|
<div className="mt-6 px-3 px-md-6 container-xl">
|
||||||
<div className="container-xl">
|
<div className="container-xl">
|
||||||
<div className="gutter gutter-xl-spacious clearfix">
|
<div className="gutter gutter-xl-spacious clearfix">
|
||||||
@@ -67,6 +79,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
|
|||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
mainContext: await getMainContext(req, res),
|
mainContext: await getMainContext(req, res),
|
||||||
|
productGroups: req.context.productGroups,
|
||||||
gettingStartedLinks: req.context.featuredLinks.gettingStarted.map(
|
gettingStartedLinks: req.context.featuredLinks.gettingStarted.map(
|
||||||
({ title, href, intro }: any) => ({ title, href, intro })
|
({ title, href, intro }: any) => ({ title, href, intro })
|
||||||
),
|
),
|
||||||
|
|||||||
31
tests/rendering/homepage.js
Normal file
31
tests/rendering/homepage.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { expect, jest } from '@jest/globals'
|
||||||
|
|
||||||
|
import { getDOM } from '../helpers/e2etest.js'
|
||||||
|
|
||||||
|
describe('rendering the home page(s)', () => {
|
||||||
|
jest.setTimeout(5 * 60 * 1000)
|
||||||
|
|
||||||
|
test('homepage has product links', async () => {
|
||||||
|
const $ = await getDOM('/en')
|
||||||
|
const products = $('[data-testid=product]')
|
||||||
|
expect(products.length).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('homepage in non-default language has product links', async () => {
|
||||||
|
const $ = await getDOM('/ja')
|
||||||
|
const products = $('[data-testid=product]')
|
||||||
|
expect(products.length).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('homepage in non-default product', async () => {
|
||||||
|
const $ = await getDOM('/en/enterprise-cloud@latest')
|
||||||
|
const products = $('[data-testid=product]')
|
||||||
|
expect(products.length).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('homepage in non-default product in non-default language', async () => {
|
||||||
|
const $ = await getDOM('/ja/enterprise-cloud@latest')
|
||||||
|
const products = $('[data-testid=product]')
|
||||||
|
expect(products.length).toBe(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user