Use newly available Skin::getPortletData method to get mw-portlet class

Kept as simple as possible for now. The new class is added but no classes
are removed. This will be done in a follow up.

Bug: T256897
Bug: T253938
Change-Id: Ib31a9d8f2ac14e63b63e82abd4a9aa1fcb956f45
This commit is contained in:
jdlrobson 2020-09-23 15:29:30 -07:00 committed by Jdlrobson
parent 4dbe4076d6
commit 711a41812a
5 changed files with 39 additions and 74 deletions

View file

@ -34,14 +34,6 @@ use Vector\VectorServices;
* @internal * @internal
*/ */
class SkinVector extends SkinMustache { class SkinVector extends SkinMustache {
/** @var array of alternate message keys for menu labels */
private const MENU_LABEL_KEYS = [
'cactions' => 'vector-more-actions',
'tb' => 'toolbox',
'personal' => 'personaltools',
'lang' => 'otherlanguages',
];
/** @var int */ /** @var int */
private const MENU_TYPE_DEFAULT = 0; private const MENU_TYPE_DEFAULT = 0;
/** @var int */ /** @var int */
@ -344,8 +336,7 @@ class SkinVector extends SkinMustache {
/** /**
* @param string $label to be used to derive the id and human readable label of the menu * @param string $label to be used to derive the id and human readable label of the menu
* If the key has an entry in the constant MENU_LABEL_KEYS then that message will be used for the * Note certain keys are special cased for historic reasons in core.
* human readable text instead.
* @param array $urls to convert to list items stored as string in html-items key * @param array $urls to convert to list items stored as string in html-items key
* @param int $type of menu (optional) - a plain list (MENU_TYPE_DEFAULT), * @param int $type of menu (optional) - a plain list (MENU_TYPE_DEFAULT),
* a tab (MENU_TYPE_TABS) or a dropdown (MENU_TYPE_DROPDOWN) * a tab (MENU_TYPE_TABS) or a dropdown (MENU_TYPE_DROPDOWN)
@ -359,7 +350,7 @@ class SkinVector extends SkinMustache {
int $type = self::MENU_TYPE_DEFAULT, int $type = self::MENU_TYPE_DEFAULT,
bool $setLabelToSelected = false bool $setLabelToSelected = false
) : array { ) : array {
$skin = $this->getSkin(); $portletData = $this->getPortletData( $label, $urls );
$extraClasses = [ $extraClasses = [
self::MENU_TYPE_DROPDOWN => 'vector-menu vector-menu-dropdown vectorMenu', self::MENU_TYPE_DROPDOWN => 'vector-menu vector-menu-dropdown vectorMenu',
self::MENU_TYPE_TABS => 'vector-menu vector-menu-tabs vectorTabs', self::MENU_TYPE_TABS => 'vector-menu vector-menu-tabs vectorTabs',
@ -374,24 +365,15 @@ class SkinVector extends SkinMustache {
]; ];
$isPortal = $type === self::MENU_TYPE_PORTAL; $isPortal = $type === self::MENU_TYPE_PORTAL;
// For some menu items, there is no language key corresponding with its menu key. $props = $portletData + [
// These inconsitencies are captured in MENU_LABEL_KEYS
$msgObj = $skin->msg( self::MENU_LABEL_KEYS[ $label ] ?? $label );
$props = [
'id' => "p-$label",
'label-id' => "p-{$label}-label", 'label-id' => "p-{$label}-label",
// If no message exists fallback to plain text (T252727)
'label' => $msgObj->exists() ? $msgObj->text() : $label,
'list-classes' => $listClasses[$type] ?? 'vector-menu-content-list', 'list-classes' => $listClasses[$type] ?? 'vector-menu-content-list',
'html-items' => '',
'is-dropdown' => $type === self::MENU_TYPE_DROPDOWN, 'is-dropdown' => $type === self::MENU_TYPE_DROPDOWN,
'html-tooltip' => Linker::tooltip( 'p-' . $label ),
]; ];
// Special casing for Variant to change label to selected.
// Hopefully we can revisit and possibly remove this code when the language switcher is moved.
foreach ( $urls as $key => $item ) { foreach ( $urls as $key => $item ) {
$props['html-items'] .= $this->getSkin()->makeListItem( $key, $item );
// Check the class of the item for a `selected` class and if so, propagate the items
// label to the main label.
if ( $setLabelToSelected ) { if ( $setLabelToSelected ) {
if ( isset( $item['class'] ) && stripos( $item['class'], 'selected' ) !== false ) { if ( isset( $item['class'] ) && stripos( $item['class'], 'selected' ) !== false ) {
$props['label'] = $item['text']; $props['label'] = $item['text'];
@ -399,27 +381,8 @@ class SkinVector extends SkinMustache {
} }
} }
$afterPortal = '';
if ( $isPortal ) {
// The BaseTemplate::getAfterPortlet method ran the SkinAfterPortlet
// hook and if content is added appends it to the html-after-portal method.
// This replicates that historic behaviour.
// This code should eventually be upstreamed to SkinMustache in core.
// Currently in production this supports the Wikibase 'edit' link.
$content = $this->getAfterPortlet( $label );
if ( $content !== '' ) {
$afterPortal = Html::rawElement(
'div',
[ 'class' => [ 'after-portlet', 'after-portlet-' . $label ] ],
$content
);
}
}
$props['html-after-portal'] = $afterPortal;
// Mark the portal as empty if it has no content // Mark the portal as empty if it has no content
$class = ( count( $urls ) == 0 && !$props['html-after-portal'] ) $class = $props['class'];
? 'vector-menu-empty emptyPortlet' : '';
$props['class'] = trim( "$class $extraClasses[$type]" ); $props['class'] = trim( "$class $extraClasses[$type]" );
return $props; return $props;
} }

View file

@ -6,6 +6,19 @@ type MwApiConstructor = new( options?: Object ) => MwApi;
interface MediaWiki { interface MediaWiki {
util: { util: {
/**
* @param {string} id of portlet
*/
showPortlet( id: string ): () => void;
/**
* @param {string} id of portlet
*/
hidePortlet( id: string ): () => void;
/**
* @param {string} id of portlet
* @return {bool}
*/
isPortletVisible( id: string ): () => boolean,
/** /**
* Return a wrapper function that is debounced for the given duration. * Return a wrapper function that is debounced for the given duration.
* *

View file

@ -2,8 +2,8 @@
* Collapsible tabs for Vector * Collapsible tabs for Vector
*/ */
function init() { function init() {
// eslint-disable-next-line no-jquery/no-global-selector var cactionsId = 'p-cactions',
var $cactions = $( '#p-cactions' ), $cactions = $( '#' + cactionsId ),
// eslint-disable-next-line no-jquery/no-global-selector // eslint-disable-next-line no-jquery/no-global-selector
$tabContainer = $( '#p-views ul' ), $tabContainer = $( '#p-views ul' ),
initialCactionsWidth = function () { initialCactionsWidth = function () {
@ -27,9 +27,8 @@ function init() {
.on( 'beforeTabCollapse', function () { .on( 'beforeTabCollapse', function () {
var expandedWidth; var expandedWidth;
// If the dropdown was hidden, show it // If the dropdown was hidden, show it
// eslint-disable-next-line no-jquery/no-class-state if ( mw.util.isPortletVisible( cactionsId ) ) {
if ( $cactions.hasClass( 'emptyPortlet' ) ) { mw.util.showPortlet( cactionsId );
$cactions.removeClass( 'emptyPortlet vector-menu-empty' );
// Now that it is visible, force-render it virtually // Now that it is visible, force-render it virtually
// to get its expanded width, then shrink it 1px before we // to get its expanded width, then shrink it 1px before we
// yield from JS (which means the expansion won't be visible). // yield from JS (which means the expansion won't be visible).
@ -46,8 +45,8 @@ function init() {
if ( $cactions.find( 'li' ).length === 1 ) { if ( $cactions.find( 'li' ).length === 1 ) {
// eslint-disable-next-line no-jquery/no-animate // eslint-disable-next-line no-jquery/no-animate
$cactions.find( 'h3' ).animate( { width: '1px' }, 'normal', function () { $cactions.find( 'h3' ).animate( { width: '1px' }, 'normal', function () {
$( this ).attr( 'style', '' ) $( this ).attr( 'style', '' );
.parent().addClass( 'emptyPortlet vector-menu-empty' ); mw.util.hidePortlet( cactionsId );
} ); } );
} }
} ) } )
@ -85,8 +84,7 @@ function init() {
} }
// Always collapse if the "More" button is already shown. // Always collapse if the "More" button is already shown.
// eslint-disable-next-line no-jquery/no-class-state if ( mw.util.isPortletVisible( cactionsId ) ) {
if ( !$cactions.hasClass( 'emptyPortlet' ) ) {
return true; return true;
} }

View file

@ -1,18 +1,6 @@
@import '../../variables.less'; @import '../../variables.less';
@import 'mediawiki.mixins.less'; @import 'mediawiki.mixins.less';
/* Hide empty portlets */
// This class has special magical powers. It can be removed by
// mw.util.addPortletLink
// in core:resources/src/mediawiki.util/util.js
// When I93fb6c96df9f238d1c0281cb66512b135ca2afc2 has been merged
// and is deployed this line of CSS should be removed or replaced
// with the class ".vector-menu-empty"
// See T253912.
.emptyPortlet {
display: none;
}
/* Personal */ /* Personal */
.vector-menu { .vector-menu {
// Hidden by default, but displayed by certain menus // Hidden by default, but displayed by certain menus

View file

@ -76,15 +76,18 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
$this->assertSame( $this->assertSame(
[ [
// Provided by core
'id' => 'p-views', 'id' => 'p-views',
'label-id' => 'p-views-label', 'class' => 'mw-portlet mw-portlet-views emptyPortlet vector-menu vector-menu-tabs vectorTabs',
'label' => $context->msg( 'views' )->text(),
'list-classes' => 'vector-menu-content-list',
'html-items' => '',
'is-dropdown' => false,
'html-tooltip' => '', 'html-tooltip' => '',
'html-items' => '',
'html-after-portal' => '', 'html-after-portal' => '',
'class' => 'vector-menu-empty emptyPortlet vector-menu vector-menu-tabs vectorTabs', 'label' => $context->msg( 'views' )->text(),
// provided by VECTOR
'label-id' => 'p-views-label',
'list-classes' => 'vector-menu-content-list',
'is-dropdown' => false,
], ],
$views $views
); );
@ -92,19 +95,19 @@ class SkinVectorTest extends MediaWikiIntegrationTestCase {
$variants = $props['data-variants']; $variants = $props['data-variants'];
$actions = $props['data-page-actions-more']; $actions = $props['data-page-actions-more'];
$this->assertSame( $this->assertSame(
'vector-menu-empty emptyPortlet vector-menu vector-menu-tabs vectorTabs', 'mw-portlet mw-portlet-namespaces emptyPortlet vector-menu vector-menu-tabs vectorTabs',
$namespaces['class'] $namespaces['class']
); );
$this->assertSame( $this->assertSame(
'vector-menu-empty emptyPortlet vector-menu vector-menu-dropdown vectorMenu', 'mw-portlet mw-portlet-variants emptyPortlet vector-menu vector-menu-dropdown vectorMenu',
$variants['class'] $variants['class']
); );
$this->assertSame( $this->assertSame(
'vector-menu-empty emptyPortlet vector-menu vector-menu-dropdown vectorMenu', 'mw-portlet mw-portlet-cactions emptyPortlet vector-menu vector-menu-dropdown vectorMenu',
$actions['class'] $actions['class']
); );
$this->assertSame( $this->assertSame(
'vector-menu-empty emptyPortlet vector-menu', 'mw-portlet mw-portlet-personal emptyPortlet vector-menu',
$props['data-personal-menu']['class'] $props['data-personal-menu']['class']
); );
} }