Remove custom rendering of the user links overflow menu in favor of using 'vector-user-menu-overflow', a vector specific menu bucket in Hooks.php

- Rename 'vector-user-menu-more' class to 'vector-user-menu-overflow'
- Update storybook and tests

Visual changes
- Small intentional change on the create account button

Bug: T306662
Change-Id: I371bb11903d8cdd8f0da89266fcf549050c0da8c
This commit is contained in:
bwang 2022-04-19 12:50:01 -05:00 committed by Bernard Wang
parent ea5c4d3735
commit 37149c9e67
12 changed files with 131 additions and 155 deletions

View File

@ -83,6 +83,10 @@
background-image: url("data:image/svg+xml;charset=utf8,%3C?xml version='1.0' encoding='UTF-8'?%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Ctitle%3Euser avatar%3C/title%3E%3Cpath d='M10 11c-5.92 0-8 3-8 5v3h16v-3c0-2-2.08-5-8-5z'/%3E%3Ccircle cx='10' cy='5.5' r='4.5'/%3E%3C/svg%3E");
}
.mw-ui-icon-wikimedia-watchlist:before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Ctitle%3E watchlist %3C/title%3E%3Cpath d='M1 3h16v2H1V3Zm0 6h6v2H1V9Zm0 6h8v2H1v-2Zm8-4.24h3.85L14.5 7l1.65 3.76H20l-3 3.17.9 4.05-3.4-2.14L11.1 18l.9-4.05-3-3.19Z'/%3E%3C/svg%3E")
}
.vector-user-menu-legacy #pt-anonuserpage,
.vector-user-menu-legacy #pt-tmpuserpage,
.vector-user-menu-legacy #pt-userpage a {

View File

