Search code examples
javascriptangularaccessibility

How can I make the dropdown for this button ADA accessible?


I have this button which when clicked opens a dropdown of checkboxes. I am able to use the 'tab' key to navigate to the button as well as hit the 'space' or 'enter' key to open the dropdown. But I am not able to navigate through the checkboxes of the dropdown by using the up and down arrow keys. This is the relevant code:

<button class = “setting-button" mat-button [matmenuTriggerfor] =“settings" <fa-icon [icon]-"settingsIcon"></fa-icon> </button>

<mat-menu #settings="matMenu" >
<div *ngFor="let id of arraycolumns">
<div *nglf=" ![‘Cashless', 'Borrower', 'options', 'isfavorite', 'Progress', 'Deal Name'].includes(id)" mat-menu-item >
 <mat-checkbox class="chkbx" #singleCheckbox [checked]= 'fieldcolumnsChecked.indexof(id) >= 0' (change)="checkcolumnsChanged(id)" (click)="$event.stopPropagation();">
{{id}} 
</mat-checkbox>
</div> 
</div> 
</mat-menu>

And in the ts file I have this:

@ViewChildren("singleCheckbox") checkboxes!: QueryList<MatCheckbox>;

How can I enable navigation through the dropdown itself? Please help! Thank you :)


Solution

  • I think you can make use of Angular's HostListener to listen for keyboard events and manipulate the focus.

    Add a tabIndex to the mat-checkbox to make it focusable and Implement HostListener in your component to listen for keydown events.

    When arrow keys are pressed, change the focus of the checkboxes accordingly.

    The HTML:

    <mat-menu #settings="matMenu">
        <div *ngFor="let id of arraycolumns; let i = index">
            <div *ngIf="![‘Cashless', 'Borrower', 'options', 'isfavorite', 'Progress', 'Deal Name'].includes(id)" mat-menu-item>
                <mat-checkbox class="chkbx" #singleCheckbox [checked]='fieldcolumnsChecked.indexOf(id) >= 0' (change)="checkcolumnsChanged(id)" (click)="$event.stopPropagation();" [tabIndex]="i">
                    {{id}} 
                </mat-checkbox>
            </div> 
        </div> 
    </mat-menu>
    

    typescript:

    import { HostListener, QueryList, ViewChildren } from '@angular/core';
    import { MatCheckbox } from '@angular/material/checkbox';
    
    // ... (other imports)
    
    export class YourComponent {
    
        @ViewChildren("singleCheckbox") checkboxes!: QueryList<MatCheckbox>;
    
        currentFocusIndex = 0;
    
        // ...
    
        @HostListener('document:keydown', ['$event'])
        handleKeyboardEvent(event: KeyboardEvent) {
            if (this.checkboxes && this.checkboxes.length) {
                if (event.key === 'ArrowDown') {
                    event.preventDefault();
                    this.setFocus(true);
                } else if (event.key === 'ArrowUp') {
                    event.preventDefault();
                    this.setFocus(false);
                }
            }
        }
    
        setFocus(goDown: boolean) {
            if (goDown && this.currentFocusIndex < this.checkboxes.length - 1) {
                this.currentFocusIndex++;
            } else if (!goDown && this.currentFocusIndex > 0) {
                this.currentFocusIndex--;
            }
            const checkboxArray = this.checkboxes.toArray();
            checkboxArray[this.currentFocusIndex].focus();
        }
    
        // ... (rest of your code)
    }