-
-
Notifications
You must be signed in to change notification settings - Fork 6.4k
feat: add eol page #7990
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: add eol page #7990
Changes from 23 commits
02db91b
113334f
6bc1393
967676d
5cd1b33
d2b7826
b294b44
4204159
2cadb0c
55abd69
1a4a53a
51c5fb0
ea43096
d94d5ce
1dccbd5
21014bd
ff6ddec
9c68dd0
f8cab5e
2fbd30a
7babcc8
07befbb
594531f
c4abb0e
e471cb0
6b7bde7
2a748fd
e0f7c44
5ab2cbb
26f5b81
16f5992
ccbba0f
40df1ab
f424d53
7724afb
669d21d
04e4f5c
89e5c92
748e116
9fe21cc
4836bef
c147efc
847897b
d61bd30
1a522c8
dd10604
fb31b90
6957f21
1c499dd
1a7a80a
029ee7d
54fe024
9a8c6c7
92cdd9a
f579c6f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,24 +5,20 @@ import type { FC } from 'react'; | |
import { use } from 'react'; | ||
|
||
import LinkWithArrow from '#site/components/LinkWithArrow'; | ||
import { ReleaseModalContext } from '#site/providers/releaseModalProvider'; | ||
import type { NodeRelease } from '#site/types'; | ||
import { ModalContext } from '#site/providers/modalProvider'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Context's should be imported from a hook. Like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And these should be explicitly defined as client-side hooks, so that if imported from server it fails. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the previous implementation, a "useReleaseModalContext" hook wasn't in use, this follows that principle There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, but in general contexts are provided in Hooks. Can we do that please? |
||
|
||
type DetailsButtonProps = { | ||
versionData: NodeRelease; | ||
data: unknown; | ||
}; | ||
|
||
const DetailsButton: FC<DetailsButtonProps> = ({ versionData }) => { | ||
const t = useTranslations('components.downloadReleasesTable'); | ||
const DetailsButton: FC<DetailsButtonProps> = ({ data }) => { | ||
const t = useTranslations(); | ||
|
||
const { openModal } = use(ReleaseModalContext); | ||
const { openModal } = use(ModalContext); | ||
|
||
return ( | ||
<LinkWithArrow | ||
className="cursor-pointer" | ||
onClick={() => openModal(versionData)} | ||
> | ||
{t('details')} | ||
<LinkWithArrow className="cursor-pointer" onClick={() => openModal(data)}> | ||
bmuenzenmeyer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{t('components.downloadReleasesTable.details')} | ||
</LinkWithArrow> | ||
); | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import AlertBox from '@node-core/ui-components/Common/AlertBox'; | ||
import { useTranslations } from 'next-intl'; | ||
|
||
import Link from '#site/components/Link'; | ||
|
||
const EOLAlert = () => { | ||
const t = useTranslations(); | ||
return ( | ||
<AlertBox level="warning"> | ||
{t('components.eolAlert.intro')}{' '} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually this whole thing could be an i18n string. And you pass the |
||
<Link href="/eol"> | ||
OpenJS Ecosystem Sustainability Program{' '} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This whole thing should be an i18n string. Even more due to RTL languages. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as I mentioned in my self-review, I did not make this an i18n string. It's a proper noun, like we should not translate the phrase There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're misunderstanding my intent here. This should be on a translation string. Doesn't mean it should be translated. You can blocklist the word OpenJS Foundation on Crowdin to not be translated. Or whatever other words you need. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The core here is: Putting the whole alertbox on the i18n makes it way easier for translation of the affected pieces. |
||
{t('components.eolAlert.partner')} HeroDevs | ||
</Link> | ||
</AlertBox> | ||
); | ||
}; | ||
|
||
export default EOLAlert; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { Modal, Title, Content } from '@node-core/ui-components/Common/Modal'; | ||
import { useTranslations } from 'next-intl'; | ||
import type { FC } from 'react'; | ||
|
||
import UnknownSeveritySection from '#site/components/EOL/UnknownSeveritySection'; | ||
import VulnerabilitiesTable from '#site/components/EOL/VulnerabilitiesTable'; | ||
import { SEVERITY_ORDER } from '#site/components/EOL/VulnerabilityChips'; | ||
import type { ModalProps } from '#site/providers/modalProvider'; | ||
import type { NodeRelease } from '#site/types'; | ||
import type { Vulnerability } from '#site/types/vulnerabilities'; | ||
|
||
type EOLModalData = { | ||
release: NodeRelease; | ||
vulnerabilities: Array<Vulnerability>; | ||
}; | ||
|
||
type KnownVulnerability = Vulnerability & { | ||
severity: (typeof SEVERITY_ORDER)[number]; | ||
}; | ||
|
||
const EOLModal: FC<ModalProps> = ({ open, closeModal, data }) => { | ||
const { release, vulnerabilities } = data as EOLModalData; | ||
avivkeller marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const t = useTranslations(); | ||
|
||
const modalHeading = t( | ||
bmuenzenmeyer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
release.codename | ||
? 'components.eolModal.title' | ||
: 'components.eolModal.titleWithoutCodename', | ||
{ | ||
version: release.major, | ||
codename: release.codename ?? '', | ||
} | ||
); | ||
|
||
const [knownVulns, unknownVulns] = vulnerabilities.reduce( | ||
bmuenzenmeyer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
(acc, vuln) => { | ||
acc[vuln.severity === 'unknown' ? 1 : 0].push(vuln as KnownVulnerability); | ||
return acc; | ||
}, | ||
[[], []] as [Array<KnownVulnerability>, Array<Vulnerability>] | ||
); | ||
|
||
knownVulns.sort( | ||
(a, b) => | ||
SEVERITY_ORDER.indexOf(a.severity) - SEVERITY_ORDER.indexOf(b.severity) | ||
); | ||
|
||
const hasKnownVulns = knownVulns.length > 0; | ||
const hasAnyVulns = hasKnownVulns || unknownVulns.length > 0; | ||
|
||
return ( | ||
<Modal open={open} onOpenChange={closeModal}> | ||
<Title>{modalHeading}</Title> | ||
<Content> | ||
{vulnerabilities.length > 0 && ( | ||
<p className="m-1"> | ||
{t('components.eolModal.vulnerabilitiesMessage', { | ||
count: vulnerabilities.length, | ||
})} | ||
</p> | ||
)} | ||
|
||
{hasKnownVulns && <VulnerabilitiesTable vulnerabilities={knownVulns} />} | ||
|
||
<UnknownSeveritySection | ||
vulnerabilities={unknownVulns} | ||
hasKnownVulns={hasKnownVulns} | ||
/> | ||
|
||
{!hasAnyVulns && ( | ||
<p className="m-1"> | ||
{t('components.eolModal.noVulnerabilitiesMessage')} | ||
</p> | ||
)} | ||
</Content> | ||
</Modal> | ||
); | ||
}; | ||
|
||
export default EOLModal; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { useTranslations } from 'next-intl'; | ||
import type { FC } from 'react'; | ||
|
||
import FormattedTime from '#site/components/Common/FormattedTime'; | ||
import DetailsButton from '#site/components/Downloads/DownloadReleasesTable/DetailsButton'; | ||
import provideReleaseData from '#site/next-data/providers/releaseData'; | ||
import provideVulnerabilities from '#site/next-data/providers/vulnerabilities'; | ||
|
||
import VulnerabilityChips from '../VulnerabilityChips'; | ||
|
||
const EOLReleaseTable: FC = () => { | ||
const releaseData = provideReleaseData(); | ||
const vulnerabilities = provideVulnerabilities(); | ||
const eolReleases = releaseData.filter( | ||
release => release.status === 'End-of-life' | ||
); | ||
|
||
const t = useTranslations(); | ||
|
||
return ( | ||
<table id="tbVulnerabilities"> | ||
<thead> | ||
<tr> | ||
<th> | ||
{t('components.eolTable.version')} ( | ||
{t('components.eolTable.codename')}) | ||
</th> | ||
<th>{t('components.eolTable.lastUpdated')}</th> | ||
<th>{t('components.eolTable.vulnerabilities')}</th> | ||
<th>{t('components.eolTable.details')}</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{eolReleases.map(release => ( | ||
<tr key={release.major}> | ||
<td data-label="Version"> | ||
v{release.major} {release.codename ? `(${release.codename})` : ''} | ||
</td> | ||
<td data-label="Date"> | ||
<FormattedTime date={release.releaseDate} /> | ||
</td> | ||
<td> | ||
<VulnerabilityChips | ||
vulnerabilities={vulnerabilities[release.major]} | ||
/> | ||
</td> | ||
<td> | ||
<DetailsButton | ||
data={{ | ||
release: release, | ||
vulnerabilities: vulnerabilities[release.major], | ||
}} | ||
/> | ||
</td> | ||
</tr> | ||
))} | ||
</tbody> | ||
</table> | ||
); | ||
}; | ||
|
||
export default EOLReleaseTable; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { useTranslations } from 'next-intl'; | ||
import type { FC } from 'react'; | ||
|
||
import VulnerabilitiesTable from '#site/components/EOL/VulnerabilitiesTable'; | ||
import type { Vulnerability } from '#site/types/vulnerabilities'; | ||
|
||
const UnknownSeveritySection: FC<{ | ||
bmuenzenmeyer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
vulnerabilities: Array<Vulnerability>; | ||
hasKnownVulns: boolean; | ||
}> = ({ vulnerabilities, hasKnownVulns }) => { | ||
const t = useTranslations(); | ||
|
||
if (!vulnerabilities.length) { | ||
avivkeller marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return null; | ||
} | ||
|
||
return ( | ||
<details open={!hasKnownVulns}> | ||
<summary className="cursor-pointer font-semibold"> | ||
{t('components.eolModal.showUnknownSeverities')} ( | ||
{vulnerabilities.length}) | ||
</summary> | ||
<div className="mt-4"> | ||
<VulnerabilitiesTable | ||
vulnerabilities={vulnerabilities} | ||
maxWidth={'max-w-3xs'} | ||
/> | ||
</div> | ||
</details> | ||
); | ||
}; | ||
|
||
export default UnknownSeveritySection; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like this approach of modal metadata to be injected at layout-level, it implies you cannnot have different sort of modals per page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even less making it part of the frontmatter, which is even worse IMO.