From 9c6f6709c60bd802cfacd64f44de616530434917 Mon Sep 17 00:00:00 2001 From: bwang Date: Wed, 24 Aug 2022 13:53:37 -0500 Subject: [PATCH] Create new sticky toc container Bug: T313060 Change-Id: Ia263c606dce5a6060b6b29fbaedc49cef3e17a5c --- .../TableOfContentsContainer.mustache | 7 ++-- resources/skins.vector.es6/stickyHeader.js | 6 +++- .../components/TableOfContents.less | 34 +++++++++++++++++-- .../components/TableOfContentsCollapsed.less | 4 ++- tests/jest/stickyHeader.test.js | 21 +++++++----- 5 files changed, 57 insertions(+), 15 deletions(-) diff --git a/includes/templates/TableOfContentsContainer.mustache b/includes/templates/TableOfContentsContainer.mustache index 57dd4a63..825b4be1 100644 --- a/includes/templates/TableOfContentsContainer.mustache +++ b/includes/templates/TableOfContentsContainer.mustache @@ -1,3 +1,6 @@ -
- {{#data-toc}}{{>TableOfContents}}{{/data-toc}} +
+ {{! T313060 Additional container div needed to prevent the sticky element from being siblings with the footer }} +
+ {{#data-toc}}{{>TableOfContents}}{{/data-toc}} +
diff --git a/resources/skins.vector.es6/stickyHeader.js b/resources/skins.vector.es6/stickyHeader.js index 453cffc6..2f319b18 100644 --- a/resources/skins.vector.es6/stickyHeader.js +++ b/resources/skins.vector.es6/stickyHeader.js @@ -60,8 +60,11 @@ function moveToc( position ) { return; } + // FIXME: Remove after Ia263c606dce5a6060b6b29fbaedc49cef3e17a5c has been in prod for 5 days + const isCachedHtml = document.querySelector( '.mw-table-of-contents-container.mw-sticky-header-element' ); + let newTocContainer; - const sidebarTocContainerClass = 'mw-table-of-contents-container'; + const sidebarTocContainerClass = isCachedHtml ? 'mw-table-of-contents-container' : 'vector-sticky-toc-container'; const stickyHeaderTocContainerClass = 'vector-menu-content'; // Avoid moving TOC if unnecessary if ( !currTocContainer.classList.contains( sidebarTocContainerClass ) && position === 'sidebar' ) { @@ -69,6 +72,7 @@ function moveToc( position ) { } else if ( !currTocContainer.classList.contains( stickyHeaderTocContainerClass ) && position === 'stickyheader' ) { newTocContainer = document.querySelector( `.vector-sticky-header-toc .${stickyHeaderTocContainerClass}` ); } + if ( newTocContainer ) { newTocContainer.insertAdjacentElement( 'beforeend', toc ); } diff --git a/resources/skins.vector.styles/components/TableOfContents.less b/resources/skins.vector.styles/components/TableOfContents.less index bcce8761..f1f18951 100644 --- a/resources/skins.vector.styles/components/TableOfContents.less +++ b/resources/skins.vector.styles/components/TableOfContents.less @@ -10,11 +10,19 @@ @toc-subsection-toggle-icon-size: 1.834em; .mw-table-of-contents-container { + // Needed for Grid-based layout + align-self: start; + height: 100%; +} + +// FIXME: Remove selector after Ia263c606dce5a6060b6b29fbaedc49cef3e17a5c has been in prod for 5 days +.mw-table-of-contents-container.mw-sticky-header-element { // stylelint-disable-next-line plugin/no-unsupported-browser-features position: sticky; top: 0; // Needed for Grid-based layout align-self: start; + height: unset; // Needed to align TOC with bottom of title // 1.5em from .mw-table-of-contents-container + 1.5em from .sidebar-toc = 3em @@ -25,10 +33,32 @@ } } +.vector-sticky-toc-container { + // stylelint-disable-next-line plugin/no-unsupported-browser-features + position: sticky; + top: 0; + + @media ( min-width: @min-width-desktop ) { + .vector-toc-not-collapsed & { + // Default spacing separating the sidebar TOC from the main menu or viewport. + // Need to use padding in order for the spacing to apply when sticky + padding-top: 1.5em; + } + + .vector-toc-not-collapsed @{selector-main-menu-closed} ~ .mw-table-of-contents-container & { + // Needed to align TOC with bottom of title, 1.5em padding + 1.5em margin = 3em + margin-top: 1.5em; + } + } +} + +// FIXME: Remove selector after Ia263c606dce5a6060b6b29fbaedc49cef3e17a5c has been in prod for 5 days +.mw-table-of-contents-container > .sidebar-toc { + margin-top: 1.5em; +} + .sidebar-toc { max-height: 75vh; - // Default spacing separating the sidebar TOC from the main menu or viewport. - margin-top: 1.5em; padding: @sidebar-toc-vertical-padding @sidebar-toc-right-padding @sidebar-toc-vertical-padding @sidebar-toc-left-padding; box-sizing: border-box; overflow: auto; diff --git a/resources/skins.vector.styles/components/TableOfContentsCollapsed.less b/resources/skins.vector.styles/components/TableOfContentsCollapsed.less index d61c1b72..60387e03 100644 --- a/resources/skins.vector.styles/components/TableOfContentsCollapsed.less +++ b/resources/skins.vector.styles/components/TableOfContentsCollapsed.less @@ -64,7 +64,9 @@ display: block; } - .mw-table-of-contents-container { + // FIXME: Remove this selector after Ia263c606dce5a6060b6b29fbaedc49cef3e17a5c has been in prod for 5 days + .mw-table-of-contents-container.mw-sticky-header-element, + .vector-sticky-toc-container { position: relative; } diff --git a/tests/jest/stickyHeader.test.js b/tests/jest/stickyHeader.test.js index f651d154..e971bfab 100644 --- a/tests/jest/stickyHeader.test.js +++ b/tests/jest/stickyHeader.test.js @@ -1,5 +1,6 @@ const mustache = require( 'mustache' ); const fs = require( 'fs' ); +const tocContainerTemplate = fs.readFileSync( 'includes/templates/TableOfContentsContainer.mustache', 'utf8' ); 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' ); @@ -116,17 +117,19 @@ describe( 'sticky header', () => { } ); describe( 'moveToc', () => { - const sidebarTocContainerClass = 'mw-table-of-contents-container'; - const stickyHeaderTocContainerClass = 'vector-menu-content'; + const sidebarTocContainerClass = 'vector-sticky-toc-container'; + const stickyTocContainerClass = '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 ); + const sidebarTocContainerHTML = mustache.render( tocContainerTemplate, { + 'data-toc': [ '' ] + }, { + TableOfContents: '' + } ); + var sidebarTocContainerTemplate = document.createElement( 'template' ); + sidebarTocContainerTemplate.innerHTML = sidebarTocContainerHTML; + document.body.appendChild( sidebarTocContainerTemplate.content ); } test( 'moves toc to stickyheader and sidebar', () => { @@ -136,7 +139,7 @@ describe( 'sticky header', () => { ( toc.parentNode ).classList.contains( sidebarTocContainerClass ) ).toBeTruthy(); sticky.moveToc( 'stickyheader' ); expect( /** @type {Element} */ - ( toc.parentNode ).classList.contains( stickyHeaderTocContainerClass ) ).toBeTruthy(); + ( toc.parentNode ).classList.contains( stickyTocContainerClass ) ).toBeTruthy(); sticky.moveToc( 'sidebar' ); expect( /** @type {Element} */ ( toc.parentNode ).classList.contains( sidebarTocContainerClass ) ).toBeTruthy();