Update sticky header to use ARIA attributes for section collapsing and add accessible label to toggle buttons
Bug: T303766 Change-Id: Idda4f286a42152af1d233588a1839ada5491ce95
This commit is contained in:
parent
94d9445ba3
commit
f1c70e99b7
|
@ -39,6 +39,7 @@
|
|||
"vector-intro-page": "Help:Introduction",
|
||||
"vector-toc-heading": "Contents",
|
||||
"vector-toc-beginning": "Beginning",
|
||||
"vector-toc-toggle-button-label": "Toggle $1 subsection",
|
||||
"vector-anon-user-menu-pages": "Pages for logged out editors",
|
||||
"vector-anon-user-menu-pages-learn": "learn more",
|
||||
"vector-anon-user-menu-pages-label": "Learn more about editing",
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
"vector-intro-page": "Introduction or tutorial page for the wiki. Typically either Project/Help:Introduction ([[d:Q3945]]) or Project/Help:Tutorial ([[d:Q915263]]).",
|
||||
"vector-toc-heading": "Heading of table of contents\n\n{{Identical|Content}}",
|
||||
"vector-toc-beginning": "Shown in the table of contents: Text of link to the beginning of the article.",
|
||||
"vector-toc-toggle-button-label": "Used to label TOC section toggle buttons for screen readers",
|
||||
"vector-anon-user-menu-pages": "Label describing the anon editor links in the anon user menu",
|
||||
"vector-anon-user-menu-pages-learn": "Lowercase text of link that goes to Help:Introduction and helps the user learn more about editing",
|
||||
"vector-anon-user-menu-pages-label": "Accessible version of 'vector-anon-user-menu-pages' link text, prompts user to learn more about editing",
|
||||
|
|
|
@ -528,8 +528,7 @@ abstract class SkinVector extends SkinMustache {
|
|||
true,
|
||||
'searchform',
|
||||
true
|
||||
),
|
||||
'data-toc' => $this->getTocData( $parentData['data-toc'] ?? [] )
|
||||
)
|
||||
] );
|
||||
|
||||
if ( $skin->getUser()->isRegistered() ) {
|
||||
|
@ -581,30 +580,6 @@ abstract 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 the table of contents has no items, we won't output it.
|
||||
// empty array is interpreted by Mustache as falsey.
|
||||
if ( empty( $tocData ) || empty( $tocData[ 'array-sections' ] ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_merge( $tocData, [
|
||||
'is-vector-toc-beginning-enabled' => $this->getConfig()->get(
|
||||
'VectorTableOfContentsBeginning'
|
||||
),
|
||||
'vector-is-collapse-sections-enabled' =>
|
||||
$tocData[ 'number-section-count'] >= $this->getConfig()->get(
|
||||
'VectorTableOfContentsCollapseAtCount'
|
||||
)
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotates search box with Vector-specific information
|
||||
*
|
||||
|
|
|
@ -77,6 +77,38 @@ class SkinVector22 extends SkinVector {
|
|||
return $this->isTOCEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotates table of contents data with Vector-specific information.
|
||||
*
|
||||
* @param array $tocData
|
||||
* @return array
|
||||
*/
|
||||
private function getTocData( array $tocData ): array {
|
||||
// If the table of contents has no items, we won't output it.
|
||||
// empty array is interpreted by Mustache as falsey.
|
||||
if ( empty( $tocData ) || empty( $tocData[ 'array-sections' ] ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Populate button labels for collapsible TOC sections
|
||||
foreach ( $tocData[ 'array-sections' ] as &$section ) {
|
||||
if ( $section['is-top-level-section'] && $section['is-parent-section'] ) {
|
||||
$section['vector-button-label'] =
|
||||
$this->msg( 'vector-toc-toggle-button-label', $section['line'] )->text();
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge( $tocData, [
|
||||
'is-vector-toc-beginning-enabled' => $this->getConfig()->get(
|
||||
'VectorTableOfContentsBeginning'
|
||||
),
|
||||
'vector-is-collapse-sections-enabled' =>
|
||||
$tocData[ 'number-section-count'] >= $this->getConfig()->get(
|
||||
'VectorTableOfContentsCollapseAtCount'
|
||||
)
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary function while we deprecate SkinVector class.
|
||||
*
|
||||
|
@ -92,6 +124,9 @@ class SkinVector22 extends SkinVector {
|
|||
public function getTemplateData(): array {
|
||||
$featureManager = VectorServices::getFeatureManager();
|
||||
$parentData = parent::getTemplateData();
|
||||
|
||||
$parentData['data-toc'] = $this->getTocData( $parentData['data-toc'] ?? [] );
|
||||
|
||||
if ( !$this->isTableOfContentsVisibleInSidebar() ) {
|
||||
unset( $parentData['data-toc'] );
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<nav id="mw-panel-toc" class="sidebar-toc" role="navigation" aria-labelledby="sidebar-toc-header" data-event-name="ui.sidebar-toc">
|
||||
<div class="sidebar-toc-header">
|
||||
<nav id="mw-panel-toc" class="sidebar-toc" role="navigation" aria-labelledby="sidebar-toc-label" data-event-name="ui.sidebar-toc">
|
||||
<div id="sidebar-toc-label" class="sidebar-toc-header">
|
||||
<h2 class="sidebar-toc-title" aria-hidden="true">{{ msg-vector-toc-heading }}</h2>
|
||||
</div>
|
||||
<ul class="sidebar-toc-contents">
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
<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>
|
||||
<button aria-controls="toc-{{anchor}}-sublist" class="mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle">
|
||||
{{{vector-button-label}}}
|
||||
</button>
|
||||
{{/is-parent-section}}{{/is-top-level-section}}
|
||||
<ul class="sidebar-toc-list">
|
||||
<ul id="toc-{{anchor}}-sublist" class="sidebar-toc-list">
|
||||
{{#array-sections}}
|
||||
{{>TableOfContents__line}}
|
||||
{{/array-sections}}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"@types/node-fetch": "2.5.7",
|
||||
"@vue/test-utils": "1.1.0",
|
||||
"@wikimedia/mw-node-qunit": "6.3.0",
|
||||
"@wikimedia/types-wikimedia": "^0.3.3",
|
||||
"@wikimedia/types-wikimedia": "0.3.3",
|
||||
"@wikimedia/wvui": "0.3.5",
|
||||
"babel-loader": "8.0.6",
|
||||
"commander": "9.1.0",
|
||||
|
|
|
@ -178,8 +178,10 @@ module.exports = function tableOfContents( props ) {
|
|||
}
|
||||
|
||||
const parentSection = /** @type {HTMLElement} */ ( tocSection.closest( `.${PARENT_SECTION_CLASS}` ) );
|
||||
const toggle = tocSection.querySelector( `.${TOGGLE_CLASS}` );
|
||||
|
||||
if ( parentSection && expandedSections.indexOf( parentSection ) < 0 ) {
|
||||
if ( parentSection && toggle && expandedSections.indexOf( parentSection ) < 0 ) {
|
||||
toggle.setAttribute( 'aria-expanded', 'true' );
|
||||
parentSection.classList.add( EXPANDED_SECTION_CLASS );
|
||||
expandedSections.push( parentSection );
|
||||
}
|
||||
|
@ -229,7 +231,10 @@ module.exports = function tableOfContents( props ) {
|
|||
function collapseSections( selectedIds ) {
|
||||
const sectionIdsToCollapse = selectedIds || getExpandedSectionIds();
|
||||
expandedSections = expandedSections.filter( function ( section ) {
|
||||
if ( sectionIdsToCollapse.indexOf( section.id ) > -1 ) {
|
||||
const isSelected = sectionIdsToCollapse.indexOf( section.id ) > -1;
|
||||
const toggle = isSelected ? section.getElementsByClassName( TOGGLE_CLASS ) : undefined;
|
||||
if ( isSelected && toggle && toggle.length > 0 ) {
|
||||
toggle[ 0 ].setAttribute( 'aria-expanded', 'false' );
|
||||
section.classList.remove( EXPANDED_SECTION_CLASS );
|
||||
return false;
|
||||
}
|
||||
|
@ -252,6 +257,23 @@ module.exports = function tableOfContents( props ) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set aria-expanded attribute for all toggle buttons.
|
||||
*/
|
||||
function initializeExpandedStatus() {
|
||||
const parentSections = props.container.querySelectorAll( `.${PARENT_SECTION_CLASS}` );
|
||||
parentSections.forEach( ( section ) => {
|
||||
const expanded = section.classList.contains( EXPANDED_SECTION_CLASS );
|
||||
const toggle = section.querySelector( `.${TOGGLE_CLASS}` );
|
||||
if ( toggle ) {
|
||||
toggle.setAttribute( 'aria-expanded', expanded.toString() );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind event listeners for clicking on section headings and toggle buttons.
|
||||
*/
|
||||
function bindClickListener() {
|
||||
props.container.addEventListener( 'click', function ( e ) {
|
||||
if (
|
||||
|
@ -284,6 +306,9 @@ module.exports = function tableOfContents( props ) {
|
|||
props.container.querySelectorAll( `.${EXPANDED_SECTION_CLASS}` )
|
||||
);
|
||||
|
||||
// Initialize toggle buttons aria-expanded attribute.
|
||||
initializeExpandedStatus();
|
||||
|
||||
// Bind event listeners.
|
||||
bindClickListener();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
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\\" data-event-name=\\"ui.sidebar-toc\\">
|
||||
<div class=\\"sidebar-toc-header\\">
|
||||
exports[`Table of contents renders when \`vector-is-collapse-sections-enabled\` is false 1`] = `
|
||||
"<nav id=\\"mw-panel-toc\\" class=\\"sidebar-toc\\" role=\\"navigation\\" aria-labelledby=\\"sidebar-toc-label\\" data-event-name=\\"ui.sidebar-toc\\">
|
||||
<div id=\\"sidebar-toc-label\\" class=\\"sidebar-toc-header\\">
|
||||
<h2 class=\\"sidebar-toc-title\\" aria-hidden=\\"true\\">Contents</h2>
|
||||
</div>
|
||||
<ul class=\\"sidebar-toc-contents\\">
|
||||
|
@ -16,7 +16,7 @@ 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 id=\\"toc-foo-sublist\\" class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
<li id=\\"toc-bar\\" class=\\"sidebar-toc-list-item sidebar-toc-level-1 sidebar-toc-list-item-expanded\\">
|
||||
|
@ -24,20 +24,22 @@ exports[`Table of contents when \`vector-is-collapse-sections-enabled\` is false
|
|||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">2</span>bar</div>
|
||||
</a>
|
||||
<button class=\\"mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle\\"></button>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
<button aria-controls=\\"toc-bar-sublist\\" class=\\"mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle\\" aria-expanded=\\"true\\">
|
||||
Toggle bar subsection
|
||||
</button>
|
||||
<ul id=\\"toc-bar-sublist\\" 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 id=\\"toc-baz-sublist\\" class=\\"sidebar-toc-list\\">
|
||||
<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\\">2.1.1</span>qux</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
<ul id=\\"toc-qux-sublist\\" class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -49,7 +51,7 @@ exports[`Table of contents when \`vector-is-collapse-sections-enabled\` is false
|
|||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">3</span>quux</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
<ul id=\\"toc-quux-sublist\\" class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -57,9 +59,9 @@ exports[`Table of contents when \`vector-is-collapse-sections-enabled\` is false
|
|||
"
|
||||
`;
|
||||
|
||||
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\\" data-event-name=\\"ui.sidebar-toc\\">
|
||||
<div class=\\"sidebar-toc-header\\">
|
||||
exports[`Table of contents renders when \`vector-is-collapse-sections-enabled\` is true 1`] = `
|
||||
"<nav id=\\"mw-panel-toc\\" class=\\"sidebar-toc\\" role=\\"navigation\\" aria-labelledby=\\"sidebar-toc-label\\" data-event-name=\\"ui.sidebar-toc\\">
|
||||
<div id=\\"sidebar-toc-label\\" class=\\"sidebar-toc-header\\">
|
||||
<h2 class=\\"sidebar-toc-title\\" aria-hidden=\\"true\\">Contents</h2>
|
||||
</div>
|
||||
<ul class=\\"sidebar-toc-contents\\">
|
||||
|
@ -73,7 +75,7 @@ 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 id=\\"toc-foo-sublist\\" class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
<li id=\\"toc-bar\\" class=\\"sidebar-toc-list-item sidebar-toc-level-1\\">
|
||||
|
@ -81,20 +83,22 @@ exports[`Table of contents when \`vector-is-collapse-sections-enabled\` is true
|
|||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">2</span>bar</div>
|
||||
</a>
|
||||
<button class=\\"mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle\\"></button>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
<button aria-controls=\\"toc-bar-sublist\\" class=\\"mw-ui-icon mw-ui-icon-wikimedia-downTriangle mw-ui-icon-small sidebar-toc-toggle\\" aria-expanded=\\"false\\">
|
||||
Toggle bar subsection
|
||||
</button>
|
||||
<ul id=\\"toc-bar-sublist\\" 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 id=\\"toc-baz-sublist\\" class=\\"sidebar-toc-list\\">
|
||||
<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\\">2.1.1</span>qux</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
<ul id=\\"toc-qux-sublist\\" class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -106,7 +110,7 @@ exports[`Table of contents when \`vector-is-collapse-sections-enabled\` is true
|
|||
<div class=\\"sidebar-toc-text\\">
|
||||
<span class=\\"sidebar-toc-numb\\">3</span>quux</div>
|
||||
</a>
|
||||
<ul class=\\"sidebar-toc-list\\">
|
||||
<ul id=\\"toc-quux-sublist\\" class=\\"sidebar-toc-list\\">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -37,6 +37,7 @@ function render( templateProps = {} ) {
|
|||
anchor: 'bar',
|
||||
'is-top-level-section': true,
|
||||
'is-parent-section': true,
|
||||
'vector-button-label': 'Toggle bar subsection',
|
||||
'array-sections': [ {
|
||||
toclevel: 2,
|
||||
number: '2.1',
|
||||
|
@ -91,6 +92,27 @@ function mount( templateProps = {} ) {
|
|||
}
|
||||
|
||||
describe( 'Table of contents', () => {
|
||||
describe( 'renders', () => {
|
||||
test( 'when `vector-is-collapse-sections-enabled` is false', () => {
|
||||
const toc = mount();
|
||||
expect( document.body.innerHTML ).toMatchSnapshot();
|
||||
expect( barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
} );
|
||||
test( 'when `vector-is-collapse-sections-enabled` is true', () => {
|
||||
const toc = mount( { 'vector-is-collapse-sections-enabled': true } );
|
||||
expect( document.body.innerHTML ).toMatchSnapshot();
|
||||
expect( barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
} );
|
||||
test( '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( 'binds event listeners', () => {
|
||||
test( 'for onHeadingClick', () => {
|
||||
const toc = mount();
|
||||
|
@ -110,17 +132,8 @@ 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', () => {
|
||||
describe( 'applies correct classes', () => {
|
||||
test( 'when changing active sections', () => {
|
||||
const toc = mount( { 'vector-is-collapse-sections-enabled': true } );
|
||||
toc.changeActiveSection( 'toc-foo' );
|
||||
expect( fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
||||
|
@ -150,72 +163,47 @@ describe( 'Table of contents', () => {
|
|||
expect( quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
||||
expect( quuxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'when `vector-is-collapse-sections-enabled` is false', () => {
|
||||
test( 'renders', () => {
|
||||
mount();
|
||||
expect( document.body.innerHTML ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
test( 'expands sections', () => {
|
||||
test( 'when expanding 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( 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( false );
|
||||
expect( quuxSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
} );
|
||||
|
||||
test( 'toggles expanded sections', () => {
|
||||
test( 'when toggling 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 );
|
||||
toc.toggleExpandSection( 'toc-bar' );
|
||||
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.toggleExpandSection( 'toc-bar' );
|
||||
expect( barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'applies the correct aria attributes', () => {
|
||||
test( 'when initialized', () => {
|
||||
const toc = mount();
|
||||
const toggleButton = /** @type {HTMLElement} */ ( barSection.querySelector( `.${toc.TOGGLE_CLASS}` ) );
|
||||
|
||||
expect( toggleButton.getAttribute( 'aria-expanded' ) ).toEqual( 'true' );
|
||||
} );
|
||||
|
||||
test( 'when expanding sections', () => {
|
||||
const toc = mount();
|
||||
const toggleButton = /** @type {HTMLElement} */ ( barSection.querySelector( `.${toc.TOGGLE_CLASS}` ) );
|
||||
|
||||
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 );
|
||||
expect( toggleButton.getAttribute( 'aria-expanded' ) ).toEqual( 'true' );
|
||||
} );
|
||||
|
||||
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 );
|
||||
test( 'when toggling sections', () => {
|
||||
const toc = mount();
|
||||
const toggleButton = /** @type {HTMLElement} */ ( barSection.querySelector( `.${toc.TOGGLE_CLASS}` ) );
|
||||
|
||||
toc.toggleExpandSection( 'toc-foo' );
|
||||
expect( fooSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
||||
toc.toggleExpandSection( 'toc-bar' );
|
||||
expect( toggleButton.getAttribute( 'aria-expanded' ) ).toEqual( 'false' );
|
||||
|
||||
toc.toggleExpandSection( 'toc-bar' );
|
||||
expect( toggleButton.getAttribute( 'aria-expanded' ) ).toEqual( 'true' );
|
||||
} );
|
||||
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -49,7 +49,42 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
|||
}
|
||||
|
||||
public function provideGetTocData() {
|
||||
$config = [
|
||||
'VectorTableOfContentsBeginning' => true,
|
||||
'VectorTableOfContentsCollapseAtCount' => 1
|
||||
];
|
||||
$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' => [],
|
||||
'is-top-level-section' => true,
|
||||
'is-parent-section' => false,
|
||||
],
|
||||
[
|
||||
'toclevel' => 1,
|
||||
'level' => '4',
|
||||
'line' => 'B',
|
||||
'number' => '2',
|
||||
'index' => '2',
|
||||
'fromtitle' => 'Test',
|
||||
'byteoffset' => 245,
|
||||
'anchor' => 'B',
|
||||
'array-sections' => [],
|
||||
'is-top-level-section' => true,
|
||||
'is-parent-section' => false,
|
||||
]
|
||||
]
|
||||
];
|
||||
$nestedTocData = [
|
||||
'number-section-count' => 2,
|
||||
'array-sections' => [
|
||||
[
|
||||
|
@ -62,89 +97,104 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
|||
'byteoffset' => 231,
|
||||
'anchor' => 'A',
|
||||
'array-sections' => [
|
||||
[
|
||||
'toclevel' => 2,
|
||||
'level' => '4',
|
||||
'line' => 'A1',
|
||||
'number' => '1.1',
|
||||
'index' => '2',
|
||||
'fromtitle' => 'Test',
|
||||
'byteoffset' => 245,
|
||||
'anchor' => 'A1'
|
||||
]
|
||||
]
|
||||
'toclevel' => 2,
|
||||
'level' => '4',
|
||||
'line' => 'A1',
|
||||
'number' => '1.1',
|
||||
'index' => '2',
|
||||
'fromtitle' => 'Test',
|
||||
'byteoffset' => 245,
|
||||
'anchor' => 'A1',
|
||||
'array-sections' => [],
|
||||
'is-top-level-section' => false,
|
||||
'is-parent-section' => false,
|
||||
],
|
||||
'is-top-level-section' => true,
|
||||
'is-parent-section' => true,
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
$expectedConfigData = [
|
||||
'is-vector-toc-beginning-enabled' => $config[ 'VectorTableOfContentsBeginning' ],
|
||||
'vector-is-collapse-sections-enabled' =>
|
||||
$tocData[ 'number-section-count' ] >= $config[ 'VectorTableOfContentsCollapseAtCount' ]
|
||||
];
|
||||
$expectedNestedTocData = array_merge( $nestedTocData, $expectedConfigData );
|
||||
$context = RequestContext::getMain();
|
||||
$buttonLabel = $context->msg( 'vector-toc-toggle-button-label',
|
||||
$expectedNestedTocData[ 'array-sections' ][ 0 ][ 'line' ]
|
||||
)->text();
|
||||
$expectedNestedTocData[ 'array-sections' ][ 0 ][ 'vector-button-label' ] = $buttonLabel;
|
||||
|
||||
return [
|
||||
// When zero sections
|
||||
[
|
||||
// $tocData
|
||||
[],
|
||||
// wgVectorTableOfContentsCollapseAtCount
|
||||
1,
|
||||
// expected 'vector-is-collapse-sections-enabled' value
|
||||
false
|
||||
$config,
|
||||
// TOC data is empty when given an empty array
|
||||
[]
|
||||
],
|
||||
// When number of multiple sections is lower than configured value
|
||||
[
|
||||
// $tocData
|
||||
$tocData,
|
||||
// wgVectorTableOfContentsCollapseAtCount
|
||||
3,
|
||||
// expected 'vector-is-collapse-sections-enabled' value
|
||||
false
|
||||
array_merge( $config, [ 'VectorTableOfContentsCollapseAtCount' => 3 ] ),
|
||||
// 'vector-is-collapse-sections-enabled' value is false
|
||||
array_merge( $tocData, $expectedConfigData, [
|
||||
'vector-is-collapse-sections-enabled' => false
|
||||
] )
|
||||
],
|
||||
// When number of multiple sections is equal to the configured value
|
||||
[
|
||||
// $tocData
|
||||
$tocData,
|
||||
// wgVectorTableOfContentsCollapseAtCount
|
||||
2,
|
||||
// expected 'vector-is-collapse-sections-enabled' value
|
||||
true
|
||||
array_merge( $config, [ 'VectorTableOfContentsCollapseAtCount' => 2 ] ),
|
||||
// 'vector-is-collapse-sections-enabled' value is true
|
||||
array_merge( $tocData, $expectedConfigData )
|
||||
],
|
||||
// When number of multiple sections is higher than configured value
|
||||
[
|
||||
// $tocData
|
||||
$tocData,
|
||||
// wgVectorTableOfContentsCollapseAtCount
|
||||
1,
|
||||
// expected 'vector-is-collapse-sections-enabled' value
|
||||
true
|
||||
array_merge( $config, [ 'VectorTableOfContentsCollapseAtCount' => 1 ] ),
|
||||
// 'vector-is-collapse-sections-enabled' value is true
|
||||
array_merge( $tocData, $expectedConfigData )
|
||||
],
|
||||
// When "Beginning" TOC section is configured to be turned off
|
||||
[
|
||||
$tocData,
|
||||
array_merge( $config, [ 'VectorTableOfContentsBeginning' => false ] ),
|
||||
// 'is-vector-toc-beginning-enabled' value is false
|
||||
array_merge( $tocData, $expectedConfigData, [
|
||||
'is-vector-toc-beginning-enabled' => false
|
||||
] )
|
||||
],
|
||||
// When TOC has sections with top level parent sections
|
||||
[
|
||||
$nestedTocData,
|
||||
$config,
|
||||
// 'vector-button-label' is provided for top level parent sections
|
||||
$expectedNestedTocData
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Vector\SkinVector::getTocData
|
||||
* @covers \Vector\SkinVector22::getTocData
|
||||
* @dataProvider provideGetTOCData
|
||||
*/
|
||||
public function testGetTocData(
|
||||
array $tocData,
|
||||
int $configValue,
|
||||
bool $expected
|
||||
array $config,
|
||||
array $expected
|
||||
) {
|
||||
$this->setMwGlobals( [
|
||||
'wgVectorTableOfContentsCollapseAtCount' => $configValue
|
||||
'wgVectorTableOfContentsCollapseAtCount' => $config['VectorTableOfContentsCollapseAtCount'],
|
||||
'wgVectorTableOfContentsBeginning' => $config['VectorTableOfContentsBeginning'],
|
||||
] );
|
||||
|
||||
$skinVector = new SkinVectorLegacy( [ 'name' => 'vector-2022' ] );
|
||||
$skinVector = new SkinVector22( [ '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 );
|
||||
$this->assertEquals( $expected, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue