Merge "Follow-up: Refactor button/icon class helpers, make portlet classes consistent"
This commit is contained in:
commit
be7797e012
|
@ -168,9 +168,9 @@ class Hooks implements
|
||||||
|
|
||||||
// Promote watch link from actions to views and add an icon
|
// Promote watch link from actions to views and add an icon
|
||||||
if ( $key !== null ) {
|
if ( $key !== null ) {
|
||||||
self::appendClassToListItem(
|
self::appendClassToItem(
|
||||||
$content_navigation['actions'][$key],
|
$content_navigation['actions'][$key]['class'],
|
||||||
'icon'
|
[ 'icon' ]
|
||||||
);
|
);
|
||||||
$content_navigation['views'][$key] = $content_navigation['actions'][$key];
|
$content_navigation['views'][$key] = $content_navigation['actions'][$key];
|
||||||
unset( $content_navigation['actions'][$key] );
|
unset( $content_navigation['actions'][$key] );
|
||||||
|
@ -178,36 +178,30 @@ class Hooks implements
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates class list on list item
|
* Adds class to a property
|
||||||
*
|
*
|
||||||
* @param array &$item to update for use in makeListItem
|
* @param array &$item to update
|
||||||
* @param array $classes to add to the item
|
* @param array|string $classes to add to the item
|
||||||
* @param bool $applyToLink (optional) and defaults to false.
|
|
||||||
* If set will modify `link-class` instead of `class`
|
|
||||||
*/
|
*/
|
||||||
private static function addListItemClass( &$item, $classes, $applyToLink = false ) {
|
private static function appendClassToItem( &$item, $classes ) {
|
||||||
$property = $applyToLink ? 'link-class' : 'class';
|
$existingClasses = $item;
|
||||||
$existingClass = $item[$property] ?? [];
|
|
||||||
|
|
||||||
if ( is_array( $existingClass ) ) {
|
if ( is_array( $existingClasses ) ) {
|
||||||
$item[$property] = array_merge( $existingClass, $classes );
|
// Treat as array
|
||||||
} elseif ( is_string( $existingClass ) ) {
|
$newArrayClasses = is_array( $classes ) ? $classes : [ trim( $classes ) ];
|
||||||
// treat as string
|
$item = array_merge( $existingClasses, $newArrayClasses );
|
||||||
$item[$property] = array_merge( [ $existingClass ], $classes );
|
} elseif ( is_string( $existingClasses ) ) {
|
||||||
|
// Treat as string
|
||||||
|
$newStrClasses = is_string( $classes ) ? trim( $classes ) : implode( ' ', $classes );
|
||||||
|
$item .= ' ' . $newStrClasses;
|
||||||
} else {
|
} else {
|
||||||
$item[$property] = $classes;
|
// Treat as whatever $classes is
|
||||||
|
$item = $classes;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if ( is_string( $item ) ) {
|
||||||
* Updates the class on an existing item taking into account whether
|
$item = trim( $item );
|
||||||
* a class exists there already.
|
}
|
||||||
*
|
|
||||||
* @param array &$item
|
|
||||||
* @param string $newClass
|
|
||||||
*/
|
|
||||||
private static function appendClassToListItem( &$item, $newClass ) {
|
|
||||||
self::addListItemClass( $item, [ $newClass ] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -227,12 +221,12 @@ class Hooks implements
|
||||||
if ( isset( $content_navigation['user-page']['tmpuserpage'] ) ) {
|
if ( isset( $content_navigation['user-page']['tmpuserpage'] ) ) {
|
||||||
$content_navigation['user-page']['tmpuserpage']['collapsible'] = true;
|
$content_navigation['user-page']['tmpuserpage']['collapsible'] = true;
|
||||||
$content_navigation['user-page']['tmpuserpage'] =
|
$content_navigation['user-page']['tmpuserpage'] =
|
||||||
self::updateMenuItem( $content_navigation['user-page']['tmpuserpage'] );
|
self::updateMenuItemData( $content_navigation['user-page']['tmpuserpage'] );
|
||||||
}
|
}
|
||||||
if ( isset( $content_navigation['user-menu']['tmpuserpage'] ) ) {
|
if ( isset( $content_navigation['user-menu']['tmpuserpage'] ) ) {
|
||||||
$content_navigation['user-menu']['tmpuserpage']['collapsible'] = true;
|
$content_navigation['user-menu']['tmpuserpage']['collapsible'] = true;
|
||||||
$content_navigation['user-menu']['tmpuserpage'] =
|
$content_navigation['user-menu']['tmpuserpage'] =
|
||||||
self::updateMenuItem( $content_navigation['user-menu']['tmpuserpage'] );
|
self::updateMenuItemData( $content_navigation['user-menu']['tmpuserpage'] );
|
||||||
}
|
}
|
||||||
} elseif ( $isRegistered ) {
|
} elseif ( $isRegistered ) {
|
||||||
// Remove user page from personal menu dropdown for logged in use
|
// Remove user page from personal menu dropdown for logged in use
|
||||||
|
@ -360,10 +354,7 @@ class Hooks implements
|
||||||
*/
|
*/
|
||||||
private static function makeMenuItemCollapsible( array &$item, string $prefix = 'user-links-' ) {
|
private static function makeMenuItemCollapsible( array &$item, string $prefix = 'user-links-' ) {
|
||||||
$COLLAPSE_MENU_ITEM_CLASS = $prefix . 'collapsible-item';
|
$COLLAPSE_MENU_ITEM_CLASS = $prefix . 'collapsible-item';
|
||||||
self::appendClassToListItem(
|
self::appendClassToItem( $item[ 'class' ], $COLLAPSE_MENU_ITEM_CLASS );
|
||||||
$item,
|
|
||||||
$COLLAPSE_MENU_ITEM_CLASS
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -379,14 +370,15 @@ class Hooks implements
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates template data for Vector menu items.
|
* Update template data to include classes and html that handle buttons, icons, and collapsible items.
|
||||||
*
|
*
|
||||||
* @param array $item Menu data to update
|
* @internal for use inside Vector skin.
|
||||||
* @param bool $isLinkData false if data is for li element (i.e. makeListItem()),
|
* @param array $item data to update
|
||||||
* true if for link element (i.e. makeLink())
|
* @param string $buttonClassProp property to append button classes
|
||||||
* @return array $item Updated menu data
|
* @param string $iconHtmlProp property to set icon HTML
|
||||||
|
* @return array $item Updated data
|
||||||
*/
|
*/
|
||||||
public static function updateMenuItem( $item, $isLinkData = false ) {
|
private static function updateItemData( $item, $buttonClassProp, $iconHtmlProp ) {
|
||||||
$hasButton = $item['button'] ?? false;
|
$hasButton = $item['button'] ?? false;
|
||||||
$hideText = $item['text-hidden'] ?? false;
|
$hideText = $item['text-hidden'] ?? false;
|
||||||
$isCollapsible = $item['collapsible'] ?? false;
|
$isCollapsible = $item['collapsible'] ?? false;
|
||||||
|
@ -395,11 +387,12 @@ class Hooks implements
|
||||||
unset( $item['icon'] );
|
unset( $item['icon'] );
|
||||||
unset( $item['text-hidden'] );
|
unset( $item['text-hidden'] );
|
||||||
unset( $item['collapsible'] );
|
unset( $item['collapsible'] );
|
||||||
|
|
||||||
if ( $isCollapsible ) {
|
if ( $isCollapsible ) {
|
||||||
self::makeMenuItemCollapsible( $item );
|
self::makeMenuItemCollapsible( $item );
|
||||||
}
|
}
|
||||||
if ( $hasButton ) {
|
if ( $hasButton ) {
|
||||||
self::addListItemClass( $item, [ 'mw-ui-button', 'mw-ui-quiet' ], !$isLinkData );
|
self::appendClassToItem( $item[ $buttonClassProp ], [ 'mw-ui-button', 'mw-ui-quiet' ] );
|
||||||
}
|
}
|
||||||
if ( $icon ) {
|
if ( $icon ) {
|
||||||
if ( $hideText ) {
|
if ( $hideText ) {
|
||||||
|
@ -410,14 +403,50 @@ class Hooks implements
|
||||||
// We should seek to remove all these instances.
|
// We should seek to remove all these instances.
|
||||||
'mw-ui-icon-wikimedia-' . $icon
|
'mw-ui-icon-wikimedia-' . $icon
|
||||||
];
|
];
|
||||||
self::addListItemClass( $item, $iconElementClasses, !$isLinkData );
|
self::appendClassToItem( $item[ $buttonClassProp ], $iconElementClasses );
|
||||||
} else {
|
} else {
|
||||||
$item['link-html'] = self::makeIcon( $icon );
|
$item[ $iconHtmlProp ] = self::makeIcon( $icon );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $item;
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates template data for Vector dropdown menus.
|
||||||
|
*
|
||||||
|
* @param array $item Menu data to update
|
||||||
|
* @return array $item Updated menu data
|
||||||
|
*/
|
||||||
|
public static function updateDropdownMenuData( $item ) {
|
||||||
|
$buttonClassProp = 'heading-class';
|
||||||
|
$iconHtmlProp = 'html-vector-heading-icon';
|
||||||
|
return self::updateItemData( $item, $buttonClassProp, $iconHtmlProp );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates template data for Vector link items.
|
||||||
|
*
|
||||||
|
* @param array $item link data to update
|
||||||
|
* @return array $item Updated link data
|
||||||
|
*/
|
||||||
|
public static function updateLinkData( $item ) {
|
||||||
|
$buttonClassProp = 'class';
|
||||||
|
$iconHtmlProp = 'link-html';
|
||||||
|
return self::updateItemData( $item, $buttonClassProp, $iconHtmlProp );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates template data for Vector menu items.
|
||||||
|
*
|
||||||
|
* @param array $item menu item data to update
|
||||||
|
* @return array $item Updated menu item data
|
||||||
|
*/
|
||||||
|
public static function updateMenuItemData( $item ) {
|
||||||
|
$buttonClassProp = 'link-class';
|
||||||
|
$iconHtmlProp = 'link-html';
|
||||||
|
return self::updateItemData( $item, $buttonClassProp, $iconHtmlProp );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates user interface preferences for modern Vector to upgrade icon/button menu items.
|
* Updates user interface preferences for modern Vector to upgrade icon/button menu items.
|
||||||
*
|
*
|
||||||
|
@ -426,7 +455,7 @@ class Hooks implements
|
||||||
*/
|
*/
|
||||||
private static function updateMenuItems( &$content_navigation, $menu ) {
|
private static function updateMenuItems( &$content_navigation, $menu ) {
|
||||||
foreach ( $content_navigation[$menu] as $key => $item ) {
|
foreach ( $content_navigation[$menu] as $key => $item ) {
|
||||||
$content_navigation[$menu][$key] = self::updateMenuItem( $item );
|
$content_navigation[$menu][$key] = self::updateMenuItemData( $item );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,9 +126,7 @@ abstract class SkinVector extends SkinMustache {
|
||||||
];
|
];
|
||||||
private const SEARCH_SHOW_THUMBNAIL_CLASS = 'vector-search-box-show-thumbnail';
|
private const SEARCH_SHOW_THUMBNAIL_CLASS = 'vector-search-box-show-thumbnail';
|
||||||
private const SEARCH_AUTO_EXPAND_WIDTH_CLASS = 'vector-search-box-auto-expand-width';
|
private const SEARCH_AUTO_EXPAND_WIDTH_CLASS = 'vector-search-box-auto-expand-width';
|
||||||
private const CLASS_QUIET_BUTTON = 'mw-ui-button mw-ui-quiet';
|
|
||||||
private const CLASS_PROGRESSIVE = 'mw-ui-progressive';
|
private const CLASS_PROGRESSIVE = 'mw-ui-progressive';
|
||||||
private const CLASS_ICON_BUTTON = 'mw-ui-icon mw-ui-icon-element';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* T243281: Code used to track clicks to opt-out link.
|
* T243281: Code used to track clicks to opt-out link.
|
||||||
|
@ -142,17 +140,6 @@ abstract class SkinVector extends SkinMustache {
|
||||||
*/
|
*/
|
||||||
private const OPT_OUT_LINK_TRACKING_CODE = 'vctw1';
|
private const OPT_OUT_LINK_TRACKING_CODE = 'vctw1';
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $icon the name of the icon without wikimedia- prefix.
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function iconClass( $icon ) {
|
|
||||||
if ( $icon ) {
|
|
||||||
return 'mw-ui-icon-wikimedia-' . $icon;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract protected function isLegacy(): bool;
|
abstract protected function isLegacy(): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -249,7 +236,7 @@ abstract class SkinVector extends SkinMustache {
|
||||||
'icon' => $isDropdownItem ? $createAccountData['icon'] : null,
|
'icon' => $isDropdownItem ? $createAccountData['icon'] : null,
|
||||||
'button' => !$isDropdownItem,
|
'button' => !$isDropdownItem,
|
||||||
] );
|
] );
|
||||||
$createAccountData = Hooks::updateMenuItem( $createAccountData, true );
|
$createAccountData = Hooks::updateLinkData( $createAccountData );
|
||||||
return $this->makeLink( 'create-account', $createAccountData );
|
return $this->makeLink( 'create-account', $createAccountData );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +252,7 @@ abstract class SkinVector extends SkinMustache {
|
||||||
$loginLinkData = array_merge( $this->buildLoginData( $returnto, $useCombinedLoginLink ), [
|
$loginLinkData = array_merge( $this->buildLoginData( $returnto, $useCombinedLoginLink ), [
|
||||||
'class' => [ 'vector-menu-content-item', 'vector-menu-content-item-login' ],
|
'class' => [ 'vector-menu-content-item', 'vector-menu-content-item-login' ],
|
||||||
] );
|
] );
|
||||||
$loginLinkData = Hooks::updateMenuItem( $loginLinkData, true );
|
$loginLinkData = Hooks::updateLinkData( $loginLinkData );
|
||||||
$templateData = [
|
$templateData = [
|
||||||
'htmlCreateAccount' => $this->getCreateAccountHTML( $returnto, true ),
|
'htmlCreateAccount' => $this->getCreateAccountHTML( $returnto, true ),
|
||||||
'htmlLogin' => $this->makeLink( 'login', $loginLinkData ),
|
'htmlLogin' => $this->makeLink( 'login', $loginLinkData ),
|
||||||
|
@ -296,7 +283,7 @@ abstract class SkinVector extends SkinMustache {
|
||||||
$logoutLinkData = array_merge( $this->buildLogoutLinkData(), [
|
$logoutLinkData = array_merge( $this->buildLogoutLinkData(), [
|
||||||
'class' => [ 'vector-menu-content-item', 'vector-menu-content-item-logout' ],
|
'class' => [ 'vector-menu-content-item', 'vector-menu-content-item-logout' ],
|
||||||
] );
|
] );
|
||||||
$logoutLinkData = Hooks::updateMenuItem( $logoutLinkData, true );
|
$logoutLinkData = Hooks::updateLinkData( $logoutLinkData );
|
||||||
|
|
||||||
$templateParser = $this->getTemplateParser();
|
$templateParser = $this->getTemplateParser();
|
||||||
return $templateParser->processTemplate( 'UserLinks__logout', [
|
return $templateParser->processTemplate( 'UserLinks__logout', [
|
||||||
|
@ -379,11 +366,9 @@ abstract class SkinVector extends SkinMustache {
|
||||||
'html-items' => '',
|
'html-items' => '',
|
||||||
'html-vector-menu-checkbox-attributes' => 'tabindex="-1"',
|
'html-vector-menu-checkbox-attributes' => 'tabindex="-1"',
|
||||||
'html-vector-menu-heading-attributes' => 'tabindex="-1"',
|
'html-vector-menu-heading-attributes' => 'tabindex="-1"',
|
||||||
'heading-class' => implode( ' ', [
|
'button' => true,
|
||||||
self::CLASS_QUIET_BUTTON,
|
'text-hidden' => true,
|
||||||
self::CLASS_ICON_BUTTON,
|
'icon' => 'listBullet'
|
||||||
$this->iconClass( 'listBullet' )
|
|
||||||
] ),
|
|
||||||
] );
|
] );
|
||||||
|
|
||||||
// Show sticky ULS if the ULS extension is enabled and the ULS in header is not hidden
|
// Show sticky ULS if the ULS extension is enabled and the ULS in header is not hidden
|
||||||
|
@ -728,9 +713,9 @@ abstract class SkinVector extends SkinMustache {
|
||||||
$this->getULSLabel( 'vector-language-button-aria-label', $numLanguages ),
|
$this->getULSLabel( 'vector-language-button-aria-label', $numLanguages ),
|
||||||
// ext.uls.interface attaches click handler to this selector.
|
// ext.uls.interface attaches click handler to this selector.
|
||||||
'checkbox-class' => ' mw-interlanguage-selector ',
|
'checkbox-class' => ' mw-interlanguage-selector ',
|
||||||
'html-vector-heading-icon' => Hooks::makeIcon( 'wikimedia-language-progressive' ),
|
'icon' => 'language-progressive',
|
||||||
'heading-class' => self::CLASS_QUIET_BUTTON . ' ' . self::CLASS_PROGRESSIVE .
|
'button' => true,
|
||||||
' mw-portlet-lang-heading-' . strval( $numLanguages ),
|
'heading-class' => self::CLASS_PROGRESSIVE . ' mw-portlet-lang-heading-' . strval( $numLanguages ),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Adds class to hide language button
|
// Adds class to hide language button
|
||||||
|
@ -743,13 +728,43 @@ abstract class SkinVector extends SkinMustache {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* helper for applying Vector menu classes to portlets
|
* Creates portlet data for the user menu dropdown
|
||||||
|
*
|
||||||
|
* @param array $portletData
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getUserMenuPortletData( $portletData ) {
|
||||||
|
// Add target class to apply different icon to personal menu dropdown for logged in users.
|
||||||
|
$portletData['class'] .= ' vector-user-menu';
|
||||||
|
$portletData['class'] .= $this->loggedin ?
|
||||||
|
' vector-user-menu-logged-in' :
|
||||||
|
' vector-user-menu-logged-out';
|
||||||
|
if ( $this->getUser()->isTemp() ) {
|
||||||
|
$icon = 'userAnonymous';
|
||||||
|
} elseif ( $this->loggedin ) {
|
||||||
|
$icon = 'userAvatar';
|
||||||
|
} else {
|
||||||
|
$icon = 'ellipsis';
|
||||||
|
// T287494 We use tooltip messages to provide title attributes on hover over certain menu icons.
|
||||||
|
// For modern Vector, the "tooltip-p-personal" key is set to "User menu" which is appropriate for
|
||||||
|
// the user icon (dropdown indicator for user links menu) for logged-in users.
|
||||||
|
// This overrides the tooltip for the user links menu icon which is an ellipsis for anonymous users.
|
||||||
|
$portletData['html-tooltip'] = Linker::tooltip( 'vector-anon-user-menu-title' );
|
||||||
|
}
|
||||||
|
$portletData['icon'] = $icon;
|
||||||
|
$portletData['button'] = true;
|
||||||
|
$portletData['text-hidden'] = true;
|
||||||
|
return $portletData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for applying Vector menu classes to portlets
|
||||||
*
|
*
|
||||||
* @param array $portletData returned by SkinMustache to decorate
|
* @param array $portletData returned by SkinMustache to decorate
|
||||||
* @param int $type representing one of the menu types (see MENU_TYPE_* constants)
|
* @param int $type representing one of the menu types (see MENU_TYPE_* constants)
|
||||||
* @return array modified version of portletData input
|
* @return array modified version of portletData input
|
||||||
*/
|
*/
|
||||||
private function decoratePortletClass(
|
private function updatePortletClasses(
|
||||||
array $portletData,
|
array $portletData,
|
||||||
int $type = self::MENU_TYPE_DEFAULT
|
int $type = self::MENU_TYPE_DEFAULT
|
||||||
) {
|
) {
|
||||||
|
@ -762,47 +777,17 @@ abstract class SkinVector extends SkinMustache {
|
||||||
if ( $this->isLegacy() ) {
|
if ( $this->isLegacy() ) {
|
||||||
$extraClasses[self::MENU_TYPE_TABS] .= ' vector-menu-tabs-legacy';
|
$extraClasses[self::MENU_TYPE_TABS] .= ' vector-menu-tabs-legacy';
|
||||||
}
|
}
|
||||||
|
$portletData['class'] .= ' ' . $extraClasses[$type];
|
||||||
|
|
||||||
if ( !isset( $portletData['heading-class'] ) ) {
|
if ( !isset( $portletData['heading-class'] ) ) {
|
||||||
$portletData['heading-class'] = '';
|
$portletData['heading-class'] = '';
|
||||||
}
|
}
|
||||||
// Add target class to apply different icon to personal menu dropdown for logged in users.
|
if ( $type === self::MENU_TYPE_DROPDOWN ) {
|
||||||
if ( $portletData['id'] === 'p-personal' ) {
|
$portletData = Hooks::updateDropdownMenuData( $portletData );
|
||||||
if ( $this->isLegacy() ) {
|
|
||||||
$portletData['class'] .= ' vector-user-menu-legacy';
|
|
||||||
} else {
|
|
||||||
$portletData['class'] .= ' vector-user-menu';
|
|
||||||
$portletData['class'] .= $this->loggedin ?
|
|
||||||
' vector-user-menu-logged-in' :
|
|
||||||
' vector-user-menu-logged-out';
|
|
||||||
$portletData['heading-class'] .= ' ' . self::CLASS_QUIET_BUTTON . ' ' .
|
|
||||||
self::CLASS_ICON_BUTTON . ' ';
|
|
||||||
if ( $this->getUser()->isTemp() ) {
|
|
||||||
$icon = 'userAnonymous';
|
|
||||||
} elseif ( $this->loggedin ) {
|
|
||||||
$icon = 'userAvatar';
|
|
||||||
} else {
|
|
||||||
$icon = 'ellipsis';
|
|
||||||
}
|
|
||||||
$portletData['heading-class'] .= $this->iconClass( $icon );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch ( $portletData['id'] ) {
|
|
||||||
case 'p-variants':
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $portletData['id'] === 'p-lang' && $this->isLanguagesInContent() ) {
|
$portletData['class'] = trim( $portletData['class'] );
|
||||||
$portletData = array_merge( $portletData, $this->getULSPortletData() );
|
$portletData['heading-class'] = trim( $portletData['heading-class'] );
|
||||||
}
|
|
||||||
$class = $portletData['class'];
|
|
||||||
$portletData['class'] = trim( "$class $extraClasses[$type]" );
|
|
||||||
return $portletData;
|
return $portletData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -873,10 +858,24 @@ abstract class SkinVector extends SkinMustache {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$portletData = $this->decoratePortletClass(
|
if ( $key === 'data-languages' && $this->isLanguagesInContent() ) {
|
||||||
$portletData,
|
$portletData = array_merge( $portletData, $this->getULSPortletData() );
|
||||||
$type
|
}
|
||||||
);
|
|
||||||
|
if ( $key === 'data-user-menu' && !$this->isLegacy() ) {
|
||||||
|
$portletData = $this->getUserMenuPortletData( $portletData );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $key === 'data-vector-user-menu-overflow' ) {
|
||||||
|
$portletData['class'] .= ' vector-user-menu-overflow';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $key === 'data-personal' && $this->isLegacy() ) {
|
||||||
|
// Set tooltip to empty string for the personal menu for both logged-in and logged-out users
|
||||||
|
// to avoid showing the tooltip for legacy version.
|
||||||
|
$portletData['html-tooltip'] = '';
|
||||||
|
$portletData['class'] .= ' vector-user-menu-legacy';
|
||||||
|
}
|
||||||
|
|
||||||
// Special casing for Variant to change label to selected.
|
// Special casing for Variant to change label to selected.
|
||||||
// Hopefully we can revisit and possibly remove this code when the language switcher is moved.
|
// Hopefully we can revisit and possibly remove this code when the language switcher is moved.
|
||||||
|
@ -891,19 +890,10 @@ abstract class SkinVector extends SkinMustache {
|
||||||
$portletData['aria-label'] = $this->msg( 'vector-language-variant-switcher-label' );
|
$portletData['aria-label'] = $this->msg( 'vector-language-variant-switcher-label' );
|
||||||
}
|
}
|
||||||
|
|
||||||
// T287494 We use tooltip messages to provide title attributes on hover over certain menu icons. For modern
|
$portletData = $this->updatePortletClasses(
|
||||||
// Vector, the "tooltip-p-personal" key is set to "User menu" which is appropriate for the user icon (dropdown
|
$portletData,
|
||||||
// indicator for user links menu) for logged-in users. This overrides the tooltip for the user links menu icon
|
$type
|
||||||
// which is an ellipsis for anonymous users.
|
);
|
||||||
if ( $key === 'data-user-menu' && !$this->isLegacy() && !$this->loggedin ) {
|
|
||||||
$portletData['html-tooltip'] = Linker::tooltip( 'vector-anon-user-menu-title' );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set tooltip to empty string for the personal menu for both logged-in and logged-out users to avoid showing
|
|
||||||
// the tooltip for legacy version.
|
|
||||||
if ( $key === 'data-personal' && $this->isLegacy() ) {
|
|
||||||
$portletData['html-tooltip'] = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $portletData + [
|
return $portletData + [
|
||||||
'is-dropdown' => $type === self::MENU_TYPE_DROPDOWN,
|
'is-dropdown' => $type === self::MENU_TYPE_DROPDOWN,
|
||||||
|
|
|
@ -271,11 +271,11 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
|
||||||
$namespaces['class']
|
$namespaces['class']
|
||||||
);
|
);
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
'mw-portlet mw-portlet-variants vector-menu-dropdown-noicon vector-menu-dropdown',
|
'mw-portlet mw-portlet-variants vector-menu-dropdown',
|
||||||
$variants['class']
|
$variants['class']
|
||||||
);
|
);
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
'mw-portlet mw-portlet-cactions vector-menu-dropdown-noicon vector-menu-dropdown',
|
'mw-portlet mw-portlet-cactions vector-menu-dropdown',
|
||||||
$actions['class']
|
$actions['class']
|
||||||
);
|
);
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
|
|
|
@ -400,12 +400,12 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
||||||
$skin->getContext()->setTitle( Title::newFromText( 'Foo' ) );
|
$skin->getContext()->setTitle( Title::newFromText( 'Foo' ) );
|
||||||
$contentNavWatch = [
|
$contentNavWatch = [
|
||||||
'actions' => [
|
'actions' => [
|
||||||
'watch' => [ 'class' => 'watch' ],
|
'watch' => [ 'class' => [ 'watch' ] ],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
$contentNavUnWatch = [
|
$contentNavUnWatch = [
|
||||||
'actions' => [
|
'actions' => [
|
||||||
'move' => [ 'class' => 'move' ],
|
'move' => [ 'class' => [ 'move' ] ],
|
||||||
'unwatch' => [],
|
'unwatch' => [],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -413,16 +413,16 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
||||||
Hooks::onSkinTemplateNavigation( $skin, $contentNavUnWatch );
|
Hooks::onSkinTemplateNavigation( $skin, $contentNavUnWatch );
|
||||||
Hooks::onSkinTemplateNavigation( $skin, $contentNavWatch );
|
Hooks::onSkinTemplateNavigation( $skin, $contentNavWatch );
|
||||||
|
|
||||||
$this->assertTrue(
|
$this->assertContains(
|
||||||
in_array( 'icon', $contentNavWatch['views']['watch']['class'] ) !== false,
|
'icon', $contentNavWatch['views']['watch']['class'],
|
||||||
'Watch list items require an "icon" class'
|
'Watch list items require an "icon" class'
|
||||||
);
|
);
|
||||||
$this->assertTrue(
|
$this->assertContains(
|
||||||
in_array( 'icon', $contentNavUnWatch['views']['unwatch']['class'] ) !== false,
|
'icon', $contentNavUnWatch['views']['unwatch']['class'],
|
||||||
'Unwatch list items require an "icon" class'
|
'Unwatch list items require an "icon" class'
|
||||||
);
|
);
|
||||||
$this->assertFalse(
|
$this->assertNotContains(
|
||||||
strpos( $contentNavUnWatch['actions']['move']['class'], 'icon' ) !== false,
|
'icon', $contentNavUnWatch['actions']['move']['class'],
|
||||||
'List item other than watch or unwatch should not have an "icon" class'
|
'List item other than watch or unwatch should not have an "icon" class'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -585,18 +585,79 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideUpdateMenuItem() {
|
public function provideAppendClassToItem() {
|
||||||
|
return [
|
||||||
|
// Add array class to array
|
||||||
|
[
|
||||||
|
[],
|
||||||
|
[ 'array1', 'array2' ],
|
||||||
|
[ 'array1', 'array2' ],
|
||||||
|
],
|
||||||
|
// Add string class to array
|
||||||
|
[
|
||||||
|
[],
|
||||||
|
'array1 array2',
|
||||||
|
[ 'array1 array2' ],
|
||||||
|
],
|
||||||
|
// Add array class to string
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
[ 'array1', 'array2' ],
|
||||||
|
'array1 array2',
|
||||||
|
],
|
||||||
|
// Add string class to string
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
'array1 array2',
|
||||||
|
'array1 array2',
|
||||||
|
],
|
||||||
|
// Add string class to undefined
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
'array1 array2',
|
||||||
|
'array1 array2',
|
||||||
|
],
|
||||||
|
// Add array class to undefined
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[ 'array1', 'array2' ],
|
||||||
|
[ 'array1', 'array2' ],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::appendClassToItem
|
||||||
|
* @dataProvider provideAppendClassToItem
|
||||||
|
*/
|
||||||
|
public function testAppendClassToItem(
|
||||||
|
$item,
|
||||||
|
$classes,
|
||||||
|
$expected
|
||||||
|
) {
|
||||||
|
$appendClassToItem = new ReflectionMethod(
|
||||||
|
Hooks::class,
|
||||||
|
'appendClassToItem'
|
||||||
|
);
|
||||||
|
$appendClassToItem->setAccessible( true );
|
||||||
|
$appendClassToItem->invokeArgs( null, [ &$item, $classes ] );
|
||||||
|
$this->assertEquals( $expected, $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideUpdateItemData() {
|
||||||
return [
|
return [
|
||||||
// Removes extra attributes
|
// Removes extra attributes
|
||||||
[
|
[
|
||||||
[ 'class' => [], 'icon' => '', 'button' => false, 'text-hidden' => false, 'collapsible' => false ],
|
[ 'class' => [], 'icon' => '', 'button' => false, 'text-hidden' => false, 'collapsible' => false ],
|
||||||
false,
|
'link-class',
|
||||||
|
'link-html',
|
||||||
[ 'class' => [] ],
|
[ 'class' => [] ],
|
||||||
],
|
],
|
||||||
// Adds link-html
|
// Adds icon html
|
||||||
[
|
[
|
||||||
[ 'class' => [], 'icon' => 'userpage' ],
|
[ 'class' => [], 'icon' => 'userpage' ],
|
||||||
false,
|
'link-class',
|
||||||
|
'link-html',
|
||||||
[
|
[
|
||||||
'class' => [],
|
'class' => [],
|
||||||
'link-html' =>
|
'link-html' =>
|
||||||
|
@ -606,19 +667,22 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
||||||
// Adds collapsible class
|
// Adds collapsible class
|
||||||
[
|
[
|
||||||
[ 'class' => [], 'collapsible' => true ],
|
[ 'class' => [], 'collapsible' => true ],
|
||||||
false,
|
'link-class',
|
||||||
|
'link-html',
|
||||||
[ 'class' => [ 'user-links-collapsible-item' ] ],
|
[ 'class' => [ 'user-links-collapsible-item' ] ],
|
||||||
],
|
],
|
||||||
// Adds button classes
|
// Adds button classes
|
||||||
[
|
[
|
||||||
[ 'class' => [], 'button' => true ],
|
[ 'class' => [], 'button' => true ],
|
||||||
false,
|
'link-class',
|
||||||
|
'link-html',
|
||||||
[ 'class' => [], 'link-class' => [ 'mw-ui-button', 'mw-ui-quiet' ] ],
|
[ 'class' => [], 'link-class' => [ 'mw-ui-button', 'mw-ui-quiet' ] ],
|
||||||
],
|
],
|
||||||
// Adds text hidden classes
|
// Adds text hidden classes
|
||||||
[
|
[
|
||||||
[ 'class' => [], 'text-hidden' => true, 'icon' => 'userpage' ],
|
[ 'class' => [], 'text-hidden' => true, 'icon' => 'userpage' ],
|
||||||
false,
|
'link-class',
|
||||||
|
'link-html',
|
||||||
[ 'class' => [], 'link-class' => [
|
[ 'class' => [], 'link-class' => [
|
||||||
'mw-ui-icon',
|
'mw-ui-icon',
|
||||||
'mw-ui-icon-element',
|
'mw-ui-icon-element',
|
||||||
|
@ -626,37 +690,37 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
||||||
'mw-ui-icon-wikimedia-userpage'
|
'mw-ui-icon-wikimedia-userpage'
|
||||||
] ],
|
] ],
|
||||||
],
|
],
|
||||||
// Adds classes for link data
|
// Adds button and icon classes
|
||||||
[
|
[
|
||||||
[ 'class' => [], 'button' => true, 'text-hidden' => true, 'icon' => 'userpage' ],
|
[ 'class' => [], 'button' => true, 'icon' => 'userpage' ],
|
||||||
true,
|
'class',
|
||||||
|
'link-html',
|
||||||
[ 'class' => [
|
[ 'class' => [
|
||||||
'mw-ui-button',
|
'mw-ui-button',
|
||||||
'mw-ui-quiet',
|
'mw-ui-quiet'
|
||||||
'mw-ui-icon',
|
], 'link-html' =>
|
||||||
'mw-ui-icon-element',
|
'<span class="mw-ui-icon mw-ui-icon-userpage mw-ui-icon-wikimedia-userpage"></span>'
|
||||||
'mw-ui-icon-userpage',
|
],
|
||||||
'mw-ui-icon-wikimedia-userpage'
|
|
||||||
] ],
|
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers ::updateMenuItem
|
* @covers ::updateItemData
|
||||||
* @dataProvider provideUpdateMenuItem
|
* @dataProvider provideUpdateItemData
|
||||||
*/
|
*/
|
||||||
public function testUpdateMenuItem(
|
public function testUpdateItemData(
|
||||||
array $menuItem,
|
array $item,
|
||||||
bool $isLinkData,
|
string $buttonClassProp,
|
||||||
|
string $iconHtmlProp,
|
||||||
array $expected
|
array $expected
|
||||||
) {
|
) {
|
||||||
$updateMenuItem = new ReflectionMethod(
|
$updateItemData = new ReflectionMethod(
|
||||||
Hooks::class,
|
Hooks::class,
|
||||||
'updateMenuItem'
|
'updateItemData'
|
||||||
);
|
);
|
||||||
$updateMenuItem->setAccessible( true );
|
$updateItemData->setAccessible( true );
|
||||||
$data = $updateMenuItem->invokeArgs( null, [ $menuItem, $isLinkData ] );
|
$data = $updateItemData->invokeArgs( null, [ $item, $buttonClassProp, $iconHtmlProp ] );
|
||||||
$this->assertEquals( $expected, $data );
|
$this->assertEquals( $expected, $data );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue