ngx-bootstrap icon indicating copy to clipboard operation
ngx-bootstrap copied to clipboard

Typeahead accessibility failed with screen reader NVDA

Open jacques-lebourgeois opened this issue 1 year ago • 3 comments

Bug description: When using a screen reader, the user is not informed on the currently active line in the dropdown list

Plunker/StackBlitz that reproduces the issue:

  • Install NVDA screen reader
  • Run NVDA
  • Go on the example page with min-lenth
  • Move down and up in the drop down list
  • The screen reader just read "Vide" (in french, that means empty in english)

Versions of ngx-bootstrap, Angular, and Bootstrap:

ngx-bootstrap: last one

Angular: last one

Bootstrap: last one

Build system: Angular CLI, System.js, webpack, starter seed:

Expected behavior

To make in work in our Angular project, we create a complentary directive that had aria-activedescendant="ngb-typeahead-0-0" to the input element and aria-selected="true|false" to each button of the ldropdown list

jacques-lebourgeois avatar Sep 06 '24 15:09 jacques-lebourgeois

NVDA doesn't work as it suppose to work, aria-label should do the trick, but it doesn't recognize it, but it picks up content of tag on the list. Not sure if its our bug.

lexasq avatar Oct 22 '24 14:10 lexasq

hi,

Thanks for your answer.

I am not sure it is not a problem of aria-label.

The problem seems to lie in the fact that with the [typeaheadMinLength]="0" attribute, the first element of the suggetsed list is automatically selected but the screen reader does not have the information.

As NVDA is widely used as a screen reader for the visually impaired, this was blocking the use of our WEB application. So we had in our project a directive to change typeahead behavior that add

  • aria-activedescendant: identifies the currently active element when focus is on a composite widget, combobox, textbox, group, or application.
  • aria-selected: indicates the current “selected” state of various widgets.

It works fine with both chrome and firefox.

Here is the added code

import {
  AfterViewInit,
  Directive,
  ElementRef,
  HostListener,
} from '@angular/core';

@Directive({
  selector: 'input[typeahead]',
})
export class LoTypeaheadDirective implements AfterViewInit {
  private input: HTMLInputElement;
  constructor(private elementRef: ElementRef<HTMLInputElement>) {}

  ngAfterViewInit(): void {
    this.input = this.elementRef.nativeElement;
  }
  @HostListener('keyup', ['$event'])
  public async onKeyUp(e: KeyboardEvent): Promise<void> {
    window.setTimeout(() => {
      const typeahead: HTMLSelectElement = this.input.parentElement.querySelector(
        'typeahead-container'
      );

      if (typeahead) {
        this.input.setAttribute('aria-activedescendant', typeahead.id);
        this.input.parentElement
          .querySelectorAll('typeahead-container .dropdown-item')
          .forEach((elt: HTMLOptionElement, index) => {
            elt.setAttribute('aria-selected', 'false');
          });
        const active: HTMLOptionElement = this.input.parentElement.querySelector(
          'typeahead-container .dropdown-item.active'
        );
        if (active) {
          active.setAttribute('aria-selected', 'true');
          this.input.setAttribute('aria-activedescendant', active.id);
        }
      }
    });
  }
}

jacques-lebourgeois avatar Nov 04 '24 15:11 jacques-lebourgeois

@jacques-lebourgeois We were always trying to keep our lib friendly and compliant with accessibility standards, thanks for your input,. I've tried doing this in my fork, I've tried aria-label and aria-selected without much success. I'll experiment with this approach as well and if it'll fit well, it will become a part of the next patch.

lexasq avatar Nov 04 '24 16:11 lexasq