зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1560359 - Add keyboard support for the menu on about:logins. r=sfoster,yzen
Differential Revision: https://phabricator.services.mozilla.com/D35830 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
192e63f9dd
Коммит
a4cce13895
|
@ -149,9 +149,7 @@
|
|||
<ul class="menu" role="menu" hidden>
|
||||
<button role="menuitem" class="menuitem-button menuitem-import alternate-button" hidden data-supported-platforms="Win32" data-event-name="AboutLoginsImport"></button>
|
||||
<button role="menuitem" class="menuitem-button menuitem-preferences alternate-button" data-event-name="AboutLoginsOpenPreferences"></button>
|
||||
</li>
|
||||
<li role="menuitem" class="menuitem">
|
||||
<button class="menuitem-button menuitem-feedback alternate-button" data-event-name="AboutLoginsOpenFeedback"></button>
|
||||
<button role="menuitem" class="menuitem-button menuitem-feedback alternate-button" data-event-name="AboutLoginsOpenFeedback"></button>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -24,7 +24,9 @@ export default class MenuButton extends ReflectedFluentElement {
|
|||
this._menu = this.shadowRoot.querySelector(".menu");
|
||||
this._menuButton = this.shadowRoot.querySelector(".menu-button");
|
||||
|
||||
this.addEventListener("blur", this);
|
||||
this._menuButton.addEventListener("click", this);
|
||||
this.addEventListener("keydown", this, true);
|
||||
|
||||
super.connectedCallback();
|
||||
}
|
||||
|
@ -54,6 +56,15 @@ export default class MenuButton extends ReflectedFluentElement {
|
|||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "blur": {
|
||||
if (event.relatedTarget &&
|
||||
event.relatedTarget.closest(".menu") == this._menu) {
|
||||
// Only hide the menu if focus has left the menu-button.
|
||||
return;
|
||||
}
|
||||
this._hideMenu();
|
||||
break;
|
||||
}
|
||||
case "click": {
|
||||
// Skip the catch-all event listener if it was the menu-button
|
||||
// that was clicked on.
|
||||
|
@ -76,9 +87,40 @@ export default class MenuButton extends ReflectedFluentElement {
|
|||
this._toggleMenu();
|
||||
break;
|
||||
}
|
||||
case "keydown": {
|
||||
this._handleKeyDown(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_handleKeyDown(event) {
|
||||
if (event.key == "Enter") {
|
||||
event.preventDefault();
|
||||
this._toggleMenu();
|
||||
} else if (event.key == "Escape") {
|
||||
this._hideMenu();
|
||||
this._menuButton.focus();
|
||||
}
|
||||
|
||||
if (!event.key.startsWith("Arrow")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let activeMenuitem = this.shadowRoot.activeElement ||
|
||||
this._menu.querySelector(".menuitem-button:not([hidden])");
|
||||
|
||||
let newlyFocusedItem = null;
|
||||
if (event.key == "ArrowDown") {
|
||||
newlyFocusedItem = activeMenuitem.nextElementSibling;
|
||||
} else if (event.key == "ArrowUp") {
|
||||
newlyFocusedItem = activeMenuitem.previousElementSibling;
|
||||
}
|
||||
if (!newlyFocusedItem) {
|
||||
return;
|
||||
}
|
||||
newlyFocusedItem.focus();
|
||||
}
|
||||
|
||||
_hideMenu() {
|
||||
this._menu.hidden = true;
|
||||
document.documentElement.removeEventListener("click", this, true);
|
||||
|
|
|
@ -48,36 +48,58 @@ add_task(async function test_menu_open_close() {
|
|||
is(true, menu.hidden, "menu should be hidden before pressing 'space'");
|
||||
sendKey("SPACE");
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
is(false, menu.hidden, "menu should be visible after pressing 'space'");
|
||||
ok(!menu.hidden, "menu should be visible after pressing 'space'");
|
||||
|
||||
sendKey("ESCAPE");
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
ok(menu.hidden, "menu should be hidden after pressing 'escape'");
|
||||
is(gMenuButton.shadowRoot.activeElement, gMenuButton.shadowRoot.querySelector(".menu-button"),
|
||||
"the .menu-button should be focused after closing the menu via keyboard");
|
||||
|
||||
sendKey("RETURN");
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
ok(!menu.hidden, "menu should be visible after pressing 'return'");
|
||||
|
||||
let firstVisibleItem = gMenuButton.shadowRoot.querySelector(".menuitem-button:not([hidden])");
|
||||
ok(!firstVisibleItem.matches(":focus"), "the first item should not be focused before tabbing to it");
|
||||
sendKey("TAB");
|
||||
await SimpleTest.promiseWaitForCondition(() => firstVisibleItem.matches(":focus"),
|
||||
"waiting for firstVisibleItem to get focus");
|
||||
ok(firstVisibleItem.matches(":focus"), "firstVisibleItem should be focused after tabbing to it");
|
||||
synthesizeKey("VK_TAB", { shiftKey: true });
|
||||
await SimpleTest.promiseWaitForCondition(() => !firstVisibleItem.matches(":focus"),
|
||||
"waiting for firstVisibleItem to lose focus");
|
||||
ok(!firstVisibleItem.matches(":focus"), "firstVisibleItem should lose focus after tabbing away from it");
|
||||
sendKey("TAB");
|
||||
await SimpleTest.promiseWaitForCondition(() => firstVisibleItem.matches(":focus"),
|
||||
"waiting for firstVisibleItem to get focus again");
|
||||
ok(firstVisibleItem.matches(":focus"), "firstVisibleItem should be focused after tabbing to it again");
|
||||
|
||||
if (navigator.platform == "Win32") {
|
||||
// The Import menuitem is only visible on Windows, where we will need another Tab
|
||||
// press to get to the Preferences item.
|
||||
let preferencesItem = gMenuButton.shadowRoot.querySelector(".menuitem-preferences");
|
||||
sendKey("DOWN");
|
||||
await SimpleTest.promiseWaitForCondition(() => preferencesItem.matches(":focus"),
|
||||
"waiting for preferencesItem to gain focus");
|
||||
ok(preferencesItem.matches(":focus"), `.menuitem-preferences should be now be focused (DOWN)`);
|
||||
sendKey("UP");
|
||||
await SimpleTest.promiseWaitForCondition(() => !preferencesItem.matches(":focus"),
|
||||
`waiting for preferencesItem to lose focus (UP)`);
|
||||
ok(!preferencesItem.matches(":focus"), `.menuitem-preferences should lose focus after pressing up`);
|
||||
|
||||
let feedbackItem = gMenuButton.shadowRoot.querySelector(".menuitem-feedback");
|
||||
ok(!feedbackItem.matches(":focus"), ".menuitem-feedback should not be focused before tabbing to it");
|
||||
// The Import menuitem is only visible on Windows, where we will need a second Tab
|
||||
// press to get to the Feedback item.
|
||||
let tabs = navigator.platform == "Win32" ? 2 : 1;
|
||||
while (tabs--) {
|
||||
sendKey("TAB");
|
||||
await SimpleTest.promiseWaitForCondition(() => preferencesItem.matches(":focus"),
|
||||
"waiting for preferencesItem to get focus");
|
||||
ok(preferencesItem.matches(":focus"), ".menuitem-preferences should be focused after tabbing to it");
|
||||
}
|
||||
|
||||
await SimpleTest.promiseWaitForCondition(() => feedbackItem.matches(":focus"),
|
||||
"waiting for feedbackItem to get focus");
|
||||
ok(feedbackItem.matches(":focus"), ".menuitem-feedback should be focused after tabbing to it");
|
||||
|
||||
let preferencesItem = gMenuButton.shadowRoot.querySelector(".menuitem-preferences");
|
||||
ok(!preferencesItem.matches(":focus"), ".menuitem-preferences should not be focused before tabbing to it");
|
||||
// We will need a third Tab press to get to the Preferences item.
|
||||
sendKey("TAB");
|
||||
|
||||
await SimpleTest.promiseWaitForCondition(() => preferencesItem.matches(":focus"),
|
||||
"waiting for preferencesItem to get focus");
|
||||
ok(preferencesItem.matches(":focus"), ".menuitem-preferences should be focused after tabbing to it");
|
||||
|
||||
let openPreferencesEvent = null;
|
||||
is(false, menu.hidden, "menu should be visible before pressing 'space' on .menuitem-preferences");
|
||||
ok(!menu.hidden, "menu should be visible before pressing 'space' on .menuitem-preferences");
|
||||
window.addEventListener("AboutLoginsOpenPreferences", event => openPreferencesEvent = event);
|
||||
sendKey("SPACE");
|
||||
ok(openPreferencesEvent, "AboutLoginsOpenPreferences event should be dispatched after pressing 'space' on .menuitem-preferences");
|
||||
is(true, menu.hidden, "menu should be hidden after pressing 'space' on .menuitem-preferences");
|
||||
ok(menu.hidden, "menu should be hidden after pressing 'space' on .menuitem-preferences");
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче