Skip to content

Commit

Permalink
feat(theme): enable multi level sidebar nesting (#1360) (#1835)
Browse files Browse the repository at this point in the history
close #1360
close #1361
close #1680
  • Loading branch information
kiaking authored Jan 24, 2023
1 parent 8782c82 commit c35a1f0
Show file tree
Hide file tree
Showing 12 changed files with 574 additions and 361 deletions.
8 changes: 6 additions & 2 deletions __tests__/e2e/multi-sidebar/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ describe('test multi sidebar sort root', () => {
})

test('using / sidebar', async () => {
const sidebarLocator = page.locator('.VPSidebarGroup .title-text')
const sidebarLocator = page.locator(
'.VPSidebarItem.level-0 > .item > .link > .text'
)

const sidebarContent = await sidebarLocator.allTextContents()
expect(sidebarContent).toEqual([
Expand All @@ -22,7 +24,9 @@ describe('test multi sidebar sort order', () => {
})

test('using /multi-sidebar/ sidebar', async () => {
const sidebarLocator = page.locator('.VPSidebarGroup .title-text')
const sidebarLocator = page.locator(
'.VPSidebarItem.level-0 > .item > .link > .text'
)

const sidebarContent = await sidebarLocator.allTextContents()
expect(sidebarContent).toEqual(['Multi Sidebar'])
Expand Down
199 changes: 117 additions & 82 deletions __tests__/unit/client/theme-default/support/sidebar.test.ts
Original file line number Diff line number Diff line change
@@ -1,96 +1,131 @@
import { getSidebar } from 'client/theme-default/support/sidebar'
import { getSidebar, hasActiveLink } from 'client/theme-default/support/sidebar'

describe('client/theme-default/support/sidebar', () => {
const root = [
{
text: 'A',
collapsible: true,
items: [
{
text: 'A',
link: ''
}
]
},
{
text: 'B',
items: [
{
text: 'B',
link: ''
}
]
}
]
const another = [
{
text: 'C',
items: [
describe('getSidebar', () => {
const root = [
{
text: 'A',
collapsible: true,
items: [{ text: 'A', link: '' }]
},
{
text: 'B',
items: [{ text: 'B', link: '' }]
}
]

const another = [
{
text: 'C',
items: [{ text: 'C', link: '' }]
}
]

describe('normal sidebar sort', () => {
const normalSidebar = {
'/': root,
'/multi-sidebar/': another
}

test('gets `/` sidebar', () => {
expect(getSidebar(normalSidebar, '/')).toBe(root)
})

test('gets `/multi-sidebar/` sidebar', () => {
expect(getSidebar(normalSidebar, '/multi-sidebar/')).toBe(another)
})

test('gets `/` sidebar again', () => {
expect(getSidebar(normalSidebar, '/some-entry.html')).toBe(root)
})
})

describe('reversed sidebar sort', () => {
const reversedSidebar = {
'/multi-sidebar/': another,
'/': root
}

test('gets `/` sidebar', () => {
expect(getSidebar(reversedSidebar, '/')).toBe(root)
})

test('gets `/multi-sidebar/` sidebar', () => {
expect(getSidebar(reversedSidebar, '/multi-sidebar/')).toBe(another)
})

test('gets `/` sidebar again', () => {
expect(getSidebar(reversedSidebar, '/some-entry.html')).toBe(root)
})
})

describe('nested sidebar sort', () => {
const nested = [
{
text: 'C',
link: ''
text: 'D',
items: [{ text: 'D', link: '' }]
}
]
}
]
describe('normal sidebar sort', () => {
const normalSidebar = {
'/': root,
'/multi-sidebar/': another
}
test('gets / sidebar', () => {
expect(getSidebar(normalSidebar, '/')).toBe(root)
})
test('gets /multi-sidebar/ sidebar', () => {
expect(getSidebar(normalSidebar, '/multi-sidebar/')).toBe(another)
})
test('gets / sidebar again', () => {
expect(getSidebar(normalSidebar, '/some-entry.html')).toBe(root)
})
})
describe('reversed sidebar sort', () => {
const reversedSidebar = {
'/multi-sidebar/': another,
'/': root
}
test('gets / sidebar', () => {
expect(getSidebar(reversedSidebar, '/')).toBe(root)
})
test('gets /multi-sidebar/ sidebar', () => {
expect(getSidebar(reversedSidebar, '/multi-sidebar/')).toBe(another)
})
test('gets / sidebar again', () => {
expect(getSidebar(reversedSidebar, '/some-entry.html')).toBe(root)

const nestedSidebar = {
'/': root,
'/multi-sidebar/': another,
'/multi-sidebar/nested/': nested
}

test('gets `/` sidebar', () => {
expect(getSidebar(nestedSidebar, '/')).toBe(root)
})

test('gets `/multi-sidebar/` sidebar', () => {
expect(getSidebar(nestedSidebar, '/multi-sidebar/')).toBe(another)
})

test('gets `/multi-sidebar/nested/` sidebar', () => {
expect(getSidebar(nestedSidebar, '/multi-sidebar/nested/')).toBe(nested)
})

test('gets `/` sidebar again', () => {
expect(getSidebar(nestedSidebar, '/some-entry.html')).toBe(root)
})
})
})
describe('nested sidebar sort', () => {
const nested = [
{
text: 'D',

describe('hasActiveLink', () => {
test('checks `SidebarItem`', () => {
const item = {
text: 'Item 001',
items: [
{
text: 'D',
link: ''
}
{ text: 'Item 001', link: '/active-1' },
{ text: 'Item 002', link: '/active-2' }
]
}
]
const nestedSidebar = {
'/': root,
'/multi-sidebar/': another,
'/multi-sidebar/nested/': nested
}
test('gets / sidebar', () => {
expect(getSidebar(nestedSidebar, '/')).toBe(root)
})
test('gets /multi-sidebar/ sidebar', () => {
expect(getSidebar(nestedSidebar, '/multi-sidebar/')).toBe(another)
})
test('gets /multi-sidebar/nested/ sidebar', () => {
expect(getSidebar(nestedSidebar, '/multi-sidebar/nested/')).toBe(nested)

expect(hasActiveLink('active-1', item)).toBe(true)
expect(hasActiveLink('inactive', item)).toBe(false)
})
test('gets / sidebar again', () => {
expect(getSidebar(nestedSidebar, '/some-entry.html')).toBe(root)

test('checks `SidebarItem[]`', () => {
const item = [
{
text: 'Item 001',
items: [
{ text: 'Item 001', link: '/active-1' },
{ text: 'Item 002', link: '/active-2' }
]
},
{
text: 'Item 002',
items: [
{ text: 'Item 003', link: '/active-3' },
{ text: 'Item 004', link: '/active-4' }
]
}
]

expect(hasActiveLink('active-1', item)).toBe(true)
expect(hasActiveLink('active-3', item)).toBe(true)
expect(hasActiveLink('inactive', item)).toBe(false)
})
})
})
41 changes: 30 additions & 11 deletions docs/config/theme-configs.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,22 +124,41 @@ export default {
```

```ts
type Sidebar = SidebarGroup[] | SidebarMulti
export type Sidebar = SidebarItem[] | SidebarMulti

interface SidebarMulti {
[path: string]: SidebarGroup[]
export interface SidebarMulti {
[path: string]: SidebarItem[]
}

interface SidebarGroup {
text: string
items: SidebarItem[]
export type SidebarItem = {
/**
* The text label of the item.
*/
text?: string

/**
* The link of the item.
*/
link?: string

/**
* The children of the item.
*/
items?: SidebarItem[]

/**
* If `true`, toggle button is shown.
*
* @default false
*/
collapsible?: boolean
collapsed?: boolean
}

interface SidebarItem {
text: string
link: string
/**
* If `true`, collapsible group is collapsed by default.
*
* @default false
*/
collapsed?: boolean
}
```
Expand Down
53 changes: 39 additions & 14 deletions docs/guide/theme-sidebar.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Sidebar

The sidebar is the main navigation block for your documentation. You can configure the sidebar menu in `themeConfig.sidebar`.
The sidebar is the main navigation block for your documentation. You can configure the sidebar menu in [`themeConfig.sidebar`](/config/theme-configs#sidebar).

```js
export default {
Expand All @@ -21,7 +21,7 @@ export default {

## The Basics

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.
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.

```js
export default {
Expand Down Expand Up @@ -66,6 +66,33 @@ export default {
}
```

You may further nest the sidebar items up to 6 level deep counting up from the root level. Note that deeper than 6 level of nested items gets ignored and will not be displayed on the sidebar.

```js
export default {
themeConfig: {
sidebar: [
{
text: 'Level 1',
items: [
{
text: 'Level 2',
items: [
{
text: 'Level 3',
items: [
...
]
}
]
}
]
}
]
}
}
```

## Multiple Sidebars

You may show different sidebar depending on the page path. For example, as shown on this site, you might want to create a separate sections of content in your documentation like "Guide" page and "Config" page.
Expand All @@ -90,30 +117,28 @@ Then, update your configuration to define your sidebar for each section. This ti
export default {
themeConfig: {
sidebar: {
// This sidebar gets displayed when user is
// under `guide` directory.
// This sidebar gets displayed when a user
// is on `guide` directory.
'/guide/': [
{
text: 'Guide',
items: [
// This shows `/guide/index.md` page.
{ text: 'Index', link: '/guide/' }, // /guide/index.md
{ text: 'One', link: '/guide/one' }, // /guide/one.md
{ text: 'Two', link: '/guide/two' } // /guide/two.md
{ text: 'Index', link: '/guide/' },
{ text: 'One', link: '/guide/one' },
{ text: 'Two', link: '/guide/two' }
]
}
],

// This sidebar gets displayed when user is
// under `config` directory.
// This sidebar gets displayed when a user
// is on `config` directory.
'/config/': [
{
text: 'Config',
items: [
// This shows `/config/index.md` page.
{ text: 'Index', link: '/config/' }, // /config/index.md
{ text: 'Three', link: '/config/three' }, // /config/three.md
{ text: 'Four', link: '/config/four' } // /config/four.md
{ text: 'Index', link: '/config/' },
{ text: 'Three', link: '/config/three' },
{ text: 'Four', link: '/config/four' }
]
}
]
Expand Down
Loading

0 comments on commit c35a1f0

Please sign in to comment.