Sidebar persistence for logged-in users in modern Vector.
- Creates a new user-preference called 'VectorSidebarVisible' which stores the sidebar hidden/collapsed state for logged-in users. - Updates that user-preference on the client whenever the sidebar is expanded or collapsed. - Refactors the sidebar related javascript into a separate file. Bug: T255727 Change-Id: Ib1ce934f3646cd8feebf0d3b15c38b5b969ec957
This commit is contained in:
parent
36a1516f96
commit
1fac82f895
|
@ -77,6 +77,11 @@ final class Constants {
|
|||
*/
|
||||
public const PREF_KEY_SKIN_VERSION = 'VectorSkinVersion';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const PREF_KEY_SIDEBAR_VISIBLE = 'VectorSidebarVisible';
|
||||
|
||||
// These are used in the Feature Management System.
|
||||
/**
|
||||
* Also known as `$wgFullyInitialised`. Set to true in core/includes/Setup.php.
|
||||
|
|
|
@ -129,6 +129,10 @@ class Hooks {
|
|||
// this state to determine whether to show or hide the whole section.
|
||||
'hide-if' => [ '!==', 'wpskin', Constants::SKIN_NAME ]
|
||||
],
|
||||
Constants::PREF_KEY_SIDEBAR_VISIBLE => [
|
||||
'type' => 'api',
|
||||
'default' => self::getConfig( Constants::CONFIG_KEY_DEFAULT_SIDEBAR_VISIBLE_FOR_AUTHORISED_USER )
|
||||
],
|
||||
];
|
||||
|
||||
// Seek the skin preference section to add Vector preferences just below it.
|
||||
|
|
|
@ -171,7 +171,6 @@ class VectorTemplate extends BaseTemplate {
|
|||
'main-page-href' => $mainPageHref,
|
||||
|
||||
'data-sidebar' => $this->buildSidebar(),
|
||||
// [todo] fetch user preference when logged in (T246427).
|
||||
'sidebar-visible' => $this->isSidebarVisible(),
|
||||
'msg-vector-action-toggle-sidebar' => $this->msg( 'vector-action-toggle-sidebar' )->text(),
|
||||
] + $this->getMenuProps();
|
||||
|
@ -269,9 +268,19 @@ class VectorTemplate extends BaseTemplate {
|
|||
private function isSidebarVisible() {
|
||||
$skin = $this->getSkin();
|
||||
if ( $skin->getUser()->isLoggedIn() ) {
|
||||
return $this->getConfig()->get(
|
||||
$userPrefSidebarState = $skin->getUser()->getOption(
|
||||
Constants::PREF_KEY_SIDEBAR_VISIBLE
|
||||
);
|
||||
|
||||
$defaultLoggedinSidebarState = $this->getConfig()->get(
|
||||
Constants::CONFIG_KEY_DEFAULT_SIDEBAR_VISIBLE_FOR_AUTHORISED_USER
|
||||
);
|
||||
|
||||
// If the sidebar user preference has been set, return that value,
|
||||
// if not, then the default sidebar state for logged-in users.
|
||||
return ( $userPrefSidebarState !== null )
|
||||
? (bool)$userPrefSidebarState
|
||||
: $defaultLoggedinSidebarState;
|
||||
}
|
||||
return $this->getConfig()->get(
|
||||
Constants::CONFIG_KEY_DEFAULT_SIDEBAR_VISIBLE_FOR_ANONYMOUS_USER
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
interface MwApi {
|
||||
saveOption( name: string, value: unknown ): JQuery.Promise<any>;
|
||||
}
|
||||
|
||||
type MwApiConstructor = new( options?: Object ) => MwApi;
|
||||
|
||||
interface MediaWiki {
|
||||
util: {
|
||||
/**
|
||||
|
@ -15,6 +21,10 @@ interface MediaWiki {
|
|||
*/
|
||||
debounce(delay: number, callback: Function): () => void;
|
||||
};
|
||||
Api: MwApiConstructor;
|
||||
config: {
|
||||
get( configKey: string|null ): string;
|
||||
}
|
||||
}
|
||||
|
||||
declare const mw: MediaWiki;
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* Javascsript enhancement to the collapsible sidebar.
|
||||
*
|
||||
* The sidebar provides basic show/hide functionality with CSS
|
||||
* but Javacript is used for progressive enhancements.
|
||||
*
|
||||
* JS sidebar enhancements include:
|
||||
* - Update aria-roles based on expanded/collapsed state.
|
||||
* - Update button icon based on expanded/collapsed state.
|
||||
* - Persist the sidebar state for logged-in users.
|
||||
*
|
||||
*/
|
||||
|
||||
/** @interface MwApiConstructor */
|
||||
/** @interface CheckboxHack */
|
||||
/** @interface MwApi */
|
||||
|
||||
/** @type {CheckboxHack} */ var checkboxHack =
|
||||
require( /** @type {string} */( 'mediawiki.page.ready' ) ).checkboxHack;
|
||||
var SIDEBAR_BUTTON_ID = 'mw-sidebar-button',
|
||||
SIDEBAR_CHECKBOX_ID = 'mw-sidebar-checkbox',
|
||||
SIDEBAR_PREFERENCE_NAME = 'VectorSidebarVisible';
|
||||
|
||||
var debounce = require( /** @type {string} */ ( 'mediawiki.util' ) ).debounce;
|
||||
/** @type {MwApi} */ var api;
|
||||
|
||||
/**
|
||||
* Improve the interactivity of the sidebar panel by binding optional checkbox hack enhancements
|
||||
* for focus and `aria-expanded`. Also, flip the icon image on click.
|
||||
*
|
||||
* @param {HTMLElement|null} checkbox
|
||||
* @param {HTMLElement|null} button
|
||||
* @return {void}
|
||||
*/
|
||||
function initCheckboxHack( checkbox, button ) {
|
||||
if ( checkbox instanceof HTMLInputElement && button ) {
|
||||
checkboxHack.bindToggleOnClick( checkbox, button );
|
||||
checkboxHack.bindUpdateAriaExpandedOnInput( checkbox, button );
|
||||
checkboxHack.updateAriaExpanded( checkbox, button );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a debounced API request to save the sidebar user preference.
|
||||
* The request is meant to fires 1 second after the last click on
|
||||
* the sidebar button.
|
||||
*
|
||||
* @param {HTMLInputElement} checkbox
|
||||
* @return {any}
|
||||
*/
|
||||
function saveSidebarState( checkbox ) {
|
||||
return debounce( 1000, function () {
|
||||
api = api || new mw.Api();
|
||||
api.saveOption( SIDEBAR_PREFERENCE_NAME, checkbox.checked ? 1 : 0 );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the event handler that saves the sidebar state to the click event
|
||||
* on the sidebar button.
|
||||
*
|
||||
* @param {HTMLElement|null} checkbox
|
||||
* @param {HTMLElement|null} button
|
||||
*/
|
||||
function bindSidebarClickEvent( checkbox, button ) {
|
||||
if ( checkbox instanceof HTMLInputElement && button ) {
|
||||
button.addEventListener( 'click', saveSidebarState( checkbox ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all JS sidebar enhancements.
|
||||
*
|
||||
* @param {Window} window
|
||||
*/
|
||||
function init( window ) {
|
||||
var checkbox = window.document.getElementById( SIDEBAR_CHECKBOX_ID ),
|
||||
button = window.document.getElementById( SIDEBAR_BUTTON_ID );
|
||||
|
||||
initCheckboxHack( checkbox, button );
|
||||
|
||||
if ( mw.config.get( 'wgUserName' ) ) {
|
||||
bindSidebarClickEvent( checkbox, button );
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: init
|
||||
};
|
|
@ -1,11 +1,6 @@
|
|||
/**
|
||||
* @external CheckboxHack
|
||||
*/
|
||||
|
||||
/** @type {CheckboxHack} */ var checkboxHack =
|
||||
require( /** @type {string} */ ( 'mediawiki.page.ready' ) ).checkboxHack;
|
||||
var collapsibleTabs = require( '../skins.vector.legacy.js/collapsibleTabs.js' );
|
||||
var vector = require( '../skins.vector.legacy.js/vector.js' );
|
||||
var collapsibleTabs = require( '../skins.vector.legacy.js/collapsibleTabs.js' ),
|
||||
vector = require( '../skins.vector.legacy.js/vector.js' ),
|
||||
sidebar = require( './sidebar.js' );
|
||||
|
||||
/**
|
||||
* Wait for first paint before calling this function. That's its whole purpose.
|
||||
|
@ -39,24 +34,6 @@ function enableCssAnimations( document ) {
|
|||
document.documentElement.classList.add( 'vector-animations-ready' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Improve the interactivity of the sidebar panel by binding optional checkbox hack enhancements
|
||||
* for focus and `aria-expanded`. Also, flip the icon image on click.
|
||||
*
|
||||
* @param {Document} document
|
||||
* @return {void}
|
||||
*/
|
||||
function initSidebar( document ) {
|
||||
var checkbox = document.getElementById( 'mw-sidebar-checkbox' );
|
||||
var button = document.getElementById( 'mw-sidebar-button' );
|
||||
if ( checkbox instanceof HTMLInputElement && button ) {
|
||||
checkboxHack.bindToggleOnClick( checkbox, button );
|
||||
checkboxHack.bindUpdateAriaExpandedOnInput( checkbox, button );
|
||||
|
||||
checkboxHack.updateAriaExpanded( checkbox, button );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Window} window
|
||||
* @return {void}
|
||||
|
@ -64,8 +41,8 @@ function initSidebar( document ) {
|
|||
function main( window ) {
|
||||
enableCssAnimations( window.document );
|
||||
collapsibleTabs.init();
|
||||
sidebar.init( window );
|
||||
$( vector.init );
|
||||
initSidebar( window.document );
|
||||
}
|
||||
|
||||
main( window );
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
"skins.vector.js": {
|
||||
"packageFiles": [
|
||||
"resources/skins.vector.js/skin.js",
|
||||
"resources/skins.vector.js/sidebar.js",
|
||||
"resources/skins.vector.legacy.js/collapsibleTabs.js",
|
||||
"resources/skins.vector.legacy.js/vector.js"
|
||||
],
|
||||
|
|
|
@ -37,6 +37,7 @@ class VectorHooksTest extends \MediaWikiTestCase {
|
|||
'VectorShowSkinPreferences' => true,
|
||||
// '1' is Legacy.
|
||||
'VectorDefaultSkinVersionForExistingAccounts' => '1',
|
||||
'VectorDefaultSidebarVisibleForAuthorisedUser' => true
|
||||
] );
|
||||
$this->setService( 'Vector.Config', $config );
|
||||
|
||||
|
@ -60,6 +61,10 @@ class VectorHooksTest extends \MediaWikiTestCase {
|
|||
'default' => '1',
|
||||
'hide-if' => [ '!==', 'wpskin', 'vector' ]
|
||||
],
|
||||
'VectorSidebarVisible' => [
|
||||
'type' => 'api',
|
||||
'default' => true
|
||||
],
|
||||
'bar' => []
|
||||
],
|
||||
'Preferences are inserted directly after skin.'
|
||||
|
@ -74,6 +79,7 @@ class VectorHooksTest extends \MediaWikiTestCase {
|
|||
'VectorShowSkinPreferences' => true,
|
||||
// '1' is Legacy.
|
||||
'VectorDefaultSkinVersionForExistingAccounts' => '1',
|
||||
'VectorDefaultSidebarVisibleForAuthorisedUser' => true
|
||||
] );
|
||||
$this->setService( 'Vector.Config', $config );
|
||||
|
||||
|
@ -96,6 +102,10 @@ class VectorHooksTest extends \MediaWikiTestCase {
|
|||
'default' => '1',
|
||||
'hide-if' => [ '!==', 'wpskin', 'vector' ]
|
||||
],
|
||||
'VectorSidebarVisible' => [
|
||||
'type' => 'api',
|
||||
'default' => true
|
||||
],
|
||||
],
|
||||
'Preferences are appended.'
|
||||
);
|
||||
|
@ -109,6 +119,7 @@ class VectorHooksTest extends \MediaWikiTestCase {
|
|||
'VectorShowSkinPreferences' => true,
|
||||
// '2' is latest.
|
||||
'VectorDefaultSkinVersionForExistingAccounts' => '2',
|
||||
'VectorDefaultSidebarVisibleForAuthorisedUser' => true
|
||||
] );
|
||||
$this->setService( 'Vector.Config', $config );
|
||||
|
||||
|
@ -131,6 +142,10 @@ class VectorHooksTest extends \MediaWikiTestCase {
|
|||
'default' => '0',
|
||||
'hide-if' => [ '!==', 'wpskin', 'vector' ]
|
||||
],
|
||||
'VectorSidebarVisible' => [
|
||||
'type' => 'api',
|
||||
'default' => true
|
||||
],
|
||||
],
|
||||
'Legacy skin version is disabled.'
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue