Merge "Collapse ToC by default & expand sections when clicking section headings"
This commit is contained in:
commit
0f49a257d1
|
@ -4,7 +4,7 @@
|
||||||
</div>
|
</div>
|
||||||
<ul id="table-of-contents">
|
<ul id="table-of-contents">
|
||||||
{{#array-sections}}
|
{{#array-sections}}
|
||||||
{{>TableOfContents__line}}
|
{{>TableOfContents__topSection}}
|
||||||
{{/array-sections}}
|
{{/array-sections}}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="sidebar-toc-text">
|
<div class="sidebar-toc-text">
|
||||||
<span class="sidebar-toc-numb">{{number}}</span>{{{line}}}</div>
|
<span class="sidebar-toc-numb">{{number}}</span>{{{line}}}</div>
|
||||||
</a>
|
</a>
|
||||||
<ul>
|
<ul class="sidebar-toc-list">
|
||||||
{{#array-sections}}
|
{{#array-sections}}
|
||||||
{{>TableOfContents__line}}
|
{{>TableOfContents__line}}
|
||||||
{{/array-sections}}
|
{{/array-sections}}
|
||||||
|
|
13
includes/templates/TableOfContents__topSection.mustache
Normal file
13
includes/templates/TableOfContents__topSection.mustache
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<li id="toc-{{anchor}}" class="sidebar-toc-list-item sidebar-toc-level-{{toclevel}}">
|
||||||
|
<a class="sidebar-toc-link" href="#{{anchor}}">
|
||||||
|
<div class="sidebar-toc-text">
|
||||||
|
<span class="sidebar-toc-numb">{{number}}</span>{{{line}}}</div>
|
||||||
|
</a>
|
||||||
|
{{!
|
||||||
|
The following <ul> is placed on *one* line in order to leverage the
|
||||||
|
CSS `:empty` selector and hide the downTriangle icon when there are no sub-sections.
|
||||||
|
(`:empty` means no whitespace).
|
||||||
|
}}
|
||||||
|
<ul class="sidebar-toc-list">{{#array-sections}}{{>TableOfContents__line}}{{/array-sections}}</ul>
|
||||||
|
<span class="mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle"></span>
|
||||||
|
</li>
|
|
@ -81,10 +81,14 @@ const main = () => {
|
||||||
|
|
||||||
const tableOfContents = initTableOfContents( {
|
const tableOfContents = initTableOfContents( {
|
||||||
container: tocElement,
|
container: tocElement,
|
||||||
onSectionClick: () => {
|
onSectionClick: ( id ) => {
|
||||||
|
|
||||||
// eslint-disable-next-line no-use-before-define
|
// eslint-disable-next-line no-use-before-define
|
||||||
sectionObserver.pause();
|
sectionObserver.pause();
|
||||||
|
|
||||||
|
tableOfContents.expandSection( id );
|
||||||
|
tableOfContents.changeActiveSection( id );
|
||||||
|
|
||||||
// T297614: We want the link that the user has clicked inside the TOC to
|
// T297614: We want the link that the user has clicked inside the TOC to
|
||||||
// be "active" (e.g. bolded) regardless of whether the browser's scroll
|
// be "active" (e.g. bolded) regardless of whether the browser's scroll
|
||||||
// position corresponds to that section. Therefore, we need to temporarily
|
// position corresponds to that section. Therefore, we need to temporarily
|
||||||
|
@ -116,7 +120,7 @@ const main = () => {
|
||||||
const headline = section.querySelector( HEADLINE_SELECTOR );
|
const headline = section.querySelector( HEADLINE_SELECTOR );
|
||||||
|
|
||||||
if ( headline ) {
|
if ( headline ) {
|
||||||
tableOfContents.activateSection( TOC_SECTION_ID_PREFIX + headline.id );
|
tableOfContents.changeActiveSection( TOC_SECTION_ID_PREFIX + headline.id );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -1,30 +1,9 @@
|
||||||
const ACTIVE_SECTION_CLASS = 'sidebar-toc-list-item-active';
|
const ACTIVE_SECTION_CLASS = 'sidebar-toc-list-item-active';
|
||||||
|
const EXPANDED_SECTION_CLASS = 'sidebar-toc-list-item-expanded';
|
||||||
const PARENT_SECTION_CLASS = 'sidebar-toc-level-1';
|
const PARENT_SECTION_CLASS = 'sidebar-toc-level-1';
|
||||||
const LINK_CLASS = 'sidebar-toc-link';
|
const LINK_CLASS = 'sidebar-toc-link';
|
||||||
const LIST_ITEM_CLASS = 'sidebar-toc-list-item';
|
const LIST_ITEM_CLASS = 'sidebar-toc-list-item';
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets an `ACTIVE_SECTION_CLASS` on the element with an id that matches `id`.
|
|
||||||
* If the element is not a top level heading (e.g. element with the
|
|
||||||
* `PARENT_SECTION_CLASS`), the top level heading will also have the
|
|
||||||
* `ACTIVE_SECTION_CLASS`;
|
|
||||||
*
|
|
||||||
* @callback ActivateSection
|
|
||||||
* @param {string} id The id of the element to be activated in the Table of Contents.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a list item is clicked.
|
|
||||||
*
|
|
||||||
* @callback OnSectionClick
|
|
||||||
* @param {string} id The id of the clicked list item.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} TableOfContents
|
|
||||||
* @property {ActivateSection} activateSection
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the sidebar's Table of Contents.
|
* Initializes the sidebar's Table of Contents.
|
||||||
*
|
*
|
||||||
|
@ -38,13 +17,86 @@ module.exports = function tableOfContents( props ) {
|
||||||
onSectionClick: () => {}
|
onSectionClick: () => {}
|
||||||
}, props );
|
}, props );
|
||||||
|
|
||||||
let /** @type {HTMLElement | undefined} */ activeParentSection;
|
let /** @type {HTMLElement | undefined} */ activeTopSection;
|
||||||
let /** @type {HTMLElement | undefined} */ activeChildSection;
|
let /** @type {HTMLElement | undefined} */ activeSubSection;
|
||||||
|
let /** @type {Array<HTMLElement>} */ expandedSections = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} id
|
* @typedef {Object} activeSectionIds
|
||||||
|
* @property {string|undefined} parent - The active top level section ID
|
||||||
|
* @property {string|undefined} child - The active subsection ID
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ids of the active sections.
|
||||||
|
*
|
||||||
|
* @return {activeSectionIds}
|
||||||
|
*/
|
||||||
|
function getActiveSectionIds() {
|
||||||
|
return {
|
||||||
|
parent: ( activeTopSection ) ? activeTopSection.id : undefined,
|
||||||
|
child: ( activeSubSection ) ? activeSubSection.id : undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an `ACTIVE_SECTION_CLASS` on the element with an id that matches `id`.
|
||||||
|
* If the element is not a top level heading (e.g. element with the
|
||||||
|
* `PARENT_SECTION_CLASS`), the top level heading will also have the
|
||||||
|
* `ACTIVE_SECTION_CLASS`;
|
||||||
|
*
|
||||||
|
* @param {string} id The id of the element to be activated in the Table of Contents.
|
||||||
*/
|
*/
|
||||||
function activateSection( id ) {
|
function activateSection( id ) {
|
||||||
|
const selectedTocSection = document.getElementById( id );
|
||||||
|
const {
|
||||||
|
parent: previousActiveTopId,
|
||||||
|
child: previousActiveSubSectionId
|
||||||
|
} = getActiveSectionIds();
|
||||||
|
|
||||||
|
if (
|
||||||
|
!selectedTocSection ||
|
||||||
|
( previousActiveTopId === id ) ||
|
||||||
|
( previousActiveSubSectionId === id )
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const topSection = /** @type {HTMLElement} */ ( selectedTocSection.closest( `.${PARENT_SECTION_CLASS}` ) );
|
||||||
|
|
||||||
|
if ( selectedTocSection === topSection ) {
|
||||||
|
activeTopSection = topSection;
|
||||||
|
activeTopSection.classList.add( ACTIVE_SECTION_CLASS );
|
||||||
|
} else {
|
||||||
|
activeTopSection = topSection;
|
||||||
|
activeSubSection = selectedTocSection;
|
||||||
|
activeTopSection.classList.add( ACTIVE_SECTION_CLASS );
|
||||||
|
activeSubSection.classList.add( ACTIVE_SECTION_CLASS );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the `ACTIVE_SECTION_CLASS` from all ToC sections.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function deactivateSections() {
|
||||||
|
if ( activeSubSection ) {
|
||||||
|
activeSubSection.classList.remove( ACTIVE_SECTION_CLASS );
|
||||||
|
activeSubSection = undefined;
|
||||||
|
}
|
||||||
|
if ( activeTopSection ) {
|
||||||
|
activeTopSection.classList.remove( ACTIVE_SECTION_CLASS );
|
||||||
|
activeTopSection = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the `EXPANDED_SECTION_CLASS` CSS class name
|
||||||
|
* to a top level heading in the ToC.
|
||||||
|
*
|
||||||
|
* @param {string} id
|
||||||
|
*/
|
||||||
|
function expandSection( id ) {
|
||||||
const tocSection = document.getElementById( id );
|
const tocSection = document.getElementById( id );
|
||||||
|
|
||||||
if ( !tocSection ) {
|
if ( !tocSection ) {
|
||||||
|
@ -53,23 +105,84 @@ module.exports = function tableOfContents( props ) {
|
||||||
|
|
||||||
const parentSection = /** @type {HTMLElement} */ ( tocSection.closest( `.${PARENT_SECTION_CLASS}` ) );
|
const parentSection = /** @type {HTMLElement} */ ( tocSection.closest( `.${PARENT_SECTION_CLASS}` ) );
|
||||||
|
|
||||||
if ( activeChildSection ) {
|
if ( parentSection && expandedSections.indexOf( parentSection ) < 0 ) {
|
||||||
activeChildSection.classList.remove( ACTIVE_SECTION_CLASS );
|
parentSection.classList.add( EXPANDED_SECTION_CLASS );
|
||||||
|
expandedSections.push( parentSection );
|
||||||
}
|
}
|
||||||
if ( activeParentSection ) {
|
|
||||||
activeParentSection.classList.remove( ACTIVE_SECTION_CLASS );
|
|
||||||
}
|
|
||||||
|
|
||||||
tocSection.classList.add( ACTIVE_SECTION_CLASS );
|
|
||||||
|
|
||||||
if ( parentSection ) {
|
|
||||||
parentSection.classList.add( ACTIVE_SECTION_CLASS );
|
|
||||||
}
|
|
||||||
|
|
||||||
activeChildSection = tocSection;
|
|
||||||
activeParentSection = parentSection || undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the IDs of expanded sections.
|
||||||
|
*
|
||||||
|
* @return {Array<string>}
|
||||||
|
*/
|
||||||
|
function getExpandedSectionIds() {
|
||||||
|
return expandedSections.map( ( s ) => s.id );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} id
|
||||||
|
*/
|
||||||
|
function changeActiveSection( id ) {
|
||||||
|
|
||||||
|
const { parent: activeParentId, child: activeChildId } = getActiveSectionIds();
|
||||||
|
|
||||||
|
if ( id === activeParentId && id === activeChildId ) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
deactivateSections();
|
||||||
|
activateSection( id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} id
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
function isTopLevelSection( id ) {
|
||||||
|
const section = document.getElementById( id );
|
||||||
|
return !!section && section.classList.contains( PARENT_SECTION_CLASS );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all `EXPANDED_SECTION_CLASS` CSS class names
|
||||||
|
* from the top level sections in the ToC.
|
||||||
|
*
|
||||||
|
* @param {Array<string>} [selectedIds]
|
||||||
|
*/
|
||||||
|
function collapseSections( selectedIds ) {
|
||||||
|
const sectionIdsToCollapse = selectedIds || getExpandedSectionIds();
|
||||||
|
expandedSections = expandedSections.filter( function ( section ) {
|
||||||
|
if ( sectionIdsToCollapse.indexOf( section.id ) > -1 ) {
|
||||||
|
section.classList.remove( EXPANDED_SECTION_CLASS );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} id
|
||||||
|
*/
|
||||||
|
function toggleExpandSection( id ) {
|
||||||
|
const expandedSectionIds = getExpandedSectionIds();
|
||||||
|
const indexOfExpandedSectionId = expandedSectionIds.indexOf( id );
|
||||||
|
if ( isTopLevelSection( id ) ) {
|
||||||
|
if ( indexOfExpandedSectionId >= 0 ) {
|
||||||
|
collapseSections( [ id ] );
|
||||||
|
} else {
|
||||||
|
expandSection( id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a list item is clicked.
|
||||||
|
*
|
||||||
|
* @callback OnSectionClick
|
||||||
|
* @param {string} id The id of the clicked list item.
|
||||||
|
*/
|
||||||
function bindClickListener() {
|
function bindClickListener() {
|
||||||
props.container.addEventListener( 'click', function ( e ) {
|
props.container.addEventListener( 'click', function ( e ) {
|
||||||
if (
|
if (
|
||||||
|
@ -82,7 +195,6 @@ module.exports = function tableOfContents( props ) {
|
||||||
/** @type {HTMLElement | null} */ ( e.target.closest( `.${LIST_ITEM_CLASS}` ) );
|
/** @type {HTMLElement | null} */ ( e.target.closest( `.${LIST_ITEM_CLASS}` ) );
|
||||||
|
|
||||||
if ( tocSection && tocSection.id ) {
|
if ( tocSection && tocSection.id ) {
|
||||||
activateSection( tocSection.id );
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
props.onSectionClick( tocSection.id );
|
props.onSectionClick( tocSection.id );
|
||||||
}
|
}
|
||||||
|
@ -91,7 +203,19 @@ module.exports = function tableOfContents( props ) {
|
||||||
|
|
||||||
bindClickListener();
|
bindClickListener();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} TableOfContents
|
||||||
|
* @property {changeActiveSection} changeActiveSection
|
||||||
|
* @property {expandSection} expandSection
|
||||||
|
* @property {toggleExpandSection} toggleExpandSection
|
||||||
|
* @property {string} ACTIVE_SECTION_CLASS
|
||||||
|
* @property {string} EXPANDED_SECTION_CLASS
|
||||||
|
*/
|
||||||
return {
|
return {
|
||||||
activateSection
|
expandSection,
|
||||||
|
changeActiveSection,
|
||||||
|
toggleExpandSection,
|
||||||
|
ACTIVE_SECTION_CLASS,
|
||||||
|
EXPANDED_SECTION_CLASS
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
width: 200px;
|
width: 200px;
|
||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: 12px;
|
padding: 12px 12px 12px 26px;
|
||||||
float: left;
|
float: left;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: calc( @height-sticky-header + 1.5em );
|
top: calc( @height-sticky-header + 1.5em );
|
||||||
|
@ -26,12 +26,6 @@
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-toc-list-item-active {
|
|
||||||
> a {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-toc-link > * {
|
.sidebar-toc-link > * {
|
||||||
// Prevent click events on the link's contents so that we can use event
|
// Prevent click events on the link's contents so that we can use event
|
||||||
// delegation and have the target be the anchor element instead.
|
// delegation and have the target be the anchor element instead.
|
||||||
|
@ -46,27 +40,30 @@
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
#table-of-contents,
|
||||||
|
.sidebar-toc-list {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
li {
|
.sidebar-toc-list-item {
|
||||||
list-style-type: none;
|
display: block;
|
||||||
padding-left: @subcategory-indent;
|
position: relative;
|
||||||
|
list-style-type: none;
|
||||||
|
padding-left: @subcategory-indent;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: @color-base;
|
color: @color-base;
|
||||||
font-size: @font-size-base;
|
font-size: @font-size-base;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.sidebar-toc-level-1 {
|
.sidebar-toc-list-item.sidebar-toc-level-1 {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
|
||||||
> a {
|
& > a {
|
||||||
color: @color-link;
|
color: @color-link;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,3 +72,46 @@
|
||||||
.mw-checkbox-hack-checkbox:checked ~ .mw-workspace-container .sidebar-toc {
|
.mw-checkbox-hack-checkbox:checked ~ .mw-workspace-container .sidebar-toc {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Highlight active section
|
||||||
|
.sidebar-toc .sidebar-toc-list-item-active > .sidebar-toc-link {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For no-js users, toggling is disabled and icon is hidden
|
||||||
|
.sidebar-toc .sidebar-toc-toggle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collapse ToC sections by default, excluding no-js or prefers-reduced-motion
|
||||||
|
@media ( prefers-reduced-motion: no-preference ) {
|
||||||
|
.client-js .sidebar-toc {
|
||||||
|
.sidebar-toc-level-1 .sidebar-toc-list-item {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toc-level-1.sidebar-toc-list-item-expanded .sidebar-toc-list-item {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toc-toggle {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 0.7em; // reduces size of toggle icon (by an arbitrary amount)
|
||||||
|
top: 4px; // visually center icon (at least at default font size)
|
||||||
|
left: -18px;
|
||||||
|
transform: rotate( -90deg );
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toc-level-1 > .sidebar-toc-toggle {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toc-level-1 > .sidebar-toc-list:empty + .sidebar-toc-toggle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toc-level-1.sidebar-toc-list-item-expanded .sidebar-toc-toggle {
|
||||||
|
transform: rotate( 0deg );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -258,7 +258,8 @@
|
||||||
"logIn",
|
"logIn",
|
||||||
"logOut",
|
"logOut",
|
||||||
"imageGallery",
|
"imageGallery",
|
||||||
"userGroup"
|
"userGroup",
|
||||||
|
"downTriangle"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"skins.vector.es6": {
|
"skins.vector.es6": {
|
||||||
|
|
100
tests/jest/tableOfContents.test.js
Normal file
100
tests/jest/tableOfContents.test.js
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
const tableOfContents = require( '../../resources/skins.vector.es6/tableOfContents.js' );
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li id="toc-foo" class="sidebar-toc-level-1">
|
||||||
|
<a href="#foo">foo</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li id="toc-bar" class="sidebar-toc-level-1">
|
||||||
|
<a href="#bar">bar</a>
|
||||||
|
<ul>
|
||||||
|
<li id="toc-baz">
|
||||||
|
<a href="#baz">baz</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li id="toc-qux" class="sidebar-toc-level-1">
|
||||||
|
<a href="#qux">qux</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
`;
|
||||||
|
|
||||||
|
let toc, fooSection, barSection, bazSection, quxSection;
|
||||||
|
|
||||||
|
beforeEach( () => {
|
||||||
|
document.body.innerHTML = template;
|
||||||
|
toc = tableOfContents( { container: document.body } );
|
||||||
|
fooSection = document.getElementById( 'toc-foo' );
|
||||||
|
barSection = document.getElementById( 'toc-bar' );
|
||||||
|
bazSection = document.getElementById( 'toc-baz' );
|
||||||
|
quxSection = document.getElementById( 'toc-qux' );
|
||||||
|
} );
|
||||||
|
|
||||||
|
test( 'Table of contents changes the active sections', () => {
|
||||||
|
toc.changeActiveSection( 'toc-foo' );
|
||||||
|
expect(
|
||||||
|
fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) &&
|
||||||
|
!barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) &&
|
||||||
|
!bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) &&
|
||||||
|
!quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS )
|
||||||
|
).toEqual( true );
|
||||||
|
|
||||||
|
toc.changeActiveSection( 'toc-bar' );
|
||||||
|
expect(
|
||||||
|
!fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) &&
|
||||||
|
barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) &&
|
||||||
|
!bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) &&
|
||||||
|
!quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS )
|
||||||
|
).toEqual( true );
|
||||||
|
|
||||||
|
toc.changeActiveSection( 'toc-baz' );
|
||||||
|
expect(
|
||||||
|
!fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) &&
|
||||||
|
barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) &&
|
||||||
|
bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) &&
|
||||||
|
!quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS )
|
||||||
|
).toEqual( true );
|
||||||
|
|
||||||
|
toc.changeActiveSection( 'toc-qux' );
|
||||||
|
expect(
|
||||||
|
!fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) &&
|
||||||
|
!barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) &&
|
||||||
|
!bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) &&
|
||||||
|
quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS )
|
||||||
|
).toEqual( true );
|
||||||
|
} );
|
||||||
|
|
||||||
|
test( 'Table of contents expands sections', () => {
|
||||||
|
toc.expandSection( 'toc-foo' );
|
||||||
|
expect(
|
||||||
|
fooSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) &&
|
||||||
|
!barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) &&
|
||||||
|
!bazSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) &&
|
||||||
|
!quxSection.classList.contains( toc.EXPANDED_SECTION_CLASS )
|
||||||
|
).toEqual( true );
|
||||||
|
|
||||||
|
toc.expandSection( 'toc-bar' );
|
||||||
|
expect(
|
||||||
|
fooSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) &&
|
||||||
|
barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) &&
|
||||||
|
!bazSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) &&
|
||||||
|
!quxSection.classList.contains( toc.EXPANDED_SECTION_CLASS )
|
||||||
|
).toEqual( true );
|
||||||
|
} );
|
||||||
|
|
||||||
|
test( 'Table of contents toggles expanded sections', () => {
|
||||||
|
toc.toggleExpandSection( 'toc-foo' );
|
||||||
|
expect(
|
||||||
|
fooSection.classList.contains( toc.EXPANDED_SECTION_CLASS )
|
||||||
|
).toEqual( true );
|
||||||
|
|
||||||
|
toc.toggleExpandSection( 'toc-foo' );
|
||||||
|
expect(
|
||||||
|
fooSection.classList.contains( toc.EXPANDED_SECTION_CLASS )
|
||||||
|
).toEqual( false );
|
||||||
|
} );
|
Loading…
Reference in a new issue