-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add heading level hierarchy to table of contents #2101
Conversation
✅ Deploy Preview for astro-docs-2 ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify site settings. |
Here's the test file I had in my project which can be run with vitest: import generateToc from './generateToc'
import { describe, expect, test } from 'vitest'
describe('generateToc', () => {
test('should throw when heading 3 comes before heading 2', () => {
expect(() =>
generateToc([
{
slug: 'heading 3',
text: 'heading 3',
depth: 3,
},
{
slug: 'heading 2',
text: 'heading 2',
depth: 2,
},
])
).toThrowError(/^Orphan heading found: heading 2.$/)
})
test('should pass when heading 1 comes before heading 2', () => {
const toc = generateToc([
{
slug: 'heading 1',
text: 'heading 1',
depth: 1,
},
{
slug: 'heading 2',
text: 'heading 2',
depth: 2,
},
])
expect(toc).toEqual([
{
slug: 'heading 1',
text: 'heading 1',
depth: 1,
children: [
{
slug: 'heading 2',
text: 'heading 2',
depth: 2,
children: [],
},
],
},
])
})
test('should pass when heading 2 comes before heading 2', () => {
const toc = generateToc([
{
slug: 'heading 2',
text: 'heading 2',
depth: 2,
},
{
slug: 'heading 2',
text: 'heading 2',
depth: 2,
},
{
slug: 'heading 3',
text: 'heading 3',
depth: 3,
},
{
slug: 'heading 2',
text: 'heading 2',
depth: 2,
},
])
expect(toc).toEqual([
{
slug: 'heading 2',
text: 'heading 2',
depth: 2,
children: [],
},
{
slug: 'heading 2',
text: 'heading 2',
depth: 2,
children: [
{
slug: 'heading 3',
text: 'heading 3',
depth: 3,
children: [],
},
],
},
{
slug: 'heading 2',
text: 'heading 2',
depth: 2,
children: [],
},
])
})
}) I didn't include the test in this pull request because there seems no way to run such an unit test in this project at the moment. Correct me if I am wrong. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this @chenxsan! I’ve left one suggestion.
There’s also one small styling difference in this PR:
Production site | This PR preview |
---|---|
The PR preview is missing the .current-header-link a
styling that makes the text a different shade. Looks like we’ll need to change that in index.css
to a.current-header-link
.
} else { | ||
const lastItemInToc = toc[toc.length - 1]; | ||
if (heading.depth < lastItemInToc.depth) { | ||
throw new Error(`Orphan heading found: ${heading.text}.`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this Preact code will run in the browser, it would be good to avoid throwing errors that could break there. One option would be to move all this generateToc
logic into an Astro component that wraps this one. That way we could safely throw at build time. It would also have the advantage of having all this code run only at build time and never be shipped to users. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. However I have no idea how to make it with another Astro component since we can't use Astro component inside tsx. Wrapping TableOfContents.tsx
inside Astro component would result some warnings since we pass client:media
to an Astro component.
Anyway, I've refactor the TableOfContents.tsx
component to receive a toc
prop received at build time. Note that since there's no more easy way to get the currentHeading
from toc
data, I had to refactor currentID
state into currentHeading
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Thanks so much. Just left a few tidy-up suggestions, but then this should be good to go 🚀
Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this high quality contribution @chenxsan! Really great work.
By the way, you mentioned you had a test for this ready to go. Is setting up some testing for the docs site something you’d be interested in? We don’t have anything currently but some tests that run in our CI would certainly be helpful. Let me know if you’d be interested in that!
Yeah, I had vitest to run that unit test. Since Astro is built on vite, it's sort of seamless to run unit tests with vitest. I can file a pull request if you'd like one. |
Sure! That sounds really helpful. |
What kind of changes does this PR include?
Description