Skip to content

Commit 000799d

Browse files
rseseheiskr
andauthored
Handle learning track paths from a different product (github#21776)
* Add trackProduct property * Fall back to learning track product URL param * Add product URL param to learning track banner * Add product URL param to featured track links * Fix typo :( * Add product URL param to learning track * Add multi-product learning track tests * Re-enable tests with a Code Security learning track * Re-enable more tests with Code Security learning tracks * Add more multi-product testing * Update components/sublanding/LearningTrack.tsx Co-authored-by: Kevin Heis <[email protected]> Co-authored-by: Kevin Heis <[email protected]>
1 parent 180118a commit 000799d

File tree

8 files changed

+75
-19
lines changed

8 files changed

+75
-19
lines changed

components/article/LearningTrackNav.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ type Props = {
66
}
77
export function LearningTrackNav({ track }: Props) {
88
const { t } = useTranslation('learning_track_nav')
9-
const { prevGuide, nextGuide, trackName } = track
9+
const { prevGuide, nextGuide, trackName, trackProduct } = track
1010
return (
1111
<div
1212
data-testid="learning-track-nav"
@@ -17,7 +17,7 @@ export function LearningTrackNav({ track }: Props) {
1717
<>
1818
<span className="f6 color-text-secondary">{t('prevGuide')}</span>
1919
<a
20-
href={`${prevGuide.href}?learn=${trackName}`}
20+
href={`${prevGuide.href}?learn=${trackName}&learnProduct=${trackProduct}`}
2121
className="text-bold color-text-secondary"
2222
>
2323
{prevGuide.title}
@@ -31,7 +31,7 @@ export function LearningTrackNav({ track }: Props) {
3131
<>
3232
<span className="f6 color-text-secondary">{t('nextGuide')}</span>
3333
<a
34-
href={`${nextGuide.href}?learn=${trackName}`}
34+
href={`${nextGuide.href}?learn=${trackName}&learnProduct=${trackProduct}`}
3535
className="text-bold color-text-secondary text-right f4"
3636
>
3737
{nextGuide.title}

components/context/ArticleContext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createContext, useContext } from 'react'
22

33
export type LearningTrack = {
44
trackName?: string
5+
trackProduct?: string
56
prevGuide?: { href: string; title: string }
67
nextGuide?: { href: string; title: string }
78
}

components/context/ProductSubLandingContext.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import pick from 'lodash/pick'
33

44
export type FeaturedTrack = {
55
trackName: string
6+
trackProduct: string
67
title: string
78
description: string
89
guides?: Array<{ href: string; page?: { type: string }; title: string; intro: string }>
@@ -47,14 +48,14 @@ export const getProductSubLandingContextFromRequest = (req: any): ProductSubLand
4748
title: req.context.productMap[req.context.currentProduct].name,
4849
featuredTrack: page.featuredTrack
4950
? {
50-
...pick(page.featuredTrack, ['title', 'description', 'trackName']),
51+
...pick(page.featuredTrack, ['title', 'description', 'trackName', 'trackProduct']),
5152
guides: (page.featuredTrack?.guides || []).map((guide: any) => {
5253
return pick(guide, ['title', 'intro', 'href', 'page.type'])
5354
}),
5455
}
5556
: null,
5657
learningTracks: (page.learningTracks || []).map((track: any) => ({
57-
...pick(track, ['title', 'description', 'trackName']),
58+
...pick(track, ['title', 'description', 'trackName', 'trackProduct']),
5859
guides: (track.guides || []).map((guide: any) => {
5960
return pick(guide, ['title', 'intro', 'href', 'page.type'])
6061
}),

components/sublanding/LearningTrack.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ export const LearningTrack = ({ track }: Props) => {
3333
<a
3434
className="d-inline-flex btn no-wrap mt-3 mt-md-0 flex-items-center flex-justify-center"
3535
role="button"
36-
href={`${track?.guides && track?.guides[0].href}?learn=${track?.trackName}`}
36+
href={`${track?.guides && track?.guides[0].href}?learn=${
37+
track?.trackName
38+
}&learnProduct=${track?.trackProduct}`}
3739
>
3840
<span>{t('start')}</span>
3941
<ArrowRightIcon size={20} className="ml-2" />
@@ -44,7 +46,7 @@ export const LearningTrack = ({ track }: Props) => {
4446
<div key={guide.href + track?.trackName}>
4547
<a
4648
className="Box-row d-flex flex-items-center color-text-primary no-underline"
47-
href={`${guide.href}?learn=${track?.trackName}`}
49+
href={`${guide.href}?learn=${track?.trackName}&learnProduct=${track?.trackProduct}`}
4850
>
4951
<div
5052
className="color-bg-tertiary d-inline-flex mr-4 circle flex-items-center flex-justify-center"

components/sublanding/SubLandingHero.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const SubLandingHero = () => {
1616
const guideItems = featuredTrack?.guides?.map((guide) => (
1717
<li className="px-2 d-flex flex-shrink-0" key={guide.href} style={{ width: cardWidth }}>
1818
<Link
19-
href={`${guide.href}?learn=${featuredTrack.trackName}`}
19+
href={`${guide.href}?learn=${featuredTrack.trackName}&learnProduct=${featuredTrack.trackProduct}`}
2020
className="d-inline-block Box p-5 color-bg-primary color-border-primary no-underline"
2121
>
2222
<div className="d-flex flex-justify-between flex-items-center">
@@ -71,7 +71,7 @@ export const SubLandingHero = () => {
7171
<Link
7272
className="d-inline-flex flex-items-center flex-justify-center btn px-4 py-2 f5 no-underline text-bold"
7373
role="button"
74-
href={`${featuredTrack.guides[0].href}?learn=${featuredTrack.trackName}`}
74+
href={`${featuredTrack.guides[0].href}?learn=${featuredTrack.trackName}&learnProduct=${featuredTrack.trackProduct}`}
7575
>
7676
{t(`start_path`)}
7777
<ArrowRightIcon size={20} className="ml-2" />

lib/process-learning-tracks.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export default async function processLearningTracks(rawLearningTracks, context)
3434

3535
const learningTrack = {
3636
trackName: renderedTrackName,
37+
trackProduct: context.currentProduct || null,
3738
title: await renderContent(track.title, context, renderOpts),
3839
description: await renderContent(track.description, context, renderOpts),
3940
// getLinkData respects versioning and only returns guides available in the current version;

middleware/learning-track.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,24 @@ export default async function learningTrack(req, res, next) {
1313
const trackName = req.query.learn
1414
if (!trackName) return noTrack()
1515

16-
const tracksPerProduct = req.context.site.data['learning-tracks'][req.context.currentProduct]
16+
let trackProduct = req.context.currentProduct
17+
let tracksPerProduct = req.context.site.data['learning-tracks'][trackProduct]
18+
19+
// If there are no learning tracks for the current product, try and fall
20+
// back to the learning track product set as a URL parameter. This handles
21+
// the case where a learning track has guide paths for a different product
22+
// than the current learning track product.
23+
if (!tracksPerProduct) {
24+
trackProduct = req.query.learnProduct
25+
tracksPerProduct = req.context.site.data['learning-tracks'][trackProduct]
26+
}
27+
1728
if (!tracksPerProduct) return noTrack()
1829

19-
const track = req.context.site.data['learning-tracks'][req.context.currentProduct][trackName]
30+
const track = req.context.site.data['learning-tracks'][trackProduct][trackName]
2031
if (!track) return noTrack()
2132

22-
const currentLearningTrack = { trackName }
23-
33+
const currentLearningTrack = { trackName, trackProduct }
2434
const guidePath = getPathWithoutLanguage(getPathWithoutVersion(req.pagePath))
2535
let guideIndex = track.guides.findIndex((path) => path === guidePath)
2636

tests/rendering/learning-tracks.js

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { jest } from '@jest/globals'
33

44
jest.setTimeout(3 * 60 * 1000)
55

6-
describe.skip('learning tracks', () => {
6+
describe('learning tracks', () => {
77
test('render first track as feature track', async () => {
8-
const $ = await getDOM('/en/actions/guides')
8+
const $ = await getDOM('/en/code-security/guides')
99
expect($('[data-testid=feature-track]')).toHaveLength(1)
1010
const href = $('[data-testid=feature-track] li a').first().attr('href')
1111
const found = href.match(/.*\?learn=(.*)/i)
@@ -19,7 +19,7 @@ describe.skip('learning tracks', () => {
1919
})
2020

2121
test('render other tracks', async () => {
22-
const $ = await getDOM('/en/actions/guides')
22+
const $ = await getDOM('/en/code-security/guides')
2323
expect($('[data-testid=learning-track]').length).toBeGreaterThanOrEqual(4)
2424
$('[data-testid=learning-track]').each((i, trackElem) => {
2525
const href = $(trackElem).find('.Box-header a').first().attr('href')
@@ -37,16 +37,16 @@ describe.skip('learning tracks', () => {
3737
})
3838
})
3939

40-
describe.skip('navigation banner', () => {
40+
describe('navigation banner', () => {
4141
test('render navigation banner when url includes correct learning track name', async () => {
4242
const $ = await getDOM(
43-
'/en/actions/guides/setting-up-continuous-integration-using-workflow-templates?learn=continuous_integration'
43+
'/en/code-security/security-advisories/creating-a-security-advisory?learn=security_advisories'
4444
)
4545
expect($('[data-testid=learning-track-nav]')).toHaveLength(1)
4646
const $navLinks = $('[data-testid=learning-track-nav] a')
4747
expect($navLinks).toHaveLength(2)
4848
$navLinks.each((i, elem) => {
49-
expect($(elem).attr('href')).toEqual(expect.stringContaining('?learn=continuous_integration'))
49+
expect($(elem).attr('href')).toEqual(expect.stringContaining('?learn=security_advisories'))
5050
})
5151
})
5252

@@ -62,6 +62,47 @@ describe.skip('navigation banner', () => {
6262
})
6363
})
6464

65+
test('render navigation banner when url belongs to a multi-product learning track', async () => {
66+
// This is a `code-security` product learning track and it includes a guide
67+
// path that belongs to the `repositories` product.
68+
const $ = await getDOM(
69+
'/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-security-and-analysis-settings-for-your-repository?learn=dependabot_alerts&learnProduct=code-security'
70+
)
71+
expect($('[data-testid=learning-track-nav]')).toHaveLength(1)
72+
const $navLinks = $('[data-testid=learning-track-nav] a')
73+
expect($navLinks).toHaveLength(2)
74+
$navLinks.each((i, elem) => {
75+
expect($(elem).attr('href')).toEqual(
76+
expect.stringContaining('?learn=dependabot_alerts&learnProduct=code-security')
77+
)
78+
})
79+
})
80+
81+
test('render navigation banner when url belongs to a learning track and has an incorrect `learnProduct` param', async () => {
82+
// This is a `code-security` product learning track so the path should
83+
// work as-is and we won't check `learnProduct`.
84+
const $ = await getDOM(
85+
'/en/code-security/supply-chain-security/managing-vulnerabilities-in-your-projects-dependencies/viewing-and-updating-vulnerable-dependencies-in-your-repository?learn=dependabot_alerts&learnProduct=not_real'
86+
)
87+
expect($('[data-testid=learning-track-nav]')).toHaveLength(1)
88+
const $navLinks = $('[data-testid=learning-track-nav] a')
89+
expect($navLinks).toHaveLength(2)
90+
$navLinks.each((i, elem) => {
91+
expect($(elem).attr('href')).toEqual(
92+
expect.stringContaining('?learn=dependabot_alerts&learnProduct=code-security')
93+
)
94+
})
95+
})
96+
97+
test('does not include banner with multi-product learning track and when url has incorrect `learnProduct` param', async () => {
98+
// This is a `code-security` product learning track and it includes a guide
99+
// path that belongs to the `repositories` product.
100+
const $ = await getDOM(
101+
'/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-security-and-analysis-settings-for-your-repository?learn=dependabot_alerts&learnProduct=not_real'
102+
)
103+
expect($('[data-testid=learning-track-nav]')).toHaveLength(0)
104+
})
105+
65106
test('does not include banner when url does not include `learn` param', async () => {
66107
const $ = await getDOM(
67108
'/en/actions/guides/setting-up-continuous-integration-using-workflow-templates'

0 commit comments

Comments
 (0)