Add keyboard navigation and accessibility to context menus
This commit makes the Contextual library menus fully keyboard accessible. Previously, context menus (user menu and chat message menu) were only usable with a mouse. Changes: - Add makeContextualMenuAccessible() helper function * Adds ARIA role="menu" to menu container * Adds role="menuitem" to each menu item * Makes menu items focusable with tabindex * Automatically focuses first menu item when opened - Add keyboard navigation support * Arrow Up/Down: Navigate between menu items * Enter/Space: Activate selected menu item * Escape: Close menu - Apply to both context menus * User menu (click/Enter on username) * Chat message menu (double-click on message) - Focus management * First item automatically focused when menu opens * Roving tabindex for efficient keyboard navigation * Menu closes and returns focus after activation This makes context menus fully accessible to keyboard and screen reader users. They can now navigate menus with arrow keys and activate items with Enter or Space.
This commit is contained in:
@@ -2535,6 +2535,53 @@ document.getElementById('invite-dialog').onclose = function(e) {
|
||||
makeToken(template);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds keyboard navigation to Contextual menu
|
||||
* @param {HTMLElement} menuElement
|
||||
*/
|
||||
function makeContextualMenuAccessible(menuElement) {
|
||||
// Find all menu items
|
||||
let menuItems = menuElement.querySelectorAll('.contextualMenuItem:not(.disabled)');
|
||||
if(menuItems.length === 0) return;
|
||||
|
||||
let currentIndex = 0;
|
||||
|
||||
// Make menu items focusable and add ARIA
|
||||
menuItems.forEach((item, index) => {
|
||||
item.setAttribute('role', 'menuitem');
|
||||
item.setAttribute('tabindex', index === 0 ? '0' : '-1');
|
||||
});
|
||||
|
||||
// Focus first item
|
||||
menuItems[0].focus();
|
||||
|
||||
// Add keyboard navigation
|
||||
menuElement.addEventListener('keydown', function(e) {
|
||||
if(e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
contextualCore.CloseMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
if(e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
menuItems[currentIndex].setAttribute('tabindex', '-1');
|
||||
currentIndex = (currentIndex + 1) % menuItems.length;
|
||||
menuItems[currentIndex].setAttribute('tabindex', '0');
|
||||
menuItems[currentIndex].focus();
|
||||
} else if(e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
menuItems[currentIndex].setAttribute('tabindex', '-1');
|
||||
currentIndex = currentIndex === 0 ? menuItems.length - 1 : currentIndex - 1;
|
||||
menuItems[currentIndex].setAttribute('tabindex', '0');
|
||||
menuItems[currentIndex].focus();
|
||||
} else if(e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
menuItems[currentIndex].click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} elt
|
||||
*/
|
||||
@@ -2598,6 +2645,15 @@ function userMenu(elt) {
|
||||
new Contextual({
|
||||
items: items,
|
||||
});
|
||||
|
||||
// Add keyboard navigation after menu is created
|
||||
setTimeout(() => {
|
||||
let menu = document.querySelector('.contextualMenu');
|
||||
if(menu) {
|
||||
menu.setAttribute('role', 'menu');
|
||||
makeContextualMenuAccessible(menu);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3482,6 +3538,15 @@ function chatMessageMenu(elt) {
|
||||
new Contextual({
|
||||
items: items,
|
||||
});
|
||||
|
||||
// Add keyboard navigation after menu is created
|
||||
setTimeout(() => {
|
||||
let menu = document.querySelector('.contextualMenu');
|
||||
if(menu) {
|
||||
menu.setAttribute('role', 'menu');
|
||||
makeContextualMenuAccessible(menu);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user