dotnet / docfx

Static site generator for .NET API documentation.
https://dotnet.github.io/docfx/
MIT License
4.02k stars 855 forks source link

Breadcrumb bar initially shows the item selected from dropdown, but not after navigating to child item #7375

Open KalleOlaviNiemitalo opened 3 years ago

KalleOlaviNiemitalo commented 3 years ago

Operating System: Windows

DocFX Version Used: 2.57.2

Template used: default

Steps to Reproduce:

  1. Expand docfx-dropdown-breadcrumb.zip
  2. cd docfx-dropdown-breadcrumb
  3. docfx build --serve
  4. Browse to http://localhost:8080/
  5. Click "Mobile apps" in the top navigation bar. The dropdown list opens and contains items "App A" and "App B". image
  6. Click "App A" in the dropdown list. This navigates to http://localhost:8080/apps/a/index.html. The breadcrumb bar says "App A". The table of contents shows that there is only one child item, "Credits". image
  7. Click "Credits" in the table of contents. This navigates to http://localhost:8080/apps/a/credits.html.

Expected Behavior:

The breadcrumb bar shows "App A" and "Credits".

Actual Behavior:

The breadcrumb bar shows only "Credits". image

KalleOlaviNiemitalo commented 3 years ago

In contrast, if I define the main toc.yml without a dropdown menu:

- name: App A
  topicHref: apps/a/index.md
  href: apps/a/
- name: App B
  href: apps/b/index.md
  tocHref: apps/b/toc.yml

then http://localhost:8080/apps/a/credits.html shows "App A / Credits" in the breadcrumb bar: image

KalleOlaviNiemitalo commented 3 years ago

It seems renderBreadcrumb is called twice per page: first rom renderNavbar in loadNavbar, and then from renderSidebar in loadToc. I'll ignore the first call because the second call will overwrite the breadcrumb bar.

http://localhost:8080/apps/a/index.html with the dropdown menu

During the second renderBreadcrumb call, $('#navbar a.active') contains one element, a hyperlink "App A". #navbar is:

<div class="collapse navbar-collapse" id="navbar">
    <form class="navbar-form navbar-right" role="search" id="search">
        <div class="form-group">
            <input type="text" class="form-control" id="search-query" placeholder="Search" autocomplete="off">
        </div>
    </form>
    <ul class="nav level1 navbar-nav">
        <li class="dropdown active">
            <a class="dropdown-toggle" data-toggle="dropdown" href="../../#" role="button" aria-haspopup="true" aria-expanded="false">Mobile apps <span class="caret"></span></a>
            <ul class="dropdown-menu level2">
                <li class="active"><a href="../../apps/a/index.html" class="active">App A</a></li>
                <li><a href="../../apps/b/index.html">App B</a></li>
            </ul>
        </li>
    </ul>
</div>

$('#toc a.active') is empty. #toc is:

<div class="toc" id="toc">
    <ul class="nav level1">
        <li>
            <a href="credits.html" name="" title="Credits">Credits</a>
        </li>
    </ul>
</div>

This gives "App A" in the breadcrumb bar, which is OK.

http://localhost:8080/apps/a/credits.html with the dropdown menu

During the second renderBreadcrumb call, $('#navbar a.active') is still empty. #navbar is:

<div class="collapse navbar-collapse" id="navbar">
    <form class="navbar-form navbar-right" role="search" id="search">
        <div class="form-group">
            <input type="text" class="form-control" id="search-query" placeholder="Search" autocomplete="off">
        </div>
    </form>
    <ul class="nav level1 navbar-nav">
        <li class="dropdown">
            <a class="dropdown-toggle" data-toggle="dropdown" href="../../#" role="button" aria-haspopup="true" aria-expanded="false">Mobile apps <span class="caret"></span></a>
            <ul class="dropdown-menu level2">
                <li><a href="../../apps/a/index.html">App A</a></li>
                <li><a href="../../apps/b/index.html">App B</a></li>
            </ul>
        </li>
    </ul>
</div>

$('#toc a.active') contains one element, a hyperlink "Credits". #toc is:

<div class="toc" id="toc">
    <ul class="nav level1">
        <li class="active in">
            <a href="credits.html" name="" title="Credits" class="active">Credits</a>
        </li>
    </ul>
</div>

This gives only "Credits" in the breadcrumb bar, but I want "App A / Credits" instead.

If "App A" in the dropdown menu had class="active", then the breadcrumb bar would have "App A / Credits" like I want.

KalleOlaviNiemitalo commented 3 years ago

Hybrid TOC:

- name: Mobile apps
  dropdown: true
  items:
  - name: App A
    topicHref: apps/a/index.md
    href: apps/a/
- name: App B
  href: apps/b/index.md
  tocHref: apps/b/toc.yml

This generates _site/toc.html:

<div id="sidetoggle">
  <div>
    <div class="sidefilter">
      <form class="toc-filter">
        <span class="glyphicon glyphicon-filter filter-icon"></span>
        <span class="glyphicon glyphicon-remove clear-icon" id="toc_filter_clear"></span>
        <input type="text" id="toc_filter_input" placeholder="Enter here to filter..." onkeypress="if(event.keyCode==13) {return false;}">
      </form>
    </div>
    <div class="sidetoc">
      <div class="toc" id="toc">

          <ul class="nav level1">
                <li class="dropdown">
                  <a class="dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">Mobile apps <span class="caret"></span></a>
                  <ul class="dropdown-menu level2">
                      <li><a href="apps/a/index.html">App A</a></li>
                  </ul>
                </li>
                <li>
                    <a href="apps/b/index.html" name="apps/b/toc.html" title="App B">App B</a>
                </li>
          </ul>
      </div>
    </div>
  </div>
</div>

The hyperlink "App B" has an attribute name="apps/b/toc.html", but the hyperlink "App A" does not. I think the omission confuses this originalHref check in docfx.js: https://github.com/dotnet/docfx/blob/8b9b9c92bc041e5c903d45e0112a971ea956eab3/src/docfx.website.themes/default/styles/docfx.js#L391-L397

Indeed, if I edit _site/toc.html, add name="apps/a/toc.html" to the link, and run docfx serve _site, then I get the whole "App A / Credits" breadcrumb at http://localhost:8080/apps/a/credits.html.

I think the name attribute is normally added by li.tmpl.partial here: https://github.com/dotnet/docfx/blob/8b9b9c92bc041e5c903d45e0112a971ea956eab3/src/docfx.website.themes/default/partials/li.tmpl.partial#L11

But if the dropdown flag is set, then li.tmpl.partial formats the items with {{>partials/dd-li}}, which does not add a name attribute: https://github.com/dotnet/docfx/blob/8b9b9c92bc041e5c903d45e0112a971ea956eab3/src/docfx.website.themes/default/partials/dd-li.tmpl.partial#L1-L3

KalleOlaviNiemitalo commented 3 years ago

If I override partials/dd-li.tmpl.partial with a custom template, then I get the correct breadcrumbs.

If I understand correctly, a pull request for fixing this would be required to include a test, which could become large enough to be copyrighted, and .NET Foundation would then require a CLA. So I don't think I'll make a PR.