diff --git a/.storybook/icons.less b/.storybook/icons.less
index 6ed274cff..e7c9e1fe8 100644
--- a/.storybook/icons.less
+++ b/.storybook/icons.less
@@ -55,16 +55,12 @@
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Ctitle%3Elog out%3C/title%3E%3Cpath d='M3 3h8V1H3a2 2 0 00-2 2v14a2 2 0 002 2h8v-2H3z'/%3E%3Cpath d='M13 5v4H5v2h8v4l6-5z'/%3E%3C/svg%3E");
}
-.mw-ui-icon-wikimedia-userAvatar:before {
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAx0lEQVQ4jdXSzQmEQAwFYEuYUixhSwgkA8mQgKXYgS3YgXZgCZagHWgHuxf14t8osssGcv145CVJvjk+hBRFK2TrkK1D0cqHkN7CUBRI7L21KAqXMIDModiwD9oAkLlH0i3L+ooGiTWPAPPfJQTIHLGOB9h46YZnKS+3PI8PISW2GkV7FO2Jrb79h4+ODyElsYJYm437NSRWRCWdylgj++U0u+UAZI5E22hsWW03UWQtr2NT66zlCjz8uzNQbFiDN7F5/xB8aj57Ynp2FKI0bAAAAABJRU5ErkJggg==") !important;
-}
-
.mw-ui-icon-wikimedia-ellipsis:before {
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Ctitle%3Eellipsis%3C/title%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Ccircle cx='3' cy='10' r='2'/%3E%3Ccircle cx='17' cy='10' r='2'/%3E%3C/svg%3E%0A") !important;
}
+.mw-ui-icon-wikimedia-userAvatar:before,
#pt-anonuserpage,
-#pt-userpage a {
+.vector-user-menu-legacy #pt-userpage a {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAx0lEQVQ4jdXSzQmEQAwFYEuYUixhSwgkA8mQgKXYgS3YgXZgCZagHWgHuxf14t8osssGcv145CVJvjk+hBRFK2TrkK1D0cqHkN7CUBRI7L21KAqXMIDModiwD9oAkLlH0i3L+ooGiTWPAPPfJQTIHLGOB9h46YZnKS+3PI8PISW2GkV7FO2Jrb79h4+ODyElsYJYm437NSRWRCWdylgj++U0u+UAZI5E22hsWW03UWQtr2NT66zlCjz8uzNQbFiDN7F5/xB8aj57Ynp2FKI0bAAAAABJRU5ErkJggg==") !important;
- background-repeat: no-repeat;
}
diff --git a/i18n/en.json b/i18n/en.json
index ac6d6c646..2c3332c14 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -29,5 +29,6 @@
"vector-more-actions": "More",
"vector-search-loader": "Loading search suggestions",
"vector-anon-user-menu-pages": "Pages for logged out editors",
- "vector-anon-user-menu-pages-learn": "learn more"
+ "vector-anon-user-menu-pages-learn": "learn more",
+ "vector-personal-more-label": "User links"
}
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 2327009d3..ea9565be9 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -41,5 +41,6 @@
"vector-more-actions": "Label in the Vector skin's menu for the less-important or rarer actions which are not shown as tabs (like moving the page, or for sysops deleting or protecting the page), as well as (for users with a narrow viewing window in their browser) the less-important tab actions which the user's browser is unable to fit in. {{Identical|More}}",
"vector-search-loader": "Text to display below search input while the search suggestion module is loading",
"vector-anon-user-menu-pages": "Label describing the anon editor links in the anon user menu",
- "vector-anon-user-menu-pages-learn": "Lowercase text of link that goes to Help:Introduction and helps the user learn more about editing"
+ "vector-anon-user-menu-pages-learn": "Lowercase text of link that goes to Help:Introduction and helps the user learn more about editing",
+ "vector-personal-more-label": "Label describing the user links next to the user links dropdown menu."
}
diff --git a/includes/Hooks.php b/includes/Hooks.php
index eb83095be..0d8aa7786 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -192,6 +192,21 @@ class Hooks {
// "Login" link is handled manually by Vector
unset( $content_navigation['user-menu']['login'] );
}
+
+ // ULS and user page links are hidden at lower resolutions.
+ if ( $content_navigation['user-interface-preferences'] ) {
+ self::appendClassToListItem(
+ $content_navigation['user-interface-preferences']['uls'],
+ $COLLAPSE_MENU_ITEM_CLASS
+ );
+ }
+ if ( $content_navigation['user-page'] ) {
+ self::appendClassToListItem(
+ $content_navigation['user-page']['userpage'],
+ $COLLAPSE_MENU_ITEM_CLASS
+ );
+ }
+
// Prefix user link items with associated icon.
$user_menu = $content_navigation['user-menu'];
// Loop through each menu to check/append its link classes.
diff --git a/includes/SkinVector.php b/includes/SkinVector.php
index d2f2d6077..5cb5e3178 100644
--- a/includes/SkinVector.php
+++ b/includes/SkinVector.php
@@ -246,8 +246,25 @@ class SkinVector extends SkinMustache {
$returnto = $this->getReturnToParam();
$useCombinedLoginLink = $this->useCombinedLoginLink();
$htmlCreateAccount = $this->getCreateAccountHTML( $returnto );
- $userMenuData = $menuData[ 'data-user-menu' ];
+ $templateParser = $this->getTemplateParser();
+ $userMoreHtmlItems = $templateParser->processTemplate( 'UserLinks__more', [
+ 'is-anon' => $isAnon,
+ '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' ],
+ ] );
+ $userMoreData = [
+ "id" => 'p-personal-more',
+ "class" => 'mw-portlet mw-portlet-personal-more vector-menu vector-user-menu-more',
+ "html-items" => $userMoreHtmlItems,
+ "label" => $this->msg( 'vector-personal-more-label' ),
+ "heading-class" => 'vector-menu-heading',
+ "is-dropdown" => false,
+ ];
+
+ $userMenuData = $menuData[ 'data-user-menu' ];
if ( $isAnon ) {
$userMenuData[ 'html-before-portal' ] .= $this->getLoginHTML( $returnto, $useCombinedLoginLink );
} else {
@@ -256,11 +273,7 @@ class SkinVector extends SkinMustache {
}
return [
- 'is-anon' => $isAnon,
- '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' ],
+ 'data-user-more' => $userMoreData,
'data-user-menu' => $userMenuData
];
}
diff --git a/includes/templates/UserLinks.mustache b/includes/templates/UserLinks.mustache
index 07d65f760..85f96d99d 100644
--- a/includes/templates/UserLinks.mustache
+++ b/includes/templates/UserLinks.mustache
@@ -1,13 +1,4 @@
- {{#data-user-interface-preferences}}{{>Menu}}{{/data-user-interface-preferences}}
- {{#is-anon}}
-
- {{{html-create-account}}}
-
- {{/is-anon}}
- {{^is-anon}}
- {{#data-user-page}}{{>Menu}}{{/data-user-page}}
- {{#data-notifications}}{{>Menu}}{{/data-notifications}}
- {{/is-anon}}
+ {{#data-user-more}}{{>Menu}}{{/data-user-more}}
{{#data-user-menu}}{{>Menu}}{{/data-user-menu}}
diff --git a/includes/templates/UserLinks__more.mustache b/includes/templates/UserLinks__more.mustache
new file mode 100644
index 000000000..9a0f921d2
--- /dev/null
+++ b/includes/templates/UserLinks__more.mustache
@@ -0,0 +1,10 @@
+{{#data-user-interface-preferences}}{{{html-items}}}{{/data-user-interface-preferences}}
+{{#is-anon}}
+
+ {{{html-create-account}}}
+
+{{/is-anon}}
+{{^is-anon}}
+ {{#data-user-page}}{{{html-items}}}{{/data-user-page}}
+ {{#data-notifications}}{{{html-items}}}{{/data-notifications}}
+{{/is-anon}}
diff --git a/resources/skins.vector.styles/components/UserLinks.less b/resources/skins.vector.styles/components/UserLinks.less
index 9b4c7824b..c76c5bf5c 100644
--- a/resources/skins.vector.styles/components/UserLinks.less
+++ b/resources/skins.vector.styles/components/UserLinks.less
@@ -8,6 +8,34 @@
justify-content: flex-end;
flex-shrink: 1;
+ .vector-user-menu-more {
+ .vector-menu-content-list {
+ display: flex;
+ align-items: center;
+
+ li {
+ padding-top: 0;
+ margin-left: 1em;
+ white-space: nowrap;
+
+ a {
+ color: #000;
+ text-decoration: none;
+ }
+
+ // Below tablet threshold, all menu items except the notification icons will collapse into the user menu
+ // This ensures a max of 4 icons on small screen sizes (i.e. search, 2 notification icons and the user avatar)
+ &.user-links-collapsible-item {
+ display: none;
+
+ @media ( min-width: @width-breakpoint-tablet ) {
+ display: block;
+ }
+ }
+ }
+ }
+ }
+
// Overrides personal menu styles for consolidated user links.
.vector-user-menu {
margin: 0 0 0 12px;
@@ -115,73 +143,4 @@
background-position: 100% 0%;
}
}
-
- .mw-portlet-notifications {
- li {
- float: left;
- margin-left: 0;
- }
- }
-
- .mw-portlet-user-page {
- // For logged-in users, below tablet threshold, the menu will collapse the user name/link to user page into the user menu
- display: none;
-
- @media ( min-width: @width-breakpoint-tablet ) {
- display: block;
- }
-
- li {
- margin-left: 1em;
- padding-bottom: 0.5em;
-
- a {
- background-position: left center;
- color: #000;
- text-decoration: none;
- }
- }
- }
-
- .vector-user-links-createaccount {
- margin-left: 0.75em;
- // For logged-out users, below tablet threshold, the menu will collapse the create account link into the user menu
- display: none;
-
- @media ( min-width: @width-breakpoint-tablet ) {
- display: block;
- }
- }
-
- // Adjust the user-interface-preferences menu.
- .mw-portlet-user-interface-preferences {
- margin-top: -0.2em;
- margin-right: 0;
- padding: 0;
- // The menu is collapsed into the p-personal menu at lower resolutions, because the features
- // are accessible via Special:Preferences with a few more clicks and there is not space to
- // accomodate this icon at this resolution.
- display: none;
-
- @media ( min-width: @width-breakpoint-tablet ) {
- display: block;
- }
-
- // Hide the heading of the user-interface-preferences menu.
- h3 {
- display: none;
- }
-
- li {
- font-size: 0.75em;
-
- a {
- padding-left: 2em;
- }
- }
-
- ul.vector-menu-content-list {
- padding-top: 0;
- }
- }
}
diff --git a/skin.json b/skin.json
index 6247fc30c..add53aa20 100644
--- a/skin.json
+++ b/skin.json
@@ -34,6 +34,7 @@
"createaccount",
"vector-anon-user-menu-pages",
"vector-anon-user-menu-pages-learn",
+ "vector-personal-more-label",
"parentheses",
"otherlanguages",
"tooltip-p-logo",
diff --git a/stories/UserLinks.stories.data.js b/stories/UserLinks.stories.data.js
index b31810924..b5b239aaf 100644
--- a/stories/UserLinks.stories.data.js
+++ b/stories/UserLinks.stories.data.js
@@ -2,9 +2,11 @@
* @external MenuDefinition
* @external UserLinksDefinition
*/
+import mustache from 'mustache';
import { menuTemplate } from './Menu.stories.data';
import userLinksTemplateLegacy from '!!raw-loader!../includes/templates/legacy/UserLinks.mustache';
import userLinksTemplate from '!!raw-loader!../includes/templates/UserLinks.mustache';
+import userLinksMoreTemplate from '!!raw-loader!../includes/templates/UserLinks__more.mustache';
import { helperClassName, helperMakeMenuData } from './utils';
/**
@@ -61,6 +63,19 @@ const PERSONAL_MENU_TEMPLATE_DATA = {
loggedInWithULS
};
+const additionalUserMoreData = {
+ class: 'vector-menu vector-user-menu-more',
+ "heading-class": 'vector-menu-heading',
+ "is-dropdown": false
+};
+
+const userMoreHtmlItems = ( isAnon = true ) => mustache.render( userLinksMoreTemplate, {
+ 'is-anon': isAnon,
+ 'html-create-account': `Create account`,
+ 'data-user-page': helperMakeMenuData( 'user-page', USERNAME_ITEM ),
+ 'data-notifications': helperMakeMenuData( 'notifications', ECHO_ITEMS )
+} );
+
const loggedInData = {
class: 'vector-user-menu vector-menu-dropdown vector-user-menu-logged-in',
'heading-class': 'mw-ui-icon mw-ui-icon-element mw-ui-icon-wikimedia-userAvatar',
@@ -94,9 +109,7 @@ const loggedOutData = {
* @type {UserLinksDefinition}
*/
const USER_LINKS_LOGGED_IN_TEMPLATE_DATA = {
- 'is-anon': false,
- 'data-user-page': helperMakeMenuData( 'user-page', USERNAME_ITEM ),
- 'data-notifications': helperMakeMenuData( 'notifications', ECHO_ITEMS ),
+ 'data-user-more': helperMakeMenuData( 'personal-more', userMoreHtmlItems( false ), additionalUserMoreData ),
'data-user-menu': helperMakeMenuData( 'new-personal', USER_LINKS_ITEMS, loggedInData )
};
@@ -104,8 +117,7 @@ const USER_LINKS_LOGGED_IN_TEMPLATE_DATA = {
* @type {UserLinksDefinition}
*/
const USER_LINKS_LOGGED_OUT_TEMPLATE_DATA = {
- 'is-anon': true,
- 'html-create-account': `Create account`,
+ 'data-user-more': helperMakeMenuData( 'personal-more', userMoreHtmlItems( true ), additionalUserMoreData ),
'data-user-menu': helperMakeMenuData( 'new-personal', ANON_USER_LINKS_ITEMS, loggedOutData )
};
diff --git a/stories/types.js b/stories/types.js
index d9a2fbb03..00f10a62c 100644
--- a/stories/types.js
+++ b/stories/types.js
@@ -71,10 +71,6 @@
/**
* @typedef {Object} UserLinksDefinition
- * @property {boolean} is-anon
- * @property {string} [html-create-account]
- * @property {MenuDefinition} [data-user-interface-preferences]
- * @property {MenuDefinition} [data-notifications]
- * @property {MenuDefinition} [data-user-page]
+ * @property {MenuDefinition} data-user-more
* @property {MenuDefinition} data-user-menu
*/