facebook / docusaurus

Easy to maintain open source documentation websites.
https://docusaurus.io
MIT License
55.99k stars 8.4k forks source link

Add Breadcrumb customization options #6953

Open ben-qnimble opened 2 years ago

ben-qnimble commented 2 years ago

Have you read the Contributing Guidelines on issues?

Prerequisites

Description

If you have the docs module configured where the structure matches the category index convention as mentioned for autogenerated sidebars, for example you have the following pages and URLs:

/docs/docs.md -> /docs/ /docs/item1.md -> /docs/item1 /docs/cat1/cat1.md -> /docs/cat1 /docs/cat1/sub1.md -> /docs/cat1/sub1 /docs/cat1/sub2.md -> /docs/cat1/sub2

it seems like in this usage, the breadcrumbs should match the URL structure and show all the links contained in the full URL. However, if you are on the sub2.md page, the bread crumbs goes from "/" to "/docs/cat1" and skip over the "/docs" url link:

"/" -> "/docs/cat1" -> "/docs/cat1/sub2"

I think the breadcrumb should either be

"/docs" -> "/docs/cat1" -> "/docs/cat1/sub2" or "/" -> "/docs" -> "/docs/cat1" -> "/docs/cat1/sub2"

Basically, the first idea would be to have the breadcrumbs "home" to go to "/docs" instead of "/" (when /docs is a valid URL). I think this makes sense since the breadcrumbs are specific to the docs module, so it isn't clear that "home" should go to the website home instead of the docs home. Maybe the way to implement this is for a parameter to set the breadcrumb home URL.

Another option would to (maybe optionally) add the "/docs" to the breadcrumb structure.

Reproducible demo

https://docusaurus.io/docs/sidebar/autogenerated

Steps to reproduce

Create structure like shown in https://docusaurus.io/docs/sidebar/autogenerated and enable breadcrumbs.

Expected behavior

breakcrumbs would look like

"/docs" -> "/docs/cat1" -> "/docs/cat1/sub2"

or

"/" -> "/docs" -> "/docs/cat1" -> "/docs/cat1/sub2"

Actual behavior

breadcrumbs are

"/" -> "/docs/cat1" -> "/docs/cat1/sub2"

Your environment

Tested on beta17

Self-service

Josh-Cena commented 2 years ago

Hi, in a perfect world, this would be the case; however, this is virtually not achievable.

Routes are very arbitrary. Because you can use slug to customize a page's route, the existence of /docs/cat1/sub2 doesn't imply the existence of /docs or /docs/cat1 at all. And even if those routes exist, there's no implication that they are logically connected to /docs/cat1/sub2. Therefore, we would generate breadcrumbs based on the sidebar structure.

Now, maybe the issue is just a misconception of how breadcrumbs work, but being maximally charitable, this is actually what we plan to do: including more segments in the breadcrumbs. However, the problem is that the /docs path means the root path of a plugin, but plugins don't have human-readable labels yet. Moreover, we'd also like to include the sidebar in that, so that the breadcrumbs would be like plugin name > sidebar name > position of this doc in the sidebar. See https://github.com/facebook/docusaurus/discussions/6786.

ben-qnimble commented 2 years ago

Thanks for the info, this is more complex than I appreciated. It seems like the discussions in #6786 may include changes that would address my issue. I've posted a comment there.

slorber commented 2 years ago

As commented in the other issue, it looks like a plugin option such as:

breadcrumbPrefix: [
  { to: "/", label: "🏠" },
  { to: "/docs", label: "Docs home" }
];

could work for you?

Eventually, we could also add "breadcrumb item types", to cover more dynamic cases like showing a human-readable sidebar label?

breadcrumbPrefix: [
  { to: "/", label: "🏠" },
  { to: "/docs", label: "Docs home" },
  { type: "sidebarName" }
];

Does it make sense?

