feat(v2): auto-generated sidebars, frontmatter-less sites (#4582)

* POC of autogenerated sidebars

* use combine-promises utility lib

* autogenerated sidebar poc working

* Revert "autogenerated sidebar poc working"

This reverts commit c81da980

* POC of auto-generated sidebars for community docs

* update tests

* add initial test suite for autogenerated sidebars + fix some edge cases

* Improve autogen sidebars: strip more number prefixes in folder breadcrumb + slugs

* fix typo!

* Add tests for partially generated sidebars + fix edge cases + extract sidebar generation code

* Ability to read category metadatas file from a file in the category

* fix tests

* change position of API

* ability to extract number prefix

* stable system to enable position frontmatter

* fix tests for autogen sidebar position

* renamings

* restore community sidebars

* rename frontmatter position -> sidebar_position

* make sidebarItemsGenerator fn configurable

* minor changes

* rename dirPath => dirName

* Make the init template use autogenerated sidebars

* fix options

* fix docusaurus site: remove test docs

* add _category_ file to docs pathsToWatch

* add _category_ file to docs pathsToWatch

* tutorial: use sidebar_position instead of file number prefixes

* Adapt Docusaurus tutorial for autogenerated sidebars

* remove slug: /

* polish the homepage template

* rename _category_ sidebar_position to just "position"

* test for custom sidebarItemsGenerator fn

* fix category metadata + add link to report tutorial issues

* fix absolute path breaking tests

* fix absolute path breaking tests

* Add test for floating number sidebar_position

* add sidebarItemsGenerator unit tests

* add processSidebars unit tests

* Fix init template broken links

* windows test

* increase code translations test timeout

* cleanup mockCategoryMetadataFiles after windows test fixed

* update init template positions

* fix windows tests

* fix comment

* Add autogenerated sidebar items documentation + rewrite the full sidebars page doc

* add useful comment

* fix code block title
This commit is contained in:
Sébastien Lorber 2021-04-15 16:20:11 +02:00 committed by GitHub
parent 836f92708a
commit db79d462ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 2887 additions and 306 deletions

View file

@ -4,12 +4,20 @@ title: Creating Pages
slug: /creating-pages
---
In this section, we will learn about creating ad-hoc pages in Docusaurus using React. This is most useful for creating one-off standalone pages like a showcase page, playground page or support page.
In this section, we will learn about creating pages in Docusaurus.
This is useful for creating **one-off standalone pages** like a showcase page, playground page or support page.
The functionality of pages is powered by `@docusaurus/plugin-content-pages`.
You can use React components, or Markdown.
:::note
Pages do not have sidebars, only [docs](./docs/docs-introduction.md) have.
:::
## Add a React page {#add-a-react-page}
Create a file `/src/pages/helloReact.js`:

View file

@ -4,170 +4,241 @@ title: Sidebar
slug: /sidebar
---
To generate a sidebar to your Docusaurus site:
Creating a sidebar is useful to:
- Group multiple **related documents**
- **Display a sidebar** on each of those documents
- Provide a **paginated navigation**, with next/previous button
To use sidebars on your Docusaurus site:
1. Define a file that exports a [sidebar object](#sidebar-object).
1. Pass this object into the `@docusaurus/plugin-docs` plugin directly or via `@docusaurus/preset-classic`.
```js {8-9} title="docusaurus.config.js"
```js title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// Sidebars filepath relative to the site dir.
// highlight-start
sidebarPath: require.resolve('./sidebars.js'),
// highlight-end
},
// ...
},
],
],
};
```
## Sidebar object {#sidebar-object}
## Default sidebar
A sidebar object contains [sidebar items](#understanding-sidebar-items) and it is defined like this:
```typescript
type Sidebar = {
[sidebarId: string]:
| {
[sidebarCategory: string]: SidebarItem[];
}
| SidebarItem[];
};
```
For example:
By default, Docusaurus [automatically generates a sidebar](#sidebar-item-autogenerated) for you, by using the filesystem structure of the `docs` folder:
```js title="sidebars.js"
module.exports = {
docs: [
mySidebar: [
{
type: 'category',
label: 'Getting Started',
items: ['greeting'],
},
{
type: 'category',
label: 'Docusaurus',
items: ['doc1'],
type: 'autogenerated',
dirName: '.', // generate sidebar slice from the docs folder (or versioned_docs/<version>)
},
],
};
```
In this example, notice the following:
You can also define your sidebars explicitly.
- The key `docs` is the id of the sidebar. The id can be any value, not necessarily `docs`.
- `Getting Started` is a category within the sidebar.
- `greeting` and `doc1` are both [sidebar item](#understanding-sidebar-items).
## Sidebar object {#sidebar-object}
Shorthand notation can also be used:
A sidebar is a **tree of [sidebar items](#understanding-sidebar-items)**.
```typescript
type Sidebar =
// Normal syntax
| SidebarItem[]
// Shorthand syntax
| Record<
string, // category label
SidebarItem[] // category items
>;
```
A sidebars file can contain **multiple sidebar objects**.
```typescript
type SidebarsFile = Record<
string, // sidebar id
Sidebar
>;
```
Example:
```js title="sidebars.js"
module.exports = {
docs: {
'Getting started': ['greeting'],
Docusaurus: ['doc1'],
},
mySidebar: [
{
type: 'category',
label: 'Getting Started',
items: ['doc1'],
},
{
type: 'category',
label: 'Docusaurus',
items: ['doc2', 'doc3'],
},
],
};
```
:::note
Notice the following:
Shorthand notation relies on the iteration order of JavaScript object keys for the category name. When using this notation, keep in mind that EcmaScript does not guarantee `Object.keys({a,b}) === ['a','b']`, yet this is generally true.
- There is a single sidebar `mySidebar`, containing 5 [sidebar items](#understanding-sidebar-items)
- `Getting Started` and `Docusaurus` are sidebar categories
- `doc1`, `doc2` and `doc3` are sidebar documents
:::tip
Use the **shorthand syntax** to express this sidebar more concisely:
```js title="sidebars.js"
module.exports = {
mySidebar: {
'Getting started': ['doc1'],
Docusaurus: ['doc2', 'doc3'],
},
};
```
:::
## Using multiple sidebars {#using-multiple-sidebars}
You can have multiple sidebars for different Markdown files by adding more top-level keys to the exported object.
You can create a sidebar for each **set of markdown files** that you want to **group together**.
:::tip
The Docusaurus site is a good example of using multiple sidebars:
- [Docs](../../introduction.md)
- [API](../../cli.md)
:::
Example:
```js title="sidebars.js"
module.exports = {
firstSidebar: {
'Category A': ['doc1'],
},
secondSidebar: {
'Category A': ['doc2'],
'Category B': ['doc3'],
tutorialSidebar: {
'Category A': ['doc1', 'doc2'],
},
apiSidebar: ['doc3', 'doc4'],
};
```
By default, the doc page the user is reading will display the sidebar that it is part of. This can be customized with the [sidebar type](#understanding-sidebar-items).
:::note
For example, with the above example, when displaying the `doc2` page, the sidebar will contain the items of `secondSidebar` only. Another example of multiple sidebars is the `API` and `Docs` sections on the Docusaurus website.
The keys `tutorialSidebar` and `apiSidebar` are sidebar **technical ids** and do not matter much.
For more information about sidebars and how they relate to doc pages, see [Navbar doc link](../../api/themes/theme-configuration.md#navbar-doc-link).
:::
When browsing:
- `doc1` or `doc2`: the `tutorialSidebar` will be displayed
- `doc3` or `doc4`: the `apiSidebar` will be displayed
A **paginated navigation** link documents inside the same sidebar with **next and previous buttons**.
## Understanding sidebar items {#understanding-sidebar-items}
As the name implies, `SidebarItem` is an item defined in a Sidebar. A SidebarItem as a `type` that defines what the item links to.
`SidebarItem` is an item defined in a Sidebar tree.
`type` supports the following values
There are different types of sidebar items:
- [Doc](#linking-to-a-doc-page)
- [Link](#creating-a-generic-link)
- [Ref](#creating-a-link-to-page-without-sidebar)
- [Category](#creating-a-hierarchy)
- **[Doc](#sidebar-item-doc)**: link to a doc page, assigning it to the sidebar
- **[Ref](#sidebar-item-ref)**: link to a doc page, without assigning it to the sidebar
- **[Link](#sidebar-item-link)**: link to any internal or external page
- **[Category](#sidebar-item-category)**: create a hierarchy of sidebar items
- **[Autogenerated](#sidebar-item-autogenerated)**: generate a sidebar slice automatically
### Linking to a doc page {#linking-to-a-doc-page}
### Doc: link to a doc {#sidebar-item-doc}
Set `type` to `doc` to link to a documentation page. This is the default type.
Use the `doc` type to link to a doc page and assign that doc to a sidebar:
```typescript
type SidebarItemDoc =
| string
// Normal syntax
| {
type: 'doc';
id: string;
label: string; // Sidebar label text
};
}
// Shorthand syntax
| string; // docId shortcut
```
Example:
```js
{
type: 'doc',
id: 'doc1', // string - document id
label: 'Getting started' // Sidebar label text
}
```
The `sidebar_label` in the markdown frontmatter has a higher precedence over the `label` key in `SidebarItemDoc`. Using just the [Document ID](#document-id) is also valid, the following is equivalent to the above:
```js
'doc1'; // string - document id
```
Using this type will bind the linked doc to current sidebar. This means that if you access the `doc1` page, the sidebar displayed will be the sidebar that contains this doc page.
In the example below, `doc1` is bound to `firstSidebar`.
```js title="sidebars.js"
module.exports = {
firstSidebar: {
'Category A': ['doc1'],
},
secondSidebar: {
'Category A': ['doc2'],
'Category B': ['doc3'],
},
mySidebar: [
// Normal syntax:
// highlight-start
{
type: 'doc',
id: 'doc1', // document id
label: 'Getting started', // sidebar label
},
// highlight-end
// Shorthand syntax:
// highlight-start
'doc2', // document id
// highlight-end
],
};
```
### Creating a generic link {#creating-a-generic-link}
The `sidebar_label` markdown frontmatter has a higher precedence over the `label` key in `SidebarItemDoc`.
Set `type` to `link` to link to a non-documentation page. For example, to create an external link.
:::note
Don't assign the same doc to multiple sidebars: use a [ref](#sidebar-item-ref) instead.
:::
### Ref: link to a doc, without sidebar {#sidebar-item-ref}
Use the `ref` type to link to a doc page without assigning it to a sidebar.
```typescript
type SidebarItemRef = {
type: 'ref';
id: string;
};
```
Example:
```js title="sidebars.js"
module.exports = {
mySidebar: [
{
type: 'ref',
id: 'doc1', // Document id (string).
},
],
};
```
When browsing `doc1`, Docusaurus **will not display** the `mySidebar` sidebar.
### Link: link to any page {#sidebar-item-link}
Use the `link` type to link to any page (internal or external) that is not a doc.
```typescript
type SidebarItemLink = {
@ -179,43 +250,41 @@ type SidebarItemLink = {
Example:
```js
{
type: 'link',
label: 'Custom Label', // The label that should be displayed (string).
href: 'https://example.com' // The target URL (string).
}
```
```js title="sidebars.js"
module.exports = {
myLinksSidebar: [
// highlight-start
// External link
{
type: 'link',
label: 'Facebook', // The link label
href: 'https://facebook.com', // The external URL
},
// highlight-end
### Creating a link to page without sidebar {#creating-a-link-to-page-without-sidebar}
Set `type` to `ref` to link to a documentation page without binding it to a sidebar. This means the sidebar disappears when the user displays the linked page.
```typescript
type SidebarItemRef = {
type: 'ref';
id: string;
// highlight-start
// Internal link
{
type: 'link',
label: 'Home', // The link label
href: '/', // The internal path
},
// highlight-end
],
};
```
Example:
### Category: create a hierarchy {#sidebar-item-category}
```js
{
type: 'ref',
id: 'doc1', // Document id (string).
}
```
### Creating a hierarchy {#creating-a-hierarchy}
The Sidebar item type that creates a hierarchy in the sidebar. Set `type` to `category`.
Use the `category` type to create a hierarchy of sidebar items.
```typescript
type SidebarItemCategory = {
type: 'category';
label: string; // Sidebar label text.
items: SidebarItem[]; // Array of sidebar items.
// Category options:
collapsed: boolean; // Set the category to be collapsed or open by default
};
```
@ -225,16 +294,16 @@ Example:
```js title="sidebars.js"
module.exports = {
docs: [
{
...
},
{
type: 'category',
label: 'Guides',
collapsed: false,
items: [
'guides/creating-pages',
'creating-pages',
{
Docs: ['docs-introduction', 'docs-sidebar', 'markdown-features', 'versioning'],
type: 'category',
label: 'Docs',
items: ['introduction', 'sidebar', 'markdown-features', 'versioning'],
},
],
},
@ -242,7 +311,9 @@ module.exports = {
};
```
**Note**: it's possible to use the shorthand syntax to create nested categories:
:::tip
Use the **shorthand syntax** when you don't need **category options**:
```js title="sidebars.js"
module.exports = {
@ -250,28 +321,25 @@ module.exports = {
Guides: [
'creating-pages',
{
Docs: [
'docs-introduction',
'docs-sidebar',
'markdown-features',
'versioning',
],
Docs: ['introduction', 'sidebar', 'markdown-features', 'versioning'],
},
],
},
};
```
:::
#### Collapsible categories {#collapsible-categories}
For sites with a sizable amount of content, we support the option to expand/collapse a category to toggle the display of its contents. Categories are collapsible by default. If you want them to be always expanded, set `themeConfig.sidebarCollapsible` to `false`:
```js {4} title="docusaurus.config.js"
```js title="docusaurus.config.js"
module.exports = {
// ...
themeConfig: {
// highlight-start
sidebarCollapsible: false,
// ...
// highlight-end
},
};
```
@ -296,16 +364,189 @@ module.exports = {
};
```
### Autogenerated: generate a sidebar {#sidebar-item-autogenerated}
Docusaurus can **create a sidebar automatically** from your **filesystem structure**: each folder creates a sidebar category.
An `autogenerated` item is converted by Docusaurus to a **sidebar slice**: a list of items of type `doc` and `category`.
```typescript
type SidebarItemAutogenerated = {
type: 'autogenerated';
dirName: string; // Source folder to generate the sidebar slice from (relative to docs)
};
```
Docusaurus can generate a sidebar from your docs folder:
```js title="sidebars.js"
module.exports = {
myAutogeneratedSidebar: [
// highlight-start
{
type: 'autogenerated',
dirName: '.', // '.' means the current docs folder
},
// highlight-end
],
};
```
You can also use **multiple `autogenerated` items** in a sidebar, and interleave them with regular sidebar items:
```js title="sidebars.js"
module.exports = {
mySidebar: [
'intro',
{
type: 'category',
label: 'Tutorials',
items: [
'tutorial-intro',
// highlight-start
{
type: 'autogenerated',
dirName: 'tutorials/easy', // Generate sidebar slice from docs/tutorials/easy
},
// highlight-end
'tutorial-medium',
// highlight-start
{
type: 'autogenerated',
dirName: 'tutorials/advanced', // Generate sidebar slice from docs/tutorials/hard
},
// highlight-end
'tutorial-end',
],
},
// highlight-start
{
type: 'autogenerated',
dirName: 'guides', // Generate sidebar slice from docs/guides
},
// highlight-end
{
type: 'category',
label: 'Community',
items: ['team', 'chat'],
},
],
};
```
#### Autogenerated sidebar metadatas {#autogenerated-sidebar-metadatas}
By default, the sidebar slice will be generated in **alphabetical order** (using files and folders names).
If the generated sidebar does not look good, you can assign additional metadatas to docs and categories.
**For docs**: use additional frontmatter:
```diff title="docs/tutorials/tutorial-easy.md"
+ ---
+ sidebar_label: Easy
+ sidebar_position: 2
+ ---
# Easy Tutorial
This is the easy tutorial!
```
**For categories**: add a `_category_.json` or `_category_.yml` file in the appropriate folder:
```json title="docs/tutorials/_category_.json"
{
"label": "Tutorial",
"position": 3
}
```
```yaml title="docs/tutorials/_category_.yml"
label: 'Tutorial'
position: 2.5 # float position is supported
collapsed: false # keep the category open by default
```
:::info
The position metadata is only used **inside a sidebar slice**: Docusaurus does not re-order other items of your sidebar.
:::
#### Using number prefixes
A simple way to order an autogenerated sidebar is to prefix docs and folders by number prefixes:
```bash
docs
├── 01-Intro.md
├── 02-Tutorial Easy
│   ├── 01-First Part.md
│   ├── 02-Second Part.md
│   └── 03-End.md
├── 03-Tutorial Hard
│   ├── 01-First Part.md
│   ├── 02-Second Part.md
│   ├── 03-Third Part.md
│   └── 04-End.md
└── 04-End.md
```
To make it **easier to adopt**, Docusaurus supports **multiple number prefix patterns**.
By default, Docusaurus will **remove the number prefix** from the doc id, title, label and url paths.
:::caution
**Prefer using [additional metadatas](#autogenerated-sidebar-metadatas)**.
Updating a number prefix can be annoying, as it can require **updating multiple existing markdown links**:
```diff title="docs/02-Tutorial Easy/01-First Part.md"
- Check the [Tutorial End](../04-End.md);
+ Check the [Tutorial End](../05-End.md);
```
:::
#### Customize the sidebar items generator
You can provide a custom `sidebarItemsGenerator` function in the docs plugin (or preset) config:
```js title="docusaurus.config.js"
module.exports = {
plugins: [
[
'@docusaurus/plugin-content-docs',
{
/**
* Function used to replace the sidebar items of type "autogenerated"
* by real sidebar items (docs, categories, links...)
*/
// highlight-start
sidebarItemsGenerator: function ({item, version, docs}) {
// Use the provided data to create a custom "sidebar slice"
return [{type: 'doc', id: 'doc1'}];
},
// highlight-end
},
],
],
};
```
## Hideable sidebar {#hideable-sidebar}
Using the enabled `themeConfig.hideableSidebar` option, you can make the entire sidebar hidden, allowing you to better focus your users on the content. This is especially useful when content consumption on medium screens (e.g. on tablets).
```js {4} title="docusaurus.config.js"
```js title="docusaurus.config.js"
module.exports = {
// ...
themeConfig: {
// highlight-starrt
hideableSidebar: true,
// ...
// highlight-end
},
};
```
@ -323,3 +564,21 @@ To pass in custom props to a swizzled sidebar item, add the optional `customProp
}
}
```
## Complex sidebars example {#complex-sidebars-example}
Real-world example from the Docusaurus site:
```mdx-code-block
import CodeBlock from '@theme/CodeBlock';
<CodeBlock className="language-js" title="sidebars.js">
{require('!!raw-loader!@site/sidebars.js')
.default
.split('\n')
// remove comments
.map((line) => !['#','/*','*'].some(commentPattern => line.trim().startsWith(commentPattern)) && line)
.filter(Boolean)
.join('\n')}
</CodeBlock>
```