Reactify: release notes (#19799)
* reactify release-notes pages * update GHAE/ES react release notes to not rely on javascripts/release-notes.js
This commit is contained in:
@@ -45,7 +45,7 @@ export const DefaultLayout = (props: Props) => {
|
|||||||
</Head>
|
</Head>
|
||||||
<SidebarNav />
|
<SidebarNav />
|
||||||
|
|
||||||
<main className="width-full">
|
<main className="flex-1 min-width-0">
|
||||||
<Header />
|
<Header />
|
||||||
<DeprecationBanner />
|
<DeprecationBanner />
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ export const Header = () => {
|
|||||||
const showVersionPicker =
|
const showVersionPicker =
|
||||||
relativePath === 'index.md' ||
|
relativePath === 'index.md' ||
|
||||||
currentLayoutName === 'product-landing' ||
|
currentLayoutName === 'product-landing' ||
|
||||||
currentLayoutName === 'product-sublanding'
|
currentLayoutName === 'product-sublanding' ||
|
||||||
|
currentLayoutName === 'release-notes'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-bottom color-border-secondary no-print">
|
<div className="border-bottom color-border-secondary no-print">
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export const HeaderNotifications = () => {
|
|||||||
const isLast = i === allNotifications.length - 1
|
const isLast = i === allNotifications.length - 1
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
key={content}
|
||||||
className={cx(
|
className={cx(
|
||||||
'header-notifications text-center f5 color-text-primary py-4 px-6',
|
'header-notifications text-center f5 color-text-primary py-4 px-6',
|
||||||
type === NotificationType.TRANSLATION && 'translation_notice color-bg-info',
|
type === NotificationType.TRANSLATION && 'translation_notice color-bg-info',
|
||||||
|
|||||||
25
components/hooks/useOnScreen.tsx
Normal file
25
components/hooks/useOnScreen.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { useState, useEffect, MutableRefObject, RefObject } from 'react'
|
||||||
|
|
||||||
|
export function useOnScreen<T extends Element>(
|
||||||
|
ref: MutableRefObject<T | undefined> | RefObject<T>,
|
||||||
|
rootMargin: string = '0px'
|
||||||
|
): boolean {
|
||||||
|
const [isIntersecting, setIntersecting] = useState(false)
|
||||||
|
useEffect(() => {
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
setIntersecting(entry.isIntersecting)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rootMargin,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (ref.current) {
|
||||||
|
observer.observe(ref.current)
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
ref.current && observer.unobserve(ref.current)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
return isIntersecting
|
||||||
|
}
|
||||||
59
components/release-notes/GHAEReleaseNotePatch.tsx
Normal file
59
components/release-notes/GHAEReleaseNotePatch.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { useRef, useEffect } from 'react'
|
||||||
|
|
||||||
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
|
import { useOnScreen } from 'components/hooks/useOnScreen'
|
||||||
|
import { PatchNotes } from './PatchNotes'
|
||||||
|
import { ReleaseNotePatch } from './types'
|
||||||
|
|
||||||
|
type Props = { patch: ReleaseNotePatch; didEnterView: () => void }
|
||||||
|
export function GHAEReleaseNotePatch({ patch, didEnterView }: Props) {
|
||||||
|
const { t } = useTranslation('release_notes')
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
|
const onScreen = useOnScreen(containerRef, '-40% 0px -50%')
|
||||||
|
useEffect(() => {
|
||||||
|
if (onScreen) {
|
||||||
|
didEnterView()
|
||||||
|
}
|
||||||
|
}, [onScreen])
|
||||||
|
|
||||||
|
const bannerText = patch.currentWeek
|
||||||
|
? t('banner_text_current')
|
||||||
|
: `${t('banner_text_past')} ${patch.friendlyDate}.`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className="mb-10 color-bg-secondary pb-6 border-bottom border-top"
|
||||||
|
id={patch.date}
|
||||||
|
>
|
||||||
|
<header
|
||||||
|
style={{ zIndex: 1 }}
|
||||||
|
className="container-xl position-sticky top-0 color-bg-secondary border-bottom px-3 pt-4 pb-2"
|
||||||
|
>
|
||||||
|
<div className="d-flex flex-items-center">
|
||||||
|
<h2 className="border-bottom-0 m-0 p-0">{patch.title}</h2>
|
||||||
|
|
||||||
|
{patch.release_candidate && (
|
||||||
|
<span
|
||||||
|
className="IssueLabel color-bg-warning-inverse color-text-inverse ml-3"
|
||||||
|
style={{ whiteSpace: 'pre' }}
|
||||||
|
>
|
||||||
|
Release Candidate
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button className="js-print btn-link ml-3 text-small text-bold">Print</button>
|
||||||
|
</div>
|
||||||
|
<p className="color-text-secondary mt-1">
|
||||||
|
{patch.friendlyDate} - {bannerText}
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="container-xl px-3">
|
||||||
|
<div className="mt-3" dangerouslySetInnerHTML={{ __html: patch.intro }} />
|
||||||
|
|
||||||
|
<PatchNotes patch={patch} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
89
components/release-notes/GHAEReleaseNotes.tsx
Normal file
89
components/release-notes/GHAEReleaseNotes.tsx
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import cx from 'classnames'
|
||||||
|
import { ChevronDownIcon } from '@primer/octicons-react'
|
||||||
|
import { GHAEReleaseNotePatch } from './GHAEReleaseNotePatch'
|
||||||
|
import { GHAEReleaseNotesContextT } from './types'
|
||||||
|
|
||||||
|
type GitHubAEProps = {
|
||||||
|
context: GHAEReleaseNotesContextT
|
||||||
|
}
|
||||||
|
export function GHAEReleaseNotes({ context }: GitHubAEProps) {
|
||||||
|
const { releaseNotes, releases, currentVersion } = context
|
||||||
|
const [focusedPatch, setFocusedPatch] = useState('')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="d-flex">
|
||||||
|
<article className="min-width-0 flex-1">
|
||||||
|
<div className="d-flex flex-items-center flex-justify-between color-bg-primary px-5 py-2">
|
||||||
|
<div></div>
|
||||||
|
<h1 className="f4 py-3 m-0">{currentVersion.planTitle} release notes</h1>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="markdown-body">
|
||||||
|
{releaseNotes.map((patch) => {
|
||||||
|
return (
|
||||||
|
<GHAEReleaseNotePatch
|
||||||
|
key={patch.version}
|
||||||
|
patch={patch}
|
||||||
|
didEnterView={() => setFocusedPatch(patch.version)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<aside
|
||||||
|
className="markdown-body position-sticky top-0 d-none d-md-block border-left no-print color-bg-primary flex-shrink-0"
|
||||||
|
style={{ width: 260, height: '100vh' }}
|
||||||
|
>
|
||||||
|
<nav className="height-full overflow-auto">
|
||||||
|
<ul className="list-style-none pl-0 text-bold">
|
||||||
|
{releases.map((release) => {
|
||||||
|
return (
|
||||||
|
<li key={release.version} className="border-bottom">
|
||||||
|
<details
|
||||||
|
className="my-0 details-reset release-notes-version-picker"
|
||||||
|
aria-current="page"
|
||||||
|
open
|
||||||
|
>
|
||||||
|
<summary className="px-3 py-4 my-0 d-flex flex-items-center flex-justify-between">
|
||||||
|
{release.version}
|
||||||
|
<div className="d-flex">
|
||||||
|
<span className="color-text-tertiary text-mono text-small text-normal mr-1">
|
||||||
|
{release.patches.length} releases
|
||||||
|
</span>
|
||||||
|
<ChevronDownIcon />
|
||||||
|
</div>
|
||||||
|
</summary>
|
||||||
|
<ul className="color-bg-tertiary border-top list-style-none py-4 px-0 my-0">
|
||||||
|
{release.patches.map((patch) => {
|
||||||
|
const isActive = patch.version === focusedPatch
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
key={patch.version}
|
||||||
|
className={cx(
|
||||||
|
'js-release-notes-patch-link px-3 my-0 py-1',
|
||||||
|
isActive && 'selected'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={`#${patch.date}`}
|
||||||
|
className="d-flex flex-items-center flex-justify-between"
|
||||||
|
>
|
||||||
|
{patch.friendlyDate}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
110
components/release-notes/GHESReleaseNotePatch.tsx
Normal file
110
components/release-notes/GHESReleaseNotePatch.tsx
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import { useEffect, useRef } from 'react'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
|
import { PatchNotes } from './PatchNotes'
|
||||||
|
import { Link } from 'components/Link'
|
||||||
|
import { CurrentVersion, ReleaseNotePatch, GHESMessage } from './types'
|
||||||
|
import { useOnScreen } from 'components/hooks/useOnScreen'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
patch: ReleaseNotePatch
|
||||||
|
currentVersion: CurrentVersion
|
||||||
|
latestPatch: string
|
||||||
|
latestRelease: string
|
||||||
|
message: GHESMessage
|
||||||
|
didEnterView: () => void
|
||||||
|
}
|
||||||
|
export function GHESReleaseNotePatch({
|
||||||
|
patch,
|
||||||
|
currentVersion,
|
||||||
|
latestPatch,
|
||||||
|
latestRelease,
|
||||||
|
message,
|
||||||
|
didEnterView,
|
||||||
|
}: Props) {
|
||||||
|
const { t } = useTranslation('header')
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
|
const onScreen = useOnScreen(containerRef, '-40% 0px -50%')
|
||||||
|
useEffect(() => {
|
||||||
|
if (onScreen) {
|
||||||
|
didEnterView()
|
||||||
|
}
|
||||||
|
}, [onScreen])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className="mb-10 color-bg-secondary pb-6 border-bottom border-top"
|
||||||
|
id={patch.version}
|
||||||
|
>
|
||||||
|
<header
|
||||||
|
style={{ zIndex: 1 }}
|
||||||
|
className="container-xl position-sticky top-0 color-bg-secondary border-bottom px-3 pt-4 pb-2"
|
||||||
|
>
|
||||||
|
<div className="d-flex flex-items-center">
|
||||||
|
<h2 className="border-bottom-0 m-0 p-0">
|
||||||
|
{currentVersion.versionTitle}.{patch.patchVersion}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{patch.release_candidate && (
|
||||||
|
<span
|
||||||
|
className="IssueLabel color-bg-warning-inverse color-text-inverse ml-3"
|
||||||
|
style={{ whiteSpace: 'pre' }}
|
||||||
|
>
|
||||||
|
Release Candidate
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{currentVersion.plan == 'enterprise-server' && (
|
||||||
|
<Link
|
||||||
|
href={`https://enterprise.github.com/releases/${patch.downloadVersion}/download`}
|
||||||
|
className="ml-3 text-small text-bold"
|
||||||
|
>
|
||||||
|
Download
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button className="js-print btn-link ml-3 text-small text-bold">Print</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="color-text-secondary mt-1">{dayjs(patch.date).format('MMMM, DD, YYYY')}</p>
|
||||||
|
|
||||||
|
{patch.version !== latestPatch && currentVersion.currentRelease === latestRelease && (
|
||||||
|
<p className="color-text-secondary mt-1">
|
||||||
|
<span
|
||||||
|
dangerouslySetInnerHTML={{ __html: message.ghes_release_notes_upgrade_patch_only }}
|
||||||
|
/>{' '}
|
||||||
|
{t('notices.release_notes_use_latest')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{patch.version === latestPatch && currentVersion.currentRelease !== latestRelease && (
|
||||||
|
<p className="color-text-secondary mt-1">
|
||||||
|
<span
|
||||||
|
dangerouslySetInnerHTML={{ __html: message.ghes_release_notes_upgrade_release_only }}
|
||||||
|
/>{' '}
|
||||||
|
{t('notices.release_notes_use_latest')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{patch.version !== latestPatch && currentVersion.currentRelease !== latestRelease && (
|
||||||
|
<p className="color-text-secondary mt-1">
|
||||||
|
<span
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: message.ghes_release_notes_upgrade_patch_and_release,
|
||||||
|
}}
|
||||||
|
/>{' '}
|
||||||
|
{t('notices.release_notes_use_latest')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="container-xl px-3">
|
||||||
|
<div className="mt-3" dangerouslySetInnerHTML={{ __html: patch.intro }} />
|
||||||
|
|
||||||
|
<PatchNotes patch={patch} withReleaseNoteLabel />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
170
components/release-notes/GHESReleaseNotes.tsx
Normal file
170
components/release-notes/GHESReleaseNotes.tsx
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import cx from 'classnames'
|
||||||
|
import {
|
||||||
|
ChevronDownIcon,
|
||||||
|
ChevronLeftIcon,
|
||||||
|
ChevronRightIcon,
|
||||||
|
LinkExternalIcon,
|
||||||
|
} from '@primer/octicons-react'
|
||||||
|
import { useMainContext } from 'components/context/MainContext'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
import { Link } from 'components/Link'
|
||||||
|
import { GHESReleaseNotesContextT } from './types'
|
||||||
|
import { GHESReleaseNotePatch } from './GHESReleaseNotePatch'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
context: GHESReleaseNotesContextT
|
||||||
|
}
|
||||||
|
export function GHESReleaseNotes({ context }: Props) {
|
||||||
|
const { currentLanguage, currentProduct } = useMainContext()
|
||||||
|
const [focusedPatch, setFocusedPatch] = useState('')
|
||||||
|
const {
|
||||||
|
prevRelease,
|
||||||
|
nextRelease,
|
||||||
|
latestPatch,
|
||||||
|
latestRelease,
|
||||||
|
currentVersion,
|
||||||
|
releaseNotes,
|
||||||
|
releases,
|
||||||
|
message,
|
||||||
|
} = context
|
||||||
|
return (
|
||||||
|
<div className="d-flex">
|
||||||
|
<article className="min-width-0 flex-1">
|
||||||
|
<div className="d-flex flex-items-center flex-justify-between color-bg-primary text-bold px-5 py-2">
|
||||||
|
{prevRelease ? (
|
||||||
|
<Link
|
||||||
|
className="btn btn-outline"
|
||||||
|
href={`/${currentLanguage}/${currentVersion.plan}@${prevRelease}/${currentProduct}/release-notes`}
|
||||||
|
>
|
||||||
|
<ChevronLeftIcon /> {prevRelease}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<div />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<h1 className="f4 py-3 m-0">
|
||||||
|
{currentVersion.planTitle} {currentVersion.currentRelease} release notes
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{nextRelease ? (
|
||||||
|
<Link
|
||||||
|
className="btn btn-outline"
|
||||||
|
href={`/${currentLanguage}/${currentVersion.plan}@${nextRelease}/${currentProduct}/release-notes`}
|
||||||
|
>
|
||||||
|
{nextRelease} <ChevronRightIcon />
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<div />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="markdown-body">
|
||||||
|
{releaseNotes.map((patch) => {
|
||||||
|
return (
|
||||||
|
<GHESReleaseNotePatch
|
||||||
|
key={patch.version}
|
||||||
|
patch={patch}
|
||||||
|
currentVersion={currentVersion}
|
||||||
|
latestPatch={latestPatch}
|
||||||
|
latestRelease={latestRelease}
|
||||||
|
message={message}
|
||||||
|
didEnterView={() => {
|
||||||
|
setFocusedPatch(patch.version)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<aside
|
||||||
|
className="markdown-body position-sticky top-0 d-none d-md-block border-left no-print color-bg-primary flex-shrink-0"
|
||||||
|
style={{ width: 260, height: '100vh' }}
|
||||||
|
>
|
||||||
|
<nav className="height-full overflow-auto">
|
||||||
|
<ul className="list-style-none pl-0 text-bold">
|
||||||
|
{releases.map((release) => {
|
||||||
|
const releaseLink = `/${currentLanguage}/${currentVersion.plan}@${release.version}/${currentProduct?.id}/release-notes`
|
||||||
|
|
||||||
|
if (!release.patches || release.patches.length === 0) {
|
||||||
|
return (
|
||||||
|
<li key={release.version} className="border-bottom">
|
||||||
|
<Link
|
||||||
|
href={releaseLink}
|
||||||
|
className="Link--primary no-underline px-3 py-4 my-0 d-flex flex-items-center flex-justify-between"
|
||||||
|
>
|
||||||
|
{release.version}
|
||||||
|
<LinkExternalIcon />
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (release.version === currentVersion.currentRelease) {
|
||||||
|
return (
|
||||||
|
<li key={release.version} className="border-bottom">
|
||||||
|
<details
|
||||||
|
className="my-0 details-reset release-notes-version-picker"
|
||||||
|
aria-current="page"
|
||||||
|
open
|
||||||
|
>
|
||||||
|
<summary className="px-3 py-4 my-0 d-flex flex-items-center flex-justify-between">
|
||||||
|
{release.version}
|
||||||
|
<div className="d-flex">
|
||||||
|
<span className="color-text-tertiary text-mono text-small text-normal mr-1">
|
||||||
|
{release.patches.length} releases
|
||||||
|
</span>
|
||||||
|
<ChevronDownIcon />
|
||||||
|
</div>
|
||||||
|
</summary>
|
||||||
|
<ul className="color-bg-tertiary border-top list-style-none py-4 px-0 my-0">
|
||||||
|
{release.patches.map((patch) => {
|
||||||
|
const isActive = patch.version === focusedPatch
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
key={patch.version}
|
||||||
|
className={cx(
|
||||||
|
'js-release-notes-patch-link px-3 my-0 py-1',
|
||||||
|
isActive && 'selected'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
href={`${releaseLink}#${patch.version}`}
|
||||||
|
className="d-flex flex-items-center flex-justify-between"
|
||||||
|
>
|
||||||
|
{patch.version}
|
||||||
|
<span className="color-text-tertiary text-mono text-small text-normal">
|
||||||
|
{dayjs(patch.date).format('MMMM DD, YYYY')}
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={release.version} className="border-bottom">
|
||||||
|
<Link
|
||||||
|
className="px-3 py-4 my-0 d-flex flex-items-center flex-justify-between"
|
||||||
|
href={releaseLink}
|
||||||
|
>
|
||||||
|
{release.version}
|
||||||
|
<span className="color-text-tertiary text-mono text-small text-normal mr-1">
|
||||||
|
{release.patches.length} releases
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
84
components/release-notes/PatchNotes.tsx
Normal file
84
components/release-notes/PatchNotes.tsx
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import cx from 'classnames'
|
||||||
|
import slugger from 'github-slugger'
|
||||||
|
import { ReleaseNotePatch } from './types'
|
||||||
|
import { Link } from 'components/Link'
|
||||||
|
|
||||||
|
const SectionToLabelMap: Record<string, string> = {
|
||||||
|
features: 'Features',
|
||||||
|
bugs: 'Bug fixes',
|
||||||
|
known_issues: 'Known issues',
|
||||||
|
security_fixes: 'Security fixes',
|
||||||
|
changes: 'Changes',
|
||||||
|
deprecations: 'Deprecations',
|
||||||
|
backups: 'Backups',
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
patch: ReleaseNotePatch
|
||||||
|
withReleaseNoteLabel?: boolean
|
||||||
|
}
|
||||||
|
export function PatchNotes({ patch, withReleaseNoteLabel }: Props) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{Object.entries(patch.sections).map(([key, sectionItems], i, arr) => {
|
||||||
|
const isLast = i === arr.length - 1
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={key}
|
||||||
|
className={cx(
|
||||||
|
`release-notes-section-${key}`,
|
||||||
|
'py-6 d-block d-xl-flex gutter-xl flex-items-baseline',
|
||||||
|
!withReleaseNoteLabel && 'mx-6',
|
||||||
|
!isLast && 'border-bottom'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{withReleaseNoteLabel && (
|
||||||
|
<div className="col-12 col-xl-3 mb-5">
|
||||||
|
<span className="px-3 py-2 text-small text-bold text-uppercase text-mono color-text-inverse release-notes-section-label">
|
||||||
|
{SectionToLabelMap[key] || 'INVALID SECTION'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<ul className={cx(withReleaseNoteLabel && 'col-xl-9', 'col-12 release-notes-list')}>
|
||||||
|
{sectionItems.map((item) => {
|
||||||
|
if (typeof item === 'string') {
|
||||||
|
return (
|
||||||
|
<li key={item} className="release-notes-list-item">
|
||||||
|
<span dangerouslySetInnerHTML={{ __html: item }} />
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const slug = item.heading ? slugger.slug(item.heading) : ''
|
||||||
|
return (
|
||||||
|
<li key={slug} className="release-notes-list-item list-style-none">
|
||||||
|
<h4
|
||||||
|
id={slug}
|
||||||
|
className="release-notes-section-heading text-uppercase text-bold"
|
||||||
|
>
|
||||||
|
<Link href={`#${slug}`} className="text-inherit">
|
||||||
|
{item.heading}
|
||||||
|
</Link>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<ul className="pl-0 pb-4 mt-2 release-notes-list">
|
||||||
|
{item.notes.map((note) => {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
key={note}
|
||||||
|
className="release-notes-list-item"
|
||||||
|
dangerouslySetInnerHTML={{ __html: note }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
49
components/release-notes/types.ts
Normal file
49
components/release-notes/types.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
export type CurrentVersion = {
|
||||||
|
plan: string
|
||||||
|
planTitle: string
|
||||||
|
versionTitle: string
|
||||||
|
currentRelease: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GHESMessage = {
|
||||||
|
ghes_release_notes_upgrade_patch_only: string
|
||||||
|
ghes_release_notes_upgrade_release_only: string
|
||||||
|
ghes_release_notes_upgrade_patch_and_release: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReleaseNoteSection =
|
||||||
|
| {
|
||||||
|
heading?: string
|
||||||
|
notes: Array<string>
|
||||||
|
}
|
||||||
|
| string
|
||||||
|
|
||||||
|
export type ReleaseNotePatch = {
|
||||||
|
patchVersion: string
|
||||||
|
version: string
|
||||||
|
downloadVersion: string
|
||||||
|
intro: string
|
||||||
|
date: string
|
||||||
|
friendlyDate: string
|
||||||
|
title: string
|
||||||
|
release_candidate?: boolean
|
||||||
|
currentWeek: boolean
|
||||||
|
sections: Record<string, Array<ReleaseNoteSection>>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GHAEReleaseNotesContextT = {
|
||||||
|
releaseNotes: Array<ReleaseNotePatch>
|
||||||
|
releases: Array<{ version: string; patches: Array<ReleaseNotePatch> }>
|
||||||
|
currentVersion: CurrentVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GHESReleaseNotesContextT = {
|
||||||
|
latestPatch: string
|
||||||
|
prevRelease?: string
|
||||||
|
nextRelease?: string
|
||||||
|
latestRelease: string
|
||||||
|
currentVersion: CurrentVersion
|
||||||
|
releaseNotes: Array<ReleaseNotePatch>
|
||||||
|
releases: Array<{ version: string; patches: Array<ReleaseNotePatch> }>
|
||||||
|
message: GHESMessage
|
||||||
|
}
|
||||||
@@ -20,9 +20,9 @@ header:
|
|||||||
early_access: 📣 Please <b>do not share</b> this URL publicly. This page contains content about an early access feature.
|
early_access: 📣 Please <b>do not share</b> this URL publicly. This page contains content about an early access feature.
|
||||||
release_notes_use_latest: Please use the latest release for the latest security, performance, and bug fixes.
|
release_notes_use_latest: Please use the latest release for the latest security, performance, and bug fixes.
|
||||||
# GHES release notes
|
# GHES release notes
|
||||||
ghes_release_notes_upgrade_patch_only: 📣 This is not the <a href="#{{ latestPatch }}">latest patch release</a> of Enterprise Server. {% data ui.header.notices.release_notes_use_latest %}
|
ghes_release_notes_upgrade_patch_only: 📣 This is not the <a href="#{{ latestPatch }}">latest patch release</a> of Enterprise Server.
|
||||||
ghes_release_notes_upgrade_release_only: 📣 This is not the <a href="/enterprise-server@{{ latestRelease }}/admin/release-notes">latest release</a> of Enterprise Server. {% data ui.header.notices.release_notes_use_latest %}
|
ghes_release_notes_upgrade_release_only: 📣 This is not the <a href="/enterprise-server@{{ latestRelease }}/admin/release-notes">latest release</a> of Enterprise Server.
|
||||||
ghes_release_notes_upgrade_patch_and_release: 📣 This is not the <a href="#{{ latestPatch }}">latest patch release</a> of this release series, and this is not the <a href="/enterprise-server@{{ latestRelease }}/admin/release-notes">latest release</a> of Enterprise Server. {% data ui.header.notices.release_notes_use_latest %}
|
ghes_release_notes_upgrade_patch_and_release: 📣 This is not the <a href="#{{ latestPatch }}">latest patch release</a> of this release series, and this is not the <a href="/enterprise-server@{{ latestRelease }}/admin/release-notes">latest release</a> of Enterprise Server.
|
||||||
release_notes:
|
release_notes:
|
||||||
banner_text_current: These changes will roll out over the next one week.
|
banner_text_current: These changes will roll out over the next one week.
|
||||||
banner_text_past: These changes rolled out to enterprises during the week of
|
banner_text_past: These changes rolled out to enterprises during the week of
|
||||||
|
|||||||
@@ -54,15 +54,15 @@
|
|||||||
<p class="color-text-secondary mt-1">{{ patch.date | date: "%B %d, %Y" }}</p>
|
<p class="color-text-secondary mt-1">{{ patch.date | date: "%B %d, %Y" }}</p>
|
||||||
|
|
||||||
{% if patch.version != latestPatch and currentVersionObject.currentRelease == latestRelease %}
|
{% if patch.version != latestPatch and currentVersionObject.currentRelease == latestRelease %}
|
||||||
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_patch_only %}</p>
|
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_patch_only %} {% data ui.header.notices.release_notes_use_latest %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if patch.version == latestPatch and currentVersionObject.currentRelease != latestRelease %}
|
{% if patch.version == latestPatch and currentVersionObject.currentRelease != latestRelease %}
|
||||||
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_release_only %}</p>
|
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_release_only %} {% data ui.header.notices.release_notes_use_latest %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if patch.version != latestPatch and currentVersionObject.currentRelease != latestRelease %}
|
{% if patch.version != latestPatch and currentVersionObject.currentRelease != latestRelease %}
|
||||||
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_patch_and_release %}</p>
|
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_patch_and_release %} {% data ui.header.notices.release_notes_use_latest %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export default function releaseNotes () {
|
export default function releaseNotes () {
|
||||||
|
if (window.next) return
|
||||||
const patches = Array.from(document.querySelectorAll('.js-release-notes-patch'))
|
const patches = Array.from(document.querySelectorAll('.js-release-notes-patch'))
|
||||||
if (patches.length === 0) return
|
if (patches.length === 0) return
|
||||||
|
|
||||||
|
|||||||
13
package-lock.json
generated
13
package-lock.json
generated
@@ -93,6 +93,7 @@
|
|||||||
"@graphql-inspector/core": "^2.5.0",
|
"@graphql-inspector/core": "^2.5.0",
|
||||||
"@graphql-tools/load": "^6.2.8",
|
"@graphql-tools/load": "^6.2.8",
|
||||||
"@octokit/rest": "^18.5.3",
|
"@octokit/rest": "^18.5.3",
|
||||||
|
"@types/github-slugger": "^1.3.0",
|
||||||
"@types/lodash": "^4.14.169",
|
"@types/lodash": "^4.14.169",
|
||||||
"@types/react": "^17.0.6",
|
"@types/react": "^17.0.6",
|
||||||
"@types/react-dom": "^17.0.5",
|
"@types/react-dom": "^17.0.5",
|
||||||
@@ -4040,6 +4041,12 @@
|
|||||||
"integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==",
|
"integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/github-slugger": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/github-slugger/-/github-slugger-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-J/rMZa7RqiH/rT29TEVZO4nBoDP9XJOjnbbIofg7GQKs4JIduEO3WLpte+6WeUz/TcrXKlY+bM7FYrp8yFB+3g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/graceful-fs": {
|
"node_modules/@types/graceful-fs": {
|
||||||
"version": "4.1.5",
|
"version": "4.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
|
||||||
@@ -27663,6 +27670,12 @@
|
|||||||
"integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==",
|
"integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
|
"@types/github-slugger": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/github-slugger/-/github-slugger-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-J/rMZa7RqiH/rT29TEVZO4nBoDP9XJOjnbbIofg7GQKs4JIduEO3WLpte+6WeUz/TcrXKlY+bM7FYrp8yFB+3g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/graceful-fs": {
|
"@types/graceful-fs": {
|
||||||
"version": "4.1.5",
|
"version": "4.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
|
||||||
|
|||||||
@@ -99,6 +99,7 @@
|
|||||||
"@graphql-inspector/core": "^2.5.0",
|
"@graphql-inspector/core": "^2.5.0",
|
||||||
"@graphql-tools/load": "^6.2.8",
|
"@graphql-tools/load": "^6.2.8",
|
||||||
"@octokit/rest": "^18.5.3",
|
"@octokit/rest": "^18.5.3",
|
||||||
|
"@types/github-slugger": "^1.3.0",
|
||||||
"@types/lodash": "^4.14.169",
|
"@types/lodash": "^4.14.169",
|
||||||
"@types/react": "^17.0.6",
|
"@types/react": "^17.0.6",
|
||||||
"@types/react-dom": "^17.0.5",
|
"@types/react-dom": "^17.0.5",
|
||||||
|
|||||||
81
pages/[versionId]/admin/release-notes.tsx
Normal file
81
pages/[versionId]/admin/release-notes.tsx
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { GetServerSideProps } from 'next'
|
||||||
|
import { Liquid } from 'liquidjs'
|
||||||
|
|
||||||
|
const liquid = new Liquid()
|
||||||
|
|
||||||
|
import {
|
||||||
|
MainContextT,
|
||||||
|
MainContext,
|
||||||
|
getMainContextFromRequest,
|
||||||
|
} from 'components/context/MainContext'
|
||||||
|
import { DefaultLayout } from 'components/DefaultLayout'
|
||||||
|
import { GHAEReleaseNotes } from 'components/release-notes/GHAEReleaseNotes'
|
||||||
|
import { GHESReleaseNotes } from 'components/release-notes/GHESReleaseNotes'
|
||||||
|
import {
|
||||||
|
CurrentVersion,
|
||||||
|
GHAEReleaseNotesContextT,
|
||||||
|
GHESReleaseNotesContextT,
|
||||||
|
} from 'components/release-notes/types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
mainContext: MainContextT
|
||||||
|
ghaeContext: GHAEReleaseNotesContextT
|
||||||
|
ghesContext: GHESReleaseNotesContextT
|
||||||
|
currentVersion: CurrentVersion
|
||||||
|
}
|
||||||
|
export default function ReleaseNotes({
|
||||||
|
mainContext,
|
||||||
|
ghesContext,
|
||||||
|
ghaeContext,
|
||||||
|
currentVersion,
|
||||||
|
}: Props) {
|
||||||
|
return (
|
||||||
|
<MainContext.Provider value={mainContext}>
|
||||||
|
<DefaultLayout>
|
||||||
|
{currentVersion.plan === 'enterprise-server' && <GHESReleaseNotes context={ghesContext} />}
|
||||||
|
|
||||||
|
{currentVersion.plan === 'github-ae' && <GHAEReleaseNotes context={ghaeContext} />}
|
||||||
|
</DefaultLayout>
|
||||||
|
</MainContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
||||||
|
const req = context.req as any
|
||||||
|
const currentVersion = req.context.allVersions[req.context.currentVersion]
|
||||||
|
const { latestPatch = '', latestRelease = '' } = req.context
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
mainContext: getMainContextFromRequest(req),
|
||||||
|
currentVersion,
|
||||||
|
ghesContext: {
|
||||||
|
currentVersion,
|
||||||
|
latestPatch,
|
||||||
|
latestRelease,
|
||||||
|
prevRelease: req.context.prevRelease || '',
|
||||||
|
nextRelease: req.context.nextRelease || '',
|
||||||
|
releaseNotes: req.context.releaseNotes,
|
||||||
|
releases: req.context.releases,
|
||||||
|
message: {
|
||||||
|
ghes_release_notes_upgrade_patch_only: liquid.parseAndRenderSync(
|
||||||
|
req.context.site.data.ui.header.notices.ghes_release_notes_upgrade_patch_only,
|
||||||
|
{ latestPatch, latestRelease }
|
||||||
|
),
|
||||||
|
ghes_release_notes_upgrade_release_only: liquid.parseAndRenderSync(
|
||||||
|
req.context.site.data.ui.header.notices.ghes_release_notes_upgrade_release_only,
|
||||||
|
{ latestPatch, latestRelease }
|
||||||
|
),
|
||||||
|
ghes_release_notes_upgrade_patch_and_release: liquid.parseAndRenderSync(
|
||||||
|
req.context.site.data.ui.header.notices.ghes_release_notes_upgrade_patch_and_release,
|
||||||
|
{ latestPatch, latestRelease }
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ghaeContext: {
|
||||||
|
currentVersion,
|
||||||
|
releaseNotes: req.context.releaseNotes,
|
||||||
|
releases: req.context.releases,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules"],
|
"exclude": ["node_modules"],
|
||||||
"include": ["*.d.ts", "**/*.ts", "**/*.tsx"]
|
"include": ["*.d.ts", "**/*.ts", "**/*.tsx"]
|
||||||
|
|||||||
Reference in New Issue
Block a user