Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 5, 2025

Breaking change

Proposed change

Migrates all overflow menus in src/panels/config/ from legacy ha-button-menu/ha-md-button-menu and ha-md-menu components to the new ha-dropdown component, following the pattern established in PR #28293.

Files Migrated:

  • areas/ha-config-area-page.ts
  • areas/ha-config-areas-dashboard.ts
  • dashboard/ha-config-dashboard.ts
  • cloud/cloud-login-panel.ts
  • cloud/cloud-account.ts
  • network/supervisor-network.ts
  • scene/ha-scene-editor.ts
  • scene/ha-scene-dashboard.ts
  • core/ha-config-section-updates.ts
  • automation/ha-automation-picker.ts (including row-level overflow menus)
  • devices/ha-config-devices-dashboard.ts
  • entities/ha-config-entities.ts
  • helpers/ha-config-helpers.ts
  • script/ha-script-picker.ts

Migration Pattern Applied:

  • Replace <ha-button-menu> / <ha-md-button-menu> / <ha-md-menu> with <ha-dropdown>
  • Replace <ha-list-item> (inside menus) / <ha-md-menu-item> with <ha-dropdown-item>
  • Change @action to @wa-select event handlers
  • Replace .clickAction on ha-dropdown-item with value attributes and @wa-select handlers on parent
  • Update index-based switch statements to use ev.detail.item.value
  • Replace slot="graphic" / slot="start" with slot="icon"
  • Replace class="warning" with variant="danger"
  • Replace <ha-md-divider> and <li divider> with <wa-divider>
  • Replace <ha-sub-menu> with native Web Awesome submenu pattern using slot="submenu" on child ha-dropdown-item elements
  • Use reusable render methods (e.g., _renderLabelItems(submenu), _renderAreaItems(submenu)) with conditional slot assignment (.slot=${submenu ? "submenu" : ""}) for cleaner submenu implementations
  • Use data-* HTML attributes for custom data (e.g., data-area-id, data-action) following web standards
  • Use semantic values (e.g., "select-area") instead of IDs as value attributes for clarity
  • Remove invalid attributes like has-overflow and keep-open that don't exist in Web Awesome dropdown API
  • Use type="checkbox" on ha-dropdown-item for toggleable menu items in picker pages (automation, script, scene), while keeping @click handlers with embedded ha-checkbox elements for label/area bulk selection menus (following automation picker pattern)
  • Move styling classes from ha-dropdown to trigger element (e.g., ha-button) where appropriate
  • Remove unused imports (ha-md-menu, HaMdMenu, ha-md-menu-item, HaMdMenuItem, ha-md-divider, ha-sub-menu, ha-check-list-item) after migration
  • Add proper type imports for HaDropdownItem
  • Replace programmatic menu opening patterns with inline ha-dropdown components (e.g., row-level overflow menus)
  • Add proper null safety checks for method parameters
  • Remove obsolete handler methods that are no longer used after migration

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (thank you!)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Example configuration

Additional information

Checklist

  • The code change is tested and works locally.
  • There is no commented out code in this PR.
  • Tests have been added to verify that the new code works.

If user exposed functionality or configuration variables are added/changed:

Original prompt

Objective

Migrate all overflow menus in src/panels/config/ from legacy ha-button-menu/ha-md-button-menu components to the new ha-dropdown component, following the pattern established in PR #28293.

Except src/panels/config/logs/ha-config-logs.ts that is already migrated in another PR.
Files to Migrate

Search for and migrate ALL files in src/panels/config/ that contain either:

ha-button-menu (with ha-list-item)
ha-md-button-menu (with ha-md-menu-item)

ha-dropdown-item API Reference

Based on the webawesome source code, ha-dropdown-item has the following:
Available Slots

Slot

Description

(default)

The dropdown item's label

icon

An optional icon to display before the label

details

Additional content to display after the label

submenu

Submenu items for nested menus
Available Properties

Property

Type

Description

value

string

Value for identifying the item in wa-select event

type

'normal' | 'checkbox'

Set to 'checkbox' for checkable items

checked

boolean

Whether checkbox is checked (only when type="checkbox")

disabled

boolean

Disables the item

variant

'default' | 'danger'

Visual variant for danger/warning items
CSS Parts

