diff --git a/packages/frontend/app/components/locale-chooser.js b/packages/frontend/app/components/locale-chooser.js
index 6e78e96f34..f9dab111d1 100644
--- a/packages/frontend/app/components/locale-chooser.js
+++ b/packages/frontend/app/components/locale-chooser.js
@@ -8,7 +8,6 @@ import { findById, uniqueValues } from 'ilios-common/utils/array-helpers';
export default class LocaleChooserComponent extends Component {
@service intl;
@tracked isOpen = false;
- @tracked menuElement;
get locale() {
const locale = this.intl.get('primaryLocale');
@@ -47,7 +46,7 @@ export default class LocaleChooserComponent extends Component {
if (target.nextElementSibling) {
target.nextElementSibling.focus();
} else {
- this.menuElement.querySelector('button:nth-of-type(1)').focus();
+ target.parentElement.firstElementChild.focus();
}
break;
case 'ArrowUp':
@@ -55,7 +54,7 @@ export default class LocaleChooserComponent extends Component {
if (target.previousElementSibling) {
target.previousElementSibling.focus();
} else {
- this.menuElement.querySelector('button:last-of-type').focus();
+ target.parentElement.lastElementChild.focus();
}
break;
case 'Escape':
@@ -67,9 +66,11 @@ export default class LocaleChooserComponent extends Component {
}
}
@action
- clearFocus() {
- const buttons = this.menuElement.querySelectorAll('button');
- buttons.forEach((el) => el.blur());
+ clearFocus(event) {
+ const buttons = event.target.parentElement.children;
+ for (let i = 0; i < buttons.length; i++) {
+ buttons[i].blur();
+ }
}
@action
toggleMenu({ key }) {
diff --git a/packages/frontend/tests/integration/components/locale-chooser-test.js b/packages/frontend/tests/integration/components/locale-chooser-test.js
index e21638637c..3b4b6d8ab1 100644
--- a/packages/frontend/tests/integration/components/locale-chooser-test.js
+++ b/packages/frontend/tests/integration/components/locale-chooser-test.js
@@ -72,4 +72,87 @@ module('Integration | Component | locale-chooser', function (hooks) {
assert.notOk(component.locales[1].hasFocus);
assert.notOk(component.locales[2].hasFocus);
});
+
+ test('keyboard navigation', async function (assert) {
+ await render(hbs``);
+ await component.toggle.click();
+ assert.strictEqual(component.locales.length, 3);
+ assert.ok(component.locales[0].hasFocus);
+ assert.notOk(component.locales[1].hasFocus);
+ assert.notOk(component.locales[2].hasFocus);
+ // regular down/up navigation
+ await component.locales[0].down();
+ assert.notOk(component.locales[0].hasFocus);
+ assert.ok(component.locales[1].hasFocus);
+ assert.notOk(component.locales[2].hasFocus);
+ await component.locales[1].down();
+ assert.notOk(component.locales[0].hasFocus);
+ assert.notOk(component.locales[1].hasFocus);
+ assert.ok(component.locales[2].hasFocus);
+ await component.locales[2].up();
+ assert.notOk(component.locales[0].hasFocus);
+ assert.ok(component.locales[1].hasFocus);
+ assert.notOk(component.locales[2].hasFocus);
+ await component.locales[1].up();
+ assert.ok(component.locales[0].hasFocus);
+ assert.notOk(component.locales[1].hasFocus);
+ assert.notOk(component.locales[2].hasFocus);
+ // wrap-around navigation from first to last menu item
+ await component.locales[0].up();
+ assert.notOk(component.locales[0].hasFocus);
+ assert.notOk(component.locales[1].hasFocus);
+ assert.ok(component.locales[2].hasFocus);
+ // wrap-around navigation from last to first menu item
+ await component.locales[2].down();
+ assert.ok(component.locales[0].hasFocus);
+ assert.notOk(component.locales[1].hasFocus);
+ assert.notOk(component.locales[2].hasFocus);
+ // close menu on escape, left, right, and tab keys.
+ await component.locales[0].esc();
+ assert.strictEqual(component.locales.length, 0);
+ await component.toggle.click();
+ assert.strictEqual(component.locales.length, 3);
+ assert.ok(component.locales[0].hasFocus);
+ await component.locales[0].left();
+ assert.strictEqual(component.locales.length, 0);
+ await component.toggle.click();
+ assert.strictEqual(component.locales.length, 3);
+ assert.ok(component.locales[0].hasFocus);
+ await component.locales[0].right();
+ assert.strictEqual(component.locales.length, 0);
+ await component.toggle.click();
+ assert.strictEqual(component.locales.length, 3);
+ assert.ok(component.locales[0].hasFocus);
+ await component.locales[0].tab();
+ assert.strictEqual(component.locales.length, 0);
+ });
+
+ test('mouse entering locale-button clears focus', async function (assert) {
+ await render(hbs``);
+ await component.toggle.click();
+ assert.strictEqual(component.locales.length, 3);
+ assert.ok(component.locales[0].hasFocus);
+ assert.notOk(component.locales[1].hasFocus);
+ assert.notOk(component.locales[2].hasFocus);
+ await component.locales[0].mouseEnter();
+ assert.notOk(component.locales[0].hasFocus);
+ assert.notOk(component.locales[1].hasFocus);
+ assert.notOk(component.locales[2].hasFocus);
+ });
+
+ test('changing locale', async function (assert) {
+ await render(hbs``);
+ assert.strictEqual(component.toggle.text, 'English (en)');
+ await component.toggle.click();
+ assert.strictEqual(component.locales.length, 3);
+ await component.locales[1].click();
+ assert.strictEqual(component.locales.length, 0);
+ assert.strictEqual(component.toggle.text, 'Español (es)');
+ await component.toggle.click();
+ await component.locales[2].click();
+ assert.strictEqual(component.toggle.text, 'Français (fr)');
+ await component.toggle.click();
+ await component.locales[0].click();
+ assert.strictEqual(component.toggle.text, 'English (en)');
+ });
});
diff --git a/packages/frontend/tests/pages/components/locale-chooser.js b/packages/frontend/tests/pages/components/locale-chooser.js
index 43192a4bd4..90163ab44d 100644
--- a/packages/frontend/tests/pages/components/locale-chooser.js
+++ b/packages/frontend/tests/pages/components/locale-chooser.js
@@ -11,5 +11,12 @@ export default create({
},
locales: collection('[data-test-item]', {
hasFocus: hasFocus(),
+ mouseEnter: triggerable('mouseenter'),
+ down: triggerable('keyup', '', { eventProperties: { key: 'ArrowDown' } }),
+ esc: triggerable('keyup', '', { eventProperties: { key: 'Escape' } }),
+ left: triggerable('keyup', '', { eventProperties: { key: 'ArrowLeft' } }),
+ right: triggerable('keyup', '', { eventProperties: { key: 'ArrowRight' } }),
+ tab: triggerable('keyup', '', { eventProperties: { key: 'Tab' } }),
+ up: triggerable('keyup', '', { eventProperties: { key: 'ArrowUp' } }),
}),
});