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' } }), }), });