Skip to content

Commit b4a78a2

Browse files
committed
feat: add switch in eol table
1 parent e75bc1b commit b4a78a2

File tree

8 files changed

+286
-62
lines changed

8 files changed

+286
-62
lines changed

apps/site/components/EOL/EOLReleaseTable/TableBody.tsx

Lines changed: 60 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use client';
22

3+
import Switch from '@node-core/ui-components/Common/Switch';
34
import { useTranslations } from 'next-intl';
45
import type { FC } from 'react';
56
import { Fragment, useState } from 'react';
@@ -23,45 +24,72 @@ const EOLReleaseTableBody: FC<EOLReleaseTableBodyProps> = ({
2324
const t = useTranslations();
2425

2526
const [currentModal, setCurrentModal] = useState<string | undefined>();
27+
const [hideNonLts, setHideNonLts] = useState(false);
28+
29+
const filteredReleases = hideNonLts
30+
? eolReleases.filter(release => release.isLts)
31+
: eolReleases;
2632

2733
return (
28-
<tbody>
29-
{eolReleases.map(release => (
30-
<Fragment key={release.major}>
34+
<>
35+
<Switch
36+
label={'Hide Non-LTS Releases'}
37+
checked={hideNonLts}
38+
onCheckedChange={() => setHideNonLts(!hideNonLts)}
39+
/>
40+
<table id="tbVulnerabilities">
41+
<thead>
3142
<tr>
32-
<td data-label="Version">
33-
v{release.major} {release.codename ? `(${release.codename})` : ''}
34-
</td>
43+
<th>
44+
{t('components.eolTable.version')} (
45+
{t('components.eolTable.codename')})
46+
</th>
47+
<th>{t('components.eolTable.lastUpdated')}</th>
48+
<th>{t('components.eolTable.vulnerabilities')}</th>
49+
<th>{t('components.eolTable.details')}</th>
50+
</tr>
51+
</thead>
3552

36-
<td data-label="Date">
37-
<FormattedTime date={release.releaseDate} />
38-
</td>
53+
<tbody>
54+
{filteredReleases.map(release => (
55+
<Fragment key={release.major}>
56+
<tr>
57+
<td data-label="Version">
58+
v{release.major}{' '}
59+
{release.codename ? `(${release.codename})` : ''}
60+
</td>
3961

40-
<td>
41-
<VulnerabilityChips
42-
vulnerabilities={vulnerabilities[release.major]}
43-
/>
44-
</td>
62+
<td data-label="Date">
63+
<FormattedTime date={release.releaseDate} />
64+
</td>
4565

46-
<td>
47-
<LinkWithArrow
48-
className="cursor-pointer"
49-
onClick={() => setCurrentModal(release.version)}
50-
>
51-
{t('components.downloadReleasesTable.details')}
52-
</LinkWithArrow>
53-
</td>
54-
</tr>
66+
<td>
67+
<VulnerabilityChips
68+
vulnerabilities={vulnerabilities[release.major]}
69+
/>
70+
</td>
71+
72+
<td>
73+
<LinkWithArrow
74+
className="cursor-pointer"
75+
onClick={() => setCurrentModal(release.version)}
76+
>
77+
{t('components.downloadReleasesTable.details')}
78+
</LinkWithArrow>
79+
</td>
80+
</tr>
5581

56-
<EOLModal
57-
release={release}
58-
vulnerabilities={vulnerabilities[release.major]}
59-
open={currentModal === release.version}
60-
onOpenChange={open => open || setCurrentModal(undefined)}
61-
/>
62-
</Fragment>
63-
))}
64-
</tbody>
82+
<EOLModal
83+
release={release}
84+
vulnerabilities={vulnerabilities[release.major]}
85+
open={currentModal === release.version}
86+
onOpenChange={open => open || setCurrentModal(undefined)}
87+
/>
88+
</Fragment>
89+
))}
90+
</tbody>
91+
</table>
92+
</>
6593
);
6694
};
6795

apps/site/components/EOL/EOLReleaseTable/index.tsx

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { getTranslations } from 'next-intl/server';
21
import type { FC } from 'react';
32

43
import provideReleaseData from '#site/next-data/providers/releaseData';
54
import provideVulnerabilities from '#site/next-data/providers/vulnerabilities';
65
import { EOL_VERSION_IDENTIFIER } from '#site/next.constants.mjs';
76

8-
import EOLReleaseTableBody from './TableBody';
7+
import EOLReleaseTableInner from './TableBody';
98

109
const EOLReleaseTable: FC = async () => {
1110
const releaseData = await provideReleaseData();
@@ -15,27 +14,11 @@ const EOLReleaseTable: FC = async () => {
1514
release => release.status === EOL_VERSION_IDENTIFIER
1615
);
1716

18-
const t = await getTranslations();
19-
2017
return (
21-
<table id="tbVulnerabilities">
22-
<thead>
23-
<tr>
24-
<th>
25-
{t('components.eolTable.version')} (
26-
{t('components.eolTable.codename')})
27-
</th>
28-
<th>{t('components.eolTable.lastUpdated')}</th>
29-
<th>{t('components.eolTable.vulnerabilities')}</th>
30-
<th>{t('components.eolTable.details')}</th>
31-
</tr>
32-
</thead>
33-
34-
<EOLReleaseTableBody
35-
eolReleases={eolReleases}
36-
vulnerabilities={vulnerabilities}
37-
/>
38-
</table>
18+
<EOLReleaseTableInner
19+
eolReleases={eolReleases}
20+
vulnerabilities={vulnerabilities}
21+
/>
3922
);
4023
};
4124

apps/site/next-data/generators/releaseData.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const generateReleaseData = async () => {
6363
version: latestVersion.semver.raw,
6464
versionWithPrefix: `v${latestVersion.semver.raw}`,
6565
codename: major.support.codename || '',
66-
isLts: status.endsWith('LTS'),
66+
isLts: support.ltsStart !== undefined,
6767
npm: latestVersion.dependencies.npm || '',
6868
v8: latestVersion.dependencies.v8,
6969
releaseDate: latestVersion.releaseDate,

packages/ui-components/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@radix-ui/react-label": "~2.1.7",
4343
"@radix-ui/react-select": "~2.2.6",
4444
"@radix-ui/react-separator": "~1.1.7",
45+
"@radix-ui/react-switch": "^1.2.6",
4546
"@radix-ui/react-tabs": "~1.1.13",
4647
"@radix-ui/react-toast": "~1.2.15",
4748
"@radix-ui/react-tooltip": "~1.2.8",
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
@reference "../../styles/index.css";
2+
3+
.switch {
4+
@apply inline-flex
5+
justify-end
6+
gap-3;
7+
8+
.label {
9+
@apply cursor-pointer
10+
select-none
11+
text-sm
12+
font-medium
13+
text-neutral-800
14+
dark:text-neutral-200;
15+
}
16+
17+
.root {
18+
@apply w-10.5
19+
relative
20+
inline-flex
21+
h-6
22+
cursor-pointer
23+
items-center
24+
rounded-full
25+
bg-black
26+
transition-colors
27+
duration-100
28+
ease-out
29+
focus:outline-none
30+
focus-visible:ring-2
31+
focus-visible:ring-green-500
32+
focus-visible:ring-offset-2
33+
focus-visible:ring-offset-neutral-900
34+
dark:bg-neutral-700
35+
dark:focus-visible:ring-green-400;
36+
}
37+
38+
.root[data-state='checked'] {
39+
@apply bg-green-600;
40+
}
41+
42+
.thumb {
43+
@apply pointer-events-none
44+
block
45+
h-5
46+
w-5
47+
translate-x-0.5
48+
rounded-full
49+
bg-white
50+
ring-0
51+
transition-transform
52+
duration-100
53+
ease-out;
54+
}
55+
56+
.thumb[data-state='checked'] {
57+
@apply translate-x-5;
58+
}
59+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { Meta as MetaObj, StoryObj } from '@storybook/react';
2+
import { useState } from 'react';
3+
4+
import Switch from '#ui/Common/Switch';
5+
6+
type Story = StoryObj<typeof Switch>;
7+
type Meta = MetaObj<typeof Switch>;
8+
9+
export const Uncontrolled: Story = {
10+
args: {
11+
label: 'Enable Feature',
12+
},
13+
};
14+
15+
export const Controlled: Story = {
16+
args: {
17+
label: 'Enable Feature',
18+
},
19+
render: args => {
20+
const [checked, setChecked] = useState(false);
21+
22+
return <Switch {...args} checked={checked} onCheckedChange={setChecked} />;
23+
},
24+
};
25+
26+
export const WithoutLabel: Story = {};
27+
28+
export default {
29+
component: Switch,
30+
parameters: {
31+
layout: 'centered',
32+
},
33+
} as Meta;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import * as SwitchPrimitive from '@radix-ui/react-switch';
2+
import classNames from 'classnames';
3+
import { FC, PropsWithChildren, useId } from 'react';
4+
5+
import styles from './index.module.css';
6+
7+
type SwitchProps = SwitchPrimitive.SwitchProps & {
8+
label?: string;
9+
checked?: boolean;
10+
onCheckedChange?: (checked: boolean) => void;
11+
thumbClassname?: string;
12+
};
13+
14+
const Switch: FC<PropsWithChildren<SwitchProps>> = ({
15+
label,
16+
checked,
17+
onCheckedChange,
18+
className,
19+
thumbClassname,
20+
...props
21+
}) => {
22+
const id = useId();
23+
24+
return (
25+
<div className={styles.switch}>
26+
{label && (
27+
<label className={styles.label} htmlFor={id}>
28+
{label}
29+
</label>
30+
)}
31+
<SwitchPrimitive.Root
32+
id={id}
33+
className={classNames(styles.root, className)}
34+
checked={checked}
35+
onCheckedChange={onCheckedChange}
36+
{...props}
37+
>
38+
<SwitchPrimitive.Thumb
39+
className={classNames(styles.thumb, thumbClassname)}
40+
/>
41+
</SwitchPrimitive.Root>
42+
</div>
43+
);
44+
};
45+
46+
export default Switch;

0 commit comments

Comments
 (0)