From 5282083decf2e7c2c6c58fb4e6f163721d282275 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 20 Oct 2025 17:29:31 -0700 Subject: [PATCH] Fix heading nav with folded chapters This fixes an issue when folding is enabled. The folding was not properly hiding the sub-chapters because it was assuming it could hide the next list element. However, the heading nav was the next list element, so the remaining chapters remained visible. The solution required some deeper changes to how the chapters were organized in the sidebar. Instead of nested chapters being a list element *sibling*, the nested chapter's `ol` is now a *child* of its parent chapter. This makes it much easier to just hide everything without regard of the exact sibling order. This required wrapping the chapter title and the toggle chevron inside a span so that the flex layout could be localized to just those elements, and allow the following `ol` elements to lay out regularly. Closes https://github.com/rust-lang/mdBook/issues/2880 --- crates/mdbook-html/front-end/css/chrome.css | 30 +++-- .../front-end/templates/toc.js.hbs | 114 ++++++++++-------- .../src/html_handlebars/helpers/toc.rs | 60 ++++----- tests/gui/books/heading-nav-folded/book.toml | 6 + .../books/heading-nav-folded/src/SUMMARY.md | 7 ++ .../gui/books/heading-nav-folded/src/intro.md | 9 ++ .../books/heading-nav-folded/src/next-main.md | 1 + .../books/heading-nav-folded/src/sub/index.md | 3 + .../heading-nav-folded/src/sub/inner/index.md | 3 + .../heading-nav-folded/src/sub/second.md | 3 + tests/gui/books/heading-nav/src/SUMMARY.md | 1 + .../heading-nav/src/unusual-heading-levels.md | 9 ++ tests/gui/heading-nav-collapsed.goml | 63 +++++----- tests/gui/heading-nav-folded.goml | 3 + tests/gui/heading-nav-large-intro.goml | 2 +- tests/gui/heading-nav-markup.goml | 2 +- tests/gui/heading-nav-normal-intro.goml | 2 +- tests/gui/heading-nav-unusual-levels.goml | 7 ++ tests/testsuite/index.rs | 8 +- tests/testsuite/toc.rs | 6 + 20 files changed, 212 insertions(+), 127 deletions(-) create mode 100644 tests/gui/books/heading-nav-folded/book.toml create mode 100644 tests/gui/books/heading-nav-folded/src/SUMMARY.md create mode 100644 tests/gui/books/heading-nav-folded/src/intro.md create mode 100644 tests/gui/books/heading-nav-folded/src/next-main.md create mode 100644 tests/gui/books/heading-nav-folded/src/sub/index.md create mode 100644 tests/gui/books/heading-nav-folded/src/sub/inner/index.md create mode 100644 tests/gui/books/heading-nav-folded/src/sub/second.md create mode 100644 tests/gui/books/heading-nav/src/unusual-heading-levels.md create mode 100644 tests/gui/heading-nav-folded.goml create mode 100644 tests/gui/heading-nav-unusual-levels.goml diff --git a/crates/mdbook-html/front-end/css/chrome.css b/crates/mdbook-html/front-end/css/chrome.css index c9ed00c2..45bbd5ed 100644 --- a/crates/mdbook-html/front-end/css/chrome.css +++ b/crates/mdbook-html/front-end/css/chrome.css @@ -571,17 +571,18 @@ html:not(.sidebar-resizing) .sidebar { line-height: 2.2em; } -.chapter ol { - width: 100%; -} - .chapter li { - display: flex; color: var(--sidebar-non-existant); } + +/* This is a span wrapping the chapter link and the fold chevron. */ +.chapter-link-wrapper { + /* Used to position the chevron to the right, allowing the text to wrap before it. */ + display: flex; +} + .chapter li a { - display: block; - padding: 0; + /* Remove underlines. */ text-decoration: none; color: var(--sidebar-fg); } @@ -594,21 +595,22 @@ html:not(.sidebar-resizing) .sidebar { color: var(--sidebar-active); } -.chapter li > a.toggle { +/* This is the toggle chevron. */ +.chapter-fold-toggle { cursor: pointer; - display: block; + /* Positions the chevron to the side. */ margin-inline-start: auto; padding: 0 10px; user-select: none; opacity: 0.68; } -.chapter li > a.toggle div { +.chapter-fold-toggle div { transition: transform 0.5s; } /* collapse the section */ -.chapter li:not(.expanded) + li > ol { +.chapter li:not(.expanded) > ol { display: none; } @@ -617,10 +619,12 @@ html:not(.sidebar-resizing) .sidebar { margin-block-start: 0.6em; } -.chapter li.expanded > a.toggle div { +/* When expanded, rotate the chevron to point down. */ +.chapter li.expanded > span > .chapter-fold-toggle div { transform: rotate(90deg); } +/* Horizontal line in chapter list. */ .spacer { width: 100%; height: 3px; @@ -630,6 +634,7 @@ html:not(.sidebar-resizing) .sidebar { background-color: var(--sidebar-spacer); } +/* On touch devices, add more vertical spacing to make it easier to tap links. */ @media (-moz-touch-enabled: 1), (pointer: coarse) { .chapter li a { padding: 5px 0; } .spacer { margin: 10px 0; } @@ -741,7 +746,6 @@ html:not(.sidebar-resizing) .sidebar { content: ''; position: absolute; left: -16px; - top: 0; margin-top: 10px; width: 8px; height: 8px; diff --git a/crates/mdbook-html/front-end/templates/toc.js.hbs b/crates/mdbook-html/front-end/templates/toc.js.hbs index 9d26f3da..48af642b 100644 --- a/crates/mdbook-html/front-end/templates/toc.js.hbs +++ b/crates/mdbook-html/front-end/templates/toc.js.hbs @@ -29,14 +29,9 @@ class MDBookSidebarScrollbox extends HTMLElement { && current_page.endsWith('/index.html')) { link.classList.add('active'); let parent = link.parentElement; - if (parent && parent.classList.contains('chapter-item')) { - parent.classList.add('expanded'); - } while (parent) { - if (parent.tagName === 'LI' && parent.previousElementSibling) { - if (parent.previousElementSibling.classList.contains('chapter-item')) { - parent.previousElementSibling.classList.add('expanded'); - } + if (parent.tagName === 'LI' && parent.classList.contains('chapter-item')) { + parent.classList.add('expanded'); } parent = parent.parentElement; } @@ -62,9 +57,9 @@ class MDBookSidebarScrollbox extends HTMLElement { } } // Toggle buttons - const sidebarAnchorToggles = document.querySelectorAll('#mdbook-sidebar a.toggle'); + const sidebarAnchorToggles = document.querySelectorAll('.chapter-fold-toggle'); function toggleSection(ev) { - ev.currentTarget.parentElement.classList.toggle('expanded'); + ev.currentTarget.parentElement.parentElement.classList.toggle('expanded'); } Array.from(sidebarAnchorToggles).forEach(el => { el.addEventListener('click', toggleSection); @@ -237,17 +232,12 @@ window.customElements.define('mdbook-sidebar-scrollbox', MDBookSidebarScrollbox) // be expanded. function updateHeaderExpanded(currentA) { // Add expanded to all header-item li ancestors. - let current = currentA.parentElement.parentElement.parentElement; - while (current.tagName === 'LI') { - const prevSibling = current.previousElementSibling; - if (prevSibling !== null - && prevSibling.tagName === 'LI' - && prevSibling.classList.contains('header-item')) { - prevSibling.classList.add('expanded'); - current = prevSibling.parentElement.parentElement; - } else { - break; + let current = currentA.parentElement; + while (current) { + if (current.tagName === 'LI' && current.classList.contains('header-item')) { + current.classList.add('expanded'); } + current = current.parentElement; } } @@ -343,19 +333,6 @@ window.customElements.define('mdbook-sidebar-scrollbox', MDBookSidebarScrollbox) if (activeSection === null) { return; } - const activeItem = activeSection.parentElement; - const activeList = activeItem.parentElement; - - // Build a tree of headers in the sidebar. - const rootLi = document.createElement('li'); - rootLi.classList.add('header-item'); - rootLi.classList.add('expanded'); - const rootOl = document.createElement('ol'); - rootOl.classList.add('section'); - rootLi.appendChild(rootOl); - const stack = [{ level: 0, ol: rootOl }]; - // The level where it will start folding deeply nested headers. - const foldLevel = 3; const main = document.getElementsByTagName('main')[0]; headers = Array.from(main.querySelectorAll('h2, h3, h4, h5, h6')) @@ -365,57 +342,90 @@ window.customElements.define('mdbook-sidebar-scrollbox', MDBookSidebarScrollbox) return; } + // Build a tree of headers in the sidebar. + + const stack = []; + + const firstLevel = parseInt(headers[0].tagName.charAt(1)); + for (let i = 1; i < firstLevel; i++) { + const ol = document.createElement('ol'); + ol.classList.add('section'); + if (stack.length > 0) { + stack[stack.length - 1].ol.appendChild(ol); + } + stack.push({level: i + 1, ol: ol}); + } + + // The level where it will start folding deeply nested headers. + const foldLevel = 3; + for (let i = 0; i < headers.length; i++) { const header = headers[i]; const level = parseInt(header.tagName.charAt(1)); + + const currentLevel = stack[stack.length - 1].level; + if (level > currentLevel) { + // Begin nesting to this level. + for (let nextLevel = currentLevel + 1; nextLevel <= level; nextLevel++) { + const ol = document.createElement('ol'); + ol.classList.add('section'); + const last = stack[stack.length - 1]; + const lastChild = last.ol.lastChild; + // Handle the case where jumping more than one nesting + // level, which doesn't have a list item to place this new + // list inside of. + if (lastChild) { + lastChild.appendChild(ol); + } else { + last.ol.appendChild(ol); + } + stack.push({level: nextLevel, ol: ol}); + } + } else if (level < currentLevel) { + while (stack.length > 1 && stack[stack.length - 1].level >= level) { + stack.pop(); + } + } + const li = document.createElement('li'); li.classList.add('header-item'); li.classList.add('expanded'); if (level < foldLevel) { li.classList.add('expanded'); } + const span = document.createElement('span'); + span.classList.add('chapter-link-wrapper'); const a = document.createElement('a'); + span.appendChild(a); a.href = '#' + header.id; a.classList.add('header-in-summary'); a.innerHTML = header.children[0].innerHTML; a.addEventListener('click', headerThresholdClick); - li.appendChild(a); const nextHeader = headers[i + 1]; if (nextHeader !== undefined) { const nextLevel = parseInt(nextHeader.tagName.charAt(1)); if (nextLevel > level && level >= foldLevel) { - const div = document.createElement('div'); - div.textContent = '❱'; const toggle = document.createElement('a'); - toggle.classList.add('toggle'); + toggle.classList.add('chapter-fold-toggle'); toggle.classList.add('header-toggle'); - toggle.appendChild(div); toggle.addEventListener('click', () => { li.classList.toggle('expanded'); }); - li.appendChild(toggle); + const toggleDiv = document.createElement('div'); + toggleDiv.textContent = '❱'; + toggle.appendChild(toggleDiv); + span.appendChild(toggle); headerToggles.push(li); } } - - // Find the appropriate parent level. - while (stack.length > 1 && stack[stack.length - 1].level >= level) { - stack.pop(); - } + li.appendChild(span); const currentParent = stack[stack.length - 1]; currentParent.ol.appendChild(li); - - // Create new nested ol for potential children. - const nestedOl = document.createElement('ol'); - nestedOl.classList.add('section'); - const nestedLi = document.createElement('li'); - nestedLi.appendChild(nestedOl); - currentParent.ol.appendChild(nestedLi); - stack.push({ level: level, ol: nestedOl }); } - activeList.insertBefore(rootLi, activeItem.nextSibling); + const activeItemSpan = activeSection.parentElement; + activeItemSpan.after(stack[0].ol); }); document.addEventListener('DOMContentLoaded', reloadCurrentHeader); diff --git a/crates/mdbook-html/src/html_handlebars/helpers/toc.rs b/crates/mdbook-html/src/html_handlebars/helpers/toc.rs index 36972d24..baee73f6 100644 --- a/crates/mdbook-html/src/html_handlebars/helpers/toc.rs +++ b/crates/mdbook-html/src/html_handlebars/helpers/toc.rs @@ -57,13 +57,13 @@ impl HelperDef for RenderToc { out.write("
    ")?; let mut current_level = 1; + let mut first = true; for item in chapters { - let (_section, level) = if let Some(s) = item.get("section") { - (s.as_str(), s.matches('.').count()) - } else { - ("", 1) - }; + let level = item + .get("section") + .map(|s| s.matches('.').count()) + .unwrap_or(1); // Expand if folding is disabled, or if levels that are larger than this would not // be folded. @@ -71,25 +71,31 @@ impl HelperDef for RenderToc { match level.cmp(¤t_level) { Ordering::Greater => { - while level > current_level { - out.write("
  1. ")?; - out.write("
      ")?; - current_level += 1; - } - write_li_open_tag(out, is_expanded, false)?; + // There is an assumption that when descending, it can + // only go one level down at a time. This should be + // enforced by the nature of markdown lists and the + // summary parser. + assert_eq!(level, current_level + 1); + current_level += 1; + out.write("
        ")?; + write_li_open_tag(out, is_expanded)?; } Ordering::Less => { while level < current_level { - out.write("
      ")?; out.write("")?; + out.write("
    ")?; current_level -= 1; } - write_li_open_tag(out, is_expanded, false)?; + write_li_open_tag(out, is_expanded)?; } Ordering::Equal => { - write_li_open_tag(out, is_expanded, !item.contains_key("section"))?; + if !first { + out.write("
  2. ")?; + } + write_li_open_tag(out, is_expanded)?; } } + first = false; // Spacer if item.contains_key("spacer") { @@ -105,6 +111,8 @@ impl HelperDef for RenderToc { continue; } + out.write("")?; + // Link let path_exists = match item.get("path") { Some(path) if !path.is_empty() => { @@ -121,7 +129,7 @@ impl HelperDef for RenderToc { true } _ => { - out.write("
    ")?; + out.write("")?; false } }; @@ -142,41 +150,35 @@ impl HelperDef for RenderToc { if path_exists { out.write("")?; } else { - out.write("
    ")?; + out.write("
    ")?; } // Render expand/collapse toggle if let Some(flag) = item.get("has_sub_items") { let has_sub_items = flag.parse::().unwrap_or_default(); if fold_enable && has_sub_items { - out.write("
    ")?; + // The
    here is to manage rotating the element when + // the chapter title is long and word-wraps. + out.write("
    ")?; } } - out.write("")?; + out.write("")?; } - while current_level > 1 { - out.write("
