mirror of
https://github.com/simonw/datasette.git
synced 2026-06-02 07:07:00 +02:00
Keyboard navigation and ARIA attributes for actions menus (#2727)
- Add aria-haspopup="menu" and aria-expanded to summary element - Add role="menu" to dropdown ul, role="menuitem" and tabindex="-1" to links - Sync aria-expanded via toggle event listener - Focus first menu item when menu opens - Add ArrowDown/ArrowUp navigation between menu items - Add Escape key to close menu and return focus to summary Refs #2738
This commit is contained in:
parent
d11326b250
commit
312740b97c
2 changed files with 49 additions and 3 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{% if action_links %}
|
||||
<div class="page-action-menu">
|
||||
<details class="actions-menu-links details-menu">
|
||||
<summary>
|
||||
<summary aria-haspopup="menu" aria-expanded="false">
|
||||
<div class="icon-text">
|
||||
<svg class="icon" aria-labelledby="actions-menu-links-title" role="img" style="color: #fff" xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<title id="actions-menu-links-title">{{ action_title }}</title>
|
||||
|
|
@ -13,9 +13,9 @@
|
|||
</summary>
|
||||
<div class="dropdown-menu">
|
||||
<div class="hook"></div>
|
||||
<ul>
|
||||
<ul role="menu">
|
||||
{% for link in action_links %}
|
||||
<li><a href="{{ link.href }}">{{ link.label }}
|
||||
<li role="none"><a href="{{ link.href }}" role="menuitem" tabindex="-1">{{ link.label }}
|
||||
{% if link.description %}
|
||||
<p class="dropdown-description">{{ link.description }}</p>
|
||||
{% endif %}</a>
|
||||
|
|
|
|||
|
|
@ -13,4 +13,50 @@ document.body.addEventListener('click', (ev) => {
|
|||
(details) => details.open && details != detailsClickedWithin
|
||||
).forEach(details => details.open = false);
|
||||
});
|
||||
|
||||
/* Sync aria-expanded and add keyboard navigation for details-menu elements */
|
||||
document.querySelectorAll('details.details-menu').forEach(function(details) {
|
||||
var summary = details.querySelector('summary');
|
||||
details.addEventListener('toggle', function() {
|
||||
if (summary) {
|
||||
summary.setAttribute('aria-expanded', details.open ? 'true' : 'false');
|
||||
}
|
||||
if (details.open) {
|
||||
/* Focus first menu item when menu opens */
|
||||
var firstItem = details.querySelector('[role="menuitem"]');
|
||||
if (firstItem) { firstItem.focus(); }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.body.addEventListener('keydown', function(ev) {
|
||||
/* Keyboard navigation for open details-menu elements */
|
||||
var openDetails = Array.from(document.querySelectorAll('details.details-menu[open]'));
|
||||
if (!openDetails.length) { return; }
|
||||
|
||||
if (ev.key === 'Escape') {
|
||||
openDetails.forEach(function(details) {
|
||||
details.open = false;
|
||||
var summary = details.querySelector('summary');
|
||||
if (summary) { summary.focus(); }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.key === 'ArrowDown' || ev.key === 'ArrowUp') {
|
||||
var focused = document.activeElement;
|
||||
openDetails.forEach(function(details) {
|
||||
var items = Array.from(details.querySelectorAll('[role="menuitem"]'));
|
||||
if (!items.length) { return; }
|
||||
var idx = items.indexOf(focused);
|
||||
if (idx === -1) { return; }
|
||||
ev.preventDefault();
|
||||
if (ev.key === 'ArrowDown') {
|
||||
items[(idx + 1) % items.length].focus();
|
||||
} else {
|
||||
items[(idx - 1 + items.length) % items.length].focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue