vpat 7,14: popups count and cursor fixes

- vpat_7: when a popup is showing, mark menuseparators with
role="presentation" to clear whatever semantics a separator
has. It prevents the screen readers from counting it as
an interactable element while announcing the index of
a menuitem within the popup. It also makes the screen readers
count items across all sections, instead of stopping at
the first separator.
- - vpat_14: when menupopup is hiding, delete it and
immediately re-insert back into the DOM. It forces voiceover
to move cursor back to the focused element. Otherwise, it
is not aware that the popup went away, the cursor gets
stuck inside of the invisible menu, no elements
will be announced until the screen readers is reloaded.
This commit is contained in:
Bogdan Abaev 2024-07-22 10:19:58 -07:00
parent 192ea54de0
commit ff6d1886b7

View file

@ -114,6 +114,15 @@ Services.scriptloader.loadSubScript('chrome://zotero/content/elements/itemPaneSe
);
this.toggleAttribute("needsgutter", haveCheckableChild);
// Clear whatever aria semantics the separator has so it is not counted when
// voiceover lists how many menuitems a menu has.
let clearSeparatorAriaSemantics = () => {
for (let separator of [...this.querySelectorAll("menuseparator")]) {
separator.setAttribute("role", "presentation");
}
};
clearSeparatorAriaSemantics();
/**
* Add fade animation to the popup
* animate="false" will disable the animation
@ -164,6 +173,28 @@ Services.scriptloader.loadSubScript('chrome://zotero/content/elements/itemPaneSe
}, 200);
});
this.addEventListener("popupshowing", clearSeparatorAriaSemantics);
// If a menu closes with voiceover cursor in it, the cursor gets stuck in no-longer-visible
// menu and voiceover will be quiet until it is restarted. As a workaround, remove the menu
// from DOM to force voiceover to move its cursor and insert it back after a small delay;
this.addEventListener("popuphidden", (e) => {
if (this !== e.target || this.parentNode?.closest("menupopup")) {
return;
}
let parent = this.parentNode;
let sibling = this.nextSibling;
this.remove();
setTimeout(() => {
if (sibling) {
parent.insertBefore(this, sibling);
}
else {
parent.appendChild(this);
}
});
});
// This event is triggered after clicking the menu and before popuphiding
// where we control whether the fade out animation should run
this.addEventListener("command", () => {