1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-09-23 03:22:50 +02:00

[CL-439] Improve keyboard and visual a11y for chip select (#11112)

This commit is contained in:
Victoria League 2024-09-19 13:49:00 -04:00 committed by GitHub
parent 4327fa21f6
commit eec84d893d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 34 additions and 5 deletions

View File

@ -52,10 +52,10 @@
type="button" type="button"
bitMenuItem bitMenuItem
(click)="viewOption(parent, $event)" (click)="viewOption(parent, $event)"
[title]="parent.label ? ('backTo' | i18n: parent.label) : ('back' | i18n)" [title]="'backTo' | i18n: parent.label ?? placeholderText"
> >
<i slot="start" class="bwi bwi-angle-left" aria-hidden="true"></i> <i slot="start" class="bwi bwi-angle-left" aria-hidden="true"></i>
{{ parent.label ? ("backTo" | i18n: parent.label) : ("back" | i18n) }} {{ "backTo" | i18n: parent.label ?? placeholderText }}
</button> </button>
<button <button
@ -76,6 +76,7 @@
(click)="option.children?.length ? viewOption(option, $event) : selectOption(option, $event)" (click)="option.children?.length ? viewOption(option, $event) : selectOption(option, $event)"
[disabled]="option.disabled" [disabled]="option.disabled"
[title]="option.label" [title]="option.label"
[attr.aria-haspopup]="option.children?.length ? 'menu' : null"
> >
<i <i
*ngIf="option.icon" *ngIf="option.icon"

View File

@ -1,10 +1,23 @@
import { Component, HostListener, Input, booleanAttribute, signal } from "@angular/core"; import {
AfterViewInit,
Component,
DestroyRef,
HostListener,
Input,
QueryList,
ViewChild,
ViewChildren,
booleanAttribute,
inject,
signal,
} from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
import { compareValues } from "../../../common/src/platform/misc/compare-values"; import { compareValues } from "../../../common/src/platform/misc/compare-values";
import { ButtonModule } from "../button"; import { ButtonModule } from "../button";
import { IconButtonModule } from "../icon-button"; import { IconButtonModule } from "../icon-button";
import { MenuModule } from "../menu"; import { MenuComponent, MenuItemDirective, MenuModule } from "../menu";
import { Option } from "../select/option"; import { Option } from "../select/option";
import { SharedModule } from "../shared"; import { SharedModule } from "../shared";
import { TypographyModule } from "../typography"; import { TypographyModule } from "../typography";
@ -28,7 +41,10 @@ export type ChipSelectOption<T> = Option<T> & {
}, },
], ],
}) })
export class ChipSelectComponent<T = unknown> implements ControlValueAccessor { export class ChipSelectComponent<T = unknown> implements ControlValueAccessor, AfterViewInit {
@ViewChild(MenuComponent) menu: MenuComponent;
@ViewChildren(MenuItemDirective) menuItems: QueryList<MenuItemDirective>;
/** Text to show when there is no selected option */ /** Text to show when there is no selected option */
@Input({ required: true }) placeholderText: string; @Input({ required: true }) placeholderText: string;
@ -62,6 +78,8 @@ export class ChipSelectComponent<T = unknown> implements ControlValueAccessor {
this.focusVisibleWithin.set(false); this.focusVisibleWithin.set(false);
} }
private destroyRef = inject(DestroyRef);
/** Tree constructed from `this.options` */ /** Tree constructed from `this.options` */
private rootTree: ChipSelectOption<T>; private rootTree: ChipSelectOption<T>;
@ -148,6 +166,16 @@ export class ChipSelectComponent<T = unknown> implements ControlValueAccessor {
this.renderedOptions = this.rootTree; this.renderedOptions = this.rootTree;
} }
ngAfterViewInit() {
/**
* menuItems will change when the user navigates into or out of a submenu. when that happens, we want to
* direct their focus to the first item in the new menu
*/
this.menuItems.changes.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
this.menu.keyManager.setFirstItemActive();
});
}
/** Control Value Accessor */ /** Control Value Accessor */
private notifyOnChange?: (value: T) => void; private notifyOnChange?: (value: T) => void;