Skip to content

Commit 746c040

Browse files
authored
feat: create css-only responsive table (#8098)
* feat: create responsive css-only table * feat: create remark table plugin * fix: better row label * fix: remove flex container * refactor: organize rules * feat: some stylish * fix: oops * fix: array boundaries * fix: break words * fix: align end * fix: disable table cards when column count is <= 1 * fix: no wrap whitespace * fix: remove description truncate * fix: add missing labels * feat: split minor releases columns * fix: smaller modal padding on mobile * refactor: nit * fix: break words * fix: sibling anchors * fix: review
1 parent 8293d9c commit 746c040

File tree

14 files changed

+222
-91
lines changed

14 files changed

+222
-91
lines changed

apps/site/components/EOL/VulnerabilitiesTable.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import classNames from 'classnames';
21
import { useTranslations } from 'next-intl';
32
import type { FC } from 'react';
43

@@ -9,7 +8,7 @@ import type { Vulnerability } from '#site/types/vulnerabilities';
98
const VulnerabilitiesTable: FC<{
109
vulnerabilities: Array<Vulnerability>;
1110
maxWidth?: string;
12-
}> = ({ vulnerabilities, maxWidth = 'max-w-2xs' }) => {
11+
}> = ({ vulnerabilities, maxWidth = 'md:max-w-2xs' }) => {
1312
const t = useTranslations();
1413

1514
if (!vulnerabilities.length) {
@@ -29,7 +28,7 @@ const VulnerabilitiesTable: FC<{
2928
<tbody>
3029
{vulnerabilities.map((vulnerability, i) => (
3130
<tr key={i}>
32-
<td>
31+
<td data-label={t('components.eolModal.table.cves')}>
3332
{vulnerability.cve.map(cveId => (
3433
<div key={cveId}>
3534
<LinkWithArrow
@@ -44,13 +43,16 @@ const VulnerabilitiesTable: FC<{
4443

4544
{vulnerability.cve.length > 0 || '-'}
4645
</td>
47-
<td>
46+
<td data-label={t('components.eolModal.table.severity')}>
4847
<VulnerabilityChip severity={vulnerability.severity} />
4948
</td>
50-
<td className={classNames(maxWidth, 'truncate')}>
49+
<td
50+
data-label={t('components.eolModal.table.overview')}
51+
className={maxWidth}
52+
>
5153
{vulnerability.description || vulnerability.overview || '-'}
5254
</td>
53-
<td>
55+
<td data-label={t('components.eolModal.table.details')}>
5456
{vulnerability.url && (
5557
<LinkWithArrow
5658
href={vulnerability.url}

apps/site/components/EOL/VulnerabilityChips/VulnerabilityChip/index.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,7 @@ const VulnerabilityChip: FC<VulnerabilityChipProps> = ({
2020
const t = useTranslations();
2121

2222
return (
23-
<Badge
24-
size="small"
25-
kind={SEVERITY_KIND_MAP[severity] as BadgeKind}
26-
className="mr-1"
27-
>
23+
<Badge size="small" kind={SEVERITY_KIND_MAP[severity] as BadgeKind}>
2824
{count > 0 ? <span className={styles.chipCount}>{count}</span> : null}
2925
{t(`components.eolChip.severity.${severity}`)}
3026
</Badge>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const VulnerabilityChips: FC<VulnerabilityChipsProps> = ({
2323
);
2424

2525
return (
26-
<div className="vulnerability-chips">
26+
<div className="flex flex-row flex-wrap gap-1 max-sm:justify-end">
2727
{SEVERITY_ORDER.filter(severity => groupedBySeverity[severity] > 0).map(
2828
severity => (
2929
<VulnerabilityChip

apps/site/components/Releases/MinorReleasesTable/index.tsx

Lines changed: 57 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -21,70 +21,64 @@ const MinorReleasesTable: FC<MinorReleasesTableProps> = ({ releases }) => {
2121
const t = useTranslations();
2222

2323
return (
24-
<div className={styles.scrollable}>
25-
<table>
26-
<thead className={styles.header}>
27-
<tr>
28-
<th>{t('components.minorReleasesTable.version')}</th>
29-
<th>{t('components.minorReleasesTable.information')}</th>
30-
<th>{t('components.minorReleasesTable.links')}</th>
31-
</tr>
32-
</thead>
33-
<tbody>
34-
{releases.map(release => (
35-
<tr key={release.version}>
36-
<td>
37-
<Link href={`/download/archive/v${release.version}`}>
38-
v{release.version}
24+
<table>
25+
<thead>
26+
<tr>
27+
<th>{t('components.minorReleasesTable.version')}</th>
28+
<th>{t('components.minorReleasesTable.nApiVersion')}</th>
29+
<th>{t('components.minorReleasesTable.npmVersion')}</th>
30+
<th>{t('components.minorReleasesTable.v8Version')}</th>
31+
<th>{t('components.minorReleasesTable.links')}</th>
32+
</tr>
33+
</thead>
34+
<tbody>
35+
{releases.map(release => (
36+
<tr key={release.version}>
37+
<td data-label={t('components.minorReleasesTable.version')}>
38+
<Link href={`/download/archive/v${release.version}`}>
39+
v{release.version}
40+
</Link>
41+
</td>
42+
<td data-label={t('components.minorReleasesTable.nApiVersion')}>
43+
{release.modules && (
44+
<ReleaseOverviewItem
45+
Icon={CodeBracketSquareIcon}
46+
title={`v${release.modules}`}
47+
className={styles.releaseOverviewItem}
48+
/>
49+
)}
50+
</td>
51+
<td data-label={t('components.minorReleasesTable.npmVersion')}>
52+
{release.npm && (
53+
<ReleaseOverviewItem
54+
Icon={NpmIcon}
55+
title={`v${release.npm}`}
56+
className={styles.releaseOverviewItem}
57+
/>
58+
)}
59+
</td>
60+
<td data-label={t('components.minorReleasesTable.v8Version')}>
61+
<ReleaseOverviewItem
62+
Icon={CodeBracketSquareIcon}
63+
title={`v${release.v8}`}
64+
className={styles.releaseOverviewItem}
65+
/>
66+
</td>
67+
<td>
68+
<div className={styles.additionalLinks}>
69+
<Link href={getNodeApiUrl(`v${release.version}`)}>
70+
{t('components.minorReleasesTable.actions.docs')}
3971
</Link>
40-
</td>
41-
<td>
42-
<div className={styles.items}>
43-
{release.modules && (
44-
<>
45-
<ReleaseOverviewItem
46-
Icon={CodeBracketSquareIcon}
47-
title={`v${release.modules}`}
48-
subtitle={t('components.releaseOverview.nApiVersion')}
49-
/>
50-
<Separator orientation="vertical" />
51-
</>
52-
)}
53-
{release.npm && (
54-
<>
55-
<ReleaseOverviewItem
56-
Icon={NpmIcon}
57-
title={`v${release.npm}`}
58-
subtitle={t('components.releaseOverview.npmVersion')}
59-
/>
60-
<Separator orientation="vertical" />
61-
</>
62-
)}
63-
<ReleaseOverviewItem
64-
Icon={CodeBracketSquareIcon}
65-
title={`v${release.v8}`}
66-
subtitle={t('components.releaseOverview.v8Version')}
67-
/>
68-
</div>
69-
</td>
70-
<td>
71-
<div className={styles.additionalLinks}>
72-
<Link href={getNodeApiUrl(`v${release.version}`)}>
73-
{t('components.minorReleasesTable.actions.docs')}
74-
</Link>
75-
<Separator orientation="vertical" />
76-
<LinkWithArrow
77-
href={`${BASE_CHANGELOG_URL}${release.version}`}
78-
>
79-
{t('components.minorReleasesTable.actions.changelog')}
80-
</LinkWithArrow>
81-
</div>
82-
</td>
83-
</tr>
84-
))}
85-
</tbody>
86-
</table>
87-
</div>
72+
<Separator orientation="vertical" />
73+
<LinkWithArrow href={`${BASE_CHANGELOG_URL}${release.version}`}>
74+
{t('components.minorReleasesTable.actions.changelog')}
75+
</LinkWithArrow>
76+
</div>
77+
</td>
78+
</tr>
79+
))}
80+
</tbody>
81+
</table>
8882
);
8983
};
9084

apps/site/components/Releases/PreviousReleasesTable.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,24 +43,30 @@ const PreviousReleasesTable: FC = () => {
4343
<tbody>
4444
{releaseData.map(release => (
4545
<Fragment key={release.major}>
46-
<tr key={release.major}>
47-
<td data-label="Version">
46+
<tr data-label={release.versionWithPrefix}>
47+
<td data-label={t('components.downloadReleasesTable.version')}>
4848
<Link href={`/download/archive/${release.versionWithPrefix}`}>
4949
v{release.major}
5050
</Link>
5151
</td>
5252

53-
<td data-label="LTS">{release.codename || '-'}</td>
53+
<td data-label={t('components.downloadReleasesTable.codename')}>
54+
{release.codename || '-'}
55+
</td>
5456

55-
<td data-label="Date">
57+
<td
58+
data-label={t('components.downloadReleasesTable.firstReleased')}
59+
>
5660
<FormattedTime date={release.currentStart} />
5761
</td>
5862

59-
<td data-label="Date">
63+
<td
64+
data-label={t('components.downloadReleasesTable.lastUpdated')}
65+
>
6066
<FormattedTime date={release.releaseDate} />
6167
</td>
6268

63-
<td data-label="Status">
69+
<td data-label={t('components.downloadReleasesTable.status')}>
6470
<Badge kind={BADGE_KIND_MAP[release.status]} size="small">
6571
{release.status}
6672
{release.status === 'End-of-life' ? ' (EoL)' : ''}

apps/site/components/Releases/ReleaseOverview/ReleaseOverviewItem/index.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
1+
import classNames from 'classnames';
12
import type { FC, ReactNode, SVGProps } from 'react';
23

34
import styles from './index.module.css';
45

56
type ReleaseOverviewItemProps = {
67
Icon: FC<SVGProps<SVGSVGElement>>;
78
title: ReactNode;
8-
subtitle: ReactNode;
9+
subtitle?: ReactNode;
10+
className?: string;
911
};
1012

1113
const ReleaseOverviewItem: FC<ReleaseOverviewItemProps> = ({
1214
Icon,
1315
title,
1416
subtitle,
17+
className,
1518
}) => {
1619
return (
17-
<div className={styles.item}>
20+
<div className={classNames(styles.item, className)}>
1821
<Icon />
1922
<div>
20-
<h2>{subtitle}</h2>
23+
{subtitle && <h2>{subtitle}</h2>}
2124
<h1>{title}</h1>
2225
</div>
2326
</div>

apps/site/next.mdx.plugins.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import rehypeSlug from 'rehype-slug';
77
import remarkGfm from 'remark-gfm';
88
import readingTime from 'remark-reading-time';
99

10+
import remarkTableTitles from './util/table';
11+
1012
/**
1113
* Provides all our Rehype Plugins that are used within MDX
1214
*/
@@ -30,4 +32,5 @@ export const REMARK_PLUGINS = [
3032
remarkHeadings,
3133
// Calculates the reading time of the content
3234
readingTime,
35+
remarkTableTitles,
3336
];

apps/site/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"feed": "~5.1.0",
6060
"github-slugger": "~2.0.0",
6161
"gray-matter": "~4.0.3",
62+
"mdast-util-to-string": "^4.0.0",
6263
"next": "15.5.2",
6364
"next-intl": "~4.3.5",
6465
"next-themes": "~0.4.6",
@@ -73,6 +74,7 @@
7374
"semver": "~7.7.2",
7475
"sval": "^0.6.3",
7576
"tailwindcss": "catalog:",
77+
"unist-util-visit": "^5.0.0",
7678
"vfile": "~6.0.3",
7779
"vfile-matter": "~5.0.1"
7880
},
@@ -83,6 +85,7 @@
8385
"@opennextjs/cloudflare": "^1.6.4",
8486
"@playwright/test": "^1.54.1",
8587
"@testing-library/user-event": "~14.6.1",
88+
"@types/mdast": "^4.0.4",
8689
"@types/mdx": "^2.0.13",
8790
"@types/semver": "~7.7.0",
8891
"dedent": "^1.6.0",

apps/site/util/table.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { Root } from 'mdast';
2+
import { toString } from 'mdast-util-to-string';
3+
import { visit } from 'unist-util-visit';
4+
5+
/**
6+
* Remark plugin that adds data-label attributes to table cells (td)
7+
* based on their corresponding table headers (th).
8+
*/
9+
export default function remarkTableTitles() {
10+
return (tree: Root) => {
11+
visit(tree, 'table', table => {
12+
// Ensure table has at least a header row and one data row
13+
if (table.children.length < 2) {
14+
return;
15+
}
16+
17+
const [headerRow, ...dataRows] = table.children;
18+
19+
if (headerRow.children.length <= 1) {
20+
table.data ??= {};
21+
22+
table.data.hProperties = {
23+
'data-cards': 'false',
24+
};
25+
}
26+
27+
// Extract header labels from the first row
28+
const headerLabels = headerRow.children.map(headerCell =>
29+
toString(headerCell.children)
30+
);
31+
32+
// Assign data-label to each cell in data rows
33+
dataRows.forEach(row => {
34+
row.children.forEach((cell, idx) => {
35+
cell.data ??= {};
36+
37+
if (idx > headerLabels.length) {
38+
return;
39+
}
40+
41+
cell.data.hProperties = {
42+
'data-label': headerLabels[idx],
43+
};
44+
});
45+
});
46+
});
47+
};
48+
}

packages/i18n/src/locales/en.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,9 @@
217217
"minorReleasesTable": {
218218
"version": "Version",
219219
"links": "Links",
220-
"information": "Version Informations",
220+
"nApiVersion": "N-API version",
221+
"npmVersion": "npm version",
222+
"v8Version": "V8 version",
221223
"actions": {
222224
"release": "Release",
223225
"changelog": "Changelog",

0 commit comments

Comments
 (0)