@ -265,42 +265,66 @@ class Hooks implements
}
/**
* Updates personal navigation menu (user links) overflow items for modern Vector
* including 'notification', 'user-interface-preferences', 'user-page', 'vector-user-menu-overflow'
* Populates 'vector-user-menu-overflow' bucket for modern Vector with modified personal navigation (user links)
* menu items, including 'notification', 'user-interface-preferences', 'user-page', 'vector-user-menu-overflow'
*
* @param SkinTemplate $sk
* @param array &$content_navigation
* @suppress PhanTypeInvalidDimOffset False positives
* @suppress PhanTypeMismatchArgumentInternal False positives
*/
private static function updateUserLinksOverflowItems( &$content_navigation ) {
// Upgrade preferences, notifications, and watchlist to icon buttons
// for extensions that have opted in.
if ( isset( $content_navigation['notifications'] ) ) {
self::updateMenuItems( $content_navigation, 'notifications' );
}
private static function updateUserLinksOverflowItems( $sk, &$content_navigation ) {
$overflow = 'vector-user-menu-overflow';
$content_navigation[$overflow] = [];
// Logged in and logged out overflow items
if ( isset( $content_navigation['user-interface-preferences']['uls'] ) ) {
$content_navigation['user-interface-preferences']['uls'] += [
$content_navigation[$overflow]['uls'] = array_merge(
$content_navigation['user-interface-preferences']['uls'], [
'collapsible' => true,
];
self::updateMenuItems( $content_navigation, 'user-interface-preferences' );
] );
}
// Logged in overflow items
if ( isset( $content_navigation['user-page']['userpage'] ) ) {
$content_navigation['user-page']['userpage'] = array_merge( $content_navigation['user-page']['userpage'], [
$content_navigation[$overflow]['userpage'] = array_merge(
$content_navigation['user-page']['userpage'], [
'button' => true,
'collapsible' => true,
'icon' => null,
// Remove icon
'icon' => '',
] );
self::updateMenuItems( $content_navigation, 'user-page' );
}
if ( isset( $content_navigation['vector-user-menu-overflow']['watchlist'] ) ) {
$content_navigation['vector-user-menu-overflow']['watchlist'] += [
if ( isset( $content_navigation['notifications'] ) ) {
foreach ( $content_navigation['notifications'] as $key => $data ) {
$content_navigation[$overflow][$key] = $data;
}
}
if ( isset( $content_navigation['user-menu']['watchlist'] ) ) {
$content_navigation[$overflow]['watchlist'] = array_merge(
$content_navigation['user-menu']['watchlist'], [
'id' => 'pt-watchlist-2',
'button' => true,
'collapsible' => true,
'text-hidden' => true,
'id' => 'pt-watchlist-2',
];
self::updateMenuItems( $content_navigation, 'vector-user-menu-overflow' );
] );
}
// Anon/temp overflow items
$user = $sk->getUser();
$isTemp = $user->isTemp();
$isRegistered = $user->isRegistered();
$isCreateAccountAllowed = ( !$isRegistered || $isTemp );
if ( isset( $content_navigation['user-menu']['createaccount'] ) && $isCreateAccountAllowed ) {
$content_navigation[$overflow]['createaccount'] = array_merge(
$content_navigation['user-menu']['createaccount'], [
'id' => 'pt-createaccount-2',
'button' => true,
'collapsible' => true,
// Remove icon
'icon' => '',
] );
}
self::updateMenuItems( $content_navigation, $overflow );
}
/**
@ -318,18 +342,8 @@ class Hooks implements
// users in legacy Vector.
unset( $content_navigation['user-page'] );
} else {
if ( isset( $content_navigation['user-menu'] ) ) {
if ( isset( $content_navigation['user-menu']['watchlist'] ) ) {
// Copy watchlist data into 'vector-user-menu-overflow'
$content_navigation['vector-user-menu-overflow'] = [
'watchlist' => $content_navigation['user-menu']['watchlist']
];
}
self::updateUserLinksDropdownItems( $sk, $content_navigation );
}
self::updateUserLinksOverflowItems( $content_navigation );
self::updateUserLinksOverflowItems( $sk, $content_navigation );
self::updateUserLinksDropdownItems( $sk, $content_navigation );
}
}

View File

@ -253,15 +253,6 @@ abstract class SkinVector extends SkinMustache {
return $this->makeLink( 'create-account', $createAccountData );
}
/**
* Returns HTML for the watchlist link inside user links
* @param array|null $watchlistMenuData (optional)
* @return string
*/
private function getWatchlistHTML( $watchlistMenuData = null ) {
return $watchlistMenuData ? $watchlistMenuData['html-items'] : '';
}
/**
* Returns HTML for the create account button, login button and learn more link inside the anon user menu
* @param string[] $returnto array of query strings used to build the login link
@ -324,33 +315,7 @@ abstract class SkinVector extends SkinMustache {
$isTempUser = $user->isTemp();
$returnto = $this->getReturnToParam();
$useCombinedLoginLink = $this->useCombinedLoginLink();
$htmlCreateAccount = $this->getCreateAccountHTML( $returnto, false );
$templateParser = $this->getTemplateParser();
// See T288428#7303233. The following conditional checks whether config is disabling account creation for
// anonymous users in modern Vector. This check excludes the use case of extensions using core and legacy hooks
// to remove the "Create account" link from the personal toolbar. Ideally this should be managed with a new hook
// that tracks account creation ability.
// Supporting removing items via hook involves unnecessary additional complexity we'd rather avoid at this time.
// (see https://gerrit.wikimedia.org/r/c/mediawiki/skins/Vector/+/713505/3)
// Account creation can be disabled by setting `$wgGroupPermissions['*']['createaccount'] = false;`
$isCreateAccountAllowed = ( $isAnon || $isTempUser )
&& $this->getAuthority()->isAllowed( 'createaccount' );
$userMoreHtmlItems = $templateParser->processTemplate( 'UserLinks__more', [
'is-anon' => $isAnon,
'is-create-account-allowed' => $isCreateAccountAllowed,
'html-create-account' => $htmlCreateAccount,
'data-user-interface-preferences' => $menuData[ 'data-user-interface-preferences' ],
'data-notifications' => $menuData[ 'data-notifications' ],
'data-user-page' => $menuData[ 'data-user-page' ],
'html-vector-watchlist' => $this->getWatchlistHTML( $menuData[ 'data-vector-user-menu-overflow' ] ?? null ),
] );
$userMoreData = $this->decoratePortletData( 'data-user-more', [
'id' => 'p-personal-more',
'class' => 'mw-portlet mw-portlet-personal-more vector-user-menu-more',
'html-items' => $userMoreHtmlItems,
] );
$userMenuOverflowData = $menuData[ 'data-vector-user-menu-overflow' ];
$userMenuData = $menuData[ 'data-user-menu' ];
if ( $isAnon || $isTempUser ) {
$userMenuData[ 'html-before-portal' ] .= $this->getAnonMenuBeforePortletHTML(
@ -363,10 +328,10 @@ abstract class SkinVector extends SkinMustache {
$userMenuData[ 'html-after-portal' ] .= $this->getLogoutHTML();
}
$moreItems = substr_count( $userMoreData['html-items'], '<li' );
$moreItems = substr_count( $userMenuOverflowData['html-items'], '<li' );
return [
'is-wide' => $moreItems > 3,
'data-user-more' => $userMoreData,
'data-user-menu-overflow' => $menuData[ 'data-vector-user-menu-overflow' ],
'data-user-menu' => $userMenuData
];
}
@ -468,7 +433,6 @@ abstract class SkinVector extends SkinMustache {
$skin = $this;
$parentData = $this->decoratePortletsData( parent::getTemplateData() );
$featureManager = VectorServices::getFeatureManager();
// SkinVector sometimes serves new Vector as part of removing the
// skin version user preference. TCho avoid T302461 we need to unset it here.
@ -814,6 +778,9 @@ abstract class SkinVector extends SkinMustache {
case 'p-cactions':
$portletData['class'] .= ' vector-menu-dropdown-noicon';
break;
case 'p-vector-user-menu-overflow':
$portletData['class'] .= ' vector-user-menu-overflow';
break;
default:
break;
}
@ -880,7 +847,7 @@ abstract class SkinVector extends SkinMustache {
case 'data-notifications':
case 'data-personal':
case 'data-user-page':
case 'data-user-more':
case 'data-vector-user-menu-overflow':
$type = self::MENU_TYPE_DEFAULT;
break;
case 'data-languages':

View File

@ -1,4 +1,4 @@
<nav class="vector-user-links{{#is-wide}} vector-user-links-wide{{/is-wide}}" aria-label="{{msg-personaltools}}" role="navigation">
{{#data-user-more}}{{>Menu}}{{/data-user-more}}
<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}}{{>Menu}}{{/data-user-menu}}
</nav>

View File

@ -1,11 +0,0 @@
{{#data-user-interface-preferences}}{{{html-items}}}{{/data-user-interface-preferences}}
{{#is-create-account-allowed}}
<li id="p-createaccount" class="user-links-collapsible-item">
{{{html-create-account}}}
</li>
{{/is-create-account-allowed}}
{{^is-anon}}
{{#data-user-page}}{{{html-items}}}{{/data-user-page}}
{{#data-notifications}}{{{html-items}}}{{/data-notifications}}
{{{html-vector-watchlist}}}
{{/is-anon}}

View File

@ -49,7 +49,9 @@
/**
* Horizontal links menu - logged in and logged out.
*/
.vector-user-menu-more {
// TODO: Remove vector-user-menu-more after I371bb11903d8cdd8f0da89266fcf549050c0da8c has been in prod for a week
.vector-user-menu-more,
.vector-user-menu-overflow {
.vector-menu-content-list {
display: flex;
align-items: center;

View File

@ -3,7 +3,6 @@ import mustache from 'mustache';
import { menuTemplate, legacyMenuTemplate } from './Menu.stories.data';
import userLinksTemplateLegacy from '!!raw-loader!../includes/templates/LegacyUserLinks.mustache';
import userLinksTemplate from '!!raw-loader!../includes/templates/UserLinks.mustache';
import userLinksMoreTemplate from '!!raw-loader!../includes/templates/UserLinks__more.mustache';
import userLinksLogoutTemplate from '!!raw-loader!../includes/templates/UserLinks__logout.mustache';
import userLinksLoginTemplate from '!!raw-loader!../includes/templates/UserLinks__login.mustache';
import { helperClassName, helperMakeMenuData } from './utils';
@ -59,73 +58,77 @@ const PERSONAL_MENU_TEMPLATE_DATA = {
*/
const LOGGED_IN_ITEMS = `
<li id="pt-userpage" class="user-links-collapsible-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-userAvatar" href="/wiki/User:Test" dir="auto" title="Your user page [ctrl-option-.]" accesskey="."><span>Test</span></a></li>
<li id="pt-mytalk"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-userTalk" href="/wiki/User_talk:WikiUser" title="Your talk page [⌃⌥n]" accesskey="n"><span>Talk</span></a></li>
<li id="pt-sandbox"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-sandbox" href="/wiki/User:WikiUser/sandbox" title="Your sandbox"><span>Sandbox</span></a></li>
<li id="pt-preferences"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-settings" href="/wiki/Special:Preferences" title="Your preferences"><span>Preferences</span></a></li>
<li id="pt-watchlist"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-unStar" href="/wiki/Special:Watchlist" title="A list of pages you are monitoring for changes [⌃⌥l]" accesskey="l"><span>Watchlist</span></a></li>
<li id="pt-mycontris"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-userContributions" href="/wiki/Special:Contributions/WikiUser" title="A list of your contributions [⌃⌥y]" accesskey="y"><span>Contributions</span></a></li>
<li id="pt-userpage" class="user-links-collapsible-item mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-userAvatar mw-ui-icon-wikimedia-userAvatar" href="/wiki/User:Admin" title="Your user page [⌃⌥.]" accesskey="."><span>Admin</span></a></li>
<li id="pt-mytalk" class="mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-userTalk mw-ui-icon-wikimedia-userTalk" href="/wiki/User_talk:Admin" title="Your talk page [⌃⌥n]" accesskey="n"><span>Talk</span></a></li>
<li id="pt-sandbox" class="new mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-sandbox mw-ui-icon-wikimedia-sandbox" href="/w/index.php?title=User:Admin/sandbox&amp;action=edit&amp;redlink=1" title="Your sandbox (page does not exist)"><span>Sandbox</span></a></li>
<li id="pt-preferences" class="mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-settings mw-ui-icon-wikimedia-settings" href="/wiki/Special:Preferences" title="Your preferences"><span>Preferences</span></a></li>
<li id="pt-betafeatures" class="mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-labFlask mw-ui-icon-wikimedia-labFlask" href="/wiki/Special:Preferences#mw-prefsection-betafeatures" title="Beta features"><span>Beta</span></a></li>
<li id="pt-watchlist" class="user-links-collapsible-item mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-watchlist mw-ui-icon-wikimedia-watchlist" href="/wiki/Special:Watchlist" title="A list of pages you are monitoring for changes [⌃⌥l]" accesskey="l"><span>Watchlist</span></a></li>
<li id="pt-uploads" class="mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-imageGallery mw-ui-icon-wikimedia-imageGallery" href="/w/index.php?title=Special:ListFiles/Admin&amp;ilshowall=1" title="List of files you have uploaded"><span>Uploads</span></a></li>
<li id="pt-mycontris" class="mw-list-item"><a class="mw-ui-icon mw-ui-icon-before mw-ui-icon-userContributions mw-ui-icon-wikimedia-userContributions" href="/wiki/Special:Contributions/Admin" title="A list of your contributions [⌃⌥y]" accesskey="y"><span>Contributions</span></a></li>
`;
const LOGGED_OUT_ITEMS = `
<li id="pt-anontalk"><a href="/wiki/Special:MyTalk" title="Discussion about edits from this IP address [ctrl-option-n]" accesskey="n"><span>Talk</span></a></li>
<li id="pt-anoncontribs"><a href="/wiki/Special:MyContributions" title="A list of edits made from this IP address [ctrl-option-y]" accesskey="y"><span>Contributions</span></a></li>
<li id="pt-anontalk" class="mw-list-item"><a href="/wiki/Special:MyTalk" title="Discussion about edits from this IP address [⌃⌥n]" accesskey="n"><span>Talk</span></a></li>
<li id="pt-anoncontribs" class="mw-list-item"><a href="/wiki/Special:MyContributions" title="A list of edits made from this IP address [⌃⌥y]" accesskey="y"><span>Contributions</span></a></li>
`;
const additionalUserMoreData = {
class: 'vector-user-menu-more',
'heading-class': '',
'is-dropdown': false
};
const LOGGED_IN_OVERFLOW_ITEMS = `
<li id="pt-userpage-2" class="user-links-collapsible-item mw-list-item"><a href="/wiki/User:Admin" class="mw-ui-button mw-ui-quiet" title="Your user page [⌃⌥.]" accesskey="."><span>Admin</span></a></li>
<li id="pt-notifications-alert" class="mw-list-item"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-bell mw-echo-notifications-badge-all-read" data-counter-num="0" data-counter-text="0" title="Your alerts"><span>Alerts (0)</span></a></li>
<li id="pt-notifications-notice" class="mw-list-item"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-tray mw-echo-notifications-badge-all-read" data-counter-num="0" data-counter-text="0" title="Your notices"><span>Notices (0)</span></a></li>
<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>
`;
const userMoreHtmlItems = ( isAnon = true ) => mustache.render( userLinksMoreTemplate, {
'is-anon': isAnon,
'is-create-account-allowed': isAnon,
'html-create-account': `<a href="/w/index.php?title=Special:CreateAccount&amp;returnto=Main+Page" class="mw-ui-button mw-ui-quiet" title="You are encouraged to create an account and log in; however, it is not mandatory">Create account</a>`,
'data-user-page': helperMakeMenuData( 'user-page', `
<li id="pt-userpage-2" class="user-links-collapsible-item">
<a href="/wiki/User:WikiUser" dir="auto" title="Your user page [⌃⌥.]" accesskey="." class="mw-ui-button mw-ui-quiet"><span>WikiUser</span></a>
</li>
` ),
'data-notifications': helperMakeMenuData( 'notifications', ECHO_ITEMS )
} );
const LOGGED_OUT_OVERFLOW_ITEMS = `
<li id="pt-createaccount-2" class="user-links-collapsible-item mw-list-item"><a href="/w/index.php?title=Special:CreateAccount&amp;returnto=Main+Page" class="mw-ui-button mw-ui-quiet" title="You are encouraged to create an account and log in; however, it is not mandatory"><span>Create account</span></a></li>
`;
const loggedInData = {
class: 'vector-user-menu vector-menu-dropdown vector-user-menu-logged-in',
'is-dropdown': true,
'heading-class': 'mw-ui-button mw-ui-quiet mw-ui-icon mw-ui-icon-element mw-ui-icon-wikimedia-userAvatar',
'is-anon': false,
'html-after-portal': mustache.render( userLinksLogoutTemplate, {
htmlLogout: `<a class="vector-menu-content-item vector-menu-content-item-logout mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-logOut" data-mw="interface" href="/w/index.php?title=Special:UserLogout&amp;returnto=Main+Page"><span>Log out</span></a>`
} )
} ),
'is-anon': false,
'is-dropdown': true,
'has-label': true
};
const loggedOutData = {
class: 'vector-user-menu vector-menu-dropdown vector-user-menu-logged-out',
'is-dropdown': true,
'heading-class': 'mw-ui-button mw-ui-quiet mw-ui-icon mw-ui-icon-element mw-ui-icon-wikimedia-ellipsis',
'is-anon': true,
'html-before-portal': mustache.render( userLinksLoginTemplate, {
htmlCreateAccount: `<a href="/w/index.php?title=Special:CreateAccount&amp;returnto=Special%3AUserLogout" icon="userAvatar" class="user-links-collapsible-item vector-menu-content-item mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-userAvatar" title="You are encouraged to create an account and log in; however, it is not mandatory"><span>Create account</span></a>`,
htmlLogin: `<a class="vector-menu-content-item vector-menu-content-item-login mw-ui-icon mw-ui-icon-before mw-ui-icon-wikimedia-logIn" href="/w/index.php?title=Special:UserLogin&amp;returnto=Main+Page" title="You are encouraged to log in; however, it is not mandatory [ctrl-option-o]" accesskey="o"><span>Log in</span></a>`,
msgLearnMore: msgs[ 'vector-anon-user-menu-pages' ],
htmlLearnMoreLink: `<a href="/wiki/Help:Introduction"><span>${msgs[ 'vector-anon-user-menu-pages-learn' ]}</span></a>:`
} )
} ),
'is-anon': false,
'is-dropdown': true,
'has-label': true
};
const overflowData = {
class: 'vector-menu vector-user-menu-overflow',
'heading-class': '',
'is-dropdown': false
};
/**
* @type {UserLinksDefinition}
*/
const USER_LINKS_LOGGED_IN_TEMPLATE_DATA = {
'data-user-more': helperMakeMenuData( 'personal-more', userMoreHtmlItems( false ), additionalUserMoreData ),
'data-user-menu': helperMakeMenuData( 'new-personal', LOGGED_IN_ITEMS, loggedInData )
'data-user-menu-overflow': helperMakeMenuData( 'vector-user-menu-overflow', LOGGED_IN_OVERFLOW_ITEMS, overflowData ),
'data-user-menu': helperMakeMenuData( 'personal-more', LOGGED_IN_ITEMS, loggedInData )
};
/**
* @type {UserLinksDefinition}
*/
const USER_LINKS_LOGGED_OUT_TEMPLATE_DATA = {
'data-user-more': helperMakeMenuData( 'personal-more', userMoreHtmlItems( true ), additionalUserMoreData ),
'data-user-menu': helperMakeMenuData( 'new-personal', LOGGED_OUT_ITEMS, loggedOutData )
'data-user-menu-overflow': helperMakeMenuData( 'vector-user-menu-overflow', LOGGED_OUT_OVERFLOW_ITEMS, overflowData ),
'data-user-menu': helperMakeMenuData( 'personal-more', LOGGED_OUT_ITEMS, loggedOutData )
};
const USER_LINK_PARTIALS = {

View File

@ -81,6 +81,6 @@
/**
* @typedef {Object} UserLinksDefinition
* @property {MenuDefinition} data-user-more
* @property {MenuDefinition} data-user-menu-overflow
* @property {MenuDefinition} data-user-menu
*/

View File

@ -3,14 +3,15 @@
exports[`UserLinks renders 1`] = `
"<nav class=\\"vector-user-links\\" aria-label=\\"\\" role=\\"navigation\\">
<div id=\\"p-personal-more\\" class=\\"vector-menu mw-portlet mw-portlet-personal-more vector-user-menu-more\\">
<div id=\\"p-personal-more\\" class=\\"vector-menu mw-portlet mw-portlet-vector-user-menu-overflow vector-user-menu-overflow\\">
<div class=\\"vector-menu-content\\">
<ul class=\\"vector-menu-content-list\\">
<li id=\\"ca-uls\\" class=\\"user-links-collapsible-item mw-list-item active\\"><a href=\\"#\\" class=\\"uls-trigger mw-ui-button mw-ui-quiet\\"><span class=\\"mw-ui-icon mw-ui-icon-wikimedia-language\\"></span> <span>English</span></a></li>
<li id=\\"pt-userpage-2\\" class=\\"user-links-collapsible-item mw-list-item\\"><a href=\\"/wiki/User:Admin\\" class=\\"mw-ui-button mw-ui-quiet\\" title=\\"Your user page [.]\\" accesskey=\\".\\"><span>Admin</span></a></li>
<li id=\\"pt-notifications-alert\\" class=\\"mw-list-item\\"><a href=\\"/wiki/Special:Notifications\\" class=\\"mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-bell mw-echo-notifications-badge-all-read\\" data-counter-num=\\"0\\" data-counter-text=\\"0\\" title=\\"Your alerts\\"><span>Alerts (0)</span></a></li><li id=\\"pt-notifications-notice\\" class=\\"mw-list-item\\"><a href=\\"/wiki/Special:Notifications\\" class=\\"mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-tray mw-echo-notifications-badge-all-read\\" data-counter-num=\\"0\\" data-counter-text=\\"0\\" title=\\"Your notices\\"><span>Notices (0)</span></a></li>
<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>
<li id=\\"pt-userpage-2\\" class=\\"user-links-collapsible-item mw-list-item\\"><a href=\\"/wiki/User:Admin\\" class=\\"mw-ui-button mw-ui-quiet\\" title=\\"Your user page [⌃⌥.]\\" accesskey=\\".\\"><span>Admin</span></a></li>
<li id=\\"pt-notifications-alert\\" class=\\"mw-list-item\\"><a href=\\"/wiki/Special:Notifications\\" class=\\"mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-bell mw-echo-notifications-badge-all-read\\" data-counter-num=\\"0\\" data-counter-text=\\"0\\" title=\\"Your alerts\\"><span>Alerts (0)</span></a></li>
<li id=\\"pt-notifications-notice\\" class=\\"mw-list-item\\"><a href=\\"/wiki/Special:Notifications\\" class=\\"mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-tray mw-echo-notifications-badge-all-read\\" data-counter-num=\\"0\\" data-counter-text=\\"0\\" title=\\"Your notices\\"><span>Notices (0)</span></a></li>
<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>
</ul>
</div>

View File

@ -5,15 +5,16 @@ const menuTemplate = fs.readFileSync( 'includes/templates/Menu.mustache', 'utf8'
const templateData = {
'is-wide': false,
'data-user-more': {
'data-user-menu-overflow': {
id: 'p-personal-more',
class: 'mw-portlet mw-portlet-personal-more vector-user-menu-more',
class: 'mw-portlet mw-portlet-vector-user-menu-overflow vector-user-menu-overflow',
label: 'Toggle sidebar',
'html-items': `
<li id="ca-uls" class="user-links-collapsible-item mw-list-item active"><a href="#" class="uls-trigger mw-ui-button mw-ui-quiet"><span class="mw-ui-icon mw-ui-icon-wikimedia-language"></span> <span>English</span></a></li>
<li id="pt-userpage-2" class="user-links-collapsible-item mw-list-item"><a href="/wiki/User:Admin" class="mw-ui-button mw-ui-quiet" title="Your user page [.]" accesskey="."><span>Admin</span></a></li>
<li id="pt-notifications-alert" class="mw-list-item"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-bell mw-echo-notifications-badge-all-read" data-counter-num="0" data-counter-text="0" title="Your alerts"><span>Alerts (0)</span></a></li><li id="pt-notifications-notice" class="mw-list-item"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-tray mw-echo-notifications-badge-all-read" data-counter-num="0" data-counter-text="0" title="Your notices"><span>Notices (0)</span></a></li>
<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>
<li id="pt-userpage-2" class="user-links-collapsible-item mw-list-item"><a href="/wiki/User:Admin" class="mw-ui-button mw-ui-quiet" title="Your user page [⌃⌥.]" accesskey="."><span>Admin</span></a></li>
<li id="pt-notifications-alert" class="mw-list-item"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-bell mw-echo-notifications-badge-all-read" data-counter-num="0" data-counter-text="0" title="Your alerts"><span>Alerts (0)</span></a></li>
<li id="pt-notifications-notice" class="mw-list-item"><a href="/wiki/Special:Notifications" class="mw-echo-notifications-badge mw-echo-notification-badge-nojs oo-ui-icon-tray mw-echo-notifications-badge-all-read" data-counter-num="0" data-counter-text="0" title="Your notices"><span>Notices (0)</span></a></li>
<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': {

View File

@ -466,5 +466,4 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
$shouldLanguageAlertBeInSidebarMethod->invoke( $mockSkinVector )
);
}
}

View File

@ -440,11 +440,6 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
'login' => [ 'class' => [], 'icon' => 'login' ],
]
];
$contentNavWatchlist = [
'user-menu' => [
'watchlist' => [ 'class' => [], 'icon' => 'watchlist' ],
]
];
$vectorLegacySkin = new SkinVectorLegacy( [ 'name' => 'vector' ] );
$contentNavLegacy = [
'user-page' => [
@ -453,22 +448,14 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
];
Hooks::onSkinTemplateNavigation( $vector2022Skin, $contentNav );
$this->assertFalse( isset( $contentNav['vector-user-menu-overflow'] ),
'watchlist data is not copied to vector-user-menu-overflow when not provided'
);
$this->assertFalse( isset( $contentNav['user-page']['login'] ),
'updateUserLinksDropdownItems is called when user-page is defined'
'updateUserLinksDropdownItems is called when not legacy'
);
$this->assertContains( 'mw-ui-button',
$contentNav['user-page']['userpage']['link-class'],
$contentNav['vector-user-menu-overflow']['userpage']['link-class'],
'updateUserLinksOverflowItems is called when not legacy'
);
Hooks::onSkinTemplateNavigation( $vector2022Skin, $contentNavWatchlist );
$this->assertTrue( isset( $contentNavWatchlist['vector-user-menu-overflow'] ),
'watchlist data is copied to vector-user-menu-overflow when provided'
);
Hooks::onSkinTemplateNavigation( $vectorLegacySkin, $contentNavLegacy );
$this->assertFalse( isset( $contentNavLegacy['user-page'] ),
'user-page is unset for legacy vector'
@ -545,6 +532,14 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
'updateUserLinksOverflowItems'
);
$updateUserLinksOverflowItems->setAccessible( true );
$skin = new SkinVector22( [ 'name' => 'vector-2022' ] );
// Registered user
$registeredUser = $this->createMock( User::class );
$registeredUser->method( 'isRegistered' )->willReturn( true );
$context = new RequestContext();
$context->setUser( $registeredUser );
$skin->setContext( $context );
$content = [
'notifications' => [
'alert' => [ 'class' => [], 'icon' => 'alert' ],
@ -554,30 +549,31 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
],
'user-page' => [
'userpage' => [ 'class' => [], 'icon' => 'userpage' ],
'watchlist' => [ 'class' => [], 'icon' => 'watchlist' ],
],
'vector-user-menu-overflow' => [
'user-menu' => [
'watchlist' => [ 'class' => [], 'icon' => 'watchlist' ],
],
];
$updateUserLinksOverflowItems->invokeArgs( null, [ &$content ] );
$updateUserLinksOverflowItems->invokeArgs( null, [ $skin, &$content ] );
$this->assertContains( 'user-links-collapsible-item',
$content['user-interface-preferences']['uls']['class'],
$content['vector-user-menu-overflow']['uls']['class'],
'ULS link in user links overflow requires collapsible class'
);
$this->assertContains( 'user-links-collapsible-item',
$content['user-page']['userpage']['class'],
$content['vector-user-menu-overflow']['userpage']['class'],
'User page link in user links overflow requires collapsible class'
);
$this->assertContains( 'mw-ui-button',
$content['user-page']['userpage']['link-class'],
$content['vector-user-menu-overflow']['userpage']['link-class'],
'User page link in user links overflow requires button classes'
);
$this->assertContains( 'mw-ui-quiet',
$content['user-page']['userpage']['link-class'],
$content['vector-user-menu-overflow']['userpage']['link-class'],
'User page link in user links overflow requires quiet button classes'
);
$this->assertNotContains( 'mw-ui-icon',
$content['user-page']['userpage']['class'],
$content['vector-user-menu-overflow']['userpage']['class'],
'User page link in user links overflow does not have icon classes'
);
$this->assertContains( 'user-links-collapsible-item',