mediawiki-skins-Vector/resources/skins.vector.js/checkbox.js
bwang e1d1de809f Use JS to move the ToC into page title to fix a11y issues and simplify styles
- The collapsible ToC absolute positioning solution has been preserved to be used on no-js mobile resolutions
- The collapsible ToC grid based positioning solution has been removed
- ToC styles have been refactored and organzied

This patch involves HTML changes. In order to avoid additional complexity, this patch disables the collapsible ToC feature for users above the mobile viewport with cached HTML. The ToC continues to be automatically collapsed on mobile viewports for cached HTML users.

This patch results in 9 expected visual changes. In order to see them you need to take the following steps:
1. Run `./pixel.js reference`
2. Checkout the following PR in pixel https://github.com/wikimedia/pixel/pull/149
3. Run ./pixel.js test -c 859143
It should look like this: https://jmp.sh/ZVQqDZw7

Bug: T318013
Change-Id: Iea0d73005b91589c58ae38a3a640fa90c18a860d
2022-12-05 17:24:55 -06:00

125 lines
3.7 KiB
JavaScript

/**
* JavaScript enhancement for Vector specific checkbox hacks
*
* Most checkbox hacks use core JS for progressive enhancements (i.e. dropdownMenus.js),
* However the main menu and collapsible TOC use a variation of the checkbox hack
* that requires their own JS for enhancements.
*
*/
/** @interface MwApiConstructor */
/** @interface CheckboxHack */
var checkboxHack = /** @type {CheckboxHack} */ require( /** @type {string} */( 'mediawiki.page.ready' ) ).checkboxHack;
/**
* 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 main menu by binding checkbox hack enhancements.
*
* @param {HTMLElement|null} checkbox
* @param {HTMLElement|null} button
* @param {HTMLElement|null} target
* @return {void}
*/
function initMainMenu( checkbox, button, target ) {
if ( checkbox instanceof HTMLInputElement && button && target ) {
checkboxHack.bindToggleOnClick( checkbox, button );
bindUpdateAriaExpandedOnInput( checkbox, button );
updateAriaExpanded( checkbox, button );
bindToggleOnSpaceEnter( button );
}
}
/**
* Initialize main menu and collapsed TOC enhancements.
*
* @param {Document} document
*/
function init( document ) {
initMainMenu(
document.getElementById( 'mw-sidebar-checkbox' ),
document.getElementById( 'mw-sidebar-button' ),
document.getElementById( 'mw-navigation' )
);
}
module.exports = {
init: init
};