Merge "Use TOC template data for showing collapsible section arrows"
This commit is contained in:
commit
d93f9e9bed
|
@ -4,7 +4,7 @@
|
|||
</div>
|
||||
<ul id="table-of-contents">
|
||||
{{#array-sections}}
|
||||
{{>TableOfContents__topSection}}
|
||||
{{>TableOfContents__line}}
|
||||
{{/array-sections}}
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
<li id="toc-{{anchor}}" class="sidebar-toc-list-item sidebar-toc-level-{{toclevel}}">
|
||||
<li id="toc-{{anchor}}"
|
||||
class="sidebar-toc-list-item sidebar-toc-level-{{toclevel}}{{#is-top-level-section}}{{^vector-is-collapse-sections-enabled}} sidebar-toc-list-item-expanded{{/vector-is-collapse-sections-enabled}}{{/is-top-level-section}}">
|
||||
<a class="sidebar-toc-link" href="#{{anchor}}">
|
||||
<div class="sidebar-toc-text">
|
||||
<span class="sidebar-toc-numb">{{number}}</span>{{{line}}}</div>
|
||||
</a>
|
||||
{{#is-top-level-section}}{{#is-parent-section}}
|
||||
<button class="mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle"></button>
|
||||
{{/is-parent-section}}{{/is-top-level-section}}
|
||||
<ul class="sidebar-toc-list">
|
||||
{{#array-sections}}
|
||||
{{>TableOfContents__line}}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<li id="toc-{{anchor}}" class="sidebar-toc-list-item sidebar-toc-level-{{toclevel}}{{^vector-is-collapse-sections-enabled}} sidebar-toc-list-item-expanded{{/vector-is-collapse-sections-enabled}}">
|
||||
<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>
|
||||
<button class="mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle"></button>
|
||||
</li>
|
|
@ -1,3 +1,5 @@
|
|||
/** @module SectionObserver */
|
||||
|
||||
/**
|
||||
* @callback OnIntersection
|
||||
* @param {HTMLElement} element The section that triggered the new intersection change.
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/** @module TableOfContents */
|
||||
|
||||
const SECTION_CLASS = 'sidebar-toc-list-item';
|
||||
const ACTIVE_SECTION_CLASS = 'sidebar-toc-list-item-active';
|
||||
const EXPANDED_SECTION_CLASS = 'sidebar-toc-list-item-expanded';
|
||||
|
|
|
@ -94,20 +94,13 @@
|
|||
|
||||
.sidebar-toc-toggle {
|
||||
position: absolute;
|
||||
display: block;
|
||||
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 );
|
||||
}
|
||||
|
|
|
@ -11,32 +11,41 @@ exports[`Table of contents when \`vector-is-collapse-sections-enabled\` is false
|
|||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">1</span>foo</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\"></ul>
|
||||
<button class=\\"mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle\\"></button>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
<li id=\\"toc-bar\\" class=\\"sidebar-toc-list-item sidebar-toc-level-1 sidebar-toc-list-item-expanded\\">
|
||||
<a class=\\"sidebar-toc-link\\" href=\\"#bar\\">
|
||||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">2</span>bar</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\"><li id=\\"toc-baz\\" class=\\"sidebar-toc-list-item sidebar-toc-level-2\\">
|
||||
<button class=\\"mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle\\"></button>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
<li id=\\"toc-baz\\" class=\\"sidebar-toc-list-item sidebar-toc-level-2\\">
|
||||
<a class=\\"sidebar-toc-link\\" href=\\"#baz\\">
|
||||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">2.1</span>baz</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<button class=\\"mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle\\"></button>
|
||||
</li>
|
||||
<li id=\\"toc-qux\\" class=\\"sidebar-toc-list-item sidebar-toc-level-1 sidebar-toc-list-item-expanded\\">
|
||||
<li id=\\"toc-qux\\" class=\\"sidebar-toc-list-item sidebar-toc-level-3\\">
|
||||
<a class=\\"sidebar-toc-link\\" href=\\"#qux\\">
|
||||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">3</span>qux</div>
|
||||
<span class=\\"sidebar-toc-numb\\">2.1.1</span>qux</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\"></ul>
|
||||
<button class=\\"mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle\\"></button>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li id=\\"toc-quux\\" class=\\"sidebar-toc-list-item sidebar-toc-level-1 sidebar-toc-list-item-expanded\\">
|
||||
<a class=\\"sidebar-toc-link\\" href=\\"#quux\\">
|
||||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">3</span>quux</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
@ -54,32 +63,41 @@ exports[`Table of contents when \`vector-is-collapse-sections-enabled\` is true
|
|||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">1</span>foo</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\"></ul>
|
||||
<button class=\\"mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle\\"></button>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
<li id=\\"toc-bar\\" class=\\"sidebar-toc-list-item sidebar-toc-level-1\\">
|
||||
<a class=\\"sidebar-toc-link\\" href=\\"#bar\\">
|
||||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">2</span>bar</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\"><li id=\\"toc-baz\\" class=\\"sidebar-toc-list-item sidebar-toc-level-2\\">
|
||||
<button class=\\"mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle\\"></button>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
<li id=\\"toc-baz\\" class=\\"sidebar-toc-list-item sidebar-toc-level-2\\">
|
||||
<a class=\\"sidebar-toc-link\\" href=\\"#baz\\">
|
||||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">2.1</span>baz</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<button class=\\"mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle\\"></button>
|
||||
</li>
|
||||
<li id=\\"toc-qux\\" class=\\"sidebar-toc-list-item sidebar-toc-level-1\\">
|
||||
<li id=\\"toc-qux\\" class=\\"sidebar-toc-list-item sidebar-toc-level-3\\">
|
||||
<a class=\\"sidebar-toc-link\\" href=\\"#qux\\">
|
||||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">3</span>qux</div>
|
||||
<span class=\\"sidebar-toc-numb\\">2.1.1</span>qux</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\"></ul>
|
||||
<button class=\\"mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle\\"></button>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li id=\\"toc-quux\\" class=\\"sidebar-toc-list-item sidebar-toc-level-1\\">
|
||||
<a class=\\"sidebar-toc-link\\" href=\\"#quux\\">
|
||||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">3</span>quux</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
// @ts-nocheck
|
||||
const mustache = require( 'mustache' );
|
||||
const fs = require( 'fs' );
|
||||
const tableOfContentsTemplate = fs.readFileSync( 'includes/templates/TableOfContents.mustache', 'utf8' );
|
||||
const tableOfContentsTopSectionTemplate = fs.readFileSync( 'includes/templates/TableOfContents__topSection.mustache', 'utf8' );
|
||||
const tableOfContentsLineTemplate = fs.readFileSync( 'includes/templates/TableOfContents__line.mustache', 'utf8' );
|
||||
const initTableOfContents = require( '../../resources/skins.vector.es6/tableOfContents.js' );
|
||||
|
||||
let fooSection, barSection, bazSection, quxSection;
|
||||
let /** @type {HTMLElement} */ fooSection,
|
||||
/** @type {HTMLElement} */ barSection,
|
||||
/** @type {HTMLElement} */ bazSection,
|
||||
/** @type {HTMLElement} */ quxSection,
|
||||
/** @type {HTMLElement} */ quuxSection;
|
||||
const onHeadingClick = jest.fn();
|
||||
const onToggleClick = jest.fn();
|
||||
|
||||
|
@ -22,53 +24,66 @@ function render( templateProps = {} ) {
|
|||
number: '1',
|
||||
line: 'foo',
|
||||
anchor: 'foo',
|
||||
'is-top-level-section': true,
|
||||
'is-parent-section': false,
|
||||
'array-sections': null
|
||||
}, {
|
||||
toclevel: 1,
|
||||
number: '2',
|
||||
line: 'bar',
|
||||
anchor: 'bar',
|
||||
'is-top-level-section': true,
|
||||
'is-parent-section': true,
|
||||
'array-sections': [ {
|
||||
toclevel: 2,
|
||||
number: '2.1',
|
||||
line: 'baz',
|
||||
anchor: 'baz',
|
||||
'array-sections': null
|
||||
'is-top-level-section': false,
|
||||
'is-parent-section': true,
|
||||
'array-sections': [ {
|
||||
toclevel: 3,
|
||||
number: '2.1.1',
|
||||
line: 'qux',
|
||||
anchor: 'qux',
|
||||
'is-top-level-section': false,
|
||||
'is-parent-section': false,
|
||||
'array-sections': null
|
||||
} ]
|
||||
} ]
|
||||
}, {
|
||||
toclevel: 1,
|
||||
number: '3',
|
||||
line: 'qux',
|
||||
anchor: 'qux',
|
||||
line: 'quux',
|
||||
anchor: 'quux',
|
||||
'is-top-level-section': true,
|
||||
'is-parent-section': false,
|
||||
'array-sections': null
|
||||
} ]
|
||||
}, templateProps );
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
return mustache.render( tableOfContentsTemplate, templateData, {
|
||||
TableOfContents__topSection: tableOfContentsTopSectionTemplate,
|
||||
TableOfContents__line: tableOfContentsLineTemplate
|
||||
TableOfContents__line: tableOfContentsLineTemplate // eslint-disable-line camelcase
|
||||
} );
|
||||
/* eslint-enable camelcase */
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} templateProps
|
||||
* @return {initTableOfContents.TableOfContents}
|
||||
* @return {module:TableOfContents~TableOfContents}
|
||||
*/
|
||||
function mount( templateProps = {} ) {
|
||||
document.body.innerHTML = render( templateProps );
|
||||
const toc = initTableOfContents( {
|
||||
container: /** @type {HTMLElement} */ document.getElementById( 'mw-panel-toc' ),
|
||||
container: /** @type {HTMLElement} */ ( document.getElementById( 'mw-panel-toc' ) ),
|
||||
onHeadingClick,
|
||||
onToggleClick
|
||||
} );
|
||||
|
||||
fooSection = /** @type {HTMLElement} */ document.getElementById( 'toc-foo' );
|
||||
barSection = /** @type {HTMLElement} */ document.getElementById( 'toc-bar' );
|
||||
bazSection = /** @type {HTMLElement} */ document.getElementById( 'toc-baz' );
|
||||
quxSection = /** @type {HTMLElement} */ document.getElementById( 'toc-qux' );
|
||||
|
||||
fooSection = /** @type {HTMLElement} */ ( document.getElementById( 'toc-foo' ) );
|
||||
barSection = /** @type {HTMLElement} */ ( document.getElementById( 'toc-bar' ) );
|
||||
bazSection = /** @type {HTMLElement} */ ( document.getElementById( 'toc-baz' ) );
|
||||
quxSection = /** @type {HTMLElement} */ ( document.getElementById( 'toc-qux' ) );
|
||||
quuxSection = /** @type {HTMLElement} */ ( document.getElementById( 'toc-quux' ) );
|
||||
return toc;
|
||||
}
|
||||
|
||||
|
@ -76,7 +91,7 @@ describe( 'Table of contents', () => {
|
|||
describe( 'binds event listeners', () => {
|
||||
test( 'for onHeadingClick', () => {
|
||||
const toc = mount();
|
||||
const heading = document.querySelector( `#toc-foo .${toc.LINK_CLASS}` );
|
||||
const heading = /** @type {HTMLElement} */ ( document.querySelector( `#toc-foo .${toc.LINK_CLASS}` ) );
|
||||
heading.click();
|
||||
|
||||
expect( onToggleClick ).not.toBeCalled();
|
||||
|
@ -84,7 +99,7 @@ describe( 'Table of contents', () => {
|
|||
} );
|
||||
test( 'for onToggleClick', () => {
|
||||
const toc = mount();
|
||||
const toggle = document.querySelector( `#toc-bar .${toc.TOGGLE_CLASS}` );
|
||||
const toggle = /** @type {HTMLElement} */ ( document.querySelector( `#toc-bar .${toc.TOGGLE_CLASS}` ) );
|
||||
toggle.click();
|
||||
|
||||
expect( onHeadingClick ).not.toBeCalled();
|
||||
|
@ -92,6 +107,15 @@ describe( 'Table of contents', () => {
|
|||
} );
|
||||
} );
|
||||
|
||||
test( 'renders toggles for top level parent sections', () => {
|
||||
const toc = mount();
|
||||
expect( fooSection.getElementsByClassName( toc.TOGGLE_CLASS ).length ).toEqual( 0 );
|
||||
expect( barSection.getElementsByClassName( toc.TOGGLE_CLASS ).length ).toEqual( 1 );
|
||||
expect( bazSection.getElementsByClassName( toc.TOGGLE_CLASS ).length ).toEqual( 0 );
|
||||
expect( quxSection.getElementsByClassName( toc.TOGGLE_CLASS ).length ).toEqual( 0 );
|
||||
expect( quuxSection.getElementsByClassName( toc.TOGGLE_CLASS ).length ).toEqual( 0 );
|
||||
} );
|
||||
|
||||
describe( 'when changing sections', () => {
|
||||
test( 'applies correct class', () => {
|
||||
const toc = mount( { 'vector-is-collapse-sections-enabled': true } );
|
||||
|
@ -100,24 +124,28 @@ describe( 'Table of contents', () => {
|
|||
expect( barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quuxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
|
||||
toc.changeActiveSection( 'toc-bar' );
|
||||
expect( fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quuxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
|
||||
toc.changeActiveSection( 'toc-baz' );
|
||||
expect( fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quuxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
|
||||
toc.changeActiveSection( 'toc-qux' );
|
||||
expect( fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( quuxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
} );
|
||||
} );
|
||||
|
||||
|
@ -133,13 +161,15 @@ describe( 'Table of contents', () => {
|
|||
expect( fooSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( bazSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quxSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( quxSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quuxSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
|
||||
toc.expandSection( 'toc-bar' );
|
||||
expect( fooSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( bazSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quxSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( quxSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quuxSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
} );
|
||||
|
||||
test( 'toggles expanded sections', () => {
|
||||
|
@ -165,12 +195,14 @@ describe( 'Table of contents', () => {
|
|||
expect( barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( bazSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quxSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quuxSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
|
||||
toc.expandSection( 'toc-bar' );
|
||||
expect( fooSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( bazSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quxSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quuxSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
} );
|
||||
|
||||
test( 'toggles expanded sections', () => {
|
||||
|
|
Loading…
Reference in New Issue