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
This commit is contained in:
parent
816913bd72
commit
5282083dec
20 changed files with 212 additions and 127 deletions
|
|
@ -571,17 +571,18 @@ html:not(.sidebar-resizing) .sidebar {
|
||||||
line-height: 2.2em;
|
line-height: 2.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chapter ol {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chapter li {
|
.chapter li {
|
||||||
display: flex;
|
|
||||||
color: var(--sidebar-non-existant);
|
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 {
|
.chapter li a {
|
||||||
display: block;
|
/* Remove underlines. */
|
||||||
padding: 0;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--sidebar-fg);
|
color: var(--sidebar-fg);
|
||||||
}
|
}
|
||||||
|
|
@ -594,21 +595,22 @@ html:not(.sidebar-resizing) .sidebar {
|
||||||
color: var(--sidebar-active);
|
color: var(--sidebar-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chapter li > a.toggle {
|
/* This is the toggle chevron. */
|
||||||
|
.chapter-fold-toggle {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
/* Positions the chevron to the side. */
|
||||||
margin-inline-start: auto;
|
margin-inline-start: auto;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
opacity: 0.68;
|
opacity: 0.68;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chapter li > a.toggle div {
|
.chapter-fold-toggle div {
|
||||||
transition: transform 0.5s;
|
transition: transform 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* collapse the section */
|
/* collapse the section */
|
||||||
.chapter li:not(.expanded) + li > ol {
|
.chapter li:not(.expanded) > ol {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -617,10 +619,12 @@ html:not(.sidebar-resizing) .sidebar {
|
||||||
margin-block-start: 0.6em;
|
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);
|
transform: rotate(90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Horizontal line in chapter list. */
|
||||||
.spacer {
|
.spacer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
|
|
@ -630,6 +634,7 @@ html:not(.sidebar-resizing) .sidebar {
|
||||||
background-color: var(--sidebar-spacer);
|
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) {
|
@media (-moz-touch-enabled: 1), (pointer: coarse) {
|
||||||
.chapter li a { padding: 5px 0; }
|
.chapter li a { padding: 5px 0; }
|
||||||
.spacer { margin: 10px 0; }
|
.spacer { margin: 10px 0; }
|
||||||
|
|
@ -741,7 +746,6 @@ html:not(.sidebar-resizing) .sidebar {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -16px;
|
left: -16px;
|
||||||
top: 0;
|
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,9 @@ class MDBookSidebarScrollbox extends HTMLElement {
|
||||||
&& current_page.endsWith('/index.html')) {
|
&& current_page.endsWith('/index.html')) {
|
||||||
link.classList.add('active');
|
link.classList.add('active');
|
||||||
let parent = link.parentElement;
|
let parent = link.parentElement;
|
||||||
if (parent && parent.classList.contains('chapter-item')) {
|
|
||||||
parent.classList.add('expanded');
|
|
||||||
}
|
|
||||||
while (parent) {
|
while (parent) {
|
||||||
if (parent.tagName === 'LI' && parent.previousElementSibling) {
|
if (parent.tagName === 'LI' && parent.classList.contains('chapter-item')) {
|
||||||
if (parent.previousElementSibling.classList.contains('chapter-item')) {
|
parent.classList.add('expanded');
|
||||||
parent.previousElementSibling.classList.add('expanded');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
parent = parent.parentElement;
|
parent = parent.parentElement;
|
||||||
}
|
}
|
||||||
|
|
@ -62,9 +57,9 @@ class MDBookSidebarScrollbox extends HTMLElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Toggle buttons
|
// Toggle buttons
|
||||||
const sidebarAnchorToggles = document.querySelectorAll('#mdbook-sidebar a.toggle');
|
const sidebarAnchorToggles = document.querySelectorAll('.chapter-fold-toggle');
|
||||||
function toggleSection(ev) {
|
function toggleSection(ev) {
|
||||||
ev.currentTarget.parentElement.classList.toggle('expanded');
|
ev.currentTarget.parentElement.parentElement.classList.toggle('expanded');
|
||||||
}
|
}
|
||||||
Array.from(sidebarAnchorToggles).forEach(el => {
|
Array.from(sidebarAnchorToggles).forEach(el => {
|
||||||
el.addEventListener('click', toggleSection);
|
el.addEventListener('click', toggleSection);
|
||||||
|
|
@ -237,17 +232,12 @@ window.customElements.define('mdbook-sidebar-scrollbox', MDBookSidebarScrollbox)
|
||||||
// be expanded.
|
// be expanded.
|
||||||
function updateHeaderExpanded(currentA) {
|
function updateHeaderExpanded(currentA) {
|
||||||
// Add expanded to all header-item li ancestors.
|
// Add expanded to all header-item li ancestors.
|
||||||
let current = currentA.parentElement.parentElement.parentElement;
|
let current = currentA.parentElement;
|
||||||
while (current.tagName === 'LI') {
|
while (current) {
|
||||||
const prevSibling = current.previousElementSibling;
|
if (current.tagName === 'LI' && current.classList.contains('header-item')) {
|
||||||
if (prevSibling !== null
|
current.classList.add('expanded');
|
||||||
&& prevSibling.tagName === 'LI'
|
|
||||||
&& prevSibling.classList.contains('header-item')) {
|
|
||||||
prevSibling.classList.add('expanded');
|
|
||||||
current = prevSibling.parentElement.parentElement;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
current = current.parentElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -343,19 +333,6 @@ window.customElements.define('mdbook-sidebar-scrollbox', MDBookSidebarScrollbox)
|
||||||
if (activeSection === null) {
|
if (activeSection === null) {
|
||||||
return;
|
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];
|
const main = document.getElementsByTagName('main')[0];
|
||||||
headers = Array.from(main.querySelectorAll('h2, h3, h4, h5, h6'))
|
headers = Array.from(main.querySelectorAll('h2, h3, h4, h5, h6'))
|
||||||
|
|
@ -365,57 +342,90 @@ window.customElements.define('mdbook-sidebar-scrollbox', MDBookSidebarScrollbox)
|
||||||
return;
|
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++) {
|
for (let i = 0; i < headers.length; i++) {
|
||||||
const header = headers[i];
|
const header = headers[i];
|
||||||
const level = parseInt(header.tagName.charAt(1));
|
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');
|
const li = document.createElement('li');
|
||||||
li.classList.add('header-item');
|
li.classList.add('header-item');
|
||||||
li.classList.add('expanded');
|
li.classList.add('expanded');
|
||||||
if (level < foldLevel) {
|
if (level < foldLevel) {
|
||||||
li.classList.add('expanded');
|
li.classList.add('expanded');
|
||||||
}
|
}
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.classList.add('chapter-link-wrapper');
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
|
span.appendChild(a);
|
||||||
a.href = '#' + header.id;
|
a.href = '#' + header.id;
|
||||||
a.classList.add('header-in-summary');
|
a.classList.add('header-in-summary');
|
||||||
a.innerHTML = header.children[0].innerHTML;
|
a.innerHTML = header.children[0].innerHTML;
|
||||||
a.addEventListener('click', headerThresholdClick);
|
a.addEventListener('click', headerThresholdClick);
|
||||||
li.appendChild(a);
|
|
||||||
const nextHeader = headers[i + 1];
|
const nextHeader = headers[i + 1];
|
||||||
if (nextHeader !== undefined) {
|
if (nextHeader !== undefined) {
|
||||||
const nextLevel = parseInt(nextHeader.tagName.charAt(1));
|
const nextLevel = parseInt(nextHeader.tagName.charAt(1));
|
||||||
if (nextLevel > level && level >= foldLevel) {
|
if (nextLevel > level && level >= foldLevel) {
|
||||||
const div = document.createElement('div');
|
|
||||||
div.textContent = '❱';
|
|
||||||
const toggle = document.createElement('a');
|
const toggle = document.createElement('a');
|
||||||
toggle.classList.add('toggle');
|
toggle.classList.add('chapter-fold-toggle');
|
||||||
toggle.classList.add('header-toggle');
|
toggle.classList.add('header-toggle');
|
||||||
toggle.appendChild(div);
|
|
||||||
toggle.addEventListener('click', () => {
|
toggle.addEventListener('click', () => {
|
||||||
li.classList.toggle('expanded');
|
li.classList.toggle('expanded');
|
||||||
});
|
});
|
||||||
li.appendChild(toggle);
|
const toggleDiv = document.createElement('div');
|
||||||
|
toggleDiv.textContent = '❱';
|
||||||
|
toggle.appendChild(toggleDiv);
|
||||||
|
span.appendChild(toggle);
|
||||||
headerToggles.push(li);
|
headerToggles.push(li);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
li.appendChild(span);
|
||||||
// Find the appropriate parent level.
|
|
||||||
while (stack.length > 1 && stack[stack.length - 1].level >= level) {
|
|
||||||
stack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentParent = stack[stack.length - 1];
|
const currentParent = stack[stack.length - 1];
|
||||||
currentParent.ol.appendChild(li);
|
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);
|
document.addEventListener('DOMContentLoaded', reloadCurrentHeader);
|
||||||
|
|
|
||||||
|
|
@ -57,13 +57,13 @@ impl HelperDef for RenderToc {
|
||||||
out.write("<ol class=\"chapter\">")?;
|
out.write("<ol class=\"chapter\">")?;
|
||||||
|
|
||||||
let mut current_level = 1;
|
let mut current_level = 1;
|
||||||
|
let mut first = true;
|
||||||
|
|
||||||
for item in chapters {
|
for item in chapters {
|
||||||
let (_section, level) = if let Some(s) = item.get("section") {
|
let level = item
|
||||||
(s.as_str(), s.matches('.').count())
|
.get("section")
|
||||||
} else {
|
.map(|s| s.matches('.').count())
|
||||||
("", 1)
|
.unwrap_or(1);
|
||||||
};
|
|
||||||
|
|
||||||
// Expand if folding is disabled, or if levels that are larger than this would not
|
// Expand if folding is disabled, or if levels that are larger than this would not
|
||||||
// be folded.
|
// be folded.
|
||||||
|
|
@ -71,25 +71,31 @@ impl HelperDef for RenderToc {
|
||||||
|
|
||||||
match level.cmp(¤t_level) {
|
match level.cmp(¤t_level) {
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
while level > current_level {
|
// There is an assumption that when descending, it can
|
||||||
out.write("<li>")?;
|
// only go one level down at a time. This should be
|
||||||
out.write("<ol class=\"section\">")?;
|
// enforced by the nature of markdown lists and the
|
||||||
current_level += 1;
|
// summary parser.
|
||||||
}
|
assert_eq!(level, current_level + 1);
|
||||||
write_li_open_tag(out, is_expanded, false)?;
|
current_level += 1;
|
||||||
|
out.write("<ol class=\"section\">")?;
|
||||||
|
write_li_open_tag(out, is_expanded)?;
|
||||||
}
|
}
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
while level < current_level {
|
while level < current_level {
|
||||||
out.write("</ol>")?;
|
|
||||||
out.write("</li>")?;
|
out.write("</li>")?;
|
||||||
|
out.write("</ol>")?;
|
||||||
current_level -= 1;
|
current_level -= 1;
|
||||||
}
|
}
|
||||||
write_li_open_tag(out, is_expanded, false)?;
|
write_li_open_tag(out, is_expanded)?;
|
||||||
}
|
}
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
write_li_open_tag(out, is_expanded, !item.contains_key("section"))?;
|
if !first {
|
||||||
|
out.write("</li>")?;
|
||||||
|
}
|
||||||
|
write_li_open_tag(out, is_expanded)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
first = false;
|
||||||
|
|
||||||
// Spacer
|
// Spacer
|
||||||
if item.contains_key("spacer") {
|
if item.contains_key("spacer") {
|
||||||
|
|
@ -105,6 +111,8 @@ impl HelperDef for RenderToc {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out.write("<span class=\"chapter-link-wrapper\">")?;
|
||||||
|
|
||||||
// Link
|
// Link
|
||||||
let path_exists = match item.get("path") {
|
let path_exists = match item.get("path") {
|
||||||
Some(path) if !path.is_empty() => {
|
Some(path) if !path.is_empty() => {
|
||||||
|
|
@ -121,7 +129,7 @@ impl HelperDef for RenderToc {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
out.write("<div>")?;
|
out.write("<span>")?;
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -142,41 +150,35 @@ impl HelperDef for RenderToc {
|
||||||
if path_exists {
|
if path_exists {
|
||||||
out.write("</a>")?;
|
out.write("</a>")?;
|
||||||
} else {
|
} else {
|
||||||
out.write("</div>")?;
|
out.write("</span>")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render expand/collapse toggle
|
// Render expand/collapse toggle
|
||||||
if let Some(flag) = item.get("has_sub_items") {
|
if let Some(flag) = item.get("has_sub_items") {
|
||||||
let has_sub_items = flag.parse::<bool>().unwrap_or_default();
|
let has_sub_items = flag.parse::<bool>().unwrap_or_default();
|
||||||
if fold_enable && has_sub_items {
|
if fold_enable && has_sub_items {
|
||||||
out.write("<a class=\"toggle\"><div>❱</div></a>")?;
|
// The <div> here is to manage rotating the element when
|
||||||
|
// the chapter title is long and word-wraps.
|
||||||
|
out.write("<a class=\"chapter-fold-toggle\"><div>❱</div></a>")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.write("</li>")?;
|
out.write("</span>")?;
|
||||||
}
|
}
|
||||||
while current_level > 1 {
|
while current_level > 0 {
|
||||||
out.write("</ol>")?;
|
|
||||||
out.write("</li>")?;
|
out.write("</li>")?;
|
||||||
|
out.write("</ol>")?;
|
||||||
current_level -= 1;
|
current_level -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
out.write("</ol>")?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_li_open_tag(
|
fn write_li_open_tag(out: &mut dyn Output, is_expanded: bool) -> Result<(), std::io::Error> {
|
||||||
out: &mut dyn Output,
|
|
||||||
is_expanded: bool,
|
|
||||||
is_affix: bool,
|
|
||||||
) -> Result<(), std::io::Error> {
|
|
||||||
let mut li = String::from("<li class=\"chapter-item ");
|
let mut li = String::from("<li class=\"chapter-item ");
|
||||||
if is_expanded {
|
if is_expanded {
|
||||||
li.push_str("expanded ");
|
li.push_str("expanded ");
|
||||||
}
|
}
|
||||||
if is_affix {
|
|
||||||
li.push_str("affix ");
|
|
||||||
}
|
|
||||||
li.push_str("\">");
|
li.push_str("\">");
|
||||||
out.write(&li)
|
out.write(&li)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
tests/gui/books/heading-nav-folded/book.toml
Normal file
6
tests/gui/books/heading-nav-folded/book.toml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[book]
|
||||||
|
title = "heading-nav-folded"
|
||||||
|
|
||||||
|
[output.html.fold]
|
||||||
|
enable = true
|
||||||
|
level = 0
|
||||||
7
tests/gui/books/heading-nav-folded/src/SUMMARY.md
Normal file
7
tests/gui/books/heading-nav-folded/src/SUMMARY.md
Normal file
|
|
@ -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)
|
||||||
9
tests/gui/books/heading-nav-folded/src/intro.md
Normal file
9
tests/gui/books/heading-nav-folded/src/intro.md
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
## Heading A
|
||||||
|
|
||||||
|
### Heading A2
|
||||||
|
|
||||||
|
### Heading A3
|
||||||
|
|
||||||
|
## Heading B
|
||||||
1
tests/gui/books/heading-nav-folded/src/next-main.md
Normal file
1
tests/gui/books/heading-nav-folded/src/next-main.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# Next main chapter
|
||||||
3
tests/gui/books/heading-nav-folded/src/sub/index.md
Normal file
3
tests/gui/books/heading-nav-folded/src/sub/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Sub chapter
|
||||||
|
|
||||||
|
## Sub-chapter heading
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Sub inner
|
||||||
|
|
||||||
|
## Inner chapter heading
|
||||||
3
tests/gui/books/heading-nav-folded/src/sub/second.md
Normal file
3
tests/gui/books/heading-nav-folded/src/sub/second.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Sub second chapter
|
||||||
|
|
||||||
|
## Second chapter heading
|
||||||
|
|
@ -6,3 +6,4 @@
|
||||||
- [Collapsed headings](collapsed.md)
|
- [Collapsed headings](collapsed.md)
|
||||||
- [Headings with markup](markup.md)
|
- [Headings with markup](markup.md)
|
||||||
- [Current scrolls to bottom](current-to-bottom.md)
|
- [Current scrolls to bottom](current-to-bottom.md)
|
||||||
|
- [Unusual heading levels](unusual-heading-levels.md)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Unusual heading levels
|
||||||
|
|
||||||
|
### Heading 3
|
||||||
|
|
||||||
|
## Heading 2
|
||||||
|
|
||||||
|
#### Heading 5
|
||||||
|
|
||||||
|
#### Heading 5.1
|
||||||
|
|
@ -3,49 +3,54 @@
|
||||||
set-window-size: (1400, 800)
|
set-window-size: (1400, 800)
|
||||||
go-to: |DOC_PATH| + "heading-nav/collapsed.html"
|
go-to: |DOC_PATH| + "heading-nav/collapsed.html"
|
||||||
|
|
||||||
assert-count: (".header-item", 12)
|
assert-count: (".header-item", 11)
|
||||||
assert-count: (".current-header", 1)
|
assert-count: (".current-header", 1)
|
||||||
assert-text: (".current-header", "Heading 1")
|
assert-text: (".current-header", "Heading 1")
|
||||||
// Collapsed elements do not have "expanded" class.
|
// Collapsed elements do not have "expanded" class.
|
||||||
assert-attribute: ("li:has(> a[href='#heading-12'])", {"class": "header-item"})
|
assert-attribute: ("li:has(> span > 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-21'])", {"class": "header-item"})
|
||||||
|
|
||||||
// Click 1.2, doesn't change expanded.
|
// Click 1.2, expands it.
|
||||||
click: "a.header-in-summary[href='#heading-12']"
|
click: "a.header-in-summary[href='#heading-12']"
|
||||||
assert-attribute: ("li:has(> a[href='#heading-12'])", {"class": "header-item"})
|
assert-attribute: ("li:has(> span > a[href='#heading-12'])", {"class": "header-item expanded"})
|
||||||
assert-attribute: ("li:has(> a[href='#heading-21'])", {"class": "header-item"})
|
assert-attribute: ("li:has(> span > a[href='#heading-21'])", {"class": "header-item"})
|
||||||
assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-12']]]/ol", {"display": "none"})
|
assert-css: ("//a[@href='#heading-12']/../following-sibling::ol", {"display": "block"})
|
||||||
// Click expand chevron.
|
|
||||||
// 1.2.1 and 1.2.2 should be visible
|
// 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"
|
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(> span > a[href='#heading-12'])", {"class": "header-item expanded"})
|
||||||
assert-attribute: ("li:has(> a[href='#heading-21'])", {"class": "header-item"})
|
assert-attribute: ("li:has(> span > a[href='#heading-21'])", {"class": "header-item"})
|
||||||
assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-12']]]/ol", {"display": "block"})
|
assert-css: ("//a[@href='#heading-12']/../following-sibling::ol", {"display": "block"})
|
||||||
|
|
||||||
// Click 1.3
|
// Click 1.3
|
||||||
click: "a.header-in-summary[href='#heading-13']"
|
click: "a.header-in-summary[href='#heading-13']"
|
||||||
// Everything should be collapsed
|
// Everything should be collapsed
|
||||||
assert-attribute: ("li:has(> a[href='#heading-12'])", {"class": "header-item"})
|
assert-attribute: ("li:has(> span > 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-21'])", {"class": "header-item"})
|
||||||
assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-12']]]/ol", {"display": "none"})
|
assert-css: ("//a[@href='#heading-12']/../following-sibling::ol", {"display": "none"})
|
||||||
assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-21']]]/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
|
// Scroll to bottom of page
|
||||||
press-key: 'PageDown'
|
press-key: 'PageDown'
|
||||||
press-key: 'PageDown'
|
press-key: 'PageDown'
|
||||||
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
|
// 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(> span > a[href='#heading-12'])", {"class": "header-item"})
|
||||||
assert-attribute: ("li:has(> a[href='#heading-21'])", {"class": "header-item expanded"})
|
assert-attribute: ("li:has(> span > a[href='#heading-21'])", {"class": "header-item expanded"})
|
||||||
assert-attribute: ("li:has(> a[href='#heading-211'])", {"class": "header-item expanded"})
|
assert-attribute: ("li:has(> span > a[href='#heading-211'])", {"class": "header-item expanded"})
|
||||||
assert-attribute: ("li:has(> a[href='#heading-2111'])", {"class": "header-item expanded"})
|
assert-attribute: ("li:has(> span > a[href='#heading-2111'])", {"class": "header-item expanded"})
|
||||||
assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-12']]]/ol", {"display": "none"})
|
assert-css: ("//a[@href='#heading-12']/../following-sibling::ol", {"display": "none"})
|
||||||
assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-21']]]/ol", {"display": "block"})
|
assert-css: ("//a[@href='#heading-21']/../following-sibling::ol", {"display": "block"})
|
||||||
assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-211']]]/ol", {"display": "block"})
|
assert-css: ("//a[@href='#heading-211']/../following-sibling::ol", {"display": "block"})
|
||||||
assert-css: ("//li[preceding-sibling::li[1][a[@href='#heading-2111']]]/ol", {"display": "block"})
|
assert-css: ("//a[@href='#heading-2111']/../following-sibling::ol", {"display": "block"})
|
||||||
|
|
|
||||||
3
tests/gui/heading-nav-folded.goml
Normal file
3
tests/gui/heading-nav-folded.goml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Tests when chapter folding is enabled.
|
||||||
|
|
||||||
|
go-to: |DOC_PATH| + "heading-nav-folded/index.html"
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
set-window-size: (1400, 800)
|
set-window-size: (1400, 800)
|
||||||
go-to: |DOC_PATH| + "heading-nav/large-intro.html"
|
go-to: |DOC_PATH| + "heading-nav/large-intro.html"
|
||||||
assert-count: (".header-item", 2)
|
assert-count: (".header-item", 1)
|
||||||
assert-count: (".current-header", 0)
|
assert-count: (".current-header", 0)
|
||||||
|
|
||||||
scroll-to: "#first-header"
|
scroll-to: "#first-header"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
set-window-size: (1400, 800)
|
set-window-size: (1400, 800)
|
||||||
go-to: |DOC_PATH| + "heading-nav/markup.html"
|
go-to: |DOC_PATH| + "heading-nav/markup.html"
|
||||||
|
|
||||||
assert-count: (".header-item", 5)
|
assert-count: (".header-item", 4)
|
||||||
assert-count: (".current-header", 1)
|
assert-count: (".current-header", 1)
|
||||||
assert-text: (".current-header", "Heading with code or italic or bold or strike")
|
assert-text: (".current-header", "Heading with code or italic or bold or strike")
|
||||||
assert-property: (".current-header", {"innerHTML": "Heading with <code>code</code> or <em>italic</em> or <strong>bold</strong> or <del>strike</del>"})
|
assert-property: (".current-header", {"innerHTML": "Heading with <code>code</code> or <em>italic</em> or <strong>bold</strong> or <del>strike</del>"})
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
set-window-size: (1400, 800)
|
set-window-size: (1400, 800)
|
||||||
go-to: |DOC_PATH| + "heading-nav/normal-intro.html"
|
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-count: (".current-header", 1)
|
||||||
assert-text: (".current-header", "The first heading")
|
assert-text: (".current-header", "The first heading")
|
||||||
|
|
||||||
|
|
|
||||||
7
tests/gui/heading-nav-unusual-levels.goml
Normal file
7
tests/gui/heading-nav-unusual-levels.goml
Normal file
|
|
@ -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": '<ol class="section"><li class="header-item expanded"><span class="chapter-link-wrapper"><a href="#heading-3" class="header-in-summary current-header">Heading 3</a></span></li></ol><li class="header-item expanded"><span class="chapter-link-wrapper"><a href="#heading-2" class="header-in-summary">Heading 2</a></span><ol class="section"><ol class="section"><li class="header-item expanded"><span class="chapter-link-wrapper"><a href="#heading-5" class="header-in-summary">Heading 5</a></span></li><li class="header-item expanded"><span class="chapter-link-wrapper"><a href="#heading-51" class="header-in-summary">Heading 5.1</a></span></li></ol></ol></li>'})
|
||||||
|
|
||||||
|
|
@ -20,16 +20,22 @@ fn readme_to_index() {
|
||||||
)
|
)
|
||||||
.check_toc_js(str![[r#"
|
.check_toc_js(str![[r#"
|
||||||
<ol class="chapter">
|
<ol class="chapter">
|
||||||
<li class="chapter-item expanded affix ">
|
<li class="chapter-item expanded ">
|
||||||
|
<span class="chapter-link-wrapper">
|
||||||
<a href="index.html">Intro</a>
|
<a href="index.html">Intro</a>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="chapter-item expanded ">
|
<li class="chapter-item expanded ">
|
||||||
|
<span class="chapter-link-wrapper">
|
||||||
<a href="first/index.html">
|
<a href="first/index.html">
|
||||||
<strong aria-hidden="true">1.</strong> First</a>
|
<strong aria-hidden="true">1.</strong> First</a>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="chapter-item expanded ">
|
<li class="chapter-item expanded ">
|
||||||
|
<span class="chapter-link-wrapper">
|
||||||
<a href="second/index.html">
|
<a href="second/index.html">
|
||||||
<strong aria-hidden="true">2.</strong> Second</a>
|
<strong aria-hidden="true">2.</strong> Second</a>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
"#]]);
|
"#]]);
|
||||||
|
|
|
||||||
|
|
@ -148,16 +148,22 @@ fn summary_with_markdown_formatting() {
|
||||||
.check_toc_js(str![[r#"
|
.check_toc_js(str![[r#"
|
||||||
<ol class="chapter">
|
<ol class="chapter">
|
||||||
<li class="chapter-item expanded ">
|
<li class="chapter-item expanded ">
|
||||||
|
<span class="chapter-link-wrapper">
|
||||||
<a href="formatted-summary.html">
|
<a href="formatted-summary.html">
|
||||||
<strong aria-hidden="true">1.</strong> Italic code *escape* `escape2`</a>
|
<strong aria-hidden="true">1.</strong> Italic code *escape* `escape2`</a>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="chapter-item expanded ">
|
<li class="chapter-item expanded ">
|
||||||
|
<span class="chapter-link-wrapper">
|
||||||
<a href="soft.html">
|
<a href="soft.html">
|
||||||
<strong aria-hidden="true">2.</strong> Soft line break</a>
|
<strong aria-hidden="true">2.</strong> Soft line break</a>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="chapter-item expanded ">
|
<li class="chapter-item expanded ">
|
||||||
|
<span class="chapter-link-wrapper">
|
||||||
<a href="escaped-tag.html">
|
<a href="escaped-tag.html">
|
||||||
<strong aria-hidden="true">3.</strong> <escaped tag></a>
|
<strong aria-hidden="true">3.</strong> <escaped tag></a>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
"#]])
|
"#]])
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue