Skip to content

Commit

Permalink
feat(components-toc): re-implement ArticleToc component (#774)
Browse files Browse the repository at this point in the history
issue #701
close #773
  • Loading branch information
sabertazimi committed May 2, 2022
1 parent 7a1f49b commit 1ef8800
Show file tree
Hide file tree
Showing 19 changed files with 121 additions and 241 deletions.
1 change: 0 additions & 1 deletion __mocks__/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ const posts = basePosts.map((post, index) => ({
title: `${index} Basic Notes`,
},
excerpt: `${index + 1} Basic Notes Basic Concepts`,
toc: `${index + 1} Table of Contents`,
}));

const mockData = {
Expand Down
10 changes: 10 additions & 0 deletions components/Anchor/Anchor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { AnchorLinkProps, AnchorProps } from 'antd';
import { Anchor as AntAnchor } from 'antd';

interface AProps extends AnchorProps {}
interface LProps extends AnchorLinkProps {}

const Anchor = (props: AProps): JSX.Element => <AntAnchor {...props} />;
const Link = (props: LProps): JSX.Element => <AntAnchor.Link {...props} />;

export { Anchor, Link };
1 change: 1 addition & 0 deletions components/Anchor/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Anchor, Link } from './Anchor';
25 changes: 0 additions & 25 deletions components/Article/Article.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import mockData from '@mocks/data';
import { render } from '@testing-library/react';
import { axe } from 'jest-axe';
import Article from './Article';

describe('Article', () => {
Expand All @@ -18,28 +17,4 @@ describe('Article', () => {

expect(container).toMatchSnapshot();
});

test('should render accessibility guidelines (AXE)', async () => {
const { container } = render(<Article post={mockPost} />);

const a11y = await axe(container, {
rules: {
'nested-interactive': { enabled: false },
},
});

expect(a11y).toHaveNoViolations();
});

test('should render accessibility guidelines (AXE) with partial data', async () => {
const { container } = render(<Article post={mockBasePost} />);

const a11y = await axe(container, {
rules: {
'nested-interactive': { enabled: false },
},
});

expect(a11y).toHaveNoViolations();
});
});
4 changes: 2 additions & 2 deletions components/Article/Article.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface Props {
}