(note we don't have any human-readable docs-plugin label or sidebar label yet)


Some related questions:

ben-qnimble commented 2 years ago

I don't think I fully appreciate what can be done if we have the option type: "sidebarName", but I like the breadcrumbPrefix idea in general.

I think having an individual doc override the breadcrumb makes sense. Something like:

---
id: MainDoc
slug: /
breadcrumbPrefix: [ { to: "/", label: "🏠" }]
---

I think this might be useful when the default breadcrumbPrefix is ["/", "/docs"] for the doc that has a slug of "/" (full url is "/docs") so the breadcrumb isn't "

/ -> /docs -> /docs

where the last item is the current page, which is also the last item in the breadcrumbPrefix. (Although maybe this could be address in other logic.)

slorber commented 2 years ago

I don't think I fully appreciate what can be done if we have the option type: "sidebarName",

Docusaurus site has 2 sidebars: Docs and API

This means that one of those labels could eventually appear in the breadcrumb.

For example: Home > API > CLI instead of

image

adding frontMatter can be convenient yes.

Maybe for maximum flexibility you should be able to provide the full breadcrumb too?

breadcrumb: [ { to: "/", label: "🏠" } , {to:  "/docs/xyz", label :"XYZ" }]

breadcrumb:
  prefix: [ { to: "/", label: "🏠" }]

🤷‍♂️


I don't understand the last part 😅

ben-qnimble commented 2 years ago

Thanks for the explanation. Having { type: "sidebarName" } as an option makes sense now. I think sites with multiple sidebars could use that functionality.

My point about an individual doc overriding the default breadcrumb prefix is that when on a page that is included in the default breadcrumb prefix, that might generate a breadcrumb that lists the page twice: once as part of the prefix, and once as the current page. Having the means to change the breadcrumb prefix for just that page might be a useful way to avoid that issue. (But again, there are other solutions).

I think having a breadcrumb prefix and a full breadcrumb override makes sense. One use case could be shortening the breadcrumbs (skipping some entries) for sites that have lots of structure. Or localizing it so you can't go 'too far' back in one shot.

chernodub commented 2 years ago

Hi guys, firstly, thanks for your contribution to the community, I really appreciate what you're doing and my team enjoys using Docusaurus.

Preface/context We're currently using it to build an internal wiki website that would have several tech-stack scopes. Basically, the structure is similar like in @slorber's [example](https://github.com/facebook/docusaurus/issues/6953#issuecomment-1076498639). At the same time, we'd like to preserve the consistency within the `docs/` directory. Docs structure would be similar to the following (example, similar to ours, in a way): ```text docs/ frontend/ frameworks/ angular/ best-practices.md whatever.md backend/ python/ best-practices.md whatever.md ``` We're using several autogenerated sidebars (each one represents a directory within `docs/` folder). By using `autogenerated` + `dirName` in `sidebars.js`. I've also provided an [example on stackblitz](https://stackblitz.com/edit/github-puw1pj), consider checking it out. It's all working fine except the breadcrumbs, it basically ignores the starting directory in `docs/`, since sidebar considers a directory from `dirName` to be a root. (e.g. for `/docs/backend/python/best-practices/` we would get `🏠 > Python > Best Practices`). This is not what we really need, and I'd like to propose a solution too.

As the guys suggested, we could use the breadcrumb prefix like: [{ to: "/", label: "🏠" } , {to: "/docs/xyz", label :"XYZ" }]. But, when leveraging autogenerated sidebar, it's going to be a pain to add this meta-header for each file within a directory (also, this would break the auto-generation purpose, in a way, since each child document would know where it lies).

So, my idea would be to also be able to use _category_.json (or some other meta file) for providing a prefix for all of its descendants. Here's an example:

docs/
  frontend/
    _category_.json <!-- { breadcrumbPrefix: [ { to: "/", label: "🏠" } , {to:  "/frontend", label :"Frontend" }] } -->
    frameworks/
      angular/
        best-practices.md
        whatever.md
  backend/
    _category_.json <!-- { breadcrumbPrefix: [ { to: "/", label: "🏠" } , {to:  "/backend", label :"Backend" }] } -->
    python/
      best-practices.md
      whatever.md

This way, a breacrumb prefix could be auto-generated for all of the pages within these directories. For /docs/frameworks/angular/best-practices it would be 🏠 > Frontend > Framework > Angular > Best Practices. Please note that best-practices.md doesn't have any information regarding the breadcrumb, it's inherited by one of the parent's _category_.json.

Also, so as not to duplicate the original root prefix, and to avoid the possible conflicts with it (in case it's changed in future), we could use some string constant, like "default_root" instead of { to: "/", label: "🏠" }. As a result, we would get something like breadcrumbsPrefix: ["default_root", {to: "/backend", label :"Backend" }]. Or, in case this is not going to work, we could also use some other header that would implicitly append our prefix to the root. Example: additionalBreadcrumbPrefix: [{to: "/backend", label :"Backend" }]

I'm interested to hear your opinion on this, guys. And thanks again!

ben-qnimble commented 2 years ago

Hi chernodub,

I think I'm understanding what you want, and it seems to me, unless I'm missing something, that as currently structured the breadcrumbs will do what you want (except for the link to /docs). If you use the category index convention, and have:

docs/
  frontend/
    index.md   -- this is the page for the url "docs/frontend/"
    frameworks/
      index.md   -- this is the page for the url "docs/frontend/frameworks/"
      angular/
        index.md   -- this is the page for the url "docs/frontend/frameworks/angular/"
        best-practices.md
        whatever.md
  backend/
   index.md   -- this is the page for the url "docs/backend/"
    python/
      index.md   -- this is the page for the url "docs/backend/python/"
      best-practices.md
      whatever.md

then you will get an auto-generated breadcrumbs for /docs/backend/python/whatever of "Home" -> "Backend" -> "Python" -> "Whatever" and for /docs/frontend/frameworks/angular/whatever of "Home" -> "Frontend" -> "Frameworks" -> "Angular" -> "Whatever"

I think the only part that is missing is if you want "Home" -> "Docs" -> "Frontend" and/or to "Docs/" -> "Frontend" without the "Home"

Am I misunderstanding anything?

chernodub commented 2 years ago

Yes, you're right, that was my mistake, since I didn't know (at the time of writing my previous comment) that a root folder (specified in sidebars.js) is not considered to be a category in Docusaurus. So, _category_.json/index.md doesn't really fit here. Consider this example, root folders are not present in breadcrumb. What I need is to set a breadcrumbPrefix for each descendant of frontend/backend roots. I think it is only achievable either by providing some kind of root metadata, or by passing an additional property to autogenerated sidebar, like the following:

sidebars.js

const sidebars = {
  frontend: [{type: 'autogenerated', dirName: 'frontend', breadcrumb: 'Frontend'}],
  dotnet: [{type: 'autogenerated', dirName: 'dotnet', breadcrumb: 'Backend'}],
};

I still may miss something, so feel free to lead me to a right direction

Josh-Cena commented 2 years ago

Autogenerated will be eventually unwrapped to regular sidebar items, so the idea is that we need to set metadata for each sidebar, like frontend: { breakdcrumb: "Frontend", items: [{ type: "autogenerated", dirName: "frontend"] } }, which is currently not possible (each sidebar is only an array of items; no place for metadata).

chernodub commented 2 years ago

Sounds reasonable, this will definitely fit a use-case I described before

kolos450 commented 2 years ago

It would be great to have an option to provide some arbitrary breadcrumbs to Docusaurus.

Previously I used a custom breadcrumbs component for some pages of a Docusaurus website with a complex structure. But now there's a cute out-of-the-box component and I would like to enable it for the rest of the website. The point is that I cannot use a single template currently and have to copy it to ensure the components look indistinguishable.

Perhaps it would be acceptable to introduce something like sidebarItemsGenerator docs plugin option but for breadcrumbs?

ezbeazy commented 2 years ago

just a breadcrumb_label option in doc front matter would be great

slorber commented 2 years ago

frontMatter.breadcrumb_label + sidebarCategoryItem.breadcrumbLabel 👍 we can easily add this today until we figure more advanced customization cases.

Does anyone want to send a PR?

3v0k4 commented 1 year ago

I was trying to have the home breadcrumb link go to a custom URL (not /) and landed on this issue.

Solution

Eject

npm run swizzle @docusaurus/theme-classic DocBreadcrumbs -- --eject

Customize the URL

/src/theme/DocBreadcrumbs/index.js:

-  const homeHref = useBaseUrl('/');
+  const homeHref = useBaseUrl('/overview');

You should also update the aria labels in HomeBreadcrumbItem.


@slorber What if we extracted HomeBreadcrumbItem into a separate component that can be ejected without ejecting all the DocBreadcrumbs? I'd be happy to look into it and open a PR if it's useful 🙂

slorber commented 1 year ago

I'm not against extracting a few breadcrumbs components if that can simplify things on the short term until we figure out a good api/options.

We can keep the components as unsafe to swizzle until we find the best structure

octogonz commented 4 months ago

I was trying to have the home breadcrumb link go to a custom URL (not /) and landed on this issue.

For what it's worth, I encountered the same requirement.