Dynamically expand/collapse sub-sections in ToC based on # of headings
Server render the table of contents in a collapsed state when the total number of headings is equal or greater than the value of `$wgVectorTableOfContentsCollapseAtCount`. Otherwise, the table of contents will be server rendered in its "expanded" state. In addition: * Revise table of contents tests to call one `assertion` per element so that it is easier to see the exact element that may fail an assertion. * Revise table of contents tests to call a mount function that can merge props to allow for a more flexible set of tests. * Revise table of contents tests by wrapping a `describe` around tests that expect the same prop state. * Adds typedef for table of sections props Bug: T300973 Depends-On: Ifaee451e1903f2accd0ada2f2ed6dfa3f83037b6 Change-Id: I382200bc603b6abf757a91f14a8a55a6581969bd
This commit is contained in:
parent
8dde172451
commit
6e9506dcad
|
@ -639,6 +639,7 @@ class SkinVector extends SkinMustache {
|
|||
Constants::FEATURE_STICKY_HEADER_EDIT
|
||||
)
|
||||
) : false,
|
||||
'data-toc' => $this->getTocData( $parentData['data-toc'] ?? [] )
|
||||
] );
|
||||
|
||||
if ( !$this->isTableOfContentsVisibleInSidebar() ) {
|
||||
|
@ -696,6 +697,25 @@ class SkinVector extends SkinMustache {
|
|||
return $commonSkinData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotates table of contents data with Vector-specific information.
|
||||
*
|
||||
* @param array $tocData
|
||||
* @return array
|
||||
*/
|
||||
private function getTocData( array $tocData ): array {
|
||||
if ( empty( $tocData ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_merge( $tocData, [
|
||||
'vector-is-collapse-sections-enabled' =>
|
||||
$tocData[ 'number-section-count'] >= $this->getConfig()->get(
|
||||
'VectorTableOfContentsCollapseAtCount'
|
||||
)
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotates search box with Vector-specific information
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<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}}{{^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>
|
||||
|
|
|
@ -6,32 +6,32 @@ const LINK_CLASS = 'sidebar-toc-link';
|
|||
const TOGGLE_CLASS = 'sidebar-toc-toggle';
|
||||
|
||||
/**
|
||||
* Called when a list item is clicked.
|
||||
*
|
||||
* @callback onHeadingClick
|
||||
* @param {string} id The id of the clicked list item.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Called when an arrow is clicked.
|
||||
*
|
||||
* @callback onToggleClick
|
||||
* @param {string} id The id of the list item corresponding to the arrow.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TableOfContentsProps
|
||||
* @property {HTMLElement} container The container element for the table of contents.
|
||||
* @property {onHeadingClick} onHeadingClick Called when an arrow is clicked.
|
||||
* @property {onToggleClick} onToggleClick Called when a list item is clicked.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initializes the sidebar's Table of Contents.
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {HTMLElement} props.container
|
||||
* @param {onHeadingClick} props.onHeadingClick
|
||||
* @param {onToggleClick} props.onToggleClick
|
||||
* @param {TableOfContentsProps} props
|
||||
* @return {TableOfContents}
|
||||
*/
|
||||
module.exports = function tableOfContents( props ) {
|
||||
let /** @type {HTMLElement | undefined} */ activeTopSection;
|
||||
let /** @type {HTMLElement | undefined} */ activeSubSection;
|
||||
let /** @type {Array<HTMLElement>} */ expandedSections = [];
|
||||
let /** @type {Array<HTMLElement>} */ expandedSections;
|
||||
|
||||
/**
|
||||
* @typedef {Object} activeSectionIds
|
||||
|
@ -212,7 +212,20 @@ module.exports = function tableOfContents( props ) {
|
|||
} );
|
||||
}
|
||||
|
||||
bindClickListener();
|
||||
/**
|
||||
* Binds event listeners and sets the default state of the component.
|
||||
*/
|
||||
function initialize() {
|
||||
// Sync component state to the default rendered state of the table of contents.
|
||||
expandedSections = Array.from(
|
||||
props.container.querySelectorAll( `.${EXPANDED_SECTION_CLASS}` )
|
||||
);
|
||||
|
||||
// Bind event listeners.
|
||||
bindClickListener();
|
||||
}
|
||||
|
||||
initialize();
|
||||
|
||||
/**
|
||||
* @typedef {Object} TableOfContents
|
||||
|
|
|
@ -496,6 +496,10 @@
|
|||
"default": false
|
||||
},
|
||||
"description": "@var When `VectorTableOfContents` is enabled, the sticky table of contents is shown."
|
||||
},
|
||||
"VectorTableOfContentsCollapseAtCount": {
|
||||
"value": 20,
|
||||
"description": "@var When `VectorTableOfContents` is enabled, the minimum number of headings required to collapse all headings in the sticky table of contents by default."
|
||||
}
|
||||
},
|
||||
"ServiceWiringFiles": [
|
||||
|
|
|
@ -1,6 +1,49 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Table of contents renders 1`] = `
|
||||
exports[`Table of contents when \`vector-is-collapse-sections-enabled\` is false renders 1`] = `
|
||||
"<nav id=\\"mw-panel-toc\\" class=\\"sidebar-toc\\" role=\\"navigation\\" aria-labelledby=\\"sidebar-toc-header\\">
|
||||
<div class=\\"sidebar-toc-header\\">
|
||||
<h2 class=\\"sidebar-toc-title\\" aria-hidden=\\"true\\">Contents</h2>
|
||||
</div>
|
||||
<ul id=\\"table-of-contents\\">
|
||||
<li id=\\"toc-foo\\" class=\\"sidebar-toc-list-item sidebar-toc-level-1 sidebar-toc-list-item-expanded\\">
|
||||
<a class=\\"sidebar-toc-link\\" href=\\"#foo\\">
|
||||
<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>
|
||||
</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\\">
|
||||
<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\\">
|
||||
<a class=\\"sidebar-toc-link\\" href=\\"#qux\\">
|
||||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">3</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>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Table of contents when \`vector-is-collapse-sections-enabled\` is true renders 1`] = `
|
||||
"<nav id=\\"mw-panel-toc\\" class=\\"sidebar-toc\\" role=\\"navigation\\" aria-labelledby=\\"sidebar-toc-header\\">
|
||||
<div class=\\"sidebar-toc-header\\">
|
||||
<h2 class=\\"sidebar-toc-title\\" aria-hidden=\\"true\\">Contents</h2>
|
||||
|
|
|
@ -6,139 +6,181 @@ const tableOfContentsTopSectionTemplate = fs.readFileSync( 'includes/templates/T
|
|||
const tableOfContentsLineTemplate = fs.readFileSync( 'includes/templates/TableOfContents__line.mustache', 'utf8' );
|
||||
const initTableOfContents = require( '../../resources/skins.vector.es6/tableOfContents.js' );
|
||||
|
||||
let toc, fooSection, barSection, bazSection, quxSection;
|
||||
let fooSection, barSection, bazSection, quxSection;
|
||||
const onHeadingClick = jest.fn();
|
||||
const onToggleClick = jest.fn();
|
||||
|
||||
const templateData = {
|
||||
'array-sections': [ {
|
||||
toclevel: 1,
|
||||
number: '1',
|
||||
line: 'foo',
|
||||
anchor: 'foo',
|
||||
'array-sections': null
|
||||
}, {
|
||||
toclevel: 1,
|
||||
number: '2',
|
||||
line: 'bar',
|
||||
anchor: 'bar',
|
||||
/**
|
||||
* @param {Object} templateProps
|
||||
* @return {string}
|
||||
*/
|
||||
function render( templateProps = {} ) {
|
||||
const templateData = Object.assign( {
|
||||
'vector-is-collapse-sections-enabled': false,
|
||||
'array-sections': [ {
|
||||
toclevel: 2,
|
||||
number: '2.1',
|
||||
line: 'baz',
|
||||
anchor: 'baz',
|
||||
toclevel: 1,
|
||||
number: '1',
|
||||
line: 'foo',
|
||||
anchor: 'foo',
|
||||
'array-sections': null
|
||||
}, {
|
||||
toclevel: 1,
|
||||
number: '2',
|
||||
line: 'bar',
|
||||
anchor: 'bar',
|
||||
'array-sections': [ {
|
||||
toclevel: 2,
|
||||
number: '2.1',
|
||||
line: 'baz',
|
||||
anchor: 'baz',
|
||||
'array-sections': null
|
||||
} ]
|
||||
}, {
|
||||
toclevel: 1,
|
||||
number: '3',
|
||||
line: 'qux',
|
||||
anchor: 'qux',
|
||||
'array-sections': null
|
||||
} ]
|
||||
}, {
|
||||
toclevel: 1,
|
||||
number: '3',
|
||||
line: 'qux',
|
||||
anchor: 'qux',
|
||||
'array-sections': null
|
||||
} ]
|
||||
};
|
||||
}, templateProps );
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
const renderedHTML = mustache.render( tableOfContentsTemplate, templateData, {
|
||||
TableOfContents__topSection: tableOfContentsTopSectionTemplate,
|
||||
TableOfContents__line: tableOfContentsLineTemplate
|
||||
} );
|
||||
/* eslint-enable camelcase */
|
||||
/* eslint-disable camelcase */
|
||||
return mustache.render( tableOfContentsTemplate, templateData, {
|
||||
TableOfContents__topSection: tableOfContentsTopSectionTemplate,
|
||||
TableOfContents__line: tableOfContentsLineTemplate
|
||||
} );
|
||||
/* eslint-enable camelcase */
|
||||
}
|
||||
|
||||
beforeEach( () => {
|
||||
document.body.innerHTML = renderedHTML;
|
||||
toc = initTableOfContents( {
|
||||
/**
|
||||
* @param {Object} templateProps
|
||||
* @return {initTableOfContents.TableOfContents}
|
||||
*/
|
||||
function mount( templateProps = {} ) {
|
||||
document.body.innerHTML = render( templateProps );
|
||||
const toc = initTableOfContents( {
|
||||
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' );
|
||||
} );
|
||||
|
||||
test( 'Table of contents renders', () => {
|
||||
expect( document.body.innerHTML ).toMatchSnapshot();
|
||||
} );
|
||||
return toc;
|
||||
}
|
||||
|
||||
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 );
|
||||
describe( 'Table of contents', () => {
|
||||
describe( 'binds event listeners', () => {
|
||||
test( 'for onHeadingClick', () => {
|
||||
const toc = mount();
|
||||
const heading = document.querySelector( `#toc-foo .${toc.LINK_CLASS}` );
|
||||
heading.click();
|
||||
|
||||
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 );
|
||||
expect( onToggleClick ).not.toBeCalled();
|
||||
expect( onHeadingClick ).toBeCalled();
|
||||
} );
|
||||
test( 'for onToggleClick', () => {
|
||||
const toc = mount();
|
||||
const toggle = document.querySelector( `#toc-bar .${toc.TOGGLE_CLASS}` );
|
||||
toggle.click();
|
||||
|
||||
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 );
|
||||
} );
|
||||
|
||||
describe( 'Table of contents binds event listeners', () => {
|
||||
test( 'for onHeadingClick', () => {
|
||||
const heading = document.querySelector( `#toc-foo .${toc.LINK_CLASS}` );
|
||||
heading.click();
|
||||
|
||||
expect( onToggleClick ).not.toBeCalled();
|
||||
expect( onHeadingClick ).toBeCalled();
|
||||
expect( onHeadingClick ).not.toBeCalled();
|
||||
expect( onToggleClick ).toBeCalled();
|
||||
} );
|
||||
} );
|
||||
test( 'for onToggleClick', () => {
|
||||
const toggle = document.querySelector( `#toc-bar .${toc.TOGGLE_CLASS}` );
|
||||
toggle.click();
|
||||
|
||||
expect( onHeadingClick ).not.toBeCalled();
|
||||
expect( onToggleClick ).toBeCalled();
|
||||
describe( 'when changing sections', () => {
|
||||
test( 'applies correct class', () => {
|
||||
const toc = mount( { 'vector-is-collapse-sections-enabled': true } );
|
||||
toc.changeActiveSection( 'toc-foo' );
|
||||
expect( fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
||||
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 );
|
||||
|
||||
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 );
|
||||
|
||||
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 );
|
||||
|
||||
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( bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
expect( quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'when `vector-is-collapse-sections-enabled` is false', () => {
|
||||
test( 'renders', () => {
|
||||
mount();
|
||||
expect( document.body.innerHTML ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
test( 'expands sections', () => {
|
||||
const toc = mount();
|
||||
toc.expandSection( 'toc-foo' );
|
||||
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 );
|
||||
|
||||
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 );
|
||||
} );
|
||||
|
||||
test( 'toggles expanded sections', () => {
|
||||
const toc = mount();
|
||||
toc.toggleExpandSection( 'toc-foo' );
|
||||
expect( fooSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
|
||||
toc.toggleExpandSection( 'toc-foo' );
|
||||
expect( fooSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'when `vector-is-collapse-sections-enabled` is true', () => {
|
||||
test( 'renders', () => {
|
||||
mount( { 'vector-is-collapse-sections-enabled': true } );
|
||||
expect( document.body.innerHTML ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
test( 'expands sections', () => {
|
||||
const toc = mount( { 'vector-is-collapse-sections-enabled': true } );
|
||||
toc.expandSection( 'toc-foo' );
|
||||
expect( fooSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
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 );
|
||||
|
||||
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 );
|
||||
} );
|
||||
|
||||
test( 'toggles expanded sections', () => {
|
||||
const toc = mount( { 'vector-is-collapse-sections-enabled': true } );
|
||||
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 );
|
||||
} );
|
||||
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -18,7 +18,7 @@ use Wikimedia\TestingAccessWrapper;
|
|||
class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
||||
|
||||
/**
|
||||
* @return \VectorTemplate
|
||||
* @return \SkinVector
|
||||
*/
|
||||
private function provideVectorTemplateObject() {
|
||||
$template = new SkinVector( [ 'name' => 'vector' ] );
|
||||
|
@ -44,6 +44,105 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
|||
return in_array( $search, $values );
|
||||
}
|
||||
|
||||
public function provideGetTocData() {
|
||||
$tocData = [
|
||||
'number-section-count' => 2,
|
||||
'array-sections' => [
|
||||
[
|
||||
'toclevel' => 1,
|
||||
'level' => '2',
|
||||
'line' => 'A',
|
||||
'number' => '1',
|
||||
'index' => '1',
|
||||
'fromtitle' => 'Test',
|
||||
'byteoffset' => 231,
|
||||
'anchor' => 'A',
|
||||
'array-sections' => [
|
||||
[
|
||||
'toclevel' => 2,
|
||||
'level' => '4',
|
||||
'line' => 'A1',
|
||||
'number' => '1.1',
|
||||
'index' => '2',
|
||||
'fromtitle' => 'Test',
|
||||
'byteoffset' => 245,
|
||||
'anchor' => 'A1'
|
||||
]
|
||||
]
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
return [
|
||||
// When zero sections
|
||||
[
|
||||
// $tocData
|
||||
[],
|
||||
// wgVectorTableOfContentsCollapseAtCount
|
||||
1,
|
||||
// expected 'vector-is-collapse-sections-enabled' value
|
||||
false
|
||||
],
|
||||
// When number of multiple sections is lower than configured value
|
||||
[
|
||||
// $tocData
|
||||
$tocData,
|
||||
// wgVectorTableOfContentsCollapseAtCount
|
||||
3,
|
||||
// expected 'vector-is-collapse-sections-enabled' value
|
||||
false
|
||||
],
|
||||
// When number of multiple sections is equal to the configured value
|
||||
[
|
||||
// $tocData
|
||||
$tocData,
|
||||
// wgVectorTableOfContentsCollapseAtCount
|
||||
2,
|
||||
// expected 'vector-is-collapse-sections-enabled' value
|
||||
true
|
||||
],
|
||||
// When number of multiple sections is higher than configured value
|
||||
[
|
||||
// $tocData
|
||||
$tocData,
|
||||
// wgVectorTableOfContentsCollapseAtCount
|
||||
1,
|
||||
// expected 'vector-is-collapse-sections-enabled' value
|
||||
true
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getTocData
|
||||
* @dataProvider provideGetTOCData
|
||||
*/
|
||||
public function testGetTocData(
|
||||
array $tocData,
|
||||
int $configValue,
|
||||
bool $expected
|
||||
) {
|
||||
$this->setMwGlobals( [
|
||||
'wgVectorTableOfContentsCollapseAtCount' => $configValue
|
||||
] );
|
||||
|
||||
$skinVector = new SkinVector( [ 'name' => 'vector-2022' ] );
|
||||
$openSkinVector = TestingAccessWrapper::newFromObject( $skinVector );
|
||||
$data = $openSkinVector->getTocData( $tocData );
|
||||
|
||||
if ( empty( $tocData ) ) {
|
||||
$this->assertEquals( [], $data, 'toc data is empty when given an empty array' );
|
||||
return;
|
||||
}
|
||||
$this->assertArrayHasKey( 'vector-is-collapse-sections-enabled', $data );
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$data['vector-is-collapse-sections-enabled'],
|
||||
'vector-is-collapse-sections-enabled has correct value'
|
||||
);
|
||||
$this->assertArrayHasKey( 'array-sections', $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getTemplateData
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue