mirror of
https://gerrit.wikimedia.org/r/mediawiki/skins/Vector.git
synced 2024-11-24 07:43:47 +00:00
Merge "Refactor page tools, main menu, and TOC components"
This commit is contained in:
commit
9463861054
|
@ -19,55 +19,52 @@ class VectorComponentMainMenu implements VectorComponent {
|
|||
private $languageData;
|
||||
/** @var MessageLocalizer */
|
||||
private $localizer;
|
||||
/** @var User */
|
||||
private $user;
|
||||
/** @var VectorComponentPinnableHeader|null */
|
||||
private $pinnableHeader;
|
||||
/** @var string */
|
||||
public const ID = 'vector-main-menu';
|
||||
|
||||
/**
|
||||
* @param array $sidebarData
|
||||
* @param Skin $skin
|
||||
* @param bool $shouldLanguageAlertBeInSidebar
|
||||
* @param array $languageData
|
||||
* @param MessageLocalizer $localizer
|
||||
* @param User $user
|
||||
* @param Skin $skin
|
||||
*/
|
||||
public function __construct(
|
||||
array $sidebarData,
|
||||
Skin $skin,
|
||||
bool $shouldLanguageAlertBeInSidebar,
|
||||
array $languageData
|
||||
array $languageData,
|
||||
MessageLocalizer $localizer,
|
||||
User $user,
|
||||
Skin $skin
|
||||
) {
|
||||
$this->sidebarData = $sidebarData;
|
||||
$this->localizer = $skin->getContext();
|
||||
$this->languageData = $languageData;
|
||||
$user = $skin->getUser();
|
||||
$this->user = $user;
|
||||
$this->localizer = $localizer;
|
||||
|
||||
if ( $user->isRegistered() ) {
|
||||
$this->optOut = new VectorComponentMainMenuActionOptOut( $skin );
|
||||
$this->pinnableHeader = new VectorComponentPinnableHeader(
|
||||
$this->localizer,
|
||||
false,
|
||||
self::ID,
|
||||
null
|
||||
);
|
||||
}
|
||||
if ( $shouldLanguageAlertBeInSidebar ) {
|
||||
$this->alert = new VectorComponentMainMenuActionLanguageSwitchAlert( $skin );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User
|
||||
*/
|
||||
private function getUser(): User {
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getTemplateData(): array {
|
||||
$action = $this->optOut;
|
||||
$alert = $this->alert;
|
||||
|
||||
$id = 'vector-main-menu';
|
||||
$pinnableHeader = new VectorComponentPinnableHeader(
|
||||
$this->localizer,
|
||||
false,
|
||||
$id,
|
||||
null
|
||||
);
|
||||
$pinnableHeader = $this->pinnableHeader;
|
||||
|
||||
$portletsRest = [];
|
||||
foreach ( $this->sidebarData[ 'array-portlets-rest' ] as $data ) {
|
||||
|
@ -81,7 +78,7 @@ class VectorComponentMainMenu implements VectorComponent {
|
|||
'data-main-menu-action' => $action ? $action->getTemplateData() : null,
|
||||
// T295555 Add language switch alert message temporarily (to be removed).
|
||||
'data-vector-language-switch-alert' => $alert ? $alert->getTemplateData() : null,
|
||||
'data-pinnable-header' => $pinnableHeader->getTemplateData(),
|
||||
'data-pinnable-header' => $pinnableHeader ? $pinnableHeader->getTemplateData() : null,
|
||||
'data-languages' => $languageMenu->getTemplateData(),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
namespace MediaWiki\Skins\Vector\Components;
|
||||
|
||||
use MediaWiki\Skins\Vector\Constants;
|
||||
use MediaWiki\Skins\Vector\FeatureManagement\FeatureManager;
|
||||
use MessageLocalizer;
|
||||
use User;
|
||||
|
||||
|
@ -12,6 +14,9 @@ class VectorComponentPageTools implements VectorComponent {
|
|||
/** @var array */
|
||||
private $menus;
|
||||
|
||||
/** @var MessageLocalizer */
|
||||
private $localizer;
|
||||
|
||||
/** @var bool */
|
||||
private $isPinned;
|
||||
|
||||
|
@ -27,29 +32,26 @@ class VectorComponentPageTools implements VectorComponent {
|
|||
/** @var string */
|
||||
private const ACTIONS_ID = 'p-cactions';
|
||||
|
||||
/** @var MessageLocalizer */
|
||||
private $localizer;
|
||||
|
||||
/**
|
||||
* @param array $menus
|
||||
* @param bool $isPinned
|
||||
* @param MessageLocalizer $localizer
|
||||
* @param User $user
|
||||
* @param FeatureManager $featureManager
|
||||
*/
|
||||
public function __construct(
|
||||
array $menus,
|
||||
bool $isPinned,
|
||||
MessageLocalizer $localizer,
|
||||
User $user
|
||||
User $user,
|
||||
FeatureManager $featureManager
|
||||
) {
|
||||
$this->menus = $menus;
|
||||
$this->isPinned = $isPinned;
|
||||
$this->localizer = $localizer;
|
||||
$this->isPinned = $featureManager->isFeatureEnabled( Constants::FEATURE_PAGE_TOOLS_PINNED );
|
||||
$this->pinnableHeader = $user->isRegistered() ? new VectorComponentPinnableHeader(
|
||||
$localizer,
|
||||
$isPinned,
|
||||
$this->isPinned,
|
||||
// Name
|
||||
'vector-page-tools',
|
||||
self::ID,
|
||||
// Feature name
|
||||
'page-tools-pinned'
|
||||
) : null;
|
||||
|
@ -64,10 +66,10 @@ class VectorComponentPageTools implements VectorComponent {
|
|||
return array_map( function ( $menu ) {
|
||||
switch ( $menu['id'] ?? '' ) {
|
||||
case self::TOOLBOX_ID:
|
||||
$menu['label'] = $this->localizer->msg( 'vector-page-tools-general-label' );
|
||||
$menu['label'] = $this->localizer->msg( 'vector-page-tools-general-label' )->text();
|
||||
break;
|
||||
case self::ACTIONS_ID:
|
||||
$menu['label'] = $this->localizer->msg( 'vector-page-tools-actions-label' );
|
||||
$menu['label'] = $this->localizer->msg( 'vector-page-tools-actions-label' )->text();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,91 @@
|
|||
<?php
|
||||
namespace MediaWiki\Skins\Vector\Components;
|
||||
|
||||
use Config;
|
||||
use MessageLocalizer;
|
||||
|
||||
/**
|
||||
* VectorComponentTableOfContents component
|
||||
*/
|
||||
class VectorComponentTableOfContents implements VectorComponent {
|
||||
|
||||
/** @var array */
|
||||
private $tocData;
|
||||
|
||||
/** @var MessageLocalizer */
|
||||
private $localizer;
|
||||
|
||||
/** @var bool */
|
||||
private $isPinned;
|
||||
|
||||
/** @var Config */
|
||||
private $config;
|
||||
|
||||
/** @var VectorComponentPinnableHeader */
|
||||
private $pinnableHeader;
|
||||
|
||||
/** @var string */
|
||||
public const ID = 'vector-toc';
|
||||
|
||||
/**
|
||||
* @param array $tocData
|
||||
* @param MessageLocalizer $localizer
|
||||
* @param Config $config
|
||||
*/
|
||||
public function __construct(
|
||||
array $tocData,
|
||||
MessageLocalizer $localizer,
|
||||
Config $config
|
||||
) {
|
||||
$this->tocData = $tocData;
|
||||
$this->localizer = $localizer;
|
||||
// ToC is pinned by default, hardcoded for now
|
||||
$this->isPinned = true;
|
||||
$this->config = $config;
|
||||
$this->pinnableHeader = new VectorComponentPinnableHeader(
|
||||
$this->localizer,
|
||||
$this->isPinned,
|
||||
'vector-toc',
|
||||
null,
|
||||
false,
|
||||
'h2'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* In tableOfContents.js we have tableOfContents::getTableOfContentsSectionsData(),
|
||||
* that yields the same result as this function, please make sure to keep them in sync.
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getTemplateData(): array {
|
||||
$pinnableElementName = 'vector-toc';
|
||||
$pinnedContainer = new VectorComponentPinnedContainer( $pinnableElementName );
|
||||
$pinnableElement = new VectorComponentPinnableElement( $pinnableElementName );
|
||||
return $pinnableElement->getTemplateData() + $pinnedContainer->getTemplateData();
|
||||
// If the table of contents has no items, we won't output it.
|
||||
// empty array is interpreted by Mustache as falsey.
|
||||
if ( empty( $this->tocData ) || empty( $this->tocData[ 'array-sections' ] ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Populate button labels for collapsible TOC sections
|
||||
foreach ( $this->tocData[ 'array-sections' ] as &$section ) {
|
||||
if ( $section['is-top-level-section'] && $section['is-parent-section'] ) {
|
||||
$section['vector-button-label'] =
|
||||
$this->localizer->msg( 'vector-toc-toggle-button-label', $section['line'] )->text();
|
||||
}
|
||||
}
|
||||
|
||||
$pinnedContainer = new VectorComponentPinnedContainer( self::ID );
|
||||
$pinnableElement = new VectorComponentPinnableElement( self::ID );
|
||||
|
||||
return $pinnableElement->getTemplateData() +
|
||||
$pinnedContainer->getTemplateData() +
|
||||
array_merge( $this->tocData, [
|
||||
'is-vector-toc-beginning-enabled' => $this->config->get(
|
||||
'VectorTableOfContentsBeginning'
|
||||
),
|
||||
'vector-is-collapse-sections-enabled' =>
|
||||
$this->tocData[ 'number-section-count'] >= $this->config->get(
|
||||
'VectorTableOfContentsCollapseAtCount'
|
||||
),
|
||||
'data-pinnable-header' => $this->pinnableHeader->getTemplateData(),
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,9 @@ use Wikimedia\Assert\Assert;
|
|||
*
|
||||
* @package MediaWiki\Skins\Vector\FeatureManagement
|
||||
* @internal
|
||||
* @final
|
||||
*/
|
||||
final class FeatureManager {
|
||||
class FeatureManager {
|
||||
|
||||
/**
|
||||
* A map of feature name to the array of requirements (referenced by name). A feature is only
|
||||
|
|
|
@ -285,7 +285,7 @@ abstract class SkinVector extends SkinMustache {
|
|||
$returnto = $this->getReturnToParam();
|
||||
$useCombinedLoginLink = $this->useCombinedLoginLink();
|
||||
$userMenuOverflowData = Hooks::updateDropdownMenuData( $overflowMenuData );
|
||||
$userMenu = $this->getUserMenuDropdown( $userMenuData );
|
||||
$userMenuDropdown = $this->getUserMenuDropdown( $userMenuData );
|
||||
unset( $userMenuOverflowData[ 'label' ] );
|
||||
|
||||
if ( $isAnon || $isTempUser ) {
|
||||
|
@ -311,7 +311,7 @@ abstract class SkinVector extends SkinMustache {
|
|||
'is-temp-user' => $isTempUser,
|
||||
'is-wide' => $moreItems > 3,
|
||||
'data-user-menu-overflow' => $userMenuOverflowData,
|
||||
'data-user-menu' => $userMenu->getTemplateData(),
|
||||
'data-user-menu-dropdown' => $userMenuDropdown->getTemplateData(),
|
||||
'html-items' => $userMenuData['html-items'],
|
||||
];
|
||||
}
|
||||
|
@ -371,7 +371,7 @@ abstract class SkinVector extends SkinMustache {
|
|||
}
|
||||
$btns[] = $this->getAddSectionButtonData();
|
||||
|
||||
// FIXME: Sync with SkinVector22:getTocData
|
||||
// FIXME: Sync with VectorComponentTableOfContents:getTemplateData
|
||||
$tocPortletData = Hooks::updateDropdownMenuData( [
|
||||
'id' => 'vector-sticky-header-toc',
|
||||
'class' => 'mw-portlet mw-portlet-sticky-header-toc vector-sticky-header-toc',
|
||||
|
@ -530,7 +530,7 @@ abstract class SkinVector extends SkinMustache {
|
|||
// completion of T319356.
|
||||
//
|
||||
// Also, add target class to apply different icon to personal menu dropdown for logged in users.
|
||||
$portletData['class'] = 'mw-portlet mw-portlet-personal vector-user-menu vector-menu-dropdown';
|
||||
$portletData['class'] = 'mw-portlet mw-portlet-personal vector-user-menu';
|
||||
$portletData['class'] .= $this->loggedin ?
|
||||
' vector-user-menu-logged-in' :
|
||||
' vector-user-menu-logged-out';
|
||||
|
|
|
@ -6,7 +6,6 @@ use MediaWiki\MediaWikiServices;
|
|||
use MediaWiki\Skins\Vector\Components\VectorComponentDropdown;
|
||||
use MediaWiki\Skins\Vector\Components\VectorComponentMainMenu;
|
||||
use MediaWiki\Skins\Vector\Components\VectorComponentPageTools;
|
||||
use MediaWiki\Skins\Vector\Components\VectorComponentPinnableHeader;
|
||||
use MediaWiki\Skins\Vector\Components\VectorComponentSearchBox;
|
||||
use MediaWiki\Skins\Vector\Components\VectorComponentStickyHeader;
|
||||
use MediaWiki\Skins\Vector\Components\VectorComponentTableOfContents;
|
||||
|
@ -45,55 +44,6 @@ class SkinVector22 extends SkinVector {
|
|||
$shouldShowOnMainPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotates table of contents data with Vector-specific information.
|
||||
*
|
||||
* In tableOfContents.js we have tableOfContents::getTableOfContentsSectionsData(),
|
||||
* that yields the same result as this function, please make sure to keep them in sync.
|
||||
* FIXME: This code should be moved to VectorComponentTableOfContents.
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
|
||||
// ToC is pinned by default, hardcoded for now
|
||||
$isTocPinned = true;
|
||||
$pinnableHeader = new VectorComponentPinnableHeader(
|
||||
$this->getContext(),
|
||||
$isTocPinned,
|
||||
'vector-toc',
|
||||
null,
|
||||
false,
|
||||
'h2'
|
||||
);
|
||||
|
||||
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'
|
||||
),
|
||||
'is-pinned' => $isTocPinned,
|
||||
'data-pinnable-header' => $pinnableHeader->getTemplateData(),
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
|
@ -188,11 +138,6 @@ class SkinVector22 extends SkinVector {
|
|||
$featureManager = VectorServices::getFeatureManager();
|
||||
$parentData = parent::getTemplateData();
|
||||
$stickyHeader = new VectorComponentStickyHeader();
|
||||
$toc = new VectorComponentTableOfContents();
|
||||
$tocData = $parentData['data-toc'] ?? [];
|
||||
$parentData['data-toc'] = !empty( $tocData ) ?
|
||||
$toc->getTemplateData() + $this->getTocData( $tocData ) : null;
|
||||
|
||||
$parentData = $this->mergeViewOverflowIntoActions( $parentData );
|
||||
|
||||
// FIXME: Move to component (T322089)
|
||||
|
@ -214,8 +159,26 @@ class SkinVector22 extends SkinVector {
|
|||
);
|
||||
}
|
||||
|
||||
$toc = new VectorComponentTableOfContents(
|
||||
$parentData['data-toc'],
|
||||
$this->getContext(),
|
||||
$this->getConfig()
|
||||
);
|
||||
|
||||
$config = $this->getConfig();
|
||||
$searchBox = new VectorComponentSearchBox(
|
||||
$parentData['data-search-box'],
|
||||
true,
|
||||
// is primary mode of search
|
||||
true,
|
||||
'searchform',
|
||||
true,
|
||||
$config,
|
||||
Constants::SEARCH_BOX_INPUT_LOCATION_MOVED,
|
||||
$this->getContext()
|
||||
);
|
||||
|
||||
$isPageToolsEnabled = $featureManager->isFeatureEnabled( Constants::FEATURE_PAGE_TOOLS );
|
||||
$isPageToolsPinned = $featureManager->isFeatureEnabled( Constants::FEATURE_PAGE_TOOLS_PINNED );
|
||||
$sidebar = $parentData[ 'data-portlets-sidebar' ];
|
||||
$pageToolsMenus = [];
|
||||
$restPortlets = $parentData[ 'data-portlets-sidebar' ][ 'array-portlets-rest' ];
|
||||
|
@ -236,43 +199,42 @@ class SkinVector22 extends SkinVector {
|
|||
$sidebar = $parentData[ 'data-portlets-sidebar' ];
|
||||
}
|
||||
}
|
||||
$config = $this->getConfig();
|
||||
$mainMenuDropdownClass = $this->getUser()->isAnon() ? 'vector-main-menu-btn-dropdown-anon ' : '';
|
||||
$mainMenuDropdownClass .= 'vector-main-menu-dropdown';
|
||||
|
||||
$isRegistered = $this->getUser()->isRegistered();
|
||||
$mainMenu = new VectorComponentMainMenu(
|
||||
$sidebar,
|
||||
$this->shouldLanguageAlertBeInSidebar(),
|
||||
$parentData['data-portlets']['data-languages'] ?? [],
|
||||
$this->getContext(),
|
||||
$this->getUser(),
|
||||
$this,
|
||||
);
|
||||
$mainMenuDropdown = new VectorComponentDropdown(
|
||||
'vector-main-menu-dropdown',
|
||||
$this->msg( 'vector-main-menu-label' )->text(),
|
||||
$mainMenuDropdownClass,
|
||||
$mainMenu::ID . '-dropdown',
|
||||
$this->msg( $mainMenu::ID . '-label' )->text(),
|
||||
$mainMenu::ID . '-dropdown',
|
||||
'menu'
|
||||
);
|
||||
|
||||
$pageTools = new VectorComponentPageTools(
|
||||
array_merge( [ $parentData['data-portlets']['data-actions'] ?? [] ], $pageToolsMenus ),
|
||||
$this->getContext(),
|
||||
$this->getUser(),
|
||||
VectorServices::getFeatureManager()
|
||||
);
|
||||
$pageToolsDropdown = new VectorComponentDropdown(
|
||||
$pageTools::ID . '-dropdown',
|
||||
$this->msg( 'toolbox' )->text(),
|
||||
$pageTools::ID . '-dropdown',
|
||||
);
|
||||
|
||||
$components = [
|
||||
'data-search-box' => new VectorComponentSearchBox(
|
||||
$parentData['data-search-box'],
|
||||
true,
|
||||
// is primary mode of search
|
||||
true,
|
||||
'searchform',
|
||||
true,
|
||||
$config,
|
||||
Constants::SEARCH_BOX_INPUT_LOCATION_MOVED,
|
||||
$this->getContext()
|
||||
),
|
||||
'data-toc' => $toc,
|
||||
'data-search-box' => $searchBox,
|
||||
'data-portlets-main-menu' => $mainMenu,
|
||||
'data-main-menu-dropdown' => $mainMenuDropdown,
|
||||
'data-portlets-main-menu' => new VectorComponentMainMenu(
|
||||
$sidebar,
|
||||
$this,
|
||||
$this->shouldLanguageAlertBeInSidebar(),
|
||||
$parentData['data-portlets']['data-languages'] ?? [],
|
||||
),
|
||||
'data-page-tools' => $isPageToolsEnabled ? new VectorComponentPageTools(
|
||||
array_merge( [ $parentData['data-portlets']['data-actions'] ?? [] ], $pageToolsMenus ),
|
||||
$isPageToolsPinned,
|
||||
$this->getContext(),
|
||||
$this->getUser()
|
||||
) : null,
|
||||
'data-page-tools-dropdown' => $isPageToolsEnabled ?
|
||||
new VectorComponentDropdown( 'vector-page-tools', $this->msg( 'toolbox' )->text() ) : null,
|
||||
'data-page-tools' => $isPageToolsEnabled ? $pageTools : null,
|
||||
'data-page-tools-dropdown' => $isPageToolsEnabled ? $pageToolsDropdown : null,
|
||||
];
|
||||
foreach ( $components as $key => $component ) {
|
||||
// Array of components or null values.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<div id="{{id}}" class="vector-menu{{#class}} {{.}}{{/class}}" {{{html-tooltip}}} {{{html-user-language-attributes}}}>
|
||||
{{#label}}
|
||||
<div class="vector-menu-heading{{#label-class}} {{.}}{{/label-class}}">
|
||||
{{{.}}}
|
||||
{{.}}
|
||||
</div>
|
||||
{{/label}}
|
||||
{{>MenuContents}}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<nav class="vector-user-links{{#is-wide}} vector-user-links-wide{{/is-wide}}" aria-label="{{msg-personaltools}}" role="navigation" >
|
||||
{{#data-user-menu-overflow}}{{>Menu}}{{/data-user-menu-overflow}}
|
||||
{{#data-user-menu}}
|
||||
|
||||
{{#data-user-menu-dropdown}}
|
||||
{{>Dropdown/Open}}
|
||||
{{#is-anon}}
|
||||
{{#is-temp-user}}
|
||||
|
@ -19,5 +18,5 @@
|
|||
</div>
|
||||
{{/is-anon}}
|
||||
{{>Dropdown/Close}}
|
||||
{{/data-user-menu}}
|
||||
{{/data-user-menu-dropdown}}
|
||||
</nav>
|
||||
|
|
|
@ -478,7 +478,8 @@ module.exports = function tableOfContents( props ) {
|
|||
/**
|
||||
* Prepares the data for rendering the table of contents,
|
||||
* nesting child sections within their parent sections.
|
||||
* This shoul yield the same result as the php function SkinVector22::getTocData(),
|
||||
* This should yield the same result as the php function
|
||||
* VectorComponentTableOfContents::getTemplateData(),
|
||||
* please make sure to keep them in sync.
|
||||
*
|
||||
* @param {Section[]} sections
|
||||
|
|
|
@ -22,10 +22,3 @@
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// For anons the menu is not pinnable.
|
||||
.vector-main-menu-btn-dropdown-anon {
|
||||
.vector-pinnable-header {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ exports[`UserLinks renders 1`] = `
|
|||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id=\\"p-personal\\" class=\\"vector-menu vector-dropdown vector-menu-dropdown mw-portlet mw-portlet-personal vector-user-menu vector-user-menu-logged-in vector-menu-dropdown\\">
|
||||
<input type=\\"checkbox\\" id=\\"p-personal-checkbox\\" role=\\"button\\" aria-haspopup=\\"true\\" data-event-name=\\"ui.dropdown-p-personal\\" class=\\"vector-menu-checkbox\\">
|
||||
<label id=\\"p-personal-label\\" for=\\"p-personal-checkbox\\" class=\\"vector-menu-heading\\">
|
||||
|
|
|
@ -26,7 +26,7 @@ const templateData = {
|
|||
<li id="pt-watchlist-2" class="user-links-collapsible-item mw-list-item"><a href="/wiki/Special:Watchlist" class="mw-ui-button mw-ui-quiet mw-ui-icon mw-ui-icon-element mw-ui-icon-watchlist mw-ui-icon-wikimedia-watchlist" title="A list of pages you are monitoring for changes [⌃⌥l]" accesskey="l"><span>Watchlist</span></a></li>
|
||||
`
|
||||
},
|
||||
'data-user-menu': {
|
||||
'data-user-menu-dropdown': {
|
||||
id: 'p-personal',
|
||||
class: 'mw-portlet mw-portlet-personal vector-user-menu vector-user-menu-logged-in vector-menu-dropdown',
|
||||
label: 'Personal tools',
|
||||
|
|
|
@ -47,163 +47,6 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
|||
return in_array( $search, $values );
|
||||
}
|
||||
|
||||
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',
|
||||
'linkAnchor' => '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',
|
||||
'linkAnchor' => 'B',
|
||||
'array-sections' => [],
|
||||
'is-top-level-section' => true,
|
||||
'is-parent-section' => false,
|
||||
]
|
||||
]
|
||||
];
|
||||
$nestedTocData = [
|
||||
'number-section-count' => 2,
|
||||
'array-sections' => [
|
||||
[
|
||||
'toclevel' => 1,
|
||||
'level' => '2',
|
||||
'line' => 'A',
|
||||
'number' => '1',
|
||||
'index' => '1',
|
||||
'fromtitle' => 'Test',
|
||||
'byteoffset' => 231,
|
||||
'anchor' => 'A',
|
||||
'linkAnchor' => 'A',
|
||||
'vector-button-label' => '(vector-toc-toggle-button-label: A)',
|
||||
'array-sections' => [
|
||||
'toclevel' => 2,
|
||||
'level' => '4',
|
||||
'line' => 'A1',
|
||||
'number' => '1.1',
|
||||
'index' => '2',
|
||||
'fromtitle' => 'Test',
|
||||
'byteoffset' => 245,
|
||||
'anchor' => 'A1',
|
||||
'linkAnchor' => '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' ],
|
||||
'data-pinnable-header' => [
|
||||
'is-pinned' => true,
|
||||
'data-name' => 'vector-toc',
|
||||
'data-feature-name' => null,
|
||||
'label' => '(vector-toc-label)',
|
||||
'unpin-label' => '(vector-unpin-element-label)',
|
||||
'pin-label' => '(vector-pin-element-label)',
|
||||
'label-tag-name' => 'h2'
|
||||
],
|
||||
'is-pinned' => true,
|
||||
];
|
||||
$expectedNestedTocData = array_merge( $nestedTocData, $expectedConfigData );
|
||||
|
||||
return [
|
||||
// When zero sections
|
||||
[
|
||||
[],
|
||||
$config,
|
||||
// TOC data is empty when given an empty array
|
||||
[]
|
||||
],
|
||||
// When number of multiple sections is lower than configured value
|
||||
[
|
||||
$tocData,
|
||||
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,
|
||||
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,
|
||||
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 \MediaWiki\Skins\Vector\SkinVector22::getTocData
|
||||
* @dataProvider provideGetTOCData
|
||||
*/
|
||||
public function testGetTocData(
|
||||
array $tocData,
|
||||
array $config,
|
||||
array $expected
|
||||
) {
|
||||
$this->overrideConfigValues( $config );
|
||||
$this->setUserLang( 'qqx' );
|
||||
|
||||
$skinVector = new SkinVector22( [ 'name' => 'vector-2022' ] );
|
||||
$openSkinVector = TestingAccessWrapper::newFromObject( $skinVector );
|
||||
$data = $openSkinVector->getTocData( $tocData );
|
||||
$this->assertEquals( $expected, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \MediaWiki\Skins\Vector\SkinVector::getTemplateData
|
||||
*/
|
||||
|
|
132
tests/phpunit/unit/components/VectorComponentPageToolsTest.php
Normal file
132
tests/phpunit/unit/components/VectorComponentPageToolsTest.php
Normal file
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @since 1.35
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Skins\Vector\Tests\Unit\Components;
|
||||
|
||||
use MediaWiki\Skins\Vector\Components\VectorComponentPageTools;
|
||||
use MediaWiki\Skins\Vector\FeatureManagement\FeatureManager;
|
||||
use Message;
|
||||
use MessageLocalizer;
|
||||
use User;
|
||||
|
||||
/**
|
||||
* @group Vector
|
||||
* @group Components
|
||||
* @coversDefaultClass \MediaWiki\Skins\Vector\Components\VectorComponentPageTools
|
||||
*/
|
||||
class VectorComponentPageToolsTest extends \MediaWikiUnitTestCase {
|
||||
|
||||
public function provideConstructorData() {
|
||||
$menus = [ [
|
||||
'id' => 'p-cactions',
|
||||
'array-items' => [ [
|
||||
'id' => 'ca-delete',
|
||||
'html-item' => "<li><a><span>Delete</span></a></li>"
|
||||
] ]
|
||||
], [
|
||||
'id' => 'p-tb',
|
||||
'array-items' => [ [
|
||||
'id' => 't-whatlinkshere',
|
||||
'html-item' => "<li><a><span>What links here</span></a></li>"
|
||||
] ]
|
||||
] ];
|
||||
|
||||
$expectedMenus = $menus;
|
||||
$expectedMenus[ 0 ][ 'label' ] = 'vector-page-tools-actions-label';
|
||||
$expectedMenus[ 1 ][ 'label' ] = 'vector-page-tools-general-label';
|
||||
|
||||
return [
|
||||
[
|
||||
$menus,
|
||||
false,
|
||||
false,
|
||||
[
|
||||
'id' => 'vector-page-tools',
|
||||
'is-pinned' => false,
|
||||
'data-pinnable-header' => null,
|
||||
'data-menus' => $expectedMenus
|
||||
]
|
||||
], [
|
||||
$menus,
|
||||
false,
|
||||
true,
|
||||
[
|
||||
'id' => 'vector-page-tools',
|
||||
'is-pinned' => true,
|
||||
'data-pinnable-header' => null,
|
||||
'data-menus' => $expectedMenus
|
||||
]
|
||||
], [
|
||||
$menus,
|
||||
true,
|
||||
false,
|
||||
[
|
||||
'id' => 'vector-page-tools',
|
||||
'is-pinned' => false,
|
||||
'data-pinnable-header' => [
|
||||
'is-pinned' => false,
|
||||
'label' => 'vector-page-tools-label',
|
||||
'label-tag-name' => 'div',
|
||||
'pin-label' => 'vector-pin-element-label',
|
||||
'unpin-label' => 'vector-unpin-element-label',
|
||||
'data-name' => 'vector-page-tools',
|
||||
'data-feature-name' => 'page-tools-pinned',
|
||||
'data-pinnable-element-id' => 'vector-page-tools-pinnable-element',
|
||||
'data-unpinned-container-id' => 'vector-page-tools-unpinned-container',
|
||||
'data-pinned-container-id' => 'vector-page-tools-pinned-container',
|
||||
],
|
||||
'data-menus' => $expectedMenus
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getTemplateData
|
||||
* @dataProvider provideConstructorData
|
||||
*/
|
||||
public function testGetTemplateData(
|
||||
array $menus,
|
||||
bool $isRegistered,
|
||||
bool $isPinned,
|
||||
array $expected
|
||||
) {
|
||||
$localizer = $this->createMock( MessageLocalizer::class );
|
||||
$localizer->method( 'msg' )->willReturnCallback( function ( $key, ...$params ) {
|
||||
$msg = $this->createMock( Message::class );
|
||||
$msg->method( '__toString' )->willReturn( $key );
|
||||
$msg->method( 'text' )->willReturn( $key );
|
||||
return $msg;
|
||||
} );
|
||||
$user = $this->createMock( User::class );
|
||||
$user->method( 'isRegistered' )->willReturn( $isRegistered );
|
||||
$featureManager = $this->createMock( FeatureManager::class );
|
||||
$featureManager->method( 'isFeatureEnabled' )->willReturn( $isPinned );
|
||||
|
||||
$pageTools = new VectorComponentPageTools(
|
||||
$menus,
|
||||
$localizer,
|
||||
$user,
|
||||
$featureManager
|
||||
);
|
||||
$this->assertEquals( $expected, $pageTools->getTemplateData() );
|
||||
}
|
||||
}
|
|
@ -21,7 +21,10 @@
|
|||
|
||||
namespace MediaWiki\Skins\Vector\Tests\Unit\Components;
|
||||
|
||||
use HashConfig;
|
||||
use MediaWiki\Skins\Vector\Components\VectorComponentTableOfContents;
|
||||
use Message;
|
||||
use MessageLocalizer;
|
||||
|
||||
/**
|
||||
* @group Vector
|
||||
|
@ -29,17 +32,169 @@ use MediaWiki\Skins\Vector\Components\VectorComponentTableOfContents;
|
|||
* @coversDefaultClass \MediaWiki\Skins\Vector\Components\VectorComponentTableOfContents
|
||||
*/
|
||||
class VectorComponentTableOfContentsTest extends \MediaWikiUnitTestCase {
|
||||
|
||||
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',
|
||||
'linkAnchor' => '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',
|
||||
'linkAnchor' => 'B',
|
||||
'array-sections' => [],
|
||||
'is-top-level-section' => true,
|
||||
'is-parent-section' => false,
|
||||
]
|
||||
]
|
||||
];
|
||||
$nestedTocData = [
|
||||
'number-section-count' => 2,
|
||||
'array-sections' => [
|
||||
[
|
||||
'toclevel' => 1,
|
||||
'level' => '2',
|
||||
'line' => 'A',
|
||||
'number' => '1',
|
||||
'index' => '1',
|
||||
'fromtitle' => 'Test',
|
||||
'byteoffset' => 231,
|
||||
'anchor' => 'A',
|
||||
'linkAnchor' => 'A',
|
||||
'vector-button-label' => 'vector-toc-toggle-button-label',
|
||||
'array-sections' => [
|
||||
'toclevel' => 2,
|
||||
'level' => '4',
|
||||
'line' => 'A1',
|
||||
'number' => '1.1',
|
||||
'index' => '2',
|
||||
'fromtitle' => 'Test',
|
||||
'byteoffset' => 245,
|
||||
'anchor' => 'A1',
|
||||
'linkAnchor' => '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' ],
|
||||
'data-pinnable-header' => [
|
||||
'is-pinned' => true,
|
||||
'data-name' => 'vector-toc',
|
||||
'data-feature-name' => null,
|
||||
'label' => 'vector-toc-label',
|
||||
'unpin-label' => 'vector-unpin-element-label',
|
||||
'pin-label' => 'vector-pin-element-label',
|
||||
'label-tag-name' => 'h2'
|
||||
],
|
||||
'is-pinned' => true,
|
||||
'id' => 'vector-toc'
|
||||
];
|
||||
$expectedNestedTocData = array_merge( $nestedTocData, $expectedConfigData );
|
||||
|
||||
return [
|
||||
// When zero sections
|
||||
[
|
||||
[],
|
||||
$config,
|
||||
// TOC data is empty when given an empty array
|
||||
[]
|
||||
],
|
||||
// When number of multiple sections is lower than configured value
|
||||
[
|
||||
$tocData,
|
||||
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,
|
||||
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,
|
||||
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 ::getTemplateData
|
||||
* @dataProvider provideGetTOCData
|
||||
*/
|
||||
public function testGetTemplateData() {
|
||||
$toc = new VectorComponentTableOfContents();
|
||||
$this->assertEquals(
|
||||
[
|
||||
'id' => 'vector-toc',
|
||||
'is-pinned' => true,
|
||||
],
|
||||
$toc->getTemplateData()
|
||||
public function testGetTemplateData(
|
||||
array $tocData,
|
||||
array $config,
|
||||
array $expected
|
||||
) {
|
||||
$localizer = $this->createMock( MessageLocalizer::class );
|
||||
$localizer->method( 'msg' )->willReturnCallback( function ( $key, ...$params ) {
|
||||
$msg = $this->createMock( Message::class );
|
||||
$msg->method( '__toString' )->willReturn( $key );
|
||||
$msg->method( 'text' )->willReturn( $key );
|
||||
return $msg;
|
||||
} );
|
||||
|
||||
$toc = new VectorComponentTableOfContents(
|
||||
$tocData,
|
||||
$localizer,
|
||||
new HashConfig( $config )
|
||||
);
|
||||
$this->assertEquals( $expected, $toc->getTemplateData() );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue