Skip to content

Commit a28fb1a

Browse files
authored
Merge branch 'main' into kh-enable_jsx_a11y_linter
2 parents 982b8bf + 346b38d commit a28fb1a

40 files changed

+1841
-1063
lines changed

components/DefaultLayout.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const DefaultLayout = (props: Props) => {
4545
</Head>
4646
<SidebarNav />
4747

48-
<main className="width-full">
48+
<main className="flex-1 min-width-0">
4949
<Header />
5050
<DeprecationBanner />
5151

components/Header.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ export const Header = () => {
2222
const showVersionPicker =
2323
relativePath === 'index.md' ||
2424
currentLayoutName === 'product-landing' ||
25-
currentLayoutName === 'product-sublanding'
25+
currentLayoutName === 'product-sublanding' ||
26+
currentLayoutName === 'release-notes'
2627

2728
return (
2829
<div className="border-bottom color-border-secondary no-print">

components/HeaderNotifications.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export const HeaderNotifications = () => {
8787
const isLast = i === allNotifications.length - 1
8888
return (
8989
<div
90+
key={content}
9091
className={cx(
9192
'header-notifications text-center f5 color-text-primary py-4 px-6',
9293
type === NotificationType.TRANSLATION && 'translation_notice color-bg-info',

components/context/MainContext.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type EnterpriseServerReleases = {
5959
isOldestReleaseDeprecated: boolean
6060
oldestSupported: string
6161
nextDeprecationDate: string
62+
supported: Array<string>
6263
}
6364
export type MainContextT = {
6465
breadcrumbs: {
@@ -154,6 +155,7 @@ export const getMainContextFromRequest = (req: any): MainContextT => {
154155
'isOldestReleaseDeprecated',
155156
'oldestSupported',
156157
'nextDeprecationDate',
158+
'supported',
157159
]),
158160
enterpriseServerVersions: req.context.enterpriseServerVersions,
159161
currentLanguage: req.context.currentLanguage,

components/context/ProductLandingContext.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ export type ProductLandingContextT = {
5252
changelogUrl?: string
5353
whatsNewChangelog?: Array<{ href: string; title: string; date: string }>
5454
tocItems: Array<TocItem>
55+
releases: Array<{
56+
version: string
57+
firstPreviousRelease: string
58+
secondPreviousRelease: string
59+
patches: Array<{ date: string; version: string }>
60+
}>
5561
}
5662

5763
export const ProductLandingContext = createContext<ProductLandingContextT | null>(null)
@@ -89,6 +95,7 @@ export const getProductLandingContextFromRequest = (req: any): ProductLandingCon
8995
changelogUrl: req.context.changelogUrl || [],
9096
productCodeExamples: req.context.productCodeExamples || [],
9197
productCommunityExamples: req.context.productCommunityExamples || [],
98+
releases: req.context.releases || [],
9299

93100
productUserExamples: (req.context.productUserExamples || []).map(
94101
({ user, description }: any) => ({

components/hooks/useOnScreen.tsx

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { useState, useEffect, MutableRefObject, RefObject } from 'react'
2+
3+
export function useOnScreen<T extends Element>(
4+
ref: MutableRefObject<T | undefined> | RefObject<T>,
5+
rootMargin: string = '0px'
6+
): boolean {
7+
const [isIntersecting, setIntersecting] = useState(false)
8+
useEffect(() => {
9+
const observer = new IntersectionObserver(
10+
([entry]) => {
11+
setIntersecting(entry.isIntersecting)
12+
},
13+
{
14+
rootMargin,
15+
}
16+
)
17+
if (ref.current) {
18+
observer.observe(ref.current)
19+
}
20+
return () => {
21+
ref.current && observer.unobserve(ref.current)
22+
}
23+
}, [])
24+
return isIntersecting
25+
}

components/hooks/useVersion.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@ import { useRouter } from 'next/router'
33
type VersionInfo = {
44
currentVersion: string
55
isEnterprise: boolean
6+
isEnterpriseServer: boolean
67
}
78
const DEFAULT_VERSION = 'free-pro-team@latest'
89
export const useVersion = (): VersionInfo => {
910
const router = useRouter()
1011
const currentVersion = (router.query.versionId as string) || DEFAULT_VERSION
11-
return { currentVersion, isEnterprise: currentVersion.includes('enterprise') }
12+
return {
13+
currentVersion,
14+
isEnterprise: currentVersion.includes('enterprise'),
15+
isEnterpriseServer: currentVersion.includes('enterprise-server'),
16+
}
1217
}

components/landing/ProductLanding.tsx

+10-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@ import { CodeExamples } from 'components/landing/CodeExamples'
1010
import { LandingSection } from 'components/landing/LandingSection'
1111
import { useTranslation } from 'components/hooks/useTranslation'
1212
import { ProductArticlesList } from 'components/landing/ProductArticlesList'
13+
import { ProductReleases } from 'components/landing/ProductReleases'
14+
import { useRouter } from 'next/router'
15+
import { useVersion } from 'components/hooks/useVersion'
1316

1417
export const ProductLanding = () => {
18+
const router = useRouter()
19+
const { isEnterpriseServer } = useVersion()
1520
const {
1621
shortTitle,
1722
guideCards,
@@ -49,9 +54,11 @@ export const ProductLanding = () => {
4954
</LandingSection>
5055
)}
5156

52-
{/* {% if currentVersion contains 'enterprise-server' and currentProduct == 'admin' %}
53-
{% include product-releases %}
54-
{% endif %} */}
57+
{router.query.productId === 'admin' && isEnterpriseServer && (
58+
<LandingSection title={t('supported_releases')} className="my-6">
59+
<ProductReleases />
60+
</LandingSection>
61+
)}
5562

5663
{guideCards.length > 0 && (
5764
<div className="color-bg-tertiary py-6 my-8">
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { ArrowRightIcon, ArrowUpIcon, FileIcon, ListUnorderedIcon } from '@primer/octicons-react'
2+
import { useMainContext } from 'components/context/MainContext'
3+
import { useProductLandingContext } from 'components/context/ProductLandingContext'
4+
import { useTranslation } from 'components/hooks/useTranslation'
5+
import { Link } from 'components/Link'
6+
import { useRouter } from 'next/router'
7+
8+
export function ProductReleases() {
9+
const { t } = useTranslation('product_landing')
10+
const router = useRouter()
11+
const { enterpriseServerReleases, allVersions } = useMainContext()
12+
const { releases } = useProductLandingContext()
13+
const currentPath = router.asPath.split('?')[0]
14+
return (
15+
<div>
16+
<div className="d-lg-flex gutter-lg flex-items-stretch">
17+
{releases.map((release) => {
18+
const releaseNumber = release.version
19+
if (!enterpriseServerReleases.supported.includes(releaseNumber)) {
20+
return null
21+
}
22+
const releaseVersion = `enterprise-server@${releaseNumber}`
23+
const latestPatch = release.patches[0]
24+
const firstPreviousVersion = `enterprise-server@${release.firstPreviousRelease}`
25+
const secondPreviousVersion = `enterprise-server@${release.secondPreviousRelease}`
26+
return (
27+
<div key={releaseNumber} className="col-lg-4 col-12 mb-3">
28+
<div className="Box color-shadow-medium height-full d-block hover-shadow-large no-underline color-text-primary p-5">
29+
<h2>{allVersions[releaseVersion].versionTitle}</h2>
30+
<p className="mt-2 mb-4 color-text-tertiary">
31+
<ListUnorderedIcon />{' '}
32+
<Link
33+
href={`/${router.locale}/${releaseVersion}/admin/release-notes#${latestPatch.version}`}
34+
>
35+
{t('release_notes_for')} {latestPatch.version}
36+
</Link>{' '}
37+
({latestPatch.date})
38+
</p>
39+
<p className="mt-2 mb-4 color-text-tertiary">
40+
<ArrowUpIcon /> {t('upgrade_from')}{' '}
41+
<Link
42+
href={`/${router.locale}/${firstPreviousVersion}/admin/enterprise-management/upgrading-github-enterprise-server`}
43+
>
44+
{release.firstPreviousRelease}
45+
</Link>{' '}
46+
or{' '}
47+
<Link
48+
href={`/${router.locale}/${secondPreviousVersion}/admin/enterprise-management/upgrading-github-enterprise-server`}
49+
>
50+
{release.secondPreviousRelease}
51+
</Link>
52+
</p>
53+
<p className="mt-2 mb-4 color-text-tertiary">
54+
<FileIcon />{' '}
55+
<Link href={`/${router.locale}/${releaseVersion}`}>{t('browse_all_docs')}</Link>
56+
</p>
57+
</div>
58+
</div>
59+
)
60+
})}
61+
</div>
62+
63+
<Link href={`${currentPath}/release-notes}`} className="btn btn-outline float-right">
64+
{t('explore_release_notes')} <ArrowRightIcon />
65+
</Link>
66+
</div>
67+
)
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { useRef, useEffect } from 'react'
2+
3+
import { useTranslation } from 'components/hooks/useTranslation'
4+
import { useOnScreen } from 'components/hooks/useOnScreen'
5+
import { PatchNotes } from './PatchNotes'
6+
import { ReleaseNotePatch } from './types'
7+
8+
type Props = { patch: ReleaseNotePatch; didEnterView: () => void }
9+
export function GHAEReleaseNotePatch({ patch, didEnterView }: Props) {
10+
const { t } = useTranslation('release_notes')
11+
const containerRef = useRef<HTMLDivElement>(null)
12+
const onScreen = useOnScreen(containerRef, '-40% 0px -50%')
13+
useEffect(() => {
14+
if (onScreen) {
15+
didEnterView()
16+
}
17+
}, [onScreen])
18+
19+
const bannerText = patch.currentWeek
20+
? t('banner_text_current')
21+
: `${t('banner_text_past')} ${patch.friendlyDate}.`
22+
23+
return (
24+
<div
25+
ref={containerRef}
26+
className="mb-10 color-bg-secondary pb-6 border-bottom border-top"
27+
id={patch.date}
28+
>
29+
<header
30+
style={{ zIndex: 1 }}
31+
className="container-xl position-sticky top-0 color-bg-secondary border-bottom px-3 pt-4 pb-2"
32+
>
33+
<div className="d-flex flex-items-center">
34+
<h2 className="border-bottom-0 m-0 p-0">{patch.title}</h2>
35+
36+
{patch.release_candidate && (
37+
<span
38+
className="IssueLabel color-bg-warning-inverse color-text-inverse ml-3"
39+
style={{ whiteSpace: 'pre' }}
40+
>
41+
Release Candidate
42+
</span>
43+
)}
44+
45+
<button className="js-print btn-link ml-3 text-small text-bold">Print</button>
46+
</div>
47+
<p className="color-text-secondary mt-1">
48+
{patch.friendlyDate} - {bannerText}
49+
</p>
50+
</header>
51+
52+
<div className="container-xl px-3">
53+
<div className="mt-3" dangerouslySetInnerHTML={{ __html: patch.intro }} />
54+
55+
<PatchNotes patch={patch} />
56+
</div>
57+
</div>
58+
)
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { useState } from 'react'
2+
import cx from 'classnames'
3+
import { ChevronDownIcon } from '@primer/octicons-react'
4+
import { GHAEReleaseNotePatch } from './GHAEReleaseNotePatch'
5+
import { GHAEReleaseNotesContextT } from './types'
6+
7+
type GitHubAEProps = {
8+
context: GHAEReleaseNotesContextT
9+
}
10+
export function GHAEReleaseNotes({ context }: GitHubAEProps) {
11+
const { releaseNotes, releases, currentVersion } = context
12+
const [focusedPatch, setFocusedPatch] = useState('')
13+
14+
return (
15+
<div className="d-flex">
16+
<article className="min-width-0 flex-1">
17+
<div className="d-flex flex-items-center flex-justify-between color-bg-primary px-5 py-2">
18+
<div></div>
19+
<h1 className="f4 py-3 m-0">{currentVersion.planTitle} release notes</h1>
20+
<div></div>
21+
</div>
22+
23+
<div className="markdown-body">
24+
{releaseNotes.map((patch) => {
25+
return (
26+
<GHAEReleaseNotePatch
27+
key={patch.version}
28+
patch={patch}
29+
didEnterView={() => setFocusedPatch(patch.version)}
30+
/>
31+
)
32+
})}
33+
</div>
34+
</article>
35+
36+
<aside
37+
className="markdown-body position-sticky top-0 d-none d-md-block border-left no-print color-bg-primary flex-shrink-0"
38+
style={{ width: 260, height: '100vh' }}
39+
>
40+
<nav className="height-full overflow-auto">
41+
<ul className="list-style-none pl-0 text-bold">
42+
{releases.map((release) => {
43+
return (
44+
<li key={release.version} className="border-bottom">
45+
<details
46+
className="my-0 details-reset release-notes-version-picker"
47+
aria-current="page"
48+
open
49+
>
50+
<summary className="px-3 py-4 my-0 d-flex flex-items-center flex-justify-between">
51+
{release.version}
52+
<div className="d-flex">
53+
<span className="color-text-tertiary text-mono text-small text-normal mr-1">
54+
{release.patches.length} releases
55+
</span>
56+
<ChevronDownIcon />
57+
</div>
58+
</summary>
59+
<ul className="color-bg-tertiary border-top list-style-none py-4 px-0 my-0">
60+
{release.patches.map((patch) => {
61+
const isActive = patch.version === focusedPatch
62+
return (
63+
<li
64+
key={patch.version}
65+
className={cx(
66+
'js-release-notes-patch-link px-3 my-0 py-1',
67+
isActive && 'selected'
68+
)}
69+
>
70+
<a
71+
href={`#${patch.date}`}
72+
className="d-flex flex-items-center flex-justify-between"
73+
>
74+
{patch.friendlyDate}
75+
</a>
76+
</li>
77+
)
78+
})}
79+
</ul>
80+
</details>
81+
</li>
82+
)
83+
})}
84+
</ul>
85+
</nav>
86+
</aside>
87+
</div>
88+
)
89+
}

0 commit comments

Comments
 (0)