Use core checkbox hack consistently for all dropdown and provide custom JS enhancements for the sidebar button
Depends-on: Id74a80cda6cf460cc2b579269b8d5b2ce81c8ca5 Change-Id: Ibd9518dab78d1d9a541b501d920fe3bd4d362093
This commit is contained in:
parent
af2b4a0da4
commit
80141b61c9
|
@ -4,18 +4,14 @@ var
|
|||
checkboxHack = /** @type {CheckboxHack} */ require( /** @type {string} */( 'mediawiki.page.ready' ) ).checkboxHack,
|
||||
CHECKBOX_HACK_CONTAINER_SELECTOR = '.vector-menu-dropdown',
|
||||
CHECKBOX_HACK_CHECKBOX_SELECTOR = '.vector-menu-checkbox',
|
||||
// In core's checkboxHack.js, it is recommended to use a label element as a
|
||||
// button that toggles the checkbox. In Vector's dropdown menus that use the
|
||||
// Menu.mustache template, a checkbox is used as both the "button" and the
|
||||
// "checkbox".
|
||||
CHECKBOX_HACK_BUTTON_SELECTOR = '.vector-menu-checkbox',
|
||||
CHECKBOX_HACK_BUTTON_SELECTOR = '.vector-menu-heading',
|
||||
CHECKBOX_HACK_TARGET_SELECTOR = '.vector-menu-content';
|
||||
|
||||
/**
|
||||
* Add the ability for users to toggle dropdown menus using the enter key (as
|
||||
* well as space) using core's checkboxHack.
|
||||
*/
|
||||
function bindToggleOnSpaceEnter() {
|
||||
function bind() {
|
||||
// Search for all dropdown containers using the CHECKBOX_HACK_CONTAINER_SELECTOR.
|
||||
var containers = document.querySelectorAll( CHECKBOX_HACK_CONTAINER_SELECTOR );
|
||||
|
||||
|
@ -29,7 +25,7 @@ function bindToggleOnSpaceEnter() {
|
|||
return;
|
||||
}
|
||||
|
||||
checkboxHack.bindToggleOnSpaceEnter( checkbox, button );
|
||||
checkboxHack.bind( window, checkbox, button, target );
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -46,20 +42,6 @@ function bindCloseOnUnload() {
|
|||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that clicking outside a menu closes it.
|
||||
*/
|
||||
function closeDropdownsOnClickOutside() {
|
||||
$( document.body ).on( 'click', function ( ev ) {
|
||||
var $closestPortlet = $( ev.target ).closest( '.mw-portlet' );
|
||||
// Uncheck (close) any menus that are open.
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
$( '.vector-menu-checkbox:checked' ).not(
|
||||
$closestPortlet.find( '.vector-menu-checkbox' )
|
||||
).prop( 'checked', false );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds icon placeholder for gadgets to use.
|
||||
*
|
||||
|
@ -110,7 +92,6 @@ Array.prototype.forEach.call(
|
|||
mw.hook( 'util.addPortletLink' ).add( addPortletLinkHandler );
|
||||
|
||||
module.exports = function dropdownMenus() {
|
||||
closeDropdownsOnClickOutside();
|
||||
bindToggleOnSpaceEnter();
|
||||
bind();
|
||||
bindCloseOnUnload();
|
||||
};
|
||||
|
|
|
@ -15,15 +15,91 @@
|
|||
/** @interface CheckboxHack */
|
||||
/** @interface MwApi */
|
||||
|
||||
/** @type {CheckboxHack} */ var checkboxHack =
|
||||
require( /** @type {string} */( 'mediawiki.page.ready' ) ).checkboxHack;
|
||||
var SIDEBAR_BUTTON_ID = 'mw-sidebar-button',
|
||||
var checkboxHack = /** @type {CheckboxHack} */ require( /** @type {string} */( 'mediawiki.page.ready' ) ).checkboxHack,
|
||||
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;
|
||||
|
||||
/**
|
||||
* Revise the button's `aria-expanded` state to match the checked state.
|
||||
*
|
||||
* @param {HTMLInputElement} checkbox
|
||||
* @param {HTMLElement} button
|
||||
* @return {void}
|
||||
* @ignore
|
||||
*/
|
||||
function updateAriaExpanded( checkbox, button ) {
|
||||
button.setAttribute( 'aria-expanded', checkbox.checked.toString() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the `aria-expanded` attribute based on checkbox state (target visibility) changes.
|
||||
*
|
||||
* @param {HTMLInputElement} checkbox
|
||||
* @param {HTMLElement} button
|
||||
* @return {function(): void} Cleanup function that removes the added event listeners.
|
||||
* @ignore
|
||||
*/
|
||||
function bindUpdateAriaExpandedOnInput( checkbox, button ) {
|
||||
var listener = updateAriaExpanded.bind( undefined, checkbox, button );
|
||||
// Whenever the checkbox state changes, update the `aria-expanded` state.
|
||||
checkbox.addEventListener( 'input', listener );
|
||||
|
||||
return function () {
|
||||
checkbox.removeEventListener( 'input', listener );
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually change the checkbox state when the button is focused and SPACE is pressed.
|
||||
*
|
||||
* @param {HTMLElement} button
|
||||
* @return {function(): void} Cleanup function that removes the added event listeners.
|
||||
* @ignore
|
||||
*/
|
||||
function bindToggleOnSpaceEnter( button ) {
|
||||
function isEnterOrSpace( /** @type {KeyboardEvent} */ event ) {
|
||||
return event.key === ' ' || event.key === 'Enter';
|
||||
}
|
||||
|
||||
function onKeydown( /** @type {KeyboardEvent} */ event ) {
|
||||
// Only handle SPACE and ENTER.
|
||||
if ( !isEnterOrSpace( event ) ) {
|
||||
return;
|
||||
}
|
||||
// Prevent the browser from scrolling when pressing space. The browser will
|
||||
// try to do this unless the "button" element is a button or a checkbox.
|
||||
// Depending on the actual "button" element, this also possibly prevents a
|
||||
// native click event from being triggered so we programatically trigger a
|
||||
// click event in the keyup handler.
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function onKeyup( /** @type {KeyboardEvent} */ event ) {
|
||||
// Only handle SPACE and ENTER.
|
||||
if ( !isEnterOrSpace( event ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A native button element triggers a click event when the space or enter
|
||||
// keys are pressed. Since the passed in "button" may or may not be a
|
||||
// button, programmatically trigger a click event to make it act like a
|
||||
// button.
|
||||
button.click();
|
||||
}
|
||||
|
||||
button.addEventListener( 'keydown', onKeydown );
|
||||
button.addEventListener( 'keyup', onKeyup );
|
||||
|
||||
return function () {
|
||||
button.removeEventListener( 'keydown', onKeydown );
|
||||
button.removeEventListener( 'keyup', onKeyup );
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -35,9 +111,9 @@ var debounce = require( /** @type {string} */ ( 'mediawiki.util' ) ).debounce;
|
|||
function initCheckboxHack( checkbox, button ) {
|
||||
if ( checkbox instanceof HTMLInputElement && button ) {
|
||||
checkboxHack.bindToggleOnClick( checkbox, button );
|
||||
checkboxHack.bindUpdateAriaExpandedOnInput( checkbox, button );
|
||||
checkboxHack.updateAriaExpanded( checkbox, button );
|
||||
checkboxHack.bindToggleOnSpaceEnter( checkbox, button );
|
||||
bindUpdateAriaExpandedOnInput( checkbox, button );
|
||||
updateAriaExpanded( checkbox, button );
|
||||
bindToggleOnSpaceEnter( button );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue