Skip to content

Commit 99ff48b

Browse files
committed
feat(theme): render labels as inline markdown
1 parent b0da384 commit 99ff48b

10 files changed

Lines changed: 343 additions & 7 deletions

File tree

__tests__/e2e/.vitepress/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ const sidebar: DefaultTheme.Config['sidebar'] = {
8686
{
8787
text: '& &#60;Test Page &> <code>code</code>',
8888
link: '/text-literals/'
89+
},
90+
{
91+
text: 'Markdown `<Label &>`',
92+
link: '/theme-labels/'
8993
}
9094
]
9195
},

__tests__/e2e/multi-sidebar/index.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,20 @@ describe('test multi sidebar sort root', () => {
1818
'Markdown Extensions',
1919
'Team & Sponsors'
2020
])
21+
22+
expect(await sidebarLocator.nth(1).innerHTML()).toBe(
23+
'&amp; &lt;Text Literals &amp;&gt; <code>code</code>'
24+
)
25+
})
26+
27+
test('renders inline markdown in sidebar labels', async () => {
28+
const markdownLabel = page.locator(
29+
'.VPSidebarItem.level-1 a[href$="/theme-labels/"] .text'
30+
)
31+
32+
expect(await markdownLabel.innerHTML()).toBe(
33+
'Markdown <code>&lt;Label &amp;&gt;</code>'
34+
)
2135
})
2236
})
2337

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Theme Labels

__tests__/unit/node/config.test.ts

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,99 @@
11
import type { MarkdownItAsync } from 'markdown-it-async'
2-
import { mergeConfig, type UserConfig } from 'node/config'
2+
import { mergeConfig, resolveSiteData, type UserConfig } from 'node/config'
33

44
describe('node/config', () => {
5+
test('renders inline markdown in default theme text fields', async () => {
6+
const site = await resolveSiteData(process.cwd(), {
7+
themeConfig: {
8+
nav: [
9+
{ text: 'Vue `<script setup>`', link: '/guide' },
10+
{
11+
text: '**Reference**',
12+
items: [
13+
{ text: 'API `<T>`', link: '/api' },
14+
{
15+
text: 'Nested `<Menu>`',
16+
items: [{ text: '`Child`', link: '/child' }]
17+
}
18+
]
19+
}
20+
],
21+
sidebar: [
22+
{
23+
text: 'Guide `<script setup>`',
24+
docFooterText: 'Footer `<Foo>`',
25+
items: [
26+
{ text: '**Intro**', link: '/intro' },
27+
{ text: '<code>Raw HTML</code>', link: '/html' }
28+
]
29+
}
30+
],
31+
docFooter: {
32+
prev: 'Previous `<Page>`',
33+
next: false
34+
}
35+
}
36+
})
37+
38+
expect(site.themeConfig.nav[0].text).toBe(
39+
'Vue <code>&lt;script setup&gt;</code>'
40+
)
41+
expect(site.themeConfig.nav[1].text).toBe('<strong>Reference</strong>')
42+
expect(site.themeConfig.nav[1].items[0].text).toBe(
43+
'API <code>&lt;T&gt;</code>'
44+
)
45+
expect(site.themeConfig.nav[1].items[1].text).toBe(
46+
'Nested <code>&lt;Menu&gt;</code>'
47+
)
48+
expect(site.themeConfig.nav[1].items[1].items[0].text).toBe(
49+
'<code>Child</code>'
50+
)
51+
expect(site.themeConfig.sidebar[0].text).toBe(
52+
'Guide <code>&lt;script setup&gt;</code>'
53+
)
54+
expect(site.themeConfig.sidebar[0].docFooterText).toBe(
55+
'Footer <code>&lt;Foo&gt;</code>'
56+
)
57+
expect(site.themeConfig.sidebar[0].items[0].text).toBe(
58+
'<strong>Intro</strong>'
59+
)
60+
expect(site.themeConfig.sidebar[0].items[1].text).toBe(
61+
'<code>Raw HTML</code>'
62+
)
63+
expect(site.themeConfig.docFooter.prev).toBe(
64+
'Previous <code>&lt;Page&gt;</code>'
65+
)
66+
expect(site.themeConfig.docFooter.next).toBe(false)
67+
})
68+
69+
test('renders inline markdown in locale and additional default theme configs', async () => {
70+
const site = await resolveSiteData(process.cwd(), {
71+
locales: {
72+
zh: {
73+
label: 'Chinese',
74+
themeConfig: {
75+
sidebar: [{ text: 'Locale `<script setup>`' }]
76+
}
77+
}
78+
},
79+
additionalConfig: {
80+
'/guide/': {
81+
themeConfig: {
82+
nav: [{ text: 'Guide `<Item>`', link: '/guide/' }]
83+
}
84+
}
85+
}
86+
})
87+
88+
expect(site.locales.zh.themeConfig?.sidebar?.[0].text).toBe(
89+
'Locale <code>&lt;script setup&gt;</code>'
90+
)
91+
expect(
92+
typeof site.additionalConfig !== 'function' &&
93+
site.additionalConfig?.['/guide/'].themeConfig?.nav?.[0].text
94+
).toBe('Guide <code>&lt;Item&gt;</code>')
95+
})
96+
597
test('merges markdown hooks from extended configs', async () => {
698
const calls: string[] = []
799
const md = {} as MarkdownItAsync

docs/en/reference/default-theme-config.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ export default {
8888

8989
The configuration for the nav menu item. More details in [Default Theme: Nav](./default-theme-nav#navigation-links).
9090

91+
The `text` fields support inline Markdown and HTML.
92+
9193
```ts
9294
export default {
9395
themeConfig: {
@@ -136,6 +138,8 @@ interface NavItemWithChildren {
136138

137139
The configuration for the sidebar menu item. More details in [Default Theme: Sidebar](./default-theme-sidebar).
138140

141+
The `text` and `docFooterText` fields support inline Markdown and HTML.
142+
139143
```ts
140144
export default {
141145
themeConfig: {
@@ -162,7 +166,7 @@ export interface SidebarMulti {
162166

163167
export type SidebarItem = {
164168
/**
165-
* The text label of the item.
169+
* The text label of the item. Supports inline Markdown and HTML.
166170
*/
167171
text?: string
168172

@@ -192,6 +196,7 @@ export type SidebarItem = {
192196

193197
/**
194198
* Customize text that appears on the footer of previous/next page.
199+
* Supports inline Markdown and HTML.
195200
*/
196201
docFooterText?: string
197202

@@ -410,6 +415,8 @@ Learn more in [Default Theme: Carbon Ads](./default-theme-carbon-ads)
410415

411416
Can be used to customize text appearing above previous and next links. Helpful if not writing docs in English. Also can be used to disable prev/next links globally. If you want to selectively enable/disable prev/next links, you can use [frontmatter](./default-theme-prev-next-links).
412417

418+
The `prev` and `next` string values support inline Markdown and HTML.
419+
413420
```ts
414421
export default {
415422
themeConfig: {

docs/en/reference/default-theme-nav.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,15 @@ export default {
5757
}
5858
```
5959

60-
The `text` is the actual text displayed in nav, and the `link` is the link that will be navigated to when the text is clicked. For the link, set path to the actual file without `.md` prefix, and always start with `/`.
60+
The `text` is the actual text displayed in nav, and supports inline Markdown and HTML. The `link` is the link that will be navigated to when the text is clicked. For the link, set path to the actual file without `.md` prefix, and always start with `/`.
61+
62+
```js
63+
export default {
64+
themeConfig: {
65+
nav: [{ text: 'Vue `<script setup>`', link: '/guide' }]
66+
}
67+
}
68+
```
6169

6270
The `link` can also be a function that accepts [`PageData`](./runtime-api#usedata) as the argument and returns the path.
6371

docs/en/reference/default-theme-sidebar.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@ export default {
2727

2828
The simplest form of the sidebar menu is passing in a single array of links. The first level item defines the "section" for the sidebar. It should contain `text`, which is the title of the section, and `items` which are the actual navigation links.
2929

30+
The `text` fields in sidebar items support inline Markdown and HTML:
31+
32+
```js
33+
export default {
34+
themeConfig: {
35+
sidebar: [
36+
{
37+
text: 'Vue `<script setup>`',
38+
items: [{ text: 'API `<T>`', link: '/api' }]
39+
}
40+
]
41+
}
42+
}
43+
```
44+
3045
```js
3146
export default {
3247
themeConfig: {

src/node/config.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ import {
2525
type SiteData
2626
} from './shared'
2727
import type { RawConfigExports, SiteConfig, UserConfig } from './siteConfig'
28+
import {
29+
resolveAdditionalDefaultThemeConfigs,
30+
resolveDefaultThemeConfig,
31+
resolveLocaleDefaultThemeConfigs
32+
} from './themeConfig'
2833
import { glob } from './utils/glob'
2934

3035
export { resolvePages } from './plugins/dynamicRoutesPlugin'
@@ -358,6 +363,12 @@ export async function resolveSiteData(
358363
): Promise<SiteData> {
359364
userConfig = userConfig || (await resolveUserConfig(root, command, mode))[0]
360365

366+
const themeConfig = resolveDefaultThemeConfig(userConfig.themeConfig || {})
367+
const locales = resolveLocaleDefaultThemeConfigs(userConfig.locales) || {}
368+
const additionalConfig = resolveAdditionalDefaultThemeConfigs(
369+
userConfig.additionalConfig
370+
)
371+
361372
return {
362373
lang: userConfig.lang || 'en-US',
363374
dir: userConfig.dir || 'ltr',
@@ -370,11 +381,11 @@ export async function resolveSiteData(
370381
prefetchLinks: userConfig.router?.prefetchLinks ?? true
371382
},
372383
appearance: userConfig.appearance ?? true,
373-
themeConfig: userConfig.themeConfig || {},
374-
locales: userConfig.locales || {},
384+
themeConfig,
385+
locales,
375386
cleanUrls: !!userConfig.cleanUrls,
376387
contentProps: userConfig.contentProps,
377-
additionalConfig: userConfig.additionalConfig
388+
additionalConfig
378389
}
379390
}
380391

0 commit comments

Comments
 (0)