const Article = ({
post: { excerpt, toc, source, ...postMeta },
post: { excerpt, source, ...postMeta },
siteUrl = siteConfig.siteUrl,
}: Props): JSX.Element => {
const { slug, subtitle, prevPost, nextPost } = postMeta;
Expand All @@ -25,8 +25,8 @@ const Article = ({
return (
<div>
<ArticleHeader post={postMeta} />
<ArticleToc />
<Container className="max-w-screen-lg px-6">
<ArticleToc toc={toc} />
<ArticleContent source={source} />
<Divider>{subtitle || 'Blog'}</Divider>
<ArticleNav prevPost={prevPost} nextPost={nextPost} />
Expand Down
35 changes: 5 additions & 30 deletions components/Article/ArticleToc.module.css
Original file line number Diff line number Diff line change
@@ -1,31 +1,6 @@
.toc ul,
.toc li,
.toc p {
@apply my-0;
@apply text-left;
}

.toc ul {
@apply pl-4;
@apply mt-0 mb-1;
@apply text-left;
@apply list-none;
}

.toc ul ul {
@apply my-0;
}

@media screen and (prefers-reduced-motion: reduce) {
.toc a {
@apply text-sm font-normal;
@apply leading-relaxed break-all;
@apply transition-none;
}
}

.toc a {
@apply text-sm font-normal;
@apply leading-relaxed break-all;
@apply transition-colors;
.toc {
@apply hidden md:block;
@apply absolute;
@apply w-min;
@apply mt-36 ml-24;
}
24 changes: 2 additions & 22 deletions components/Article/ArticleToc.test.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,10 @@
import mockData from '@mocks/data';
import { fireEvent, render, screen } from '@testing-library/react';
import { axe } from 'jest-axe';
import { render } from '@testing-library/react';
import ArticleToc from './ArticleToc';

describe('ArticleToc', () => {
const mockToc = mockData.posts[0].toc;

test('should render correctly (snapshot)', () => {
const { container } = render(<ArticleToc toc={mockToc} />);
const { container } = render(<ArticleToc />);

expect(container).toMatchSnapshot();
});

test('Should render accessibility guidelines (AXE)', async () => {
const { container } = render(<ArticleToc toc={mockToc} />);

const a11y = await axe(container);

expect(a11y).toHaveNoViolations();
});

test('should expand ToC when clicked', () => {
render(<ArticleToc toc={mockToc} />);

fireEvent.click(screen.getByRole('button'));

expect(screen.getByText(mockToc)).toBeInTheDocument();
});
});
57 changes: 20 additions & 37 deletions components/Article/ArticleToc.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,30 @@
import Drawer from '@components/Drawer';
import IconButton from '@components/IconButton';
import { MenuFold } from '@components/Icons';
import { classNames } from '@components/utils';
import { useCallback, useState } from 'react';
import { Anchor, Link } from '@components/Anchor';
import { useEffect, useState } from 'react';
import styles from './ArticleToc.module.css';

interface Props {
toc?: string;
interface TocItem {
id: string;
title: string;
}

const ArticleToc = ({ toc = '' }: Props): JSX.Element => {
const [tocVisible, setTocVisible] = useState(false);
const ArticleToc = (): JSX.Element => {
const [tocItems, setTocItems] = useState<TocItem[]>([]);

const handleClick = useCallback(
() => setTocVisible(tocVisible => !tocVisible),
[]
);
useEffect(() => {
const items = document.querySelectorAll('h2.ant-typography');
const tocItems = Array.from(items).map(item => ({
id: `#${item.id}`,
title: item.textContent,
})) as TocItem[];
setTocItems(tocItems);
}, []);

return (
<div
className={classNames(
'fixed hidden m-0 bg-transparent',
'top-8 right-28 z-10',
'md:block'
)}
role="navigation"
>
<IconButton icon={<MenuFold aria-label="Menu" />} onClick={handleClick} />
<Drawer
title="Table of Contents"
onClose={handleClick}
visible={tocVisible}
>
<div
className={classNames('transition transform-gpu', styles.toc, {
'scale-100': tocVisible,
'scale-0': !tocVisible,
})}
dangerouslySetInnerHTML={{ __html: toc }}
onClick={handleClick}
/>
</Drawer>
</div>
<Anchor className={styles.toc}>
{tocItems.map(item => (
<Link key={item.id} href={item.id} title={item.title} />
))}
</Anchor>
);
};

Expand Down
112 changes: 60 additions & 52 deletions components/Article/__snapshots__/Article.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -127,38 +127,42 @@ exports[`Article should render correctly (snapshot) 1`] = `
</span>
</div>
</div>
<div
class="container relative block h-full mx-auto my-0 p-auto max-w-screen-lg px-6"
>
<div>
<div
class="fixed hidden m-0 bg-transparent top-8 right-28 z-10 md:block"
role="navigation"
class=""
>
<button
class="ant-btn ant-btn-circle ant-btn-default ant-btn-lg ant-btn-icon-only button-primary"
type="button"
<div
class="ant-anchor-wrapper toc"
style="max-height: 100vh;"
>
<span
aria-label="Menu"
class="anticon anticon-menu-fold"
role="img"
<div
class="ant-anchor"
>
<svg
aria-hidden="true"
data-icon="menu-fold"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<div
class="ant-anchor-ink"
>
<path
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 000 13.8z"
<span
class="ant-anchor-ink-ball"
/>
</svg>
</span>
</button>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#"
title="1 Basic Notes"
>
1 Basic Notes
</a>
</div>
</div>
</div>
</div>
</div>
<div
class="container relative block h-full mx-auto my-0 p-auto max-w-screen-lg px-6"
>
<div
style="opacity: 0;"
>
Expand Down Expand Up @@ -288,7 +292,7 @@ exports[`Article should render correctly (snapshot) 1`] = `
id="disqus_thread"
/>
<div
class="ant-space ant-space-vertical ant-space-align-center fixed left-0 top-1/2 z-10 hidden md:visible md:inline-flex"
class="ant-space ant-space-vertical ant-space-align-center fixed top-1/2 left-0 z-10 -translate-y-1/2 hidden md:visible md:inline-flex"
>
<div
class="ant-space-item"
Expand Down Expand Up @@ -546,38 +550,42 @@ exports[`Article should render correctly with partial data (snapshot) 1`] = `
</span>
</div>
</div>
<div
class="container relative block h-full mx-auto my-0 p-auto max-w-screen-lg px-6"
>
<div>
<div
class="fixed hidden m-0 bg-transparent top-8 right-28 z-10 md:block"
role="navigation"
class=""
>
<button
class="ant-btn ant-btn-circle ant-btn-default ant-btn-lg ant-btn-icon-only button-primary"
type="button"
<div
class="ant-anchor-wrapper toc"
style="max-height: 100vh;"
>
<span
aria-label="Menu"
class="anticon anticon-menu-fold"
role="img"
<div
class="ant-anchor"
>
<svg
aria-hidden="true"
data-icon="menu-fold"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<div
class="ant-anchor-ink"
>
<path
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 000 13.8z"
<span
class="ant-anchor-ink-ball"
/>
</svg>
</span>
</button>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#"
title="1 Basic Notes"
>
1 Basic Notes
</a>
</div>
</div>
</div>
</div>
</div>
<div
class="container relative block h-full mx-auto my-0 p-auto max-w-screen-lg px-6"
>
<div
style="opacity: 0;"
>
Expand Down Expand Up @@ -707,7 +715,7 @@ exports[`Article should render correctly with partial data (snapshot) 1`] = `
id="disqus_thread"
/>
<div
class="ant-space ant-space-vertical ant-space-align-center fixed left-0 top-1/2 z-10 hidden md:visible md:inline-flex"
class="ant-space ant-space-vertical ant-space-align-center fixed top-1/2 left-0 z-10 -translate-y-1/2 hidden md:visible md:inline-flex"
>
<div
class="ant-space-item"
Expand Down
Loading

1 comment on commit 1ef8800

@vercel
Copy link

@vercel vercel bot commented on 1ef8800 May 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

blog – ./

blog.tazimi.dev
blog-sabertaz.vercel.app
blog-git-main-sabertaz.vercel.app

Please sign in to comment.