Compare commits

...
Author SHA1 Message Date
Petar Petrov 22a10917f2 Rerender the navigation picker when related items change
The related section is read in render, and hass updates no longer rerender the picker.
2026-10-28 10:26:07 +02:00
Petar Petrov c139d4b802 Drop hass from the navigation picker
computeNavigationPathInfo and subscribeNavigationPathInfo take an options object of separate values.
2026-10-28 09:07:59 +02:00
5 changed files with 185 additions and 109 deletions

No files matched your search

+91 -30
View File
@@ -3,6 +3,8 @@ import { html, LitElement, nothing, type PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { consume } from "../common/decorators/consume";
import { transform } from "../common/decorators/transform";
import { fireEvent } from "../common/dom/fire_event";
import { subscribeOneCollection } from "../common/util/subscribe-one";
import { caseInsensitiveStringCompare } from "../common/string/compare";
@@ -13,12 +15,30 @@ import { SYSTEM_PANELS } from "../data/panel";
import {
CONFIG_SUB_ROUTES,
computeNavigationPathInfo,
type NavigationPathInfoOptions,
} from "../data/compute-navigation-path-info";
import {
apiContext,
configContext,
connectionContext,
internationalizationContext,
registriesContext,
uiContext,
} from "../data/context";
import { findRelated, type RelatedResult } from "../data/search";
import { PANEL_DASHBOARDS } from "../panels/config/lovelace/dashboards/ha-config-lovelace-dashboards";
import { computeAreaPath } from "../panels/lovelace/strategies/areas/helpers/areas-strategy-helper";
import { multiTermSortedSearch } from "../resources/fuseMultiTerm";
import type { HomeAssistant, ValueChangedEvent } from "../types";
import type {
HomeAssistantApi,
HomeAssistantConfig,
HomeAssistantConnection,
HomeAssistantInternationalization,
HomeAssistantRegistries,
HomeAssistantUI,
Panels,
ValueChangedEvent,
} from "../types";
import type { ActionRelatedContext } from "../panels/lovelace/components/hui-action-editor";
import "./ha-generic-picker";
import "./ha-domain-icon";
@@ -50,8 +70,6 @@ interface NavigationItem extends PickerComboBoxItem {
@customElement("ha-navigation-picker")
export class HaNavigationPicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public label?: string;
@property() public value?: string;
@@ -70,6 +88,28 @@ export class HaNavigationPicker extends LitElement {
@property({ attribute: false }) public context?: ActionRelatedContext;
@state()
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: HomeAssistantInternationalization;
@consume({ context: uiContext, subscribe: true })
@transform<HomeAssistantUI, Panels | undefined>({
transformer: (ui) => ui?.panels ?? undefined,
})
private _panels?: Panels;
@consume({ context: registriesContext, subscribe: true })
private _registries?: HomeAssistantRegistries;
@consume({ context: configContext, subscribe: true })
private _hassConfig?: HomeAssistantConfig;
@consume({ context: connectionContext, subscribe: true })
private _connection?: HomeAssistantConnection;
@consume({ context: apiContext, subscribe: true })
private _api?: HomeAssistantApi;
protected firstUpdated() {
this._loadNavigationItems();
}
@@ -98,7 +138,7 @@ export class HaNavigationPicker extends LitElement {
? [
{
id: "related",
label: this.hass.localize(
label: this._i18n.localize(
"ui.components.navigation-picker.related"
),
},
@@ -106,23 +146,27 @@ export class HaNavigationPicker extends LitElement {
: []),
{
id: "dashboards",
label: this.hass.localize("ui.components.navigation-picker.dashboards"),
label: this._i18n.localize(
"ui.components.navigation-picker.dashboards"
),
},
{
id: "views",
label: this.hass.localize("ui.components.navigation-picker.views"),
label: this._i18n.localize("ui.components.navigation-picker.views"),
},
...(this._navigationGroups.apps.length
? [
{
id: "apps",
label: this.hass.localize("ui.components.navigation-picker.apps"),
label: this._i18n.localize(
"ui.components.navigation-picker.apps"
),
},
]
: []),
{
id: "other_routes",
label: this.hass.localize(
label: this._i18n.localize(
"ui.components.navigation-picker.other_routes"
),
},
@@ -140,7 +184,7 @@ export class HaNavigationPicker extends LitElement {
.valueRenderer=${this._valueRenderer}
.rowRenderer=${this._rowRenderer}
.sections=${sections}
.customValueLabel=${this.hass.localize(
.customValueLabel=${this._i18n.localize(
"ui.components.navigation-picker.add_custom_path"
)}
.addButtonLabel=${this.addButtonLabel}
@@ -268,7 +312,7 @@ export class HaNavigationPicker extends LitElement {
}
if (!section && groupItems.length) {
items.push(
this.hass.localize(`ui.components.navigation-picker.${group}`)
this._i18n.localize(`ui.components.navigation-picker.${group}`)
);
}
items.push(...groupItems);
@@ -290,22 +334,25 @@ export class HaNavigationPicker extends LitElement {
caseInsensitiveStringCompare(
itemA.sorting_label!,
itemB.sorting_label!,
this.hass.locale.language
this._i18n.locale.language
);
private async _loadNavigationItems() {
await this._loadConfigEntries();
const panels = Object.entries(this.hass!.panels).map(([id, panel]) => ({
id,
...panel,
}));
const pathInfoOptions = this._pathInfoOptions();
const panels = Object.entries(pathInfoOptions.panels).map(
([id, panel]) => ({
id,
...panel,
})
);
const lovelacePanels = panels.filter(
(panel) => panel.component_name === "lovelace"
);
const viewConfigs = await Promise.all(
lovelacePanels.map((panel) =>
fetchConfig(this.hass!.connection, panel.url_path, true)
fetchConfig(this._connection!.connection, panel.url_path, true)
.then((config) => [panel.id, config] as [string, typeof config])
.catch((_) => [panel.id, undefined] as [string, undefined])
)
@@ -324,7 +371,7 @@ export class HaNavigationPicker extends LitElement {
// Skip app panels — they are handled by the ingress panels fetch below
if (panel.component_name === "app") continue;
const path = `/${panel.url_path}`;
const resolved = computeNavigationPathInfo(this.hass!, path);
const resolved = computeNavigationPathInfo(pathInfoOptions, path);
const isDashboardPanel =
panel.component_name === "lovelace" ||
PANEL_DASHBOARDS.includes(panel.id);
@@ -350,7 +397,7 @@ export class HaNavigationPicker extends LitElement {
config.views.forEach((view, index) => {
const viewPath = `/${panel.url_path}/${view.path ?? index}`;
const viewInfo = computeNavigationPathInfo(
this.hass!,
pathInfoOptions,
viewPath,
config
);
@@ -366,15 +413,18 @@ export class HaNavigationPicker extends LitElement {
}
// Fetch all ingress add-on panels
if (isComponentLoaded(this.hass!.config, "hassio")) {
if (
this._hassConfig?.config &&
isComponentLoaded(this._hassConfig.config, "hassio")
) {
try {
const ingressPanels = await subscribeOneCollection(
getIngressPanelInfoCollection(this.hass!.connection)
getIngressPanelInfoCollection(this._connection!.connection)
);
for (const slug of Object.keys(ingressPanels)) {
const path = `/app/${slug}`;
const resolved = computeNavigationPathInfo(
this.hass!,
pathInfoOptions,
path,
undefined,
ingressPanels
@@ -396,7 +446,7 @@ export class HaNavigationPicker extends LitElement {
for (const [subPath, route] of Object.entries(CONFIG_SUB_ROUTES)) {
const path = `/config/${subPath}`;
const label = this.hass!.localize(route.translationKey) || subPath;
const label = this._i18n.localize(route.translationKey) || subPath;
otherRoutes.push({
id: path,
primary: label,
@@ -445,9 +495,10 @@ export class HaNavigationPicker extends LitElement {
...this._navigationGroups.apps,
...this._navigationGroups.other_routes,
];
this.requestUpdate();
};
if (!this.hass || (!this.context?.entity_id && !this.context?.area_id)) {
if (!this.context?.entity_id && !this.context?.area_id) {
updateRelatedItems([]);
return;
}
@@ -472,8 +523,8 @@ export class HaNavigationPicker extends LitElement {
let relatedResult: RelatedResult | undefined;
try {
relatedResult = context.entity_id
? await findRelated(this.hass, "entity", context.entity_id)
: await findRelated(this.hass, "area", context.area_id!);
? await findRelated(this._api!, "entity", context.entity_id)
: await findRelated(this._api!, "area", context.area_id!);
} catch (err) {
// eslint-disable-next-line no-console
console.error("Error fetching related items for navigation picker", err);
@@ -486,11 +537,12 @@ export class HaNavigationPicker extends LitElement {
relatedAreaIds.add(context.area_id);
}
const pathInfoOptions = this._pathInfoOptions();
const relatedItems: NavigationItem[] = [];
for (const deviceId of relatedDeviceIds) {
const device = this.hass.devices[deviceId];
const device = pathInfoOptions.devices[deviceId];
const path = `/config/devices/device/${deviceId}`;
const resolved = computeNavigationPathInfo(this.hass, path);
const resolved = computeNavigationPathInfo(pathInfoOptions, path);
relatedItems.push({
id: path,
primary: resolved.label,
@@ -512,7 +564,7 @@ export class HaNavigationPicker extends LitElement {
for (const areaId of relatedAreaIds) {
// Area dashboard view
const viewPath = `/home/${computeAreaPath(areaId)}`;
const resolvedArea = computeNavigationPathInfo(this.hass, viewPath);
const resolvedArea = computeNavigationPathInfo(pathInfoOptions, viewPath);
relatedItems.push({
id: viewPath,
primary: resolvedArea.label,
@@ -531,7 +583,7 @@ export class HaNavigationPicker extends LitElement {
const path = `/config/areas/area/${areaId}`;
relatedItems.push({
id: path,
primary: this.hass.localize(
primary: this._i18n.localize(
"ui.components.navigation-picker.area_settings",
{ area: resolvedArea.label }
),
@@ -550,13 +602,22 @@ export class HaNavigationPicker extends LitElement {
return relatedItems;
}
private _pathInfoOptions(): NavigationPathInfoOptions {
return {
localize: this._i18n.localize,
panels: this._panels ?? {},
areas: this._registries?.areas ?? {},
devices: this._registries?.devices ?? {},
};
}
private async _loadConfigEntries() {
if (Object.keys(this._configEntryLookup).length) {
return;
}
try {
const configEntries = await getConfigEntries(this.hass);
const configEntries = await getConfigEntries(this._api!);
this._configEntryLookup = Object.fromEntries(
configEntries.map((entry) => [entry.entry_id, entry])
);
@@ -2,14 +2,11 @@ import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { NavigationSelector } from "../../data/selector";
import type { HomeAssistant } from "../../types";
import type { ActionRelatedContext } from "../../panels/lovelace/components/hui-action-editor";
import "../ha-navigation-picker";
@customElement("ha-selector-navigation")
export class HaNavigationSelector extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public selector!: NavigationSelector;
@property() public value?: string;
@@ -27,7 +24,6 @@ export class HaNavigationSelector extends LitElement {
protected render() {
return html`
<ha-navigation-picker
.hass=${this.hass}
.label=${this.label}
.value=${this.value}
.required=${this.required}
+43 -27
View File
@@ -9,7 +9,11 @@ import {
mdiShape,
mdiTextureBox,
} from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type {
Connection,
HassConfig,
UnsubscribeFunc,
} from "home-assistant-js-websocket";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { computeDeviceName } from "../common/entity/compute_device_name";
import {
@@ -25,13 +29,25 @@ import {
getPanelIconPath,
getPanelTitleFromUrlPath,
} from "./panel";
import type { LocalizeKeys } from "../common/translations/localize";
import type { HomeAssistant } from "../types";
import type {
LocalizeFunc,
LocalizeKeys,
} from "../common/translations/localize";
import type { AreaRegistryEntry } from "./area/area_registry";
import type { DeviceRegistryEntry } from "./device/device_registry";
import type { Panels } from "../types";
export type NavigationPathInfoSources = Pick<
HomeAssistant,
"localize" | "panels" | "areas" | "devices" | "config" | "connection"
>;
export interface NavigationPathInfoOptions {
localize: LocalizeFunc;
panels: Panels;
areas: Record<string, AreaRegistryEntry>;
devices: Record<string, DeviceRegistryEntry>;
}
export interface SubscribeNavigationPathInfoOptions extends NavigationPathInfoOptions {
config: HassConfig;
connection: Connection;
}
export interface NavigationPathInfo {
label: string;
@@ -86,7 +102,7 @@ export const CONFIG_SUB_ROUTES: Record<
* For lovelace views, pass the dashboard config to resolve view title/icon.
*/
export const computeNavigationPathInfo = (
hass: NavigationPathInfoSources,
options: NavigationPathInfoOptions,
path: string,
lovelaceConfig?: LovelaceRawConfig,
ingressPanels?: IngressPanelInfoMap
@@ -102,7 +118,7 @@ export const computeNavigationPathInfo = (
segments[2] === "area" &&
segments[3]
) {
return computeAreaNavigationPathInfo(hass, segments[3]);
return computeAreaNavigationPathInfo(options.areas, segments[3]);
}
// /config/devices/device/{deviceId}
@@ -112,7 +128,7 @@ export const computeNavigationPathInfo = (
segments[2] === "device" &&
segments[3]
) {
return computeDeviceNavigationPathInfo(hass, segments[3]);
return computeDeviceNavigationPathInfo(options.devices, segments[3]);
}
// /app/<slug> (ingress addon panel)
@@ -124,25 +140,25 @@ export const computeNavigationPathInfo = (
if (panelUrlPath === "config" && subPath && subPath in CONFIG_SUB_ROUTES) {
const route = CONFIG_SUB_ROUTES[subPath];
return {
label: hass.localize(route.translationKey) || subPath,
label: options.localize(route.translationKey) || subPath,
iconPath: route.iconPath,
};
}
const panel = panelUrlPath ? hass.panels[panelUrlPath] : undefined;
const panel = panelUrlPath ? options.panels[panelUrlPath] : undefined;
const panelIcon = panel ? getPanelIcon(panel) : undefined;
const panelIconPath = panel ? getPanelIconPath(panel) : undefined;
// /home/areas-{areaId} (area dashboard view)
if (subPath?.startsWith(AREA_VIEW_PREFIX)) {
const areaId = subPath.slice(AREA_VIEW_PREFIX.length);
return computeAreaNavigationPathInfo(hass, areaId);
return computeAreaNavigationPathInfo(options.areas, areaId);
}
const isDashboard = panel?.component_name === "lovelace";
const panelInfo: NavigationPathInfo = {
label: getPanelTitleFromUrlPath(hass, panelUrlPath) || panelUrlPath,
label: getPanelTitleFromUrlPath(options, panelUrlPath) || panelUrlPath,
icon: panelIcon || (isDashboard ? "mdi:view-dashboard" : undefined),
iconPath: panelIconPath || mdiLink,
};
@@ -166,10 +182,10 @@ export const computeNavigationPathInfo = (
};
const computeAreaNavigationPathInfo = (
hass: Pick<HomeAssistant, "areas">,
areas: Record<string, AreaRegistryEntry>,
areaId: string
): NavigationPathInfo => {
const area = hass.areas[areaId];
const area = areas[areaId];
return {
label: area?.name || areaId,
icon: area?.icon || undefined,
@@ -178,10 +194,10 @@ const computeAreaNavigationPathInfo = (
};
const computeDeviceNavigationPathInfo = (
hass: Pick<HomeAssistant, "devices">,
devices: Record<string, DeviceRegistryEntry>,
deviceId: string
): NavigationPathInfo => {
const device = hass.devices[deviceId];
const device = devices[deviceId];
return {
label: (device ? computeDeviceName(device) : undefined) || deviceId,
iconPath: mdiDevices,
@@ -206,7 +222,7 @@ const computeIngressNavigationPathInfo = (
* updates for view paths and ingress panel info for app paths.
*/
export const subscribeNavigationPathInfo = (
hass: NavigationPathInfoSources,
options: SubscribeNavigationPathInfoOptions,
path: string,
onChange: (info: NavigationPathInfo) => void
): UnsubscribeFunc | undefined => {
@@ -217,13 +233,13 @@ export const subscribeNavigationPathInfo = (
if (
panelUrlPath === APP_PANEL &&
segments[1] &&
isComponentLoaded(hass.config, "hassio")
isComponentLoaded(options.config, "hassio")
) {
try {
const collection = getIngressPanelInfoCollection(hass.connection);
const collection = getIngressPanelInfoCollection(options.connection);
// Use cached state for immediate resolution if available
const info = computeNavigationPathInfo(
hass,
options,
path,
undefined,
collection.state
@@ -232,7 +248,7 @@ export const subscribeNavigationPathInfo = (
let current = info;
return collection.subscribe((panels) => {
const newInfo = computeNavigationPathInfo(
hass,
options,
path,
undefined,
panels
@@ -247,15 +263,15 @@ export const subscribeNavigationPathInfo = (
}
}
const info = computeNavigationPathInfo(hass, path);
const info = computeNavigationPathInfo(options, path);
onChange(info);
const panel = panelUrlPath ? hass.panels[panelUrlPath] : undefined;
const panel = panelUrlPath ? options.panels[panelUrlPath] : undefined;
if (segments[1] && panel?.component_name === "lovelace") {
let current = info;
const collection = getLovelaceCollection(hass.connection, panelUrlPath);
const collection = getLovelaceCollection(options.connection, panelUrlPath);
return collection.subscribe((config) => {
const newInfo = computeNavigationPathInfo(hass, path, config);
const newInfo = computeNavigationPathInfo(options, path, config);
if (newInfo.label !== current.label || newInfo.icon !== current.icon) {
current = newInfo;
onChange(newInfo);
@@ -122,7 +122,6 @@ export class HomeShortcutsEditor extends LitElement {
</div>
</ha-sortable>
<ha-navigation-picker
.hass=${this.hass}
.addButtonLabel=${this.hass.localize(
"ui.panel.home.editor.add_custom_shortcut"
)}
+51 -47
View File
@@ -1,23 +1,27 @@
import { mdiDevices, mdiLink, mdiPuzzle, mdiTextureBox } from "@mdi/js";
import { describe, expect, it } from "vitest";
import { computeNavigationPathInfo } from "../../src/data/compute-navigation-path-info";
import {
computeNavigationPathInfo,
type NavigationPathInfoOptions,
} from "../../src/data/compute-navigation-path-info";
import type { IngressPanelInfoMap } from "../../src/data/hassio/ingress";
import type { HomeAssistant } from "../../src/types";
import type { LovelaceConfig } from "../../src/data/lovelace/config/types";
const createHass = (overrides: Partial<HomeAssistant> = {}): HomeAssistant =>
({
panels: {},
areas: {},
devices: {},
localize: () => "",
...overrides,
}) as unknown as HomeAssistant;
const createOptions = (
overrides: Partial<NavigationPathInfoOptions> = {}
): NavigationPathInfoOptions => ({
panels: {},
areas: {},
devices: {},
localize: () => "",
...overrides,
});
describe("computeNavigationPathInfo", () => {
describe("panel paths", () => {
it("resolves a panel with icon", () => {
const hass = createHass({
const options = createOptions({
panels: {
"my-panel": {
url_path: "my-panel",
@@ -28,15 +32,15 @@ describe("computeNavigationPathInfo", () => {
} as unknown as HomeAssistant["panels"],
});
const result = computeNavigationPathInfo(hass, "/my-panel");
const result = computeNavigationPathInfo(options, "/my-panel");
expect(result.label).toBe("My Panel");
expect(result.icon).toBe("mdi:star");
expect(result.iconPath).toBe(mdiLink);
});
it("falls back to url path for unknown panel", () => {
const hass = createHass();
const result = computeNavigationPathInfo(hass, "/unknown");
const options = createOptions();
const result = computeNavigationPathInfo(options, "/unknown");
expect(result.label).toBe("unknown");
expect(result.icon).toBeUndefined();
expect(result.iconPath).toBe(mdiLink);
@@ -45,7 +49,7 @@ describe("computeNavigationPathInfo", () => {
describe("area paths", () => {
it("resolves /config/areas/area/{areaId}", () => {
const hass = createHass({
const options = createOptions({
areas: {
living_room: {
area_id: "living_room",
@@ -56,7 +60,7 @@ describe("computeNavigationPathInfo", () => {
});
const result = computeNavigationPathInfo(
hass,
options,
"/config/areas/area/living_room"
);
expect(result.label).toBe("Living Room");
@@ -65,7 +69,7 @@ describe("computeNavigationPathInfo", () => {
});
it("resolves /home/areas-{areaId}", () => {
const hass = createHass({
const options = createOptions({
areas: {
kitchen: {
area_id: "kitchen",
@@ -75,16 +79,16 @@ describe("computeNavigationPathInfo", () => {
} as unknown as HomeAssistant["areas"],
});
const result = computeNavigationPathInfo(hass, "/home/areas-kitchen");
const result = computeNavigationPathInfo(options, "/home/areas-kitchen");
expect(result.label).toBe("Kitchen");
expect(result.icon).toBeUndefined();
expect(result.iconPath).toBe(mdiTextureBox);
});
it("falls back to area id for unknown area", () => {
const hass = createHass();
const options = createOptions();
const result = computeNavigationPathInfo(
hass,
options,
"/config/areas/area/unknown_area"
);
expect(result.label).toBe("unknown_area");
@@ -94,7 +98,7 @@ describe("computeNavigationPathInfo", () => {
describe("device paths", () => {
it("resolves /config/devices/device/{deviceId}", () => {
const hass = createHass({
const options = createOptions({
devices: {
abc123: {
id: "abc123",
@@ -105,7 +109,7 @@ describe("computeNavigationPathInfo", () => {
});
const result = computeNavigationPathInfo(
hass,
options,
"/config/devices/device/abc123"
);
expect(result.label).toBe("Smart Light");
@@ -113,7 +117,7 @@ describe("computeNavigationPathInfo", () => {
});
it("prefers user-defined device name", () => {
const hass = createHass({
const options = createOptions({
devices: {
abc123: {
id: "abc123",
@@ -124,16 +128,16 @@ describe("computeNavigationPathInfo", () => {
});
const result = computeNavigationPathInfo(
hass,
options,
"/config/devices/device/abc123"
);
expect(result.label).toBe("My Light");
});
it("falls back to device id for unknown device", () => {
const hass = createHass();
const options = createOptions();
const result = computeNavigationPathInfo(
hass,
options,
"/config/devices/device/unknown_device"
);
expect(result.label).toBe("unknown_device");
@@ -152,7 +156,7 @@ describe("computeNavigationPathInfo", () => {
};
it("resolves view with title and icon", () => {
const hass = createHass({
const options = createOptions({
panels: {
lovelace: {
url_path: "lovelace",
@@ -163,7 +167,7 @@ describe("computeNavigationPathInfo", () => {
});
const result = computeNavigationPathInfo(
hass,
options,
"/lovelace/overview",
lovelaceConfig
);
@@ -172,7 +176,7 @@ describe("computeNavigationPathInfo", () => {
});
it("resolves view without icon using default", () => {
const hass = createHass({
const options = createOptions({
panels: {
lovelace: {
url_path: "lovelace",
@@ -183,7 +187,7 @@ describe("computeNavigationPathInfo", () => {
});
const result = computeNavigationPathInfo(
hass,
options,
"/lovelace/lights",
lovelaceConfig
);
@@ -192,7 +196,7 @@ describe("computeNavigationPathInfo", () => {
});
it("uses titleCase of path when view has no title", () => {
const hass = createHass({
const options = createOptions({
panels: {
lovelace: {
url_path: "lovelace",
@@ -203,7 +207,7 @@ describe("computeNavigationPathInfo", () => {
});
const result = computeNavigationPathInfo(
hass,
options,
"/lovelace/my-view",
lovelaceConfig
);
@@ -211,7 +215,7 @@ describe("computeNavigationPathInfo", () => {
});
it("uses index as name when view has no title or path", () => {
const hass = createHass({
const options = createOptions({
panels: {
lovelace: {
url_path: "lovelace",
@@ -222,7 +226,7 @@ describe("computeNavigationPathInfo", () => {
});
const result = computeNavigationPathInfo(
hass,
options,
"/lovelace/3",
lovelaceConfig
);
@@ -230,7 +234,7 @@ describe("computeNavigationPathInfo", () => {
});
it("falls back to panel info when view not found", () => {
const hass = createHass({
const options = createOptions({
panels: {
lovelace: {
url_path: "lovelace",
@@ -241,7 +245,7 @@ describe("computeNavigationPathInfo", () => {
});
const result = computeNavigationPathInfo(
hass,
options,
"/lovelace/nonexistent",
lovelaceConfig
);
@@ -249,7 +253,7 @@ describe("computeNavigationPathInfo", () => {
});
it("falls back to panel info when no lovelace config provided", () => {
const hass = createHass({
const options = createOptions({
panels: {
lovelace: {
url_path: "lovelace",
@@ -259,7 +263,7 @@ describe("computeNavigationPathInfo", () => {
} as unknown as HomeAssistant["panels"],
});
const result = computeNavigationPathInfo(hass, "/lovelace/overview");
const result = computeNavigationPathInfo(options, "/lovelace/overview");
expect(result.label).toBe("Dashboard");
});
});
@@ -277,9 +281,9 @@ describe("computeNavigationPathInfo", () => {
};
it("resolves /app/<slug> with ingress panels data", () => {
const hass = createHass();
const options = createOptions();
const result = computeNavigationPathInfo(
hass,
options,
"/app/my_addon",
undefined,
ingressPanels
@@ -290,17 +294,17 @@ describe("computeNavigationPathInfo", () => {
});
it("falls back to slug when ingress panels not provided", () => {
const hass = createHass();
const result = computeNavigationPathInfo(hass, "/app/my_addon");
const options = createOptions();
const result = computeNavigationPathInfo(options, "/app/my_addon");
expect(result.label).toBe("my_addon");
expect(result.icon).toBeUndefined();
expect(result.iconPath).toBe(mdiPuzzle);
});
it("falls back to slug when addon not found in ingress panels", () => {
const hass = createHass();
const options = createOptions();
const result = computeNavigationPathInfo(
hass,
options,
"/app/unknown_addon",
undefined,
ingressPanels
@@ -311,9 +315,9 @@ describe("computeNavigationPathInfo", () => {
});
it("resolves addon with empty icon as undefined", () => {
const hass = createHass();
const options = createOptions();
const result = computeNavigationPathInfo(
hass,
options,
"/app/no_icon_addon",
undefined,
ingressPanels
@@ -324,7 +328,7 @@ describe("computeNavigationPathInfo", () => {
});
it("does not resolve /app without a slug", () => {
const hass = createHass({
const options = createOptions({
panels: {
app: {
url_path: "app",
@@ -334,7 +338,7 @@ describe("computeNavigationPathInfo", () => {
} as unknown as HomeAssistant["panels"],
});
const result = computeNavigationPathInfo(
hass,
options,
"/app",
undefined,
ingressPanels