Skip to content

Commit 17546fc

Browse files
authored
feat: create toc component (nodejs#8560)
* feat: create toc component * refactor(ui-components): review * fix: custom anchor component * refactor: review * feat: i18n prop * choire: patch version * review(ui-components): some nits
1 parent cf62bb9 commit 17546fc

File tree

4 files changed

+160
-1
lines changed

4 files changed

+160
-1
lines changed

packages/ui-components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@node-core/ui-components",
3-
"version": "1.5.5",
3+
"version": "1.5.6",
44
"type": "module",
55
"exports": {
66
"./*": [
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
@reference "../../styles/index.css";
2+
3+
.details {
4+
@apply my-2
5+
block
6+
rounded-md
7+
bg-neutral-200
8+
lg:hidden
9+
dark:bg-neutral-900;
10+
11+
.summary {
12+
@apply px-4
13+
py-2;
14+
}
15+
16+
.list {
17+
@apply space-y-1
18+
px-4
19+
pb-2;
20+
}
21+
22+
.link {
23+
@apply text-sm
24+
font-semibold
25+
text-neutral-900
26+
underline
27+
hover:text-neutral-700
28+
dark:text-white
29+
dark:hover:text-neutral-500;
30+
}
31+
32+
.depthThree {
33+
@apply pl-2;
34+
}
35+
36+
.depthFour {
37+
@apply pl-4;
38+
}
39+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import TableOfContents from '#ui/Common/TableOfContents';
2+
3+
import type { Meta as MetaObj, StoryObj } from '@storybook/react-webpack5';
4+
5+
type Story = StoryObj<typeof TableOfContents>;
6+
type Meta = MetaObj<typeof TableOfContents>;
7+
8+
export const Default: Story = {};
9+
10+
export const CustomDepth: Story = {
11+
args: {
12+
minDepth: 1,
13+
maxDepth: 6,
14+
},
15+
};
16+
17+
export default {
18+
component: TableOfContents,
19+
args: {
20+
ariaLabel: 'Table of Contents',
21+
summaryTitle: 'On this page',
22+
headings: [
23+
{
24+
value: 'OpenSSL update assessment, and Node.js project plans',
25+
depth: 1,
26+
data: { id: 'heading-1' },
27+
},
28+
{
29+
value: 'Summary',
30+
depth: 2,
31+
data: { id: 'summary' },
32+
},
33+
{
34+
value: 'Analysis',
35+
depth: 2,
36+
data: { id: 'analysis' },
37+
},
38+
{
39+
value: 'The c_rehash script allows command injection (CVE-2022-2068)',
40+
depth: 3,
41+
data: { id: 'the_c_rehash' },
42+
},
43+
{
44+
value: 'Contact and future updates',
45+
depth: 3,
46+
data: { id: 'contact_and_future_updates' },
47+
},
48+
{
49+
value: 'Email',
50+
depth: 4,
51+
data: { id: 'email' },
52+
},
53+
{
54+
value: 'Slack',
55+
depth: 4,
56+
data: { id: 'slack' },
57+
},
58+
{
59+
value: '#node-website',
60+
depth: 5, // h5s do not get shown
61+
data: { id: 'node-website' },
62+
},
63+
],
64+
},
65+
} as Meta;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import classNames from 'classnames';
2+
3+
import { LinkLike } from '#ui/types';
4+
5+
import type { Heading } from '@vcarl/remark-headings';
6+
import type { ComponentProps, FC } from 'react';
7+
8+
import styles from './index.module.css';
9+
10+
const depthClasses: Record<number, string> = {
11+
3: styles.depthThree,
12+
4: styles.depthFour,
13+
};
14+
15+
type TableOfContentsProps = ComponentProps<'details'> & {
16+
headings: Array<Heading>;
17+
summaryTitle: string;
18+
minDepth?: number;
19+
maxDepth?: number;
20+
as?: LinkLike;
21+
};
22+
23+
const TableOfContents: FC<TableOfContentsProps> = ({
24+
headings,
25+
summaryTitle,
26+
minDepth = 2,
27+
className,
28+
maxDepth = 4,
29+
as: Component = 'a',
30+
...props
31+
}) => {
32+
const filteredHeadings = headings.filter(
33+
({ depth }) => depth >= minDepth && depth <= maxDepth
34+
);
35+
36+
return (
37+
<details className={classNames(styles.details, className)} {...props}>
38+
<summary className={styles.summary}>{summaryTitle}</summary>
39+
<ul className={styles.list}>
40+
{filteredHeadings.map((head, index) => (
41+
<li key={head.data?.id ?? index}>
42+
<Component
43+
href={head.data?.id && `#${head.data.id}`}
44+
className={classNames(styles.link, depthClasses[head.depth])}
45+
>
46+
{head.value}
47+
</Component>
48+
</li>
49+
))}
50+
</ul>
51+
</details>
52+
);
53+
};
54+
55+
export default TableOfContents;

0 commit comments

Comments
 (0)