ha-form-multi_select accessibility improvements (#21023)

This commit is contained in:
karwosts 2024-12-23 00:21:27 -08:00 committed by GitHub
parent d4cbfd9583
commit 147098f0fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 69 additions and 34 deletions

View File

@ -5,12 +5,14 @@ import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import "../ha-button-menu"; import "../ha-button-menu";
import "../ha-check-list-item"; import "../ha-check-list-item";
import type { HaCheckListItem } from "../ha-check-list-item";
import "../ha-checkbox"; import "../ha-checkbox";
import type { HaCheckbox } from "../ha-checkbox"; import type { HaCheckbox } from "../ha-checkbox";
import "../ha-formfield"; import "../ha-formfield";
import "../ha-svg-icon"; import "../ha-icon-button";
import "../ha-textfield"; import "../ha-textfield";
import "../ha-md-button-menu";
import "../ha-md-menu-item";
import type { import type {
HaFormElement, HaFormElement,
HaFormMultiSelectData, HaFormMultiSelectData,
@ -73,13 +75,10 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
} }
return html` return html`
<ha-button-menu <ha-md-button-menu
.disabled=${this.disabled} .disabled=${this.disabled}
fixed @opening=${this._handleOpen}
@opened=${this._handleOpen} @closing=${this._handleClose}
@closed=${this._handleClose}
multi
activatable
> >
<ha-textfield <ha-textfield
slot="trigger" slot="trigger"
@ -94,28 +93,56 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
.disabled=${this.disabled} .disabled=${this.disabled}
tabindex="-1" tabindex="-1"
></ha-textfield> ></ha-textfield>
<ha-svg-icon <ha-icon-button
slot="trigger" slot="trigger"
.label=${this.label}
.path=${this._opened ? mdiMenuUp : mdiMenuDown} .path=${this._opened ? mdiMenuUp : mdiMenuDown}
></ha-svg-icon> ></ha-icon-button>
${options.map((item: string | [string, string]) => { ${options.map((item: string | [string, string]) => {
const value = optionValue(item); const value = optionValue(item);
const selected = data.includes(value); const selected = data.includes(value);
return html`<ha-check-list-item return html`<ha-md-menu-item
left type="option"
.selected=${selected} aria-checked=${selected}
.activated=${selected}
@request-selected=${this._selectedChanged}
.value=${value} .value=${value}
.disabled=${this.disabled} .action=${selected ? "remove" : "add"}
.activated=${selected}
@click=${this._toggleItem}
@keydown=${this._keydown}
keep-open
> >
<ha-checkbox
slot="start"
tabindex="-1"
.checked=${selected}
></ha-checkbox>
${optionLabel(item)} ${optionLabel(item)}
</ha-check-list-item>`; </ha-md-menu-item>`;
})} })}
</ha-button-menu> </ha-md-button-menu>
`; `;
} }
protected _keydown(ev) {
if (ev.code === "Space" || ev.code === "Enter") {
ev.preventDefault();
this._toggleItem(ev);
}
}
protected _toggleItem(ev) {
const oldData = this.data || [];
let newData: string[];
if (ev.currentTarget.action === "add") {
newData = [...oldData, ev.currentTarget.value];
} else {
newData = oldData.filter((d) => d !== ev.currentTarget.value);
}
fireEvent(this, "value-changed", {
value: newData,
});
}
protected firstUpdated() { protected firstUpdated() {
this.updateComplete.then(() => { this.updateComplete.then(() => {
const { formElement, mdcRoot } = const { formElement, mdcRoot } =
@ -139,17 +166,6 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
} }
} }
private _selectedChanged(ev: CustomEvent): void {
ev.stopPropagation();
if (ev.detail.source === "property") {
return;
}
this._handleValueChanged(
(ev.target as HaCheckListItem).value,
ev.detail.selected
);
}
private _valueChanged(ev: CustomEvent): void { private _valueChanged(ev: CustomEvent): void {
const { value, checked } = ev.target as HaCheckbox; const { value, checked } = ev.target as HaCheckbox;
this._handleValueChanged(value, checked); this._handleValueChanged(value, checked);
@ -195,7 +211,7 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
:host([own-margin]) { :host([own-margin]) {
margin-bottom: 5px; margin-bottom: 5px;
} }
ha-button-menu { ha-md-button-menu {
display: block; display: block;
cursor: pointer; cursor: pointer;
} }
@ -208,22 +224,23 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
} }
ha-textfield { ha-textfield {
display: block; display: block;
width: 100%;
pointer-events: none; pointer-events: none;
} }
ha-svg-icon { ha-icon-button {
color: var(--input-dropdown-icon-color); color: var(--input-dropdown-icon-color);
position: absolute; position: absolute;
right: 1em; right: 1em;
top: 1em; top: 4px;
cursor: pointer; cursor: pointer;
inset-inline-end: 1em; inset-inline-end: 1em;
inset-inline-start: initial; inset-inline-start: initial;
direction: var(--direction); direction: var(--direction);
} }
:host([opened]) ha-svg-icon { :host([opened]) ha-icon-button {
color: var(--primary-color); color: var(--primary-color);
} }
:host([opened]) ha-button-menu { :host([opened]) ha-md-button-menu {
--mdc-text-field-idle-line-color: var(--input-hover-line-color); --mdc-text-field-idle-line-color: var(--input-hover-line-color);
--mdc-text-field-label-ink-color: var(--primary-color); --mdc-text-field-label-ink-color: var(--primary-color);
} }

View File

@ -3,6 +3,7 @@ import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit"; import { css, html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators"; import { customElement, property, query } from "lit/decorators";
import { FOCUS_TARGET } from "../dialogs/make-dialog-manager"; import { FOCUS_TARGET } from "../dialogs/make-dialog-manager";
import { fireEvent } from "../common/dom/fire_event";
import type { HaIconButton } from "./ha-icon-button"; import type { HaIconButton } from "./ha-icon-button";
import "./ha-menu"; import "./ha-menu";
import type { HaMenu } from "./ha-menu"; import type { HaMenu } from "./ha-menu";
@ -40,12 +41,22 @@ export class HaMdButtonMenu extends LitElement {
<ha-menu <ha-menu
.positioning=${this.positioning} .positioning=${this.positioning}
.hasOverflow=${this.hasOverflow} .hasOverflow=${this.hasOverflow}
@opening=${this._handleOpening}
@closing=${this._handleClosing}
> >
<slot></slot> <slot></slot>
</ha-menu> </ha-menu>
`; `;
} }
private _handleOpening(): void {
fireEvent(this, "opening", undefined, { composed: false });
}
private _handleClosing(): void {
fireEvent(this, "closing", undefined, { composed: false });
}
private _handleClick(): void { private _handleClick(): void {
if (this.disabled) { if (this.disabled) {
return; return;
@ -88,3 +99,10 @@ declare global {
"ha-md-button-menu": HaMdButtonMenu; "ha-md-button-menu": HaMdButtonMenu;
} }
} }
declare global {
interface HASSDomEvents {
opening: undefined;
closing: undefined;
}
}