")?; + while current_level > 0 { out.write("")?; + out.write("")?; current_level -= 1; } - out.write("")?; Ok(()) } } -fn write_li_open_tag( - out: &mut dyn Output, - is_expanded: bool, - is_affix: bool, -) -> Result<(), std::io::Error> { +fn write_li_open_tag(out: &mut dyn Output, is_expanded: bool) -> Result<(), std::io::Error> { let mut li = String::from("
  • "); out.write(&li) } diff --git a/tests/gui/books/heading-nav-folded/book.toml b/tests/gui/books/heading-nav-folded/book.toml new file mode 100644 index 00000000..7dcb38f8 --- /dev/null +++ b/tests/gui/books/heading-nav-folded/book.toml @@ -0,0 +1,6 @@ +[book] +title = "heading-nav-folded" + +[output.html.fold] +enable = true +level = 0 diff --git a/tests/gui/books/heading-nav-folded/src/SUMMARY.md b/tests/gui/books/heading-nav-folded/src/SUMMARY.md new file mode 100644 index 00000000..db2f44a2 --- /dev/null +++ b/tests/gui/books/heading-nav-folded/src/SUMMARY.md @@ -0,0 +1,7 @@ +# Summary + +- [Introduction](./intro.md) + - [Sub chapter](./sub/index.md) + - [Sub inner](./sub/inner/index.md) + - [Sub second chapter](./sub/second.md) +- [Next main chapter](./next-main.md) diff --git a/tests/gui/books/heading-nav-folded/src/intro.md b/tests/gui/books/heading-nav-folded/src/intro.md new file mode 100644 index 00000000..8067174f --- /dev/null +++ b/tests/gui/books/heading-nav-folded/src/intro.md @@ -0,0 +1,9 @@ +# Introduction + +## Heading A + +### Heading A2 + +### Heading A3 + +## Heading B diff --git a/tests/gui/books/heading-nav-folded/src/next-main.md b/tests/gui/books/heading-nav-folded/src/next-main.md new file mode 100644 index 00000000..7d8c4947 --- /dev/null +++ b/tests/gui/books/heading-nav-folded/src/next-main.md @@ -0,0 +1 @@ +# Next main chapter diff --git a/tests/gui/books/heading-nav-folded/src/sub/index.md b/tests/gui/books/heading-nav-folded/src/sub/index.md new file mode 100644 index 00000000..e4a4b826 --- /dev/null +++ b/tests/gui/books/heading-nav-folded/src/sub/index.md @@ -0,0 +1,3 @@ +# Sub chapter + +## Sub-chapter heading diff --git a/tests/gui/books/heading-nav-folded/src/sub/inner/index.md b/tests/gui/books/heading-nav-folded/src/sub/inner/index.md new file mode 100644 index 00000000..3c84e712 --- /dev/null +++ b/tests/gui/books/heading-nav-folded/src/sub/inner/index.md @@ -0,0 +1,3 @@ +# Sub inner + +## Inner chapter heading diff --git a/tests/gui/books/heading-nav-folded/src/sub/second.md b/tests/gui/books/heading-nav-folded/src/sub/second.md new file mode 100644 index 00000000..49c6adb4 --- /dev/null +++ b/tests/gui/books/heading-nav-folded/src/sub/second.md @@ -0,0 +1,3 @@ +# Sub second chapter + +## Second chapter heading diff --git a/tests/gui/books/heading-nav/src/SUMMARY.md b/tests/gui/books/heading-nav/src/SUMMARY.md index a7f68ee0..bf84824b 100644 --- a/tests/gui/books/heading-nav/src/SUMMARY.md +++ b/tests/gui/books/heading-nav/src/SUMMARY.md @@ -6,3 +6,4 @@ - [Collapsed headings](collapsed.md) - [Headings with markup](markup.md) - [Current scrolls to bottom](current-to-bottom.md) +- [Unusual heading levels](unusual-heading-levels.md) diff --git a/tests/gui/books/heading-nav/src/unusual-heading-levels.md b/tests/gui/books/heading-nav/src/unusual-heading-levels.md new file mode 100644 index 00000000..8fc65a21 --- /dev/null +++ b/tests/gui/books/heading-nav/src/unusual-heading-levels.md @@ -0,0 +1,9 @@ +# Unusual heading levels + +### Heading 3 + +## Heading 2 + +#### Heading 5 + +#### Heading 5.1 diff --git a/tests/gui/heading-nav-collapsed.goml b/tests/gui/heading-nav-collapsed.goml index 0985bfb6..003d7e68 100644 --- a/tests/gui/heading-nav-collapsed.goml +++ b/tests/gui/heading-nav-collapsed.goml @@ -3,49 +3,54 @@ set-window-size: (1400, 800) go-to: |DOC_PATH| + "heading-nav/collapsed.html" -assert-count: (".header-item", 12) +assert-count: (".header-item", 11) assert-count: (".current-header", 1) assert-text: (".current-header", "Heading 1") // Collapsed elements do not have "expanded" class. -assert-attribute: ("li:has(> a[href='#heading-12'])", {"class": "header-item"}) -assert-attribute: ("li:has(> a[href='#heading-21'])", {"class": "header-item"}) +assert-attribute: ("li:has(> span > a[href='#heading-12'])", {"class": "header-item"}) +assert-attribute: ("li:has(> span > a[href='#heading-21'])", {"class": "header-item"}) -// Click 1.2, doesn't change expanded. +// Click 1.2, expands it. click: "a.header-in-summary[href='#heading-12']" -assert-attribute: ("li:has(> a[href='#heading-12'])", {"class": "header-item"}) -assert-attribute: ("li:has(> a[href='#heading-21'])", {"class": "header-item"}) -assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-12']]]/ol", {"display": "none"}) -// Click expand chevron. -// 1.2.1 and 1.2.2 should be visible +assert-attribute: ("li:has(> span > a[href='#heading-12'])", {"class": "header-item expanded"}) +assert-attribute: ("li:has(> span > a[href='#heading-21'])", {"class": "header-item"}) +assert-css: ("//a[@href='#heading-12']/../following-sibling::ol", {"display": "block"}) + +// Click 1.1, should collapse it. +click: "a.header-in-summary[href='#heading-11']" +assert-attribute: ("li:has(> span > a[href='#heading-12'])", {"class": "header-item"}) +assert-attribute: ("li:has(> span > a[href='#heading-21'])", {"class": "header-item"}) +assert-css: ("//a[@href='#heading-12']/../following-sibling::ol", {"display": "none"}) + +// Click the chevron, should expand it. click: "a.header-in-summary[href='#heading-12'] ~ a.header-toggle" -assert-attribute: ("li:has(> a[href='#heading-12'])", {"class": "header-item expanded"}) -assert-attribute: ("li:has(> a[href='#heading-21'])", {"class": "header-item"}) -assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-12']]]/ol", {"display": "block"}) +assert-attribute: ("li:has(> span > a[href='#heading-12'])", {"class": "header-item expanded"}) +assert-attribute: ("li:has(> span > a[href='#heading-21'])", {"class": "header-item"}) +assert-css: ("//a[@href='#heading-12']/../following-sibling::ol", {"display": "block"}) // Click 1.3 click: "a.header-in-summary[href='#heading-13']" // Everything should be collapsed -assert-attribute: ("li:has(> a[href='#heading-12'])", {"class": "header-item"}) -assert-attribute: ("li:has(> a[href='#heading-21'])", {"class": "header-item"}) -assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-12']]]/ol", {"display": "none"}) -assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-21']]]/ol", {"display": "none"}) +assert-attribute: ("li:has(> span > a[href='#heading-12'])", {"class": "header-item"}) +assert-attribute: ("li:has(> span > a[href='#heading-21'])", {"class": "header-item"}) +assert-css: ("//a[@href='#heading-12']/../following-sibling::ol", {"display": "none"}) +assert-css: ("//a[@href='#heading-21']/../following-sibling::ol", {"display": "none"}) +assert-attribute: ("li:has(> span > a[href='#heading-12'])", {"class": "header-item"}) +assert-attribute: ("li:has(> span > a[href='#heading-21'])", {"class": "header-item"}) +assert-attribute: ("li:has(> span > a[href='#heading-211'])", {"class": "header-item"}) +assert-attribute: ("li:has(> span > a[href='#heading-2111'])", {"class": "header-item"}) - -assert-attribute: ("li:has(> a[href='#heading-12'])", {"class": "header-item"}) -assert-attribute: ("li:has(> a[href='#heading-21'])", {"class": "header-item"}) -assert-attribute: ("li:has(> a[href='#heading-211'])", {"class": "header-item"}) -assert-attribute: ("li:has(> a[href='#heading-2111'])", {"class": "header-item"}) // Scroll to bottom of page press-key: 'PageDown' press-key: 'PageDown' press-key: 'PageDown' press-key: 'PageDown' // 2.1.1.1.1 should be visible, and all the chevrons should be open, and expanded should be on each one -assert-attribute: ("li:has(> a[href='#heading-12'])", {"class": "header-item"}) -assert-attribute: ("li:has(> a[href='#heading-21'])", {"class": "header-item expanded"}) -assert-attribute: ("li:has(> a[href='#heading-211'])", {"class": "header-item expanded"}) -assert-attribute: ("li:has(> a[href='#heading-2111'])", {"class": "header-item expanded"}) -assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-12']]]/ol", {"display": "none"}) -assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-21']]]/ol", {"display": "block"}) -assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-211']]]/ol", {"display": "block"}) -assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-2111']]]/ol", {"display": "block"}) +assert-attribute: ("li:has(> span > a[href='#heading-12'])", {"class": "header-item"}) +assert-attribute: ("li:has(> span > a[href='#heading-21'])", {"class": "header-item expanded"}) +assert-attribute: ("li:has(> span > a[href='#heading-211'])", {"class": "header-item expanded"}) +assert-attribute: ("li:has(> span > a[href='#heading-2111'])", {"class": "header-item expanded"}) +assert-css: ("//a[@href='#heading-12']/../following-sibling::ol", {"display": "none"}) +assert-css: ("//a[@href='#heading-21']/../following-sibling::ol", {"display": "block"}) +assert-css: ("//a[@href='#heading-211']/../following-sibling::ol", {"display": "block"}) +assert-css: ("//a[@href='#heading-2111']/../following-sibling::ol", {"display": "block"}) diff --git a/tests/gui/heading-nav-folded.goml b/tests/gui/heading-nav-folded.goml new file mode 100644 index 00000000..eeb3aa27 --- /dev/null +++ b/tests/gui/heading-nav-folded.goml @@ -0,0 +1,3 @@ +// Tests when chapter folding is enabled. + +go-to: |DOC_PATH| + "heading-nav-folded/index.html" diff --git a/tests/gui/heading-nav-large-intro.goml b/tests/gui/heading-nav-large-intro.goml index cb93c617..916e5700 100644 --- a/tests/gui/heading-nav-large-intro.goml +++ b/tests/gui/heading-nav-large-intro.goml @@ -3,7 +3,7 @@ set-window-size: (1400, 800) go-to: |DOC_PATH| + "heading-nav/large-intro.html" -assert-count: (".header-item", 2) +assert-count: (".header-item", 1) assert-count: (".current-header", 0) scroll-to: "#first-header" diff --git a/tests/gui/heading-nav-markup.goml b/tests/gui/heading-nav-markup.goml index c37d98d9..6da8f936 100644 --- a/tests/gui/heading-nav-markup.goml +++ b/tests/gui/heading-nav-markup.goml @@ -3,7 +3,7 @@ set-window-size: (1400, 800) go-to: |DOC_PATH| + "heading-nav/markup.html" -assert-count: (".header-item", 5) +assert-count: (".header-item", 4) assert-count: (".current-header", 1) assert-text: (".current-header", "Heading with code or italic or bold or strike") assert-property: (".current-header", {"innerHTML": "Heading with code or italic or bold or strike"}) diff --git a/tests/gui/heading-nav-normal-intro.goml b/tests/gui/heading-nav-normal-intro.goml index 4a50c99d..8ef542dc 100644 --- a/tests/gui/heading-nav-normal-intro.goml +++ b/tests/gui/heading-nav-normal-intro.goml @@ -3,7 +3,7 @@ set-window-size: (1400, 800) go-to: |DOC_PATH| + "heading-nav/normal-intro.html" -assert-count: (".header-item", 4) +assert-count: (".header-item", 3) assert-count: (".current-header", 1) assert-text: (".current-header", "The first heading") diff --git a/tests/gui/heading-nav-unusual-levels.goml b/tests/gui/heading-nav-unusual-levels.goml new file mode 100644 index 00000000..db58f3e5 --- /dev/null +++ b/tests/gui/heading-nav-unusual-levels.goml @@ -0,0 +1,7 @@ +// Tests for unusual heading levels + +set-window-size: (1400, 800) +go-to: |DOC_PATH| + "heading-nav/unusual-heading-levels.html" + +assert-property: ("//a[@href='unusual-heading-levels.html']/../following-sibling::ol", {"innerHTML": '
    1. Heading 3
  • Heading 2
      1. Heading 5
      2. Heading 5.1
  • '}) + diff --git a/tests/testsuite/index.rs b/tests/testsuite/index.rs index 0555cc63..c6c9c762 100644 --- a/tests/testsuite/index.rs +++ b/tests/testsuite/index.rs @@ -20,16 +20,22 @@ fn readme_to_index() { ) .check_toc_js(str![[r#"
      -
    1. +
    2. + Intro +
    3. + First +
    4. + Second +
    "#]]); diff --git a/tests/testsuite/toc.rs b/tests/testsuite/toc.rs index b7e5629b..40bb0001 100644 --- a/tests/testsuite/toc.rs +++ b/tests/testsuite/toc.rs @@ -148,16 +148,22 @@ fn summary_with_markdown_formatting() { .check_toc_js(str![[r#"
    1. + Italic code *escape* `escape2` +
    2. + Soft line break +
    3. + <escaped tag> +
    "#]])