mirror of
https://gerrit.wikimedia.org/r/mediawiki/skins/Vector.git
synced 2024-11-24 07:43:47 +00:00
Merge "Add collapsed TOC to sticky header by moving the TOC"
This commit is contained in:
commit
03b6715713
|
@ -373,9 +373,23 @@ abstract class SkinVector extends SkinMustache {
|
|||
}
|
||||
$btns[] = $this->getAddSectionButtonData();
|
||||
|
||||
$tocPortletData = $this->decoratePortletData( 'data-sticky-header-toc', [
|
||||
'id' => 'p-sticky-header-toc',
|
||||
'class' => 'mw-portlet mw-portlet-sticky-header-toc vector-sticky-header-toc',
|
||||
'html-items' => '',
|
||||
'html-vector-menu-checkbox-attributes' => 'tabindex="-1"',
|
||||
'html-vector-menu-heading-attributes' => 'tabindex="-1"',
|
||||
'heading-class' => implode( ' ', [
|
||||
self::CLASS_QUIET_BUTTON,
|
||||
self::CLASS_ICON_BUTTON,
|
||||
$this->iconClass( 'listBullet' )
|
||||
] ),
|
||||
] );
|
||||
|
||||
// Show sticky ULS if the ULS extension is enabled and the ULS in header is not hidden
|
||||
$showStickyULS = $this->isULSExtensionEnabled() && !$this->shouldHideLanguages();
|
||||
return [
|
||||
'data-sticky-header-toc' => $tocPortletData,
|
||||
'data-primary-action' => $showStickyULS ?
|
||||
$this->getULSButtonData() : null,
|
||||
'data-button-start' => [
|
||||
|
@ -748,7 +762,9 @@ abstract class SkinVector extends SkinMustache {
|
|||
if ( $this->isLegacy() ) {
|
||||
$extraClasses[self::MENU_TYPE_TABS] .= ' vector-menu-tabs-legacy';
|
||||
}
|
||||
$portletData['heading-class'] = '';
|
||||
if ( !isset( $portletData['heading-class'] ) ) {
|
||||
$portletData['heading-class'] = '';
|
||||
}
|
||||
// Add target class to apply different icon to personal menu dropdown for logged in users.
|
||||
if ( $portletData['id'] === 'p-personal' ) {
|
||||
if ( $this->isLegacy() ) {
|
||||
|
@ -835,6 +851,7 @@ abstract class SkinVector extends SkinMustache {
|
|||
case 'data-user-menu':
|
||||
case 'data-actions':
|
||||
case 'data-variants':
|
||||
case 'data-sticky-header-toc':
|
||||
$type = self::MENU_TYPE_DROPDOWN;
|
||||
break;
|
||||
case 'data-views':
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
data-event-name="ui.dropdown-{{id}}"
|
||||
class="vector-menu-checkbox{{#checkbox-class}} {{.}}{{/checkbox-class}}"
|
||||
{{#aria-label}}aria-label="{{.}}"{{/aria-label}}
|
||||
{{{html-vector-menu-checkbox-attributes}}}
|
||||
/>
|
||||
<label
|
||||
id="{{id}}-label"
|
||||
for="{{id}}-checkbox"
|
||||
class="vector-menu-heading{{#heading-class}} {{.}}{{/heading-class}}"
|
||||
{{{html-vector-menu-heading-attributes}}}
|
||||
>
|
||||
{{{html-vector-heading-icon}}}<span class="vector-menu-heading-label">{{label}}</span>
|
||||
</label>
|
||||
|
|
|
@ -19,6 +19,12 @@
|
|||
{{>SearchBox}}
|
||||
{{/data-search}}
|
||||
<div class="vector-sticky-header-context-bar">
|
||||
<div class="vector-sticky-header-toc-container">
|
||||
{{! TOC is cloned into this menu from the sidebar in stickyHeader.js. }}
|
||||
{{#data-sticky-header-toc}}
|
||||
{{>Menu}}
|
||||
{{/data-sticky-header-toc}}
|
||||
</div>
|
||||
<div class="vector-sticky-header-context-bar-primary" {{{html-user-language-attributes}}}>{{{html-title}}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -63,7 +63,6 @@
|
|||
@size-sidebar-button: unit( 44 / @font-size-browser, em ); // Equals `2.75em`.
|
||||
|
||||
@size-icon: unit( 20 / @font-size-browser, em );
|
||||
@size-indicator: unit( 12 / @font-size-browser, em );
|
||||
// Copied from mediawiki.ui.icons
|
||||
@icon-padding-md: unit( 12 / @font-size-browser, em );
|
||||
|
||||
|
@ -132,6 +131,7 @@
|
|||
@margin-horizontal-sidebar-button-icon: unit( 12px / @font-size-browser, em ); // 0.75em @ 16
|
||||
|
||||
// Sidebar
|
||||
@sidebar-toc-selector: ~'.mw-table-of-contents-container .sidebar-toc';
|
||||
@width-sidebar-px: 220;
|
||||
@width-sidebar-px-wide: 244;
|
||||
@margin-toc-start-content: unit( ( @width-sidebar-px + 24 ) / @font-size-browser, em );
|
||||
|
|
|
@ -9,6 +9,7 @@ const
|
|||
deferUntilFrame = require( './deferUntilFrame.js' ),
|
||||
ABTestConfig = require( /** @type {string} */ ( './config.json' ) ).wgVectorWebABTestEnrollment || {},
|
||||
stickyHeaderEditIconConfig = require( /** @type {string} */ ( './config.json' ) ).wgVectorStickyHeaderEdit || true,
|
||||
STICKY_HEADER_VISIBLE_CLASS = 'vector-sticky-header-visible',
|
||||
TOC_ID = 'mw-panel-toc',
|
||||
TOC_ID_LEGACY = 'toc',
|
||||
BODY_CONTENT_ID = 'bodyContent',
|
||||
|
@ -16,10 +17,13 @@ const
|
|||
TOC_SECTION_ID_PREFIX = 'toc-',
|
||||
TOC_LEGACY_PLACEHOLDER_TAG = 'mw:tocplace',
|
||||
TOC_SCROLL_HOOK = 'table_of_contents',
|
||||
TOC_COLLAPSED_CLASS = 'vector-toc-collapsed',
|
||||
PAGE_TITLE_SCROLL_HOOK = 'page_title',
|
||||
PAGE_TITLE_INTERSECTION_CLASS = 'vector-below-page-title',
|
||||
TOC_EXPERIMENT_NAME = 'skin-vector-toc-experiment';
|
||||
|
||||
const belowDesktopMedia = window.matchMedia( '(max-width: 999px)' );
|
||||
|
||||
/**
|
||||
* @callback OnIntersection
|
||||
* @param {HTMLElement} element The section that triggered the new intersection change.
|
||||
|
@ -106,6 +110,24 @@ function initStickyHeaderABTests( abConfig, isStickyHeaderFeatureAllowed, getEna
|
|||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates TOC's location in the DOM (in sidebar or sticky header)
|
||||
* depending on if the TOC is collapsed and if the sticky header is visible
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
const updateTocLocation = () => {
|
||||
const isTocCollapsed = document.body.classList.contains( TOC_COLLAPSED_CLASS );
|
||||
const isStickyHeaderVisible = document.body.classList.contains( STICKY_HEADER_VISIBLE_CLASS );
|
||||
const isBelowDesktop = belowDesktopMedia.matches;
|
||||
if ( isTocCollapsed ) {
|
||||
const tocLocation = isStickyHeaderVisible && !isBelowDesktop ? 'stickyheader' : 'sidebar';
|
||||
stickyHeader.moveToc( tocLocation );
|
||||
} else {
|
||||
stickyHeader.moveToc( 'sidebar' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {void}
|
||||
*/
|
||||
|
@ -117,7 +139,9 @@ const main = () => {
|
|||
searchToggle( searchToggleElement );
|
||||
}
|
||||
|
||||
//
|
||||
// Sticky header
|
||||
//
|
||||
const
|
||||
header = document.getElementById( stickyHeader.STICKY_HEADER_ID ),
|
||||
stickyIntersection = document.getElementById( stickyHeader.FIRST_HEADING_ID ),
|
||||
|
@ -141,22 +165,32 @@ const main = () => {
|
|||
)
|
||||
);
|
||||
|
||||
// Set up intersection observer for page title, used by sticky header
|
||||
// Set up intersection observer for page title
|
||||
// Used to show/hide sticky header and add class used by collapsible TOC (T307900)
|
||||
const observer = scrollObserver.initScrollObserver(
|
||||
() => {
|
||||
if ( isStickyHeaderAllowed && showStickyHeader ) {
|
||||
stickyHeader.show();
|
||||
updateTocLocation();
|
||||
}
|
||||
document.body.classList.add( PAGE_TITLE_INTERSECTION_CLASS );
|
||||
scrollObserver.fireScrollHook( 'down', PAGE_TITLE_SCROLL_HOOK );
|
||||
},
|
||||
() => {
|
||||
if ( isStickyHeaderAllowed && showStickyHeader ) {
|
||||
stickyHeader.hide();
|
||||
updateTocLocation();
|
||||
}
|
||||
document.body.classList.remove( PAGE_TITLE_INTERSECTION_CLASS );
|
||||
scrollObserver.fireScrollHook( 'up', PAGE_TITLE_SCROLL_HOOK );
|
||||
}
|
||||
);
|
||||
|
||||
// Handle toc location when sticky header is hidden on lower viewports
|
||||
belowDesktopMedia.onchange = () => {
|
||||
updateTocLocation();
|
||||
};
|
||||
|
||||
if ( !showStickyHeader ) {
|
||||
stickyHeader.hide();
|
||||
}
|
||||
|
@ -224,18 +258,6 @@ const main = () => {
|
|||
return;
|
||||
}
|
||||
|
||||
// T307900 Setup observer for collapsible TOC
|
||||
if ( stickyIntersection ) {
|
||||
scrollObserver.initScrollObserver(
|
||||
() => {
|
||||
document.body.classList.add( PAGE_TITLE_INTERSECTION_CLASS );
|
||||
},
|
||||
() => {
|
||||
document.body.classList.remove( PAGE_TITLE_INTERSECTION_CLASS );
|
||||
}
|
||||
).observe( stickyIntersection );
|
||||
}
|
||||
|
||||
const tableOfContents = initTableOfContents( {
|
||||
container: tocElement,
|
||||
onHeadingClick: ( id ) => {
|
||||
|
@ -271,7 +293,8 @@ const main = () => {
|
|||
},
|
||||
onToggleClick: ( id ) => {
|
||||
tableOfContents.toggleExpandSection( id );
|
||||
}
|
||||
},
|
||||
onToggleCollapse: updateTocLocation
|
||||
} );
|
||||
const headingSelector = [
|
||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
|
||||
|
|
|
@ -8,6 +8,7 @@ const
|
|||
STICKY_HEADER_APPENDED_PARAM = [ 'wvprov', 'sticky-header' ],
|
||||
STICKY_HEADER_VISIBLE_CLASS = 'vector-sticky-header-visible',
|
||||
STICKY_HEADER_USER_MENU_CONTAINER_CLASS = 'vector-sticky-header-icon-end',
|
||||
TOC_ID = 'mw-panel-toc',
|
||||
FIRST_HEADING_ID = 'firstHeading',
|
||||
USER_MENU_ID = 'p-personal',
|
||||
ULS_STICKY_CLASS = 'uls-dialog-sticky',
|
||||
|
@ -47,6 +48,32 @@ function hide() {
|
|||
document.body.classList.add( ULS_HIDE_CLASS );
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the TOC element to a new parent container.
|
||||
*
|
||||
* @param {string} position The position to move the TOC into: sidebar or stickyheader
|
||||
*/
|
||||
function moveToc( position ) {
|
||||
const toc = document.getElementById( TOC_ID );
|
||||
const currTocContainer = toc && toc.parentElement;
|
||||
if ( !toc || !currTocContainer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newTocContainer;
|
||||
const sidebarTocContainerClass = 'mw-table-of-contents-container';
|
||||
const stickyHeaderTocContainerClass = 'vector-menu-content';
|
||||
// Avoid moving TOC if unnecessary
|
||||
if ( !currTocContainer.classList.contains( sidebarTocContainerClass ) && position === 'sidebar' ) {
|
||||
newTocContainer = document.querySelector( `.${sidebarTocContainerClass}` );
|
||||
} else if ( !currTocContainer.classList.contains( stickyHeaderTocContainerClass ) && position === 'stickyheader' ) {
|
||||
newTocContainer = document.querySelector( `.vector-sticky-header-toc .${stickyHeaderTocContainerClass}` );
|
||||
}
|
||||
if ( newTocContainer ) {
|
||||
newTocContainer.insertAdjacentElement( 'beforeend', toc );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies attribute from an element to another.
|
||||
*
|
||||
|
@ -574,6 +601,7 @@ function initStickyHeader( props ) {
|
|||
module.exports = {
|
||||
show,
|
||||
hide,
|
||||
moveToc,
|
||||
prepareUserMenu,
|
||||
isAllowedNamespace,
|
||||
isAllowedAction,
|
||||
|
|
|
@ -18,11 +18,16 @@ const TOC_COLLAPSED_CLASS = 'vector-toc-collapsed';
|
|||
* @param {string} id The id of the list item corresponding to the arrow.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback onToggleCollapse
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @property {onToggleCollapse} onToggleCollapse Called when collapse toggle buttons are clicked.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -288,6 +293,7 @@ module.exports = function tableOfContents( props ) {
|
|||
showHideTocElement.forEach( function ( btn ) {
|
||||
btn.addEventListener( 'click', () => {
|
||||
document.body.classList.toggle( TOC_COLLAPSED_CLASS );
|
||||
props.onToggleCollapse();
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
|
|
@ -79,6 +79,23 @@
|
|||
min-width: 0;
|
||||
}
|
||||
|
||||
&-toc-container {
|
||||
position: relative;
|
||||
margin-left: -@icon-padding-md;
|
||||
|
||||
.vector-menu-heading {
|
||||
display: none;
|
||||
|
||||
.vector-toc-collapsed & {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-toc {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-context-bar-primary {
|
||||
overflow: hidden;
|
||||
font-family: @font-family-serif;
|
||||
|
|
|
@ -82,19 +82,26 @@
|
|||
}
|
||||
}
|
||||
|
||||
// T302076 Add scrollable indicator as fade
|
||||
.sidebar-toc:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: @sidebar-toc-fade-height;
|
||||
background: linear-gradient( rgba( 255, 255, 255, 0 ), @background-color-page-container );
|
||||
background-repeat: no-repeat;
|
||||
background-position: -@sidebar-toc-right-padding; // T311436 Hacky way to prevent the fade from covering the scrollbar
|
||||
pointer-events: none; // Make the link below the fade clickable
|
||||
// T302076: Add fade scrollable indicator when TOC is in sidebar
|
||||
// Avoid showing indicator when the TOC is collapsed in the page title, sticky header, or floating
|
||||
@media ( min-width: @min-width-desktop ) {
|
||||
@{sidebar-toc-selector}:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: @sidebar-toc-fade-height;
|
||||
background: linear-gradient( rgba( 255, 255, 255, 0 ), @background-color-page-container );
|
||||
background-repeat: no-repeat;
|
||||
background-position: -@sidebar-toc-right-padding; // T311436 Hacky way to prevent the fade from covering the scrollbar
|
||||
pointer-events: none; // Make the link below the fade clickable
|
||||
|
||||
.vector-toc-collapsed & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collapse ToC sections by default, excluding no-js
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
@import '../../common/variables.less';
|
||||
|
||||
@selector-collapsed-toc-closed: ~'#vector-toc-collapsed-checkbox:not( :checked )';
|
||||
@height-collapsed-toc-button: 36px;
|
||||
@padding-top-content-px: unit( @padding-top-content * @font-size-browser, px );
|
||||
@selector-collapsed-toc-open: ~'#vector-toc-collapsed-checkbox:checked';
|
||||
|
||||
#vector-toc-collapsed-button {
|
||||
display: none;
|
||||
|
@ -24,42 +26,6 @@
|
|||
z-index: @z-index-menu;
|
||||
}
|
||||
|
||||
.mixin-collapse-toc-page-title {
|
||||
#vector-toc-collapsed-button {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mw-table-of-contents-container {
|
||||
position: relative;
|
||||
|
||||
.vector-layout-legacy & {
|
||||
// !important needed to override rules in screen.less
|
||||
top: 0 !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-toc {
|
||||
position: absolute;
|
||||
top: 44px;
|
||||
left: -2px;
|
||||
// !important needed to override rules in Sidebar.less
|
||||
margin: 0 !important; /* stylelint-disable-line declaration-no-important */
|
||||
|
||||
// Dropdown styles
|
||||
border: @border-width-base @border-style-base @border-color-base;
|
||||
|
||||
// Remove TOC fade
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@{selector-collapsed-toc-closed} ~ .mw-table-of-contents-container .sidebar-toc {
|
||||
// Hide the TOC when the button is not checked
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Override button styles for the "move to sidebar/hide" links. Default hide.
|
||||
.vector-toc-collapse-button,
|
||||
.vector-toc-uncollapse-button {
|
||||
|
@ -85,42 +51,90 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Applies when TOC is collapsed in it's original DOM location
|
||||
// Doesn't apply to the collapsed TOC in the sticky header
|
||||
.mixin-toc-collapsed-unmoved() {
|
||||
#vector-toc-collapsed-button {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mw-table-of-contents-container {
|
||||
position: relative;
|
||||
|
||||
.vector-layout-legacy & {
|
||||
// !important needed to override rules in screen.less
|
||||
top: 0 !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
}
|
||||
|
||||
@{sidebar-toc-selector} {
|
||||
display: none;
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
// FIXME: Collapsed TOC styles are not consistent with other vector dropdowns
|
||||
border: @border-width-base @border-style-base @border-color-base;
|
||||
}
|
||||
|
||||
@{selector-collapsed-toc-open} ~ @{sidebar-toc-selector} {
|
||||
// Hide the TOC when the button is not checked
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.mixin-toc-collapsed-floating() {
|
||||
#vector-toc-collapsed-button,
|
||||
.sidebar-toc {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#vector-toc-collapsed-button {
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sidebar-toc {
|
||||
top: @height-collapsed-toc-button; // TOC button height
|
||||
left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
@media ( max-width: @max-width-tablet ) {
|
||||
.mixin-collapse-toc-page-title();
|
||||
// Collapsed to page title on narrow screens
|
||||
.mixin-toc-collapsed-unmoved();
|
||||
|
||||
@{sidebar-toc-selector} {
|
||||
top: ~'calc(@{height-collapsed-toc-button} + @{padding-top-content-px})'; // 44px
|
||||
left: -4px;
|
||||
}
|
||||
|
||||
// Collapsed to floating icon on narrow screens when below page
|
||||
.vector-below-page-title {
|
||||
#vector-toc-collapsed-button,
|
||||
.sidebar-toc {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#vector-toc-collapsed-button {
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sidebar-toc {
|
||||
// TOC button height
|
||||
top: 36px;
|
||||
left: 6px;
|
||||
}
|
||||
.mixin-toc-collapsed-floating();
|
||||
}
|
||||
}
|
||||
|
||||
@media ( min-width: @min-width-desktop ) {
|
||||
@supports ( display: grid ) {
|
||||
.client-js {
|
||||
// Collapsed to page title
|
||||
.vector-toc-collapsed .vector-layout-grid {
|
||||
.mixin-collapse-toc-page-title();
|
||||
.mixin-toc-collapsed-unmoved();
|
||||
|
||||
.mw-table-of-contents-container {
|
||||
grid-area: content;
|
||||
}
|
||||
|
||||
.sidebar-toc {
|
||||
@{sidebar-toc-selector} {
|
||||
top: ~'calc(@{height-collapsed-toc-button} + @{padding-top-content-px})'; // 44px
|
||||
left: -@icon-padding-md;
|
||||
}
|
||||
|
||||
// Collapsed to floating icon
|
||||
// when sticky header not visible and below page title
|
||||
body:not( .vector-sticky-header-visible ).vector-below-page-title& {
|
||||
.mixin-toc-collapsed-floating();
|
||||
}
|
||||
}
|
||||
|
||||
.vector-toc-collapsed {
|
||||
|
|
|
@ -11,6 +11,16 @@ exports[`Sticky header renders 1`] = `
|
|||
</button>
|
||||
</div>
|
||||
<div> </div> <div class=\\"vector-sticky-header-context-bar\\">
|
||||
<div class=\\"vector-sticky-header-toc-container\\">
|
||||
|
||||
<div id=\\"p-sticky-header-toc\\" class=\\"vector-menu mw-portlet mw-portlet-sticky-header-toc vector-sticky-header-toc\\">
|
||||
<div class=\\"vector-menu-content\\">
|
||||
|
||||
<ul class=\\"vector-menu-content-list\\"></ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"vector-sticky-header-context-bar-primary\\"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
// @ts-ignore
|
||||
window.matchMedia = window.matchMedia || function () {
|
||||
return {
|
||||
matches: false,
|
||||
onchange: () => {}
|
||||
};
|
||||
};
|
||||
|
||||
const { test } = require( '../../../resources/skins.vector.es6/main.js' );
|
||||
const {
|
||||
STICKY_HEADER_EXPERIMENT_NAME,
|
||||
|
|
|
@ -2,6 +2,7 @@ const mustache = require( 'mustache' );
|
|||
const fs = require( 'fs' );
|
||||
const stickyHeaderTemplate = fs.readFileSync( 'includes/templates/StickyHeader.mustache', 'utf8' );
|
||||
const buttonTemplate = fs.readFileSync( 'includes/templates/Button.mustache', 'utf8' );
|
||||
const menuTemplate = fs.readFileSync( 'includes/templates/Menu.mustache', 'utf8' );
|
||||
const sticky = require( '../../resources/skins.vector.es6/stickyHeader.js' );
|
||||
const { userLinksHTML } = require( './userLinksData.js' );
|
||||
|
||||
|
@ -58,6 +59,14 @@ const editButtonsTemplateData = [ {
|
|||
} ];
|
||||
|
||||
const templateData = {
|
||||
'data-sticky-header-toc': {
|
||||
id: 'p-sticky-header-toc',
|
||||
class: 'mw-portlet mw-portlet-sticky-header-toc vector-sticky-header-toc',
|
||||
'html-items': '',
|
||||
'html-vector-menu-checkbox-attributes': 'tabindex="-1"',
|
||||
'html-vector-menu-heading-attributes': 'tabindex="-1"',
|
||||
'heading-class': 'mw-ui-button mw-ui-quiet mw-ui-icon mw-ui-icon-element mw-ui-icon-wikimedia-listBullet'
|
||||
},
|
||||
'data-primary-action': {
|
||||
id: 'p-lang-btn-sticky-header',
|
||||
class: 'mw-interlanguage-selector',
|
||||
|
@ -81,6 +90,7 @@ const templateData = {
|
|||
|
||||
const renderedHTML = mustache.render( stickyHeaderTemplate, templateData, {
|
||||
Button: buttonTemplate,
|
||||
Menu: menuTemplate,
|
||||
SearchBox: '<div> </div>' // ignore SearchBox for this test
|
||||
} );
|
||||
|
||||
|
@ -104,4 +114,32 @@ describe( 'sticky header', () => {
|
|||
expect( newMenu.querySelectorAll( '.user-links-collapsible-item' ).length ).toBe( 0 );
|
||||
expect( newMenu.querySelectorAll( '.mw-list-item-js' ).length ).toBe( 0 );
|
||||
} );
|
||||
|
||||
describe( 'moveToc', () => {
|
||||
const sidebarTocContainerClass = 'mw-table-of-contents-container';
|
||||
const stickyHeaderTocContainerClass = 'vector-menu-content';
|
||||
const tocId = 'mw-panel-toc';
|
||||
|
||||
function setupToc() {
|
||||
const sidebarTocContainer = document.createElement( 'div' );
|
||||
sidebarTocContainer.classList.add( sidebarTocContainerClass );
|
||||
const toc = document.createElement( 'div' );
|
||||
toc.setAttribute( 'id', tocId );
|
||||
sidebarTocContainer.appendChild( toc );
|
||||
document.body.appendChild( sidebarTocContainer );
|
||||
}
|
||||
|
||||
test( 'moves toc to stickyheader and sidebar', () => {
|
||||
setupToc();
|
||||
const toc = /** @type {Element} */ ( document.getElementById( tocId ) );
|
||||
expect( /** @type {Element} */
|
||||
( toc.parentNode ).classList.contains( sidebarTocContainerClass ) ).toBeTruthy();
|
||||
sticky.moveToc( 'stickyheader' );
|
||||
expect( /** @type {Element} */
|
||||
( toc.parentNode ).classList.contains( stickyHeaderTocContainerClass ) ).toBeTruthy();
|
||||
sticky.moveToc( 'sidebar' );
|
||||
expect( /** @type {Element} */
|
||||
( toc.parentNode ).classList.contains( sidebarTocContainerClass ) ).toBeTruthy();
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -11,6 +11,7 @@ let /** @type {HTMLElement} */ fooSection,
|
|||
/** @type {HTMLElement} */ quuxSection;
|
||||
const onHeadingClick = jest.fn();
|
||||
const onToggleClick = jest.fn();
|
||||
const onToggleCollapse = jest.fn();
|
||||
|
||||
/**
|
||||
* @param {Object} templateProps
|
||||
|
@ -80,7 +81,8 @@ function mount( templateProps = {} ) {
|
|||
const toc = initTableOfContents( {
|
||||
container: /** @type {HTMLElement} */ ( document.getElementById( 'mw-panel-toc' ) ),
|
||||
onHeadingClick,
|
||||
onToggleClick
|
||||
onToggleClick,
|
||||
onToggleCollapse
|
||||
} );
|
||||
|
||||
fooSection = /** @type {HTMLElement} */ ( document.getElementById( 'toc-foo' ) );
|
||||
|
|
Loading…
Reference in a new issue