-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
feat: add toggle to show/hide LTS releases #8263
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?
Changes from all commits
b37d970
680b0ca
6f7ab98
bdf5ce9
aebe830
4502c6c
f530878
39eb665
fee09ef
a0ae7e0
8cbd84a
d73475e
ce14ae6
a90d6ce
52fd77b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| 'use client'; | ||
avivkeller marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| import Switch from '@node-core/ui-components/Common/Switch'; | ||
| import { useTranslations } from 'next-intl'; | ||
| import type { FC } from 'react'; | ||
| import { Fragment, useState } from 'react'; | ||
|
|
||
| import FormattedTime from '#site/components/Common/FormattedTime'; | ||
| import LinkWithArrow from '#site/components/Common/LinkWithArrow'; | ||
| import EOLModal from '#site/components/EOL/EOLModal'; | ||
| import VulnerabilityChips from '#site/components/EOL/VulnerabilityChips'; | ||
| import type { NodeRelease } from '#site/types/releases.js'; | ||
| import type { GroupedVulnerabilities } from '#site/types/vulnerabilities.js'; | ||
|
|
||
| type EOLReleaseTableBodyProps = { | ||
| eolReleases: Array<NodeRelease>; | ||
| vulnerabilities: GroupedVulnerabilities; | ||
| }; | ||
|
|
||
| const EOLReleaseTableClient: FC<EOLReleaseTableBodyProps> = ({ | ||
| eolReleases, | ||
| vulnerabilities, | ||
| }) => { | ||
| const t = useTranslations(); | ||
|
|
||
| const [currentModal, setCurrentModal] = useState<string | undefined>(); | ||
| const [hideNonLts, setHideNonLts] = useState(false); | ||
|
|
||
| const filteredReleases = hideNonLts | ||
| ? eolReleases.filter(release => release.isLts) | ||
| : eolReleases; | ||
|
|
||
| return ( | ||
| <> | ||
| <Switch | ||
|
Member
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. My gut feeling tells me you can do a pure HTML switch with CSS and have the table being hidden purely with CSS. Can you please do that? On this website we want to avoid feature-gating behind JavaScript. |
||
| label={t('components.eolTable.hideNonLts')} | ||
| checked={hideNonLts} | ||
| onCheckedChange={setHideNonLts} | ||
| /> | ||
| <table id="tbVulnerabilities"> | ||
|
Member
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. Why did you move this here and rename the file? Could you please revert this change? :) |
||
| <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> | ||
| {filteredReleases.map(release => ( | ||
| <Fragment key={release.major}> | ||
| <tr> | ||
| <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> | ||
| <LinkWithArrow | ||
| className="cursor-pointer" | ||
| onClick={() => setCurrentModal(release.version)} | ||
| > | ||
| {t('components.downloadReleasesTable.details')} | ||
| </LinkWithArrow> | ||
| </td> | ||
| </tr> | ||
|
|
||
| <EOLModal | ||
| release={release} | ||
| vulnerabilities={vulnerabilities[release.major]} | ||
| open={currentModal === release.version} | ||
| onOpenChange={open => open || setCurrentModal(undefined)} | ||
| /> | ||
| </Fragment> | ||
| ))} | ||
| </tbody> | ||
| </table> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default EOLReleaseTableClient; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,7 +63,7 @@ const generateReleaseData = async () => { | |
| version: latestVersion.semver.raw, | ||
| versionWithPrefix: `v${latestVersion.semver.raw}`, | ||
| codename: major.support.codename || '', | ||
| isLts: status.endsWith('LTS'), | ||
| isLts: support.ltsStart !== undefined, | ||
avivkeller marked this conversation as resolved.
Show resolved
Hide resolved
avivkeller marked this conversation as resolved.
Show resolved
Hide resolved
Member
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 has unwanted side-effects. This flag isn't used to say if a version is a LTS version-line or not, but if it is currently LTS. Please revert this change and use the codename as a field to verify a release line is a LTS release line. |
||
| npm: latestVersion.dependencies.npm || '', | ||
| v8: latestVersion.dependencies.v8, | ||
| releaseDate: latestVersion.releaseDate, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| @reference "../../styles/index.css"; | ||
|
|
||
| .switch { | ||
| @apply inline-flex | ||
| justify-end | ||
| gap-3; | ||
|
|
||
| .label { | ||
| @apply cursor-pointer | ||
| select-none | ||
| text-sm | ||
| font-medium | ||
| text-neutral-800 | ||
| dark:text-neutral-200; | ||
| } | ||
|
|
||
| .root { | ||
| @apply w-10.5 | ||
| relative | ||
| inline-flex | ||
| h-6 | ||
| cursor-pointer | ||
| items-center | ||
| rounded-full | ||
| bg-black | ||
| focus:outline-none | ||
| focus-visible:ring-2 | ||
| focus-visible:ring-green-500 | ||
| focus-visible:ring-offset-2 | ||
| focus-visible:ring-offset-neutral-100 | ||
| motion-safe:transition-colors | ||
| motion-safe:duration-100 | ||
| motion-safe:ease-out | ||
| dark:bg-neutral-700 | ||
| dark:focus-visible:ring-green-400 | ||
| dark:focus-visible:ring-offset-neutral-900; | ||
| } | ||
|
|
||
| .root[data-state='checked'] { | ||
| @apply bg-green-600; | ||
| } | ||
|
|
||
| .thumb { | ||
| @apply pointer-events-none | ||
| block | ||
| size-5 | ||
| translate-x-0.5 | ||
| rounded-full | ||
| bg-white | ||
| ring-0 | ||
| motion-safe:transition-transform | ||
| motion-safe:duration-100 | ||
| motion-safe:ease-out; | ||
| } | ||
|
|
||
| .thumb[data-state='checked'] { | ||
| @apply translate-x-5; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import type { Meta as MetaObj, StoryObj } from '@storybook/react'; | ||
| import { useState } from 'react'; | ||
|
|
||
| import Switch from '#ui/Common/Switch'; | ||
|
|
||
| type Story = StoryObj<typeof Switch>; | ||
| type Meta = MetaObj<typeof Switch>; | ||
|
|
||
| export const Uncontrolled: Story = { | ||
| args: { | ||
| label: 'Enable Feature', | ||
| }, | ||
| }; | ||
|
|
||
| export const Controlled: Story = { | ||
| args: { | ||
| label: 'Enable Feature', | ||
| }, | ||
| render: args => { | ||
| const [checked, setChecked] = useState(false); | ||
|
|
||
| return <Switch {...args} checked={checked} onCheckedChange={setChecked} />; | ||
| }, | ||
| }; | ||
|
|
||
| export const WithoutLabel: Story = {}; | ||
|
|
||
| export default { | ||
| component: Switch, | ||
| parameters: { | ||
| layout: 'centered', | ||
| }, | ||
| } as Meta; |
bmuenzenmeyer marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| 'use client'; | ||
|
Member
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 much as I love Radix! I feel we should make the switch purely with CSS and HTML and have the table entries be affected based on that. |
||
|
|
||
| import * as SwitchPrimitive from '@radix-ui/react-switch'; | ||
| import classNames from 'classnames'; | ||
| import { useId } from 'react'; | ||
| import type { FC, PropsWithChildren } from 'react'; | ||
|
|
||
| import styles from './index.module.css'; | ||
|
|
||
| type SwitchProps = SwitchPrimitive.SwitchProps & { | ||
| label?: string; | ||
| checked?: boolean; | ||
| onCheckedChange?: (checked: boolean) => void; | ||
| thumbClassName?: string; | ||
| }; | ||
|
|
||
| const Switch: FC<PropsWithChildren<SwitchProps>> = ({ | ||
| label, | ||
| checked, | ||
| onCheckedChange, | ||
| className, | ||
| thumbClassName, | ||
| ...props | ||
| }) => { | ||
| const id = useId(); | ||
|
|
||
avivkeller marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return ( | ||
| <div className={styles.switch}> | ||
| {label && ( | ||
| <label className={styles.label} htmlFor={id}> | ||
| {label} | ||
| </label> | ||
| )} | ||
| <SwitchPrimitive.Root | ||
| id={id} | ||
| className={classNames(styles.root, className)} | ||
| checked={checked} | ||
| onCheckedChange={onCheckedChange} | ||
| {...props} | ||
| > | ||
| <SwitchPrimitive.Thumb | ||
| className={classNames(styles.thumb, thumbClassName)} | ||
| /> | ||
| </SwitchPrimitive.Root> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default Switch; | ||
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.
This is a very unusual component name. Why TableClient? Because it is a client Component? Unfortunately that's an awful name 😅