Skip to content

Commit

Permalink
♿ a11y(dropdown): start focus from selected option (#1448)
Browse files Browse the repository at this point in the history
* Create PR for #1446

* feat(a11y-dropdown): focus selected option when opening dropdown popup to improve a11y with keyboard navigation

* feat(a11y-dropdown): move to option list to be consumed by other parts (like combobox) in the future

* fix(dropdonw): adjust selection

* chore: fix condtion

* chore: update ts definitions

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Marco Zirkenbach <[email protected]>
Co-authored-by: Gery Hirschfeld <[email protected]>
  • Loading branch information
3 people committed Sep 12, 2024
1 parent e2c48b0 commit 6c7a3f6
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/shy-birds-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@baloise/ds-core': minor
---

**dropdown**: focus selected option when navigating with opening dropdown popup
7 changes: 2 additions & 5 deletions e2e/cypress/e2e/a11y/bal-dropdown.a11y.cy.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import { selectors } from 'support/utils'

describe('bal-dropdown', () => {
beforeEach(() => {
cy.pageA11y('/components/bal-dropdown/test/bal-dropdown.a11y.html')
cy.platform('desktop')
cy.waitForDesignSystem()
})

it('closed state', () => {
it('inital state', () => {
cy.get('main').testA11y()
})

it('open state', () => {
it('selected state', () => {
cy.getByLabelText('Year').click()
cy.get('main').testA11y()
cy.getByRole('option', { name: 'v1990' }).click()
cy.get('main').testA11y()
})
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2277,6 +2277,10 @@ export namespace Components {
* @returns focusIndex
*/
"focusPrevious": () => Promise<number>;
/**
* Focus the selected visible option in the list, if no option is selected it selects the first one
*/
"focusSelected": () => Promise<number>;
/**
* Returns a list of options
*/
Expand Down
29 changes: 27 additions & 2 deletions packages/core/src/components/bal-option-list/bal-option-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,22 @@ export class OptionList implements ComponentInterface, Loggable {
* ------------------------------------------------------
*/

/**
* Focus the selected visible option in the list, if no option is selected it selects the first one
*/
@Method() async focusSelected(): Promise<number> {
const options = this.options
const indexToFocus = this.getSelectedOptionIndex(options)

if (indexToFocus > 0) {
this.updateFocus(options, indexToFocus)
await waitAfterFramePaint()
return indexToFocus
} else {
return await this.focusFirst()
}
}

/**
* Focus the first visible option in the list
* @returns focusIndex
Expand Down Expand Up @@ -430,10 +446,20 @@ export class OptionList implements ComponentInterface, Loggable {
return undefined
}

private getSelectedOptionIndex(options: HTMLBalOptionElement[]): number {
for (let index = 0; index < options.length; index++) {
const option = options[index]
if (option.selected && !option.hidden) {
return index
}
}
return this.focusIndex
}

private getFirstOptionIndex(options: HTMLBalOptionElement[]): number {
for (let index = 0; index < options.length; index++) {
const option = options[index]
if (!option.disabled) {
if (!option.disabled && !option.hidden) {
return index
}
}
Expand Down Expand Up @@ -516,7 +542,6 @@ export class OptionList implements ComponentInterface, Loggable {
...block.class(),
}}
id={this.inputId}
tabIndex={-1}
>
<div
role="listbox"
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/utils/dropdown/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class DropdownPopupUtil {
}
}

expandList() {
async expandList() {
if (this.component.panelEl) {
this.component.panelCleanup = autoUpdate(
this.component.el,
Expand All @@ -39,7 +39,7 @@ export class DropdownPopupUtil {
)
}
this.component.isExpanded = true
this.component.listEl?.focusFirst()
await this.component.listEl?.focusSelected()
}

collapseList() {
Expand Down

0 comments on commit 6c7a3f6

Please sign in to comment.