Skip to content

Commit a88e99c

Browse files
authored
Reactify: release notes (github#19799)
* reactify release-notes pages * update GHAE/ES react release notes to not rely on javascripts/release-notes.js
1 parent 4fa725c commit a88e99c

17 files changed

+693
-8
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/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+
}
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { useEffect, useRef } from 'react'
2+
import dayjs from 'dayjs'
3+
4+
import { useTranslation } from 'components/hooks/useTranslation'
5+
import { PatchNotes } from './PatchNotes'
6+
import { Link } from 'components/Link'
7+
import { CurrentVersion, ReleaseNotePatch, GHESMessage } from './types'
8+
import { useOnScreen } from 'components/hooks/useOnScreen'
9+
10+
type Props = {
11+
patch: ReleaseNotePatch
12+
currentVersion: CurrentVersion
13+
latestPatch: string
14+
latestRelease: string
15+
message: GHESMessage
16+
didEnterView: () => void
17+
}
18+
export function GHESReleaseNotePatch({
19+
patch,
20+
currentVersion,
21+
latestPatch,
22+
latestRelease,
23+
message,
24+
didEnterView,
25+
}: Props) {
26+
const { t } = useTranslation('header')
27+
const containerRef = useRef<HTMLDivElement>(null)
28+
const onScreen = useOnScreen(containerRef, '-40% 0px -50%')
29+
useEffect(() => {
30+
if (onScreen) {
31+
didEnterView()
32+
}
33+
}, [onScreen])
34+
35+
return (
36+
<div
37+
ref={containerRef}
38+
className="mb-10 color-bg-secondary pb-6 border-bottom border-top"
39+
id={patch.version}
40+
>
41+
<header
42+
style={{ zIndex: 1 }}
43+
className="container-xl position-sticky top-0 color-bg-secondary border-bottom px-3 pt-4 pb-2"
44+
>
45+
<div className="d-flex flex-items-center">
46+
<h2 className="border-bottom-0 m-0 p-0">
47+
{currentVersion.versionTitle}.{patch.patchVersion}
48+
</h2>
49+
50+
{patch.release_candidate && (
51+
<span
52+
className="IssueLabel color-bg-warning-inverse color-text-inverse ml-3"
53+
style={{ whiteSpace: 'pre' }}
54+
>
55+
Release Candidate
56+
</span>
57+
)}
58+
59+
{currentVersion.plan == 'enterprise-server' && (
60+
<Link
61+
href={`https://enterprise.github.com/releases/${patch.downloadVersion}/download`}
62+
className="ml-3 text-small text-bold"
63+
>
64+
Download
65+
</Link>
66+
)}
67+
68+
<button className="js-print btn-link ml-3 text-small text-bold">Print</button>
69+
</div>
70+
71+
<p className="color-text-secondary mt-1">{dayjs(patch.date).format('MMMM, DD, YYYY')}</p>
72+
73+
{patch.version !== latestPatch && currentVersion.currentRelease === latestRelease && (
74+
<p className="color-text-secondary mt-1">
75+
<span
76+
dangerouslySetInnerHTML={{ __html: message.ghes_release_notes_upgrade_patch_only }}
77+
/>{' '}
78+
{t('notices.release_notes_use_latest')}
79+
</p>
80+
)}
81+
82+
{patch.version === latestPatch && currentVersion.currentRelease !== latestRelease && (
83+
<p className="color-text-secondary mt-1">
84+
<span
85+
dangerouslySetInnerHTML={{ __html: message.ghes_release_notes_upgrade_release_only }}
86+
/>{' '}
87+
{t('notices.release_notes_use_latest')}
88+
</p>
89+
)}
90+
91+
{patch.version !== latestPatch && currentVersion.currentRelease !== latestRelease && (
92+
<p className="color-text-secondary mt-1">
93+
<span
94+
dangerouslySetInnerHTML={{
95+
__html: message.ghes_release_notes_upgrade_patch_and_release,
96+
}}
97+
/>{' '}
98+
{t('notices.release_notes_use_latest')}
99+
</p>
100+
)}
101+
</header>
102+
103+
<div className="container-xl px-3">
104+
<div className="mt-3" dangerouslySetInnerHTML={{ __html: patch.intro }} />
105+
106+
<PatchNotes patch={patch} withReleaseNoteLabel />
107+
</div>
108+
</div>
109+
)
110+
}

0 commit comments

Comments
 (0)