From 3906bbdb4667f306a363bf0ae561f006983ef4f3 Mon Sep 17 00:00:00 2001 From: Jonathan Meyer <26874831+atmgrifter00@users.noreply.github.com> Date: Wed, 31 Jul 2024 13:43:48 -0500 Subject: [PATCH] Update Select arrow down behavior to select first option in dropdown when selected option is visually hidden. --- .../nimble-components/src/select/index.ts | 20 +++++++++- .../src/select/tests/select.spec.ts | 38 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/packages/nimble-components/src/select/index.ts b/packages/nimble-components/src/select/index.ts index 4816a007f4..294115e0f0 100644 --- a/packages/nimble-components/src/select/index.ts +++ b/packages/nimble-components/src/select/index.ts @@ -567,15 +567,33 @@ export class Select */ public override keydownHandler(e: KeyboardEvent): BooleanOrVoid { const initialSelectedIndex = this.selectedIndex; - super.keydownHandler(e); const key = e.key; if (e.ctrlKey || e.shiftKey) { return true; } + if (key !== keyArrowDown) { + super.keydownHandler(e); + } + let currentActiveIndex = this.openActiveIndex ?? this.selectedIndex; let commitValueThenClose = false; switch (key) { + case keyArrowDown: { + const selectedOption = this.options[this.selectedIndex]; + if (this.open && isListOption(selectedOption) && !isOptionOrGroupVisible(selectedOption)) { + if (this.openActiveIndex === this.selectedIndex) { + this.selectFirstOption(); + } else { + this.selectNextOption(); + } + } else { + super.keydownHandler(e); + } + + currentActiveIndex = this.openActiveIndex ?? this.selectedIndex; + break; + } case keySpace: { // when dropdown is open allow user to enter a space for filter text if (this.open && this.filterMode !== FilterMode.none) { diff --git a/packages/nimble-components/src/select/tests/select.spec.ts b/packages/nimble-components/src/select/tests/select.spec.ts index 861841fa8e..5caccb065b 100644 --- a/packages/nimble-components/src/select/tests/select.spec.ts +++ b/packages/nimble-components/src/select/tests/select.spec.ts @@ -27,6 +27,7 @@ type OptionInitialState = | 'disabled hidden' | 'disabled selected hidden' | 'hidden' + | 'hidden selected' | 'visually-hidden'; async function setup( @@ -585,6 +586,43 @@ describe('Select', () => { await disconnect(); }); + it('when second option is selected and hidden, pressing arrow down while dropdown is open selects first option', async () => { + const { element, connect, disconnect } = await setup( + undefined, + false, + undefined, + 'hidden selected' + ); + const pageObject = new SelectPageObject(element); + await connect(); + await waitForUpdatesAsync(); + pageObject.clickSelect(); + pageObject.pressArrowDownKey(); + + expect(pageObject.getActiveOption()?.value).toBe('one'); + + await disconnect(); + }); + + it('when second option is selected and hidden, pressing arrow down twice while dropdown is open selects third option', async () => { + const { element, connect, disconnect } = await setup( + undefined, + false, + undefined, + 'hidden selected' + ); + const pageObject = new SelectPageObject(element); + await connect(); + await waitForUpdatesAsync(); + pageObject.clickSelect(); + pageObject.pressArrowDownKey(); + pageObject.pressArrowDownKey(); + + expect(pageObject.getActiveOption()?.value).toBe('three'); + + await disconnect(); + }); + describe('with all options disabled', () => { async function setupAllDisabled(): Promise> { const viewTemplate = html`