Part

Description

checkmark

The checkmark icon for checkbox items

icon

Container for icon slot

label

Container for label slot

details

Container for details slot

submenu-icon

Submenu indicator icon

submenu

Submenu container

Migration Rules

  1. Import Changes
    1.1 Removing Old Imports

CRITICAL: Before removing ANY import, search the ENTIRE file to verify it is ONLY used for the overflow menu being migrated.

Step-by-step verification process:

For ha-button-menu import:

Search file for all <ha-button-menu occurrences
Count total usages
If ALL are being migrated → remove import
If ANY remain → keep import
For ha-list-item import:

Search file for ALL <ha-list-item occurrences
Check EACH usage location:

Inside ? → Will be migrated
Inside ? → NOT migrated, keep import
Standalone list usage? → NOT migrated, keep import
Only remove import if ALL usages are within menus being migrated
For ha-md-button-menu import:

Same process as ha-button-menu

For ha-md-menu-item import:

Same process as ha-list-item
For ha-md-divider import:

Check if used outside of menus
Only remove if ALL usages are within menus being migrated
1.2 Adding New Imports

Import paths are RELATIVE to the current file location. Calculate the correct path!

Path calculation method:

Count directories from file location to src/
Add components/ to reach the components folder

File Location

Levels Up

Import Path Prefix

