Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/ui-components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@node-core/ui-components",
"version": "1.5.5",
"version": "1.5.6",
"type": "module",
"exports": {
"./*": [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@reference "../../styles/index.css";

.details {
@apply my-2
block
rounded-md
bg-neutral-200
lg:hidden
dark:bg-neutral-900;

.summary {
@apply px-4
py-2;
}

.list {
@apply space-y-1
px-4
pb-2;
}

.link {
@apply text-sm
font-semibold
text-neutral-900
underline
hover:text-neutral-700
dark:text-white
dark:hover:text-neutral-500;
}

.depthThree {
@apply pl-2;
}

.depthFour {
@apply pl-4;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import TableOfContents from '#ui/Common/TableOfContents';

import type { Meta as MetaObj, StoryObj } from '@storybook/react-webpack5';

type Story = StoryObj<typeof TableOfContents>;
type Meta = MetaObj<typeof TableOfContents>;

export const Default: Story = {};

export const CustomDepth: Story = {
args: {
minDepth: 1,
maxDepth: 6,
},
};

export default {
component: TableOfContents,
args: {
ariaLabel: 'Table of Contents',
summaryTitle: 'On this page',
headings: [
{
value: 'OpenSSL update assessment, and Node.js project plans',
depth: 1,
data: { id: 'heading-1' },
},
{
value: 'Summary',
depth: 2,
data: { id: 'summary' },
},
{
value: 'Analysis',
depth: 2,
data: { id: 'analysis' },
},
{
value: 'The c_rehash script allows command injection (CVE-2022-2068)',
depth: 3,
data: { id: 'the_c_rehash' },
},
{
value: 'Contact and future updates',
depth: 3,
data: { id: 'contact_and_future_updates' },
},
{
value: 'Email',
depth: 4,
data: { id: 'email' },
},
{
value: 'Slack',
depth: 4,
data: { id: 'slack' },
},
{
value: '#node-website',
depth: 5, // h5s do not get shown
data: { id: 'node-website' },
},
],
},
} as Meta;
54 changes: 54 additions & 0 deletions packages/ui-components/src/Common/TableOfContents/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import classNames from 'classnames';

import { LinkLike } from '#ui/types';

import type { Heading } from '@vcarl/remark-headings';
import type { FC } from 'react';

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

type TableOfContentsProps = {
headings: Array<Heading>;
ariaLabel: string;
summaryTitle: string;
minDepth?: number;
maxDepth?: number;
as?: LinkLike;
};

const TableOfContents: FC<TableOfContentsProps> = ({
headings,
ariaLabel,
summaryTitle,
minDepth = 2,
maxDepth = 4,
as: Component = 'a',
}) => {
const filteredHeadings = headings.filter(
({ depth }) => depth >= minDepth && depth <= maxDepth
);

return (
<details className={styles.details} aria-label={ariaLabel}>
Comment thread
araujogui marked this conversation as resolved.
Outdated
<summary className={styles.summary}>{summaryTitle}</summary>
<ul className={styles.list}>
{filteredHeadings.map((head, index) => (
<li key={head.data?.id ?? index}>
<Component
href={head.data?.id && `#${head.data.id}`}
className={classNames(
styles.link,
head.depth === 3 && styles.depthThree,
head.depth === 4 && styles.depthFour
Comment thread
araujogui marked this conversation as resolved.
Outdated
)}
Comment thread
araujogui marked this conversation as resolved.
Outdated
>
{head.value}
</Component>
</li>
))}
</ul>
</details>
);
};

export default TableOfContents;
Loading