mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
Restore screen readers support on pickers (#25553)
This commit is contained in:
parent
9b7db191a6
commit
3355986585
@ -317,6 +317,7 @@ export class HaEntityPicker extends LitElement {
|
|||||||
const secondary = [areaName, entityName ? deviceName : undefined]
|
const secondary = [areaName, entityName ? deviceName : undefined]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(isRTL ? " ◂ " : " ▸ ");
|
.join(isRTL ? " ◂ " : " ▸ ");
|
||||||
|
const a11yLabel = [deviceName, entityName].filter(Boolean).join(" - ");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: entityId,
|
id: entityId,
|
||||||
@ -332,6 +333,7 @@ export class HaEntityPicker extends LitElement {
|
|||||||
friendlyName,
|
friendlyName,
|
||||||
entityId,
|
entityId,
|
||||||
].filter(Boolean) as string[],
|
].filter(Boolean) as string[],
|
||||||
|
a11y_label: a11yLabel,
|
||||||
stateObj: stateObj,
|
stateObj: stateObj,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -267,6 +267,7 @@ export class HaStatisticPicker extends LitElement {
|
|||||||
const secondary = [areaName, entityName ? deviceName : undefined]
|
const secondary = [areaName, entityName ? deviceName : undefined]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(isRTL ? " ◂ " : " ▸ ");
|
.join(isRTL ? " ◂ " : " ▸ ");
|
||||||
|
const a11yLabel = [deviceName, entityName].filter(Boolean).join(" - ");
|
||||||
|
|
||||||
const sortingPrefix = `${TYPE_ORDER.indexOf("entity")}`;
|
const sortingPrefix = `${TYPE_ORDER.indexOf("entity")}`;
|
||||||
output.push({
|
output.push({
|
||||||
@ -274,6 +275,7 @@ export class HaStatisticPicker extends LitElement {
|
|||||||
statistic_id: id,
|
statistic_id: id,
|
||||||
primary,
|
primary,
|
||||||
secondary,
|
secondary,
|
||||||
|
a11y_label: a11yLabel,
|
||||||
stateObj: stateObj,
|
stateObj: stateObj,
|
||||||
type: "entity",
|
type: "entity",
|
||||||
sorting_label: [sortingPrefix, deviceName, entityName].join("_"),
|
sorting_label: [sortingPrefix, deviceName, entityName].join("_"),
|
||||||
|
24
src/components/ha-combo-box-textfield.ts
Normal file
24
src/components/ha-combo-box-textfield.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import type { PropertyValues } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { HaTextField } from "./ha-textfield";
|
||||||
|
|
||||||
|
@customElement("ha-combo-box-textfield")
|
||||||
|
export class HaComboBoxTextField extends HaTextField {
|
||||||
|
@property({ type: Boolean, attribute: "disable-set-value" })
|
||||||
|
public disableSetValue = false;
|
||||||
|
|
||||||
|
protected willUpdate(changedProps: PropertyValues): void {
|
||||||
|
super.willUpdate(changedProps);
|
||||||
|
if (changedProps.has("value")) {
|
||||||
|
if (this.disableSetValue) {
|
||||||
|
this.value = changedProps.get("value") as string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-combo-box-textfield": HaComboBoxTextField;
|
||||||
|
}
|
||||||
|
}
|
@ -12,11 +12,12 @@ import type {
|
|||||||
import { registerStyles } from "@vaadin/vaadin-themable-mixin/register-styles";
|
import { registerStyles } from "@vaadin/vaadin-themable-mixin/register-styles";
|
||||||
import type { TemplateResult } from "lit";
|
import type { 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, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import "./ha-combo-box-item";
|
import "./ha-combo-box-item";
|
||||||
|
import "./ha-combo-box-textfield";
|
||||||
import "./ha-icon-button";
|
import "./ha-icon-button";
|
||||||
import "./ha-textfield";
|
import "./ha-textfield";
|
||||||
import type { HaTextField } from "./ha-textfield";
|
import type { HaTextField } from "./ha-textfield";
|
||||||
@ -108,9 +109,14 @@ export class HaComboBox extends LitElement {
|
|||||||
@property({ type: Boolean, attribute: "hide-clear-icon" })
|
@property({ type: Boolean, attribute: "hide-clear-icon" })
|
||||||
public hideClearIcon = false;
|
public hideClearIcon = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "clear-initial-value" })
|
||||||
|
public clearInitialValue = false;
|
||||||
|
|
||||||
@query("vaadin-combo-box-light", true) private _comboBox!: ComboBoxLight;
|
@query("vaadin-combo-box-light", true) private _comboBox!: ComboBoxLight;
|
||||||
|
|
||||||
@query("ha-textfield", true) private _inputElement!: HaTextField;
|
@query("ha-combo-box-textfield", true) private _inputElement!: HaTextField;
|
||||||
|
|
||||||
|
@state({ type: Boolean }) private _disableSetValue = false;
|
||||||
|
|
||||||
private _overlayMutationObserver?: MutationObserver;
|
private _overlayMutationObserver?: MutationObserver;
|
||||||
|
|
||||||
@ -171,7 +177,7 @@ export class HaComboBox extends LitElement {
|
|||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
attr-for-value="value"
|
attr-for-value="value"
|
||||||
>
|
>
|
||||||
<ha-textfield
|
<ha-combo-box-textfield
|
||||||
label=${ifDefined(this.label)}
|
label=${ifDefined(this.label)}
|
||||||
placeholder=${ifDefined(this.placeholder)}
|
placeholder=${ifDefined(this.placeholder)}
|
||||||
?disabled=${this.disabled}
|
?disabled=${this.disabled}
|
||||||
@ -191,9 +197,10 @@ export class HaComboBox extends LitElement {
|
|||||||
.invalid=${this.invalid}
|
.invalid=${this.invalid}
|
||||||
.helper=${this.helper}
|
.helper=${this.helper}
|
||||||
helperPersistent
|
helperPersistent
|
||||||
|
.disableSetValue=${this._disableSetValue}
|
||||||
>
|
>
|
||||||
<slot name="icon" slot="leadingIcon"></slot>
|
<slot name="icon" slot="leadingIcon"></slot>
|
||||||
</ha-textfield>
|
</ha-combo-box-textfield>
|
||||||
${this.value && !this.hideClearIcon
|
${this.value && !this.hideClearIcon
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
role="button"
|
role="button"
|
||||||
@ -249,6 +256,18 @@ export class HaComboBox extends LitElement {
|
|||||||
fireEvent(this, "opened-changed", { value: ev.detail.value });
|
fireEvent(this, "opened-changed", { value: ev.detail.value });
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
if (this.clearInitialValue) {
|
||||||
|
this.setTextFieldValue("");
|
||||||
|
if (opened) {
|
||||||
|
// Wait 100ms to be sure vaddin-combo-box-light already tried to set the value
|
||||||
|
setTimeout(() => {
|
||||||
|
this._disableSetValue = false;
|
||||||
|
}, 100);
|
||||||
|
} else {
|
||||||
|
this._disableSetValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (opened) {
|
if (opened) {
|
||||||
const overlay = document.querySelector<HTMLElement>(
|
const overlay = document.querySelector<HTMLElement>(
|
||||||
"vaadin-combo-box-overlay"
|
"vaadin-combo-box-overlay"
|
||||||
@ -342,10 +361,10 @@ export class HaComboBox extends LitElement {
|
|||||||
position: relative;
|
position: relative;
|
||||||
--vaadin-combo-box-overlay-max-height: calc(45vh - 56px);
|
--vaadin-combo-box-overlay-max-height: calc(45vh - 56px);
|
||||||
}
|
}
|
||||||
ha-textfield {
|
ha-combo-box-textfield {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
ha-textfield > ha-icon-button {
|
ha-combo-box-textfield > ha-icon-button {
|
||||||
--mdc-icon-button-size: 24px;
|
--mdc-icon-button-size: 24px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
|
@ -2,6 +2,7 @@ import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
|||||||
import type { ComboBoxLightOpenedChangedEvent } from "@vaadin/combo-box/vaadin-combo-box-light";
|
import type { ComboBoxLightOpenedChangedEvent } from "@vaadin/combo-box/vaadin-combo-box-light";
|
||||||
import { css, html, LitElement, nothing, type CSSResultGroup } from "lit";
|
import { css, html, LitElement, nothing, type CSSResultGroup } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import "./ha-combo-box-item";
|
import "./ha-combo-box-item";
|
||||||
@ -74,6 +75,7 @@ export class HaGenericPicker extends LitElement {
|
|||||||
<ha-picker-field
|
<ha-picker-field
|
||||||
type="button"
|
type="button"
|
||||||
compact
|
compact
|
||||||
|
aria-label=${ifDefined(this.label)}
|
||||||
@click=${this.open}
|
@click=${this.open}
|
||||||
@clear=${this._clear}
|
@clear=${this._clear}
|
||||||
.placeholder=${this.placeholder}
|
.placeholder=${this.placeholder}
|
||||||
|
@ -18,6 +18,7 @@ import "./ha-icon";
|
|||||||
export interface PickerComboBoxItem {
|
export interface PickerComboBoxItem {
|
||||||
id: string;
|
id: string;
|
||||||
primary: string;
|
primary: string;
|
||||||
|
a11y_label?: string;
|
||||||
secondary?: string;
|
secondary?: string;
|
||||||
search_labels?: string[];
|
search_labels?: string[];
|
||||||
sorting_label?: string;
|
sorting_label?: string;
|
||||||
@ -27,7 +28,7 @@ export interface PickerComboBoxItem {
|
|||||||
|
|
||||||
// Hack to force empty label to always display empty value by default in the search field
|
// Hack to force empty label to always display empty value by default in the search field
|
||||||
export interface PickerComboBoxItemWithLabel extends PickerComboBoxItem {
|
export interface PickerComboBoxItemWithLabel extends PickerComboBoxItem {
|
||||||
label: "";
|
a11y_label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NO_MATCHING_ITEMS_FOUND_ID = "___no_matching_items_found___";
|
const NO_MATCHING_ITEMS_FOUND_ID = "___no_matching_items_found___";
|
||||||
@ -109,7 +110,7 @@ export class HaPickerComboBox extends LitElement {
|
|||||||
id: NO_MATCHING_ITEMS_FOUND_ID,
|
id: NO_MATCHING_ITEMS_FOUND_ID,
|
||||||
primary: label || localize("ui.components.combo-box.no_match"),
|
primary: label || localize("ui.components.combo-box.no_match"),
|
||||||
icon_path: mdiMagnify,
|
icon_path: mdiMagnify,
|
||||||
label: "",
|
a11y_label: label || localize("ui.components.combo-box.no_match"),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -118,7 +119,7 @@ export class HaPickerComboBox extends LitElement {
|
|||||||
|
|
||||||
return items.map<PickerComboBoxItemWithLabel>((item) => ({
|
return items.map<PickerComboBoxItemWithLabel>((item) => ({
|
||||||
...item,
|
...item,
|
||||||
label: "",
|
a11y_label: item.a11y_label || item.primary,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -128,7 +129,7 @@ export class HaPickerComboBox extends LitElement {
|
|||||||
const sortedItems = items
|
const sortedItems = items
|
||||||
.map<PickerComboBoxItemWithLabel>((item) => ({
|
.map<PickerComboBoxItemWithLabel>((item) => ({
|
||||||
...item,
|
...item,
|
||||||
label: "",
|
a11y_label: item.a11y_label || item.primary,
|
||||||
}))
|
}))
|
||||||
.sort((entityA, entityB) =>
|
.sort((entityA, entityB) =>
|
||||||
caseInsensitiveStringCompare(
|
caseInsensitiveStringCompare(
|
||||||
@ -175,7 +176,8 @@ export class HaPickerComboBox extends LitElement {
|
|||||||
<ha-combo-box
|
<ha-combo-box
|
||||||
item-id-path="id"
|
item-id-path="id"
|
||||||
item-value-path="id"
|
item-value-path="id"
|
||||||
item-label-path="label"
|
item-label-path="a11y_label"
|
||||||
|
clear-initial-value
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._value}
|
.value=${this._value}
|
||||||
.label=${this.label}
|
.label=${this.label}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user