src/panels/config/logs/*. ts

3

../../../components/

src/panels/config/backup/*.ts

3

../../../components/

src/panels/config/areas/*.ts

3

../../../components/

src/panels/config/cloud/login/*.ts

4

../../../../components/

src/panels/config/cloud/account/*.ts

4

../../../../components/

src/panels/config/automation/trigger/*.ts

4

../../../../components/

src/panels/config/automation/trigger/types/*.ts

5

../../../../../components/

src/panels/config/automation/condition/*.ts

4

../../../../components/

src/panels/config/integrations/*.ts

3

../../../components/

src/panels/config/integrations/integration-panels/thread/*.ts

5

../../../../../components/

Required new imports (adjust path based on file location):

TypeScript
import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import type { HaDropdownItem } from "../../../components/ha-dropdown-item";

For wa-divider (only if dividers are used):

TypeScript
import "@home-assistant/webawesome/dist/components/divider/divider";

Note: This is an absolute package import, NOT a relative path.

  1. Component Replacements

Old Component

New Component

(inside menu only)

    1. Attribute/Property Migrations
      3.1 On the dropdown container (ha-dropdown)

    Old Attribute

    Action

    Notes

    positioning="fixed"

    Remove

    Not needed

    positioning="popover"

    Replace with placement="bottom-end"

    Or appropriate placement

    anchor-corner="..."

    Replace with placement="bottom-end"

    Simplify

    menu-corner="..."

    Replace with placement="bottom-end"

    Simplify

    fixed

    Remove

    Not needed

    multi

    Remove

    Use type="checkbox" on items instead

    @action=${handler}

    Replace with @wa-select=${handler}

    Update handler logic

    @opened=${handler}

    Replace with @wa-show=${handler}

    See section 5.6

    @closed=${handler}

    Replace with @wa-hide=${handler}

    See section 5.6
    3.2 On menu items (ha-dropdown-item)

    Old Attribute

    New Attribute

    Notes

    slot="graphic"

    slot="icon"

    Icon slot renamed

    slot="start"

    ...


  • 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

    Copilot AI and others added 9 commits December 5, 2025 11:33
    … network panels
    
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    …ning class
    
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    … menus
    
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    Copilot AI changed the title [WIP] Migrate overflow menus to ha-dropdown component Migrate ha-button-menu to ha-dropdown in config panel Dec 5, 2025
    Copilot AI requested a review from wendevlin December 5, 2025 12:11
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot rework this file. there are still .clickActions and some ha-dropdown don't have a @wa-select

    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Fixed in 0b88a78. Replaced all .clickAction on ha-dropdown-item elements with proper value attributes and added @wa-select handlers on parent ha-dropdown elements. The remaining .clickAction patterns are on ha-md-menu-item inside ha-md-menu which is a different component pattern for the row-level overflow menu.

    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot thanks, I don't think ha-submenu should be used here. Checkout the webawesome dropdown docs and fix the submenu using ha-dropdown-item

    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Fixed in a1c7f91. Replaced ha-sub-menu with the native Web Awesome dropdown submenu pattern using slot="submenu" on child ha-dropdown-item elements, following the pattern from the gallery documentation.

    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot Why is there still a ha-md-menu migrate it too!

    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Fixed in 44fcb72. Migrated the row-level overflow menu from ha-md-menu to ha-dropdown by:

    • Replaced programmatic menu opening with inline dropdown pattern
    • Each data table row now has its own ha-dropdown with the overflow button as trigger
    • Removed all ha-md-menu, ha-md-menu-item, and ha-md-divider imports and usage
    • Updated action methods to accept AutomationEntity directly instead of extracting from menu context
    • Added _handleRowOverflowMenu handler with value-based dispatch logic

    …ndlers
    
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    return html`<ha-md-menu-item
    return html`<ha-dropdown-item
    .value=${label.label_id}
    .action=${selected ? "remove" : "add"}
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot I would use data-action here since it's a string. And then in _handleBulkLabel event.currentTarget.data.action.

    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Fixed in 5fd9bac. Changed from .action property binding to data-action HTML attribute, and updated the handler to access it via ev.currentTarget.dataset.action.

    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    this.narrow
    ></ha-icon-button>`}
    ${this.narrow
    ? html`<ha-sub-menu>
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot migrate submenu too

    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Fixed in 0a8c0c1. Replaced ha-sub-menu with native Web Awesome submenu pattern using slot="submenu" on child ha-dropdown-item elements, matching the pattern in other migrated files.

    …ce ha-sub-menu, move class to button
    
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    Copilot AI and others added 2 commits December 9, 2025 12:12
    …keep-open, has-overflow, replace ha-sub-menu with native submenu pattern
    
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    … handlers and replace ha-sub-menu with native submenu
    
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    .value=${area.area_id}
    .clickAction=${this._handleBulkArea}
    >
    html`<ha-dropdown-item .value=${area.area_id}>
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot value should be "select area" or smth and then we should add another attribute data-area-id=...., so we can be sure what is selected

    Comment on lines +964 to +1006
    ${this._labels?.map((label) => {
    const color = label.color ? computeCssColor(label.color) : undefined;
    const selected = this._selected.every((deviceId) =>
    this.hass.devices[deviceId]?.labels.includes(label.label_id)
    );
    const partial =
    !selected &&
    this._selected.some((deviceId) =>
    this.hass.devices[deviceId]?.labels.includes(label.label_id)
    );
    return html`<ha-dropdown-item
    slot="submenu"
    .value=${label.label_id}
    data-action=${selected ? "remove" : "add"}
    @click=${this._handleBulkLabel}
    keep-open
    >
    <ha-checkbox
    slot="icon"
    .checked=${selected}
    .indeterminate=${partial}
    reducedTouchTarget
    ></ha-checkbox>
    <ha-label
    style=${color ? `--color: ${color}` : ""}
    .description=${label.description}
    >
    ${label.icon
    ? html`<ha-icon slot="icon" .icon=${label.icon}></ha-icon>`
    : nothing}
    ${label.name}
    </ha-label>
    </ha-dropdown-item>`;
    })}
    <wa-divider slot="submenu"></wa-divider>
    <ha-dropdown-item
    slot="submenu"
    value="__create_label__"
    @click=${this._bulkCreateLabel}
    >
    ${this.hass.localize("ui.panel.config.labels.add_label")}
    </ha-dropdown-item>
    </ha-dropdown-item>`
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot checkout ha-automation-picker how I implemented the submenu. Way easier than you did it!

    Comment on lines +1017 to +1041
    ${Object.values(this.hass.areas).map(
    (area) =>
    html`<ha-dropdown-item slot="submenu" .value=${area.area_id}>
    ${area.icon
    ? html`<ha-icon slot="icon" .icon=${area.icon}></ha-icon>`
    : html`<ha-svg-icon
    slot="icon"
    .path=${mdiTextureBox}
    ></ha-svg-icon>`}
    ${area.name}
    </ha-dropdown-item>`
    )}
    <ha-dropdown-item slot="submenu" value="__no_area__">
    ${this.hass.localize(
    "ui.panel.config.devices.picker.bulk_actions.no_area"
    )}
    </ha-dropdown-item>
    <wa-divider slot="submenu"></wa-divider>
    <ha-dropdown-item slot="submenu" value="__create_area__">
    ${this.hass.localize(
    "ui.panel.config.devices.picker.bulk_actions.add_area"
    )}
    </ha-dropdown-item>
    </ha-dropdown-item>
    <wa-divider></wa-divider>`
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot fix submenu like in ha-automation-picker

    const item = ev.detail.item as HaDropdownItem;
    switch (item.value) {
    case "__no_area__":
    this._bulkAddArea(null);
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot Argument of type 'null' is not assignable to parameter of type 'string'.

    this._bulkCreateLabel();
    break;
    case "__no_area__":
    this._bulkAddArea(null);
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot Argument of type 'null' is not assignable to parameter of type 'string'.

    Comment on lines 1190 to 1192
    private _handleBulkArea = (item) => {
    const area = item.value;
    this._bulkAddArea(area);
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot not used anymore

    Comment on lines +906 to +937
    ${this._labels?.map((label) => {
    const color = label.color ? computeCssColor(label.color) : undefined;
    const selected = this._selected.every((entityId) =>
    this.hass.entities[entityId]?.labels.includes(label.label_id)
    );
    const partial =
    !selected &&
    this._selected.some((entityId) =>
    this.hass.entities[entityId]?.labels.includes(label.label_id)
    );
    return html`<ha-dropdown-item
    slot="submenu"
    .value=${label.label_id}
    data-action=${selected ? "remove" : "add"}
    keep-open
    >
    <ha-checkbox
    slot="icon"
    .checked=${selected}
    .indeterminate=${partial}
    reducedTouchTarget
    ></ha-checkbox>
    <ha-label
    style=${color ? `--color: ${color}` : ""}
    .description=${label.description}
    >
    ${label.icon
    ? html`<ha-icon slot="icon" .icon=${label.icon}></ha-icon>`
    : nothing}
    ${label.name}
    </ha-label>
    </ha-dropdown-item>`;
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot fix submenu like I did in ha-automation-picker

    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Comment on lines +826 to +847
    ${this._categories?.map(
    (category) =>
    html`<ha-dropdown-item
    slot="submenu"
    .value=${category.category_id}
    >
    ${category.icon
    ? html`<ha-icon slot="icon" .icon=${category.icon}></ha-icon>`
    : html`<ha-svg-icon slot="icon" .path=${mdiTag}></ha-svg-icon>`}
    ${category.name}
    </ha-dropdown-item>`
    )}
    <ha-dropdown-item slot="submenu" value="__no_category__">
    ${this.hass.localize(
    "ui.panel.config.automation.picker.bulk_actions.no_category"
    )}
    </ha-dropdown-item>
    <wa-divider slot="submenu"></wa-divider>
    <ha-dropdown-item slot="submenu" value="__create_category__">
    ${this.hass.localize("ui.panel.config.category.editor.add")}
    </ha-dropdown-item>
    </ha-dropdown-item>`
    Copy link
    Member

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @copilot fix submenu like I did it in ha-automation-picker

    …omation picker
    
    Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
    Copilot AI changed the title Migrate ha-button-menu to ha-dropdown in config panel Migrate ha-button-menu and ha-md-menu to ha-dropdown in config panel Dec 9, 2025
    Copilot AI requested a review from wendevlin December 9, 2025 13:11
    @wendevlin
    Copy link
    Member

    @copilot You solved the submenu change in several files wrong. Do it like I did it in ha-automation-picker

    Copy link
    Contributor Author

    Copilot AI commented Dec 9, 2025

    @copilot You solved the submenu change in several files wrong. Do it like I did it in ha-automation-picker

    Fixed in the latest commits. All files now use the same submenu pattern as ha-automation-picker:

    • Created render methods _renderLabelItems(submenu) and _renderAreaItems(submenu)
    • Use conditional slot assignment: .slot=${submenu ? "submenu" : ""}
    • Maintained @click and ha-checkbox for label items (same as automation picker)
    • Simplified templates by replacing inline logic with clean render method calls

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    2 participants