Migrate ha-button-menu to ha-dropdown in 3 files (#28337)

* Initial plan

* Migrate ha-button-menu to ha-dropdown in 3 files

- Migrate ha-config-logs.ts: Replace ha-button-menu with ha-dropdown for log provider selection
- Migrate ha-qr-scanner.ts: Replace ha-button-menu with ha-dropdown for camera selection
- Migrate ha-data-table-labels.ts: Replace ha-button-menu with ha-dropdown for overflow labels

Following the migration pattern from PR #28293

Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>

* Fix ha-data-table-labels migration to use proper @wa-select pattern

- Add HaDropdownItem type import
- Use @wa-show instead of @click for menu opening
- Use @wa-select for item selection with proper event handler
- Update comment to reference ha-dropdown instead of ha-button-menu

Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>

* Fixes

* Remove unused code

* Remove unused styles

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
Co-authored-by: Wendelin <w@pe8.at>
This commit is contained in:
Copilot 2025-12-08 21:30:35 +01:00 committed by GitHub
parent b50daecdec
commit 6e180c9fb4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 67 additions and 55 deletions

View File

@ -2,15 +2,17 @@ import type { TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import type { LabelRegistryEntry } from "../../data/label_registry";
import { computeCssColor } from "../../common/color/compute-color";
import { fireEvent } from "../../common/dom/fire_event";
import "../ha-label";
import { stopPropagation } from "../../common/dom/stop_propagation";
import { stringCompare } from "../../common/string/compare";
import type { LabelRegistryEntry } from "../../data/label_registry";
import "../chips/ha-chip-set";
import "../ha-button-menu";
import "../ha-dropdown";
import "../ha-dropdown-item";
import type { HaDropdownItem } from "../ha-dropdown-item";
import "../ha-icon";
import "../ha-list-item";
import "../ha-label";
@customElement("ha-data-table-labels")
class HaDataTableLabels extends LitElement {
@ -26,12 +28,11 @@ class HaDataTableLabels extends LitElement {
(label) => this._renderLabel(label, true)
)}
${labels.length > 2
? html`<ha-button-menu
absolute
? html`<ha-dropdown
role="button"
tabindex="0"
@click=${this._handleIconOverflowMenuOpened}
@closed=${this._handleIconOverflowMenuClosed}
@click=${stopPropagation}
@wa-select=${this._handleDropdownSelect}
>
<ha-label slot="trigger" class="plus" dense>
+${labels.length - 2}
@ -40,12 +41,12 @@ class HaDataTableLabels extends LitElement {
labels.slice(2),
(label) => label.label_id,
(label) => html`
<ha-list-item @click=${this._labelClicked} .item=${label}>
<ha-dropdown-item .value=${label.label_id} .item=${label}>
${this._renderLabel(label, false)}
</ha-list-item>
</ha-dropdown-item>
`
)}
</ha-button-menu>`
</ha-dropdown>`
: nothing}
</ha-chip-set>
`;
@ -81,21 +82,12 @@ class HaDataTableLabels extends LitElement {
fireEvent(this, "label-clicked", { label });
}
protected _handleIconOverflowMenuOpened(e) {
e.stopPropagation();
// If this component is used inside a data table, the z-index of the row
// needs to be increased. Otherwise the ha-button-menu would be displayed
// underneath the next row in the table.
const row = this.closest(".mdc-data-table__row") as HTMLDivElement | null;
if (row) {
row.style.zIndex = "1";
}
}
protected _handleIconOverflowMenuClosed() {
const row = this.closest(".mdc-data-table__row") as HTMLDivElement | null;
if (row) {
row.style.zIndex = "";
private _handleDropdownSelect(
ev: CustomEvent<{ item: HaDropdownItem & { item?: LabelRegistryEntry } }>
) {
const label = ev.detail?.item?.item;
if (label) {
fireEvent(this, "label-clicked", { label });
}
}
@ -114,9 +106,6 @@ class HaDataTableLabels extends LitElement {
--ha-label-background-color: var(--color, var(--grey-color));
--ha-label-background-opacity: 0.5;
}
ha-button-menu {
border-radius: 10px;
}
.plus {
--ha-label-background-color: transparent;
border: 1px solid var(--divider-color);

View File

@ -9,13 +9,13 @@ import { customElement, property, query, state } from "lit/decorators";
import { prepareZXingModule } from "barcode-detector";
import type QrScanner from "qr-scanner";
import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../common/dom/stop_propagation";
import { addExternalBarCodeListener } from "../external_app/external_app_entrypoint";
import type { HomeAssistant } from "../types";
import "./ha-alert";
import "./ha-button";
import "./ha-button-menu";
import "./ha-list-item";
import "./ha-dropdown";
import "./ha-dropdown-item";
import type { HaDropdownItem } from "./ha-dropdown-item";
import "./ha-spinner";
import "./ha-textfield";
import type { HaTextField } from "./ha-textfield";
@ -52,6 +52,8 @@ class HaQrScanner extends LitElement {
@state() private _warning?: string;
@state() private _selectedCamera?: string;
private _qrScanner?: QrScanner;
private _qrNotFoundCount = 0;
@ -121,7 +123,7 @@ class HaQrScanner extends LitElement {
!this._error &&
this._cameras &&
this._cameras.length > 1
? html`<ha-button-menu fixed @closed=${stopPropagation}>
? html`<ha-dropdown @wa-select=${this._handleDropdownSelect}>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize(
@ -131,15 +133,17 @@ class HaQrScanner extends LitElement {
></ha-icon-button>
${this._cameras!.map(
(camera) => html`
<ha-list-item
<ha-dropdown-item
.value=${camera.id}
@click=${this._cameraChanged}
class=${this._selectedCamera === camera.id
? "selected"
: ""}
>
${camera.label}
</ha-list-item>
</ha-dropdown-item>
`
)}
</ha-button-menu>`
</ha-dropdown>`
: nothing}
</div>`
: html`<ha-alert alert-type="warning">
@ -205,6 +209,9 @@ class HaQrScanner extends LitElement {
private async _listCameras(qrScanner: typeof QrScanner): Promise<void> {
this._cameras = await qrScanner.listCameras(true);
if (this._cameras.length > 0) {
this._selectedCamera = this._cameras[0].id;
}
}
private _qrCodeError = (err: any) => {
@ -252,8 +259,12 @@ class HaQrScanner extends LitElement {
this._qrCodeScanned(this._manualInput!.value);
}
private _cameraChanged(ev: CustomEvent): void {
this._qrScanner?.setCamera((ev.target as any).value);
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
const cameraId = ev.detail?.item?.value;
if (cameraId) {
this._selectedCamera = cameraId;
this._qrScanner?.setCamera(cameraId);
}
}
private _openExternalScanner() {
@ -359,7 +370,7 @@ class HaQrScanner extends LitElement {
#canvas-container {
position: relative;
}
ha-button-menu {
ha-icon-button {
position: absolute;
bottom: 8px;
right: 8px;
@ -369,6 +380,9 @@ class HaQrScanner extends LitElement {
color: white;
border-radius: var(--ha-border-radius-circle);
}
ha-dropdown-item.selected {
font-weight: var(--ha-font-weight-bold);
}
.row {
display: flex;
align-items: center;

View File

@ -4,10 +4,12 @@ import { css, html, LitElement } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { navigate } from "../../../common/navigate";
import { stringCompare } from "../../../common/string/compare";
import { extractSearchParam } from "../../../common/url/search-params";
import "../../../components/ha-button";
import "../../../components/ha-button-menu";
import "../../../components/ha-list-item";
import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";
import "../../../components/search-input";
import type { LogProvider } from "../../../data/error_log";
import { fetchHassioAddonsInfo } from "../../../data/hassio/addon";
@ -18,7 +20,6 @@ import type { HomeAssistant, Route } from "../../../types";
import "./error-log-card";
import "./system-log-card";
import type { SystemLogCard } from "./system-log-card";
import { stringCompare } from "../../../common/string/compare";
const logProviders: LogProvider[] = [
{
@ -117,7 +118,10 @@ export class HaConfigLogs extends LitElement {
>
${isComponentLoaded(this.hass, "hassio")
? html`
<ha-button-menu slot="toolbar-icon">
<ha-dropdown
slot="toolbar-icon"
@wa-select=${this._handleDropdownSelect}
>
<ha-button slot="trigger" appearance="filled">
<ha-svg-icon slot="end" .path=${mdiChevronDown}></ha-svg-icon>
${this._logProviders.find(
@ -126,16 +130,17 @@ export class HaConfigLogs extends LitElement {
</ha-button>
${this._logProviders.map(
(provider) => html`
<ha-list-item
?selected=${provider.key === this._selectedLogProvider}
.provider=${provider.key}
@click=${this._selectProvider}
<ha-dropdown-item
.value=${provider.key}
class=${provider.key === this._selectedLogProvider
? "selected"
: ""}
>
${provider.name}
</ha-list-item>
</ha-dropdown-item>
`
)}
</ha-button-menu>
</ha-dropdown>
`
: ""}
${search}
@ -170,8 +175,12 @@ export class HaConfigLogs extends LitElement {
this._detail = !this._detail;
}
private _selectProvider(ev) {
this._selectedLogProvider = (ev.currentTarget as any).provider;
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
const provider = ev.detail?.item?.value;
if (!provider) {
return;
}
this._selectedLogProvider = provider;
this._filter = "";
navigate(`/config/logs?provider=${this._selectedLogProvider}`);
}
@ -254,7 +263,7 @@ export class HaConfigLogs extends LitElement {
direction: ltr;
}
@media all and (max-width: 870px) {
ha-button-menu {
ha-dropdown {
max-width: 50%;
}
ha-button {
@ -265,8 +274,8 @@ export class HaConfigLogs extends LitElement {
white-space: nowrap;
}
}
ha-list-item[selected] {
color: var(--primary-color);
ha-dropdown-item.selected {
font-weight: var(--ha-font-weight-bold);
}
`,
];