Skip to content

Commit 31c2ee7

Browse files
author
Dimitri POSTOLOV
authoredOct 11, 2023
[v3] fix removeLinks when input node is link (#2431)
1 parent e2f4505 commit 31c2ee7

File tree

4 files changed

+81
-21
lines changed

4 files changed

+81
-21
lines changed
 

‎.changeset/few-rivers-sell.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'nextra-theme-docs': patch
3+
---
4+
5+
fix `removeLinks` when input node is link

‎packages/nextra-theme-docs/src/components/toc.tsx

+2-21
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import cn from 'clsx'
22
import type { Heading } from 'nextra'
33
import type { ReactElement } from 'react'
4-
import { Children, cloneElement, useEffect, useRef } from 'react'
4+
import { useEffect, useRef } from 'react'
55
import scrollIntoView from 'scroll-into-view-if-needed'
66
import { useActiveAnchor, useThemeConfig } from '../contexts'
77
import { renderComponent } from '../utils'
8+
import { removeLinks } from '../utils/remove-links'
89
import { Anchor } from './anchor'
910
import { BackToTop } from './back-to-top'
1011

@@ -18,26 +19,6 @@ const linkClassName = cn(
1819
'contrast-more:_text-gray-800 contrast-more:dark:_text-gray-50'
1920
)
2021

21-
type TOCElement = ReactElement | string
22-
23-
function isLink(node: TOCElement): node is ReactElement {
24-
return typeof node !== 'string' && !!node.props.href
25-
}
26-
27-
function removeLinks(node: TOCElement): TOCElement[] {
28-
return Children.map(node, child => {
29-
if (isLink(child)) {
30-
child = child.props.children
31-
}
32-
33-
return typeof child === 'string'
34-
? child
35-
: cloneElement(child, {
36-
children: removeLinks(child.props.children)
37-
})
38-
})
39-
}
40-
4122
export function TOC({ toc, filePath }: TOCProps): ReactElement {
4223
const activeAnchor = useActiveAnchor()
4324
const tocRef = useRef<HTMLDivElement>(null)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { removeLinks } from './remove-links'
2+
3+
describe('removeLinks()', () => {
4+
it('should return string', () => {
5+
expect(removeLinks('foo')).toBe('foo')
6+
})
7+
it('should remove link inside fragment', () => {
8+
const node = (
9+
<>
10+
foo
11+
<code>
12+
<a href="#">bar</a>
13+
</code>
14+
</>
15+
)
16+
expect(removeLinks(node)).toMatchInlineSnapshot(`
17+
[
18+
<React.Fragment>
19+
foo
20+
<code>
21+
bar
22+
</code>
23+
</React.Fragment>,
24+
]
25+
`)
26+
})
27+
it('should remove wrapper link', () => {
28+
const node = (
29+
<a href="#">
30+
foo<code>bar</code>
31+
</a>
32+
)
33+
expect(removeLinks(node)).toMatchInlineSnapshot(`
34+
[
35+
"foo",
36+
<code>
37+
bar
38+
</code>,
39+
]
40+
`)
41+
})
42+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type { ReactElement } from 'react'
2+
import { Children, cloneElement } from 'react'
3+
4+
type TOCElement = ReactElement | string
5+
6+
function isLink(node: TOCElement): node is ReactElement {
7+
return typeof node !== 'string' && !!node.props.href
8+
}
9+
10+
export function removeLinks(node: TOCElement): TOCElement[] | string {
11+
if (typeof node === 'string') {
12+
return node
13+
}
14+
// @ts-expect-error fixme
15+
return Children.map(node, child => {
16+
if (isLink(child)) {
17+
child = child.props.children
18+
}
19+
20+
if (typeof child === 'string') {
21+
return child
22+
}
23+
24+
if (Array.isArray(child)) {
25+
return removeLinks(child)
26+
}
27+
28+
const children = removeLinks(child.props.children)
29+
30+
return cloneElement(child, { children })
31+
})
32+
}

0 commit comments

Comments
 (0)
Please sign in to comment.