diff --git a/.github/commitlint.config.mjs b/.github/commitlint.config.mjs index 4be77761..5e19cbe4 100644 --- a/.github/commitlint.config.mjs +++ b/.github/commitlint.config.mjs @@ -24,5 +24,7 @@ export default { "subject-case": [0], // Allow long body lines for detailed commit descriptions "body-max-line-length": [0], + // Allow long footer lines (e.g. issue refs, BREAKING CHANGE blocks, links) + "footer-max-line-length": [0], }, }; diff --git a/custom_components/hass_datapoints/hass-datapoints-cards.js b/custom_components/hass_datapoints/hass-datapoints-cards.js index da42cc6b..2509bead 100644 --- a/custom_components/hass_datapoints/hass-datapoints-cards.js +++ b/custom_components/hass_datapoints/hass-datapoints-cards.js @@ -1343,7 +1343,7 @@ * - A ha-selector (target schema) lets the user add extra items per recording. * - On submit both are merged; the selector resets to empty afterwards. */ - var HassRecordsActionCard = class extends i$2 { + var HassDatapointsActionCard = class extends i$2 { constructor() { super(); _defineProperty(this, "_userTarget", {}); @@ -1605,7 +1605,7 @@ return this._config?.show_annotation !== false ? 10 : 7; } }; - _defineProperty(HassRecordsActionCard, "properties", { + _defineProperty(HassDatapointsActionCard, "properties", { _config: { state: true }, _hass: { state: true }, _color: { state: true }, @@ -1613,7 +1613,7 @@ _feedbackText: { state: true }, _feedbackVisible: { state: true } }); - _defineProperty(HassRecordsActionCard, "styles", styles$71); + _defineProperty(HassDatapointsActionCard, "styles", styles$71); //#endregion //#region node_modules/.pnpm/@lit+localize@0.12.2/node_modules/@lit/localize/internal/locale-status-event.js /** @@ -5634,7 +5634,7 @@ customElements.define("editor-icon-picker", EditorIconPicker); //#endregion //#region custom_components/hass_datapoints/src/cards/action/editor.ts - var HassRecordsActionCardEditor = class extends EditorBase { + var HassDatapointsActionCardEditor = class extends EditorBase { _configTarget() { return normalizeTargetValue(this._config.target ?? { entity_id: Array.isArray(this._config.entities) && this._config.entities.length ? this._config.entities : this._config.entity }) ?? {}; } @@ -5727,7 +5727,7 @@ `; } }; - _defineProperty(HassRecordsActionCardEditor, "styles", [EditorBase.styles, styles$65]); + _defineProperty(HassDatapointsActionCardEditor, "styles", [EditorBase.styles, styles$65]); //#endregion //#region node_modules/.pnpm/@kipk+load-ha-components@1.0.3/node_modules/@kipk/load-ha-components/dist/load-ha-components.js /** @@ -6026,7 +6026,7 @@ `; //#endregion //#region custom_components/hass_datapoints/src/cards/dev-tool/editor.ts - var HassRecordsDevToolCardEditor = class extends EditorBase { + var HassDatapointsDevToolCardEditor = class extends EditorBase { render() { return b`
@@ -6037,7 +6037,7 @@ `; } }; - _defineProperty(HassRecordsDevToolCardEditor, "styles", [EditorBase.styles]); + _defineProperty(HassDatapointsDevToolCardEditor, "styles", [EditorBase.styles]); //#endregion //#region custom_components/hass_datapoints/src/cards/dev-tool/dev-tool-results/dev-tool-results.styles.ts var styles$58 = i$5` @@ -6666,7 +6666,7 @@ customElements.define("dev-tool-windows", CardDevToolWindows); //#endregion //#region custom_components/hass_datapoints/src/cards/dev-tool/dev-tool.ts - var HassRecordsDevToolCard = class extends HTMLElement { + var HassDatapointsDevToolCard = class extends HTMLElement { constructor() { super(); _defineProperty(this, "_config", {}); @@ -11650,13 +11650,13 @@ const topSlot = this._el("chart-top-slot"); const legend = this._el("legend"); const scrollViewport = this._el("chart-scroll-viewport"); - const wrap = this; const cardHeight = card?.clientHeight || 0; const occupiedHeight = (header?.offsetHeight || 0) + (topSlot && !topSlot.hidden ? topSlot.offsetHeight || 0 : 0) + (legend?.offsetHeight || 0); const cardDerivedHeight = cardHeight ? Math.max(0, cardHeight - occupiedHeight) : 0; const viewportHeight = scrollViewport?.clientHeight || 0; - const wrapHeight = wrap?.clientHeight || 0; - return Math.max(minChartHeight, cardDerivedHeight || viewportHeight || wrapHeight || 0); + if (cardDerivedHeight) return Math.max(minChartHeight, cardDerivedHeight); + if (viewportHeight) return Math.max(minChartHeight, viewportHeight); + return minChartHeight; } _syncTopSlotOffset() { const topSlot = this._el("chart-top-slot"); @@ -15710,7 +15710,7 @@ } //#endregion //#region custom_components/hass_datapoints/src/cards/history/history.ts - var HassRecordsHistoryCard = class extends ChartCardBase { + var HassDatapointsHistoryCard = class extends ChartCardBase { constructor() { super(); _defineProperty(this, "_hiddenSeries", /* @__PURE__ */ new Set()); @@ -16596,8 +16596,8 @@ return document.createElement("hass-datapoints-history-card-editor"); } }; - _defineProperty(HassRecordsHistoryCard, "styles", styles$56); - customElements.define("hass-datapoints-history-card", HassRecordsHistoryCard); + _defineProperty(HassDatapointsHistoryCard, "styles", styles$56); + customElements.define("hass-datapoints-history-card", HassDatapointsHistoryCard); //#endregion //#region custom_components/hass_datapoints/src/cards/history/editor.styles.ts var styles$52 = i$5``; @@ -19131,7 +19131,7 @@ customElements.define("sidebar-chart-display-section", SidebarChartDisplaySection); //#endregion //#region custom_components/hass_datapoints/src/cards/history/editor.ts - var HassRecordsHistoryCardEditor = class extends EditorBase { + var HassDatapointsHistoryCardEditor = class extends EditorBase { constructor(..._args) { super(..._args); _defineProperty(this, "_onTargetChanged", (e) => { @@ -19451,7 +19451,7 @@ `; } }; - _defineProperty(HassRecordsHistoryCardEditor, "styles", [EditorBase.styles, styles$52]); + _defineProperty(HassDatapointsHistoryCardEditor, "styles", [EditorBase.styles, styles$52]); //#endregion //#region custom_components/hass_datapoints/src/lib/data/preferences-api.ts /** HA user-data key for the saved history page. Stored via frontend/set_user_data. */ @@ -30466,7 +30466,7 @@ /** * hass-datapoints-history-panel – Sidebar panel for annotated history exploration. */ - var HassRecordsHistoryPanel = class extends HTMLElement { + var HassDatapointsHistoryPanel = class extends HTMLElement { constructor() { super(); this.attachShadow({ mode: "open" }); @@ -34126,8 +34126,8 @@ customElements.define("list-event-item", CardListEventItem); //#endregion //#region custom_components/hass_datapoints/src/cards/list/list.ts - var _HassRecordsListCard; - var HassRecordsListCard = (_HassRecordsListCard = class HassRecordsListCard extends i$2 { + var _HassDatapointsListCard; + var HassDatapointsListCard = (_HassDatapointsListCard = class HassDatapointsListCard extends i$2 { constructor() { super(); _defineProperty(this, "_pageSize", 15); @@ -34452,7 +34452,7 @@ min_rows: rows }; } - }, _defineProperty(_HassRecordsListCard, "properties", { + }, _defineProperty(_HassDatapointsListCard, "properties", { _config: { state: true }, _hass: { state: true }, _allEvents: { state: true }, @@ -34460,14 +34460,14 @@ _page: { state: true }, _editingId: { state: true }, _editColor: { state: true } - }), _defineProperty(_HassRecordsListCard, "styles", styles$16), _HassRecordsListCard); - HassRecordsListCard = __decorate([localized()], HassRecordsListCard); + }), _defineProperty(_HassDatapointsListCard, "styles", styles$16), _HassDatapointsListCard); + HassDatapointsListCard = __decorate([localized()], HassDatapointsListCard); //#endregion //#region custom_components/hass_datapoints/src/cards/list/editor.styles.ts var styles$11 = i$5``; //#endregion //#region custom_components/hass_datapoints/src/cards/list/editor.ts - var HassRecordsListCardEditor = class extends EditorBase { + var HassDatapointsListCardEditor = class extends EditorBase { constructor(..._args) { super(..._args); _defineProperty(this, "_onTargetChanged", (e) => { @@ -34563,7 +34563,7 @@ `; } }; - _defineProperty(HassRecordsListCardEditor, "styles", [EditorBase.styles, styles$11]); + _defineProperty(HassDatapointsListCardEditor, "styles", [EditorBase.styles, styles$11]); //#endregion //#region custom_components/hass_datapoints/src/cards/quick/quick.styles.ts var styles$10 = i$5` @@ -34717,7 +34717,7 @@ customElements.define("quick-annotation", CardQuickAnnotation); //#endregion //#region custom_components/hass_datapoints/src/cards/quick/quick.ts - var HassRecordsQuickCard = class extends i$2 { + var HassDatapointsQuickCard = class extends i$2 { constructor() { super(); this._config = {}; @@ -34864,7 +34864,7 @@ return this._config?.title ? baseRows + 1 : baseRows; } }; - _defineProperty(HassRecordsQuickCard, "properties", { + _defineProperty(HassDatapointsQuickCard, "properties", { _config: { type: Object, state: true @@ -34890,7 +34890,7 @@ state: true } }); - _defineProperty(HassRecordsQuickCard, "styles", styles$10); + _defineProperty(HassDatapointsQuickCard, "styles", styles$10); //#endregion //#region custom_components/hass_datapoints/src/cards/quick/editor.styles.ts var styles$8 = i$5` @@ -34901,7 +34901,7 @@ `; //#endregion //#region custom_components/hass_datapoints/src/cards/quick/editor.ts - var HassRecordsQuickCardEditor = class extends EditorBase { + var HassDatapointsQuickCardEditor = class extends EditorBase { constructor(..._args) { super(..._args); _defineProperty(this, "_onTargetChanged", (e) => { @@ -34983,7 +34983,7 @@ `; } }; - _defineProperty(HassRecordsQuickCardEditor, "styles", [EditorBase.styles, styles$8]); + _defineProperty(HassDatapointsQuickCardEditor, "styles", [EditorBase.styles, styles$8]); //#endregion //#region custom_components/hass_datapoints/src/cards/sensor/sensor.styles.ts var styles$7 = i$5` @@ -36171,7 +36171,7 @@ * hass-datapoints-sensor-card – Sensor card with inline annotation icons. * Canvas rendering is delegated to sensor-chart; annotation list to sensor-records. */ - var HassRecordsSensorCard = class extends i$2 { + var HassDatapointsSensorCard = class extends i$2 { constructor() { super(); _defineProperty(this, "_initialized", false); @@ -36422,14 +36422,14 @@ }; } }; - _defineProperty(HassRecordsSensorCard, "properties", { + _defineProperty(HassDatapointsSensorCard, "properties", { _config: { state: true }, _hass: { state: true }, _annEvents: { state: true }, _hiddenEventIds: { state: true }, _recordsFooterHeight: { state: true } }); - _defineProperty(HassRecordsSensorCard, "styles", styles$7); + _defineProperty(HassDatapointsSensorCard, "styles", styles$7); //#endregion //#region custom_components/hass_datapoints/src/cards/sensor/editor.styles.ts var styles$2 = i$5``; @@ -36582,7 +36582,7 @@ customElements.define("editor-select", EditorSelect); //#endregion //#region custom_components/hass_datapoints/src/cards/sensor/editor.ts - var HassRecordsSensorCardEditor = class extends EditorBase { + var HassDatapointsSensorCardEditor = class extends EditorBase { render() { const c = this._config; const showRecords = !!c.show_records; @@ -36659,61 +36659,212 @@ `; } }; - _defineProperty(HassRecordsSensorCardEditor, "styles", [EditorBase.styles, styles$2]); + _defineProperty(HassDatapointsSensorCardEditor, "styles", [EditorBase.styles, styles$2]); //#endregion //#region custom_components/hass_datapoints/src/register.ts /** * Register all custom elements and advertise them to the Lovelace card picker. */ - if (!customElements.get("hass-datapoints-action-card")) customElements.define("hass-datapoints-action-card", HassRecordsActionCard); - if (!customElements.get("hass-datapoints-quick-card")) customElements.define("hass-datapoints-quick-card", HassRecordsQuickCard); - if (!customElements.get("hass-datapoints-history-card")) customElements.define("hass-datapoints-history-card", HassRecordsHistoryCard); - if (!customElements.get("hass-datapoints-sensor-card")) customElements.define("hass-datapoints-sensor-card", HassRecordsSensorCard); - if (!customElements.get("hass-datapoints-list-card")) customElements.define("hass-datapoints-list-card", HassRecordsListCard); - if (!customElements.get("hass-datapoints-history-panel")) customElements.define("hass-datapoints-history-panel", HassRecordsHistoryPanel); - if (!customElements.get("hass-datapoints-dev-tool-card")) customElements.define("hass-datapoints-dev-tool-card", HassRecordsDevToolCard); - if (!customElements.get("hass-datapoints-action-card-editor")) customElements.define("hass-datapoints-action-card-editor", HassRecordsActionCardEditor); - if (!customElements.get("hass-datapoints-quick-card-editor")) customElements.define("hass-datapoints-quick-card-editor", HassRecordsQuickCardEditor); - if (!customElements.get("hass-datapoints-history-card-editor")) customElements.define("hass-datapoints-history-card-editor", HassRecordsHistoryCardEditor); - if (!customElements.get("hass-datapoints-sensor-card-editor")) customElements.define("hass-datapoints-sensor-card-editor", HassRecordsSensorCardEditor); - if (!customElements.get("hass-datapoints-list-card-editor")) customElements.define("hass-datapoints-list-card-editor", HassRecordsListCardEditor); - if (!customElements.get("hass-datapoints-dev-tool-card-editor")) customElements.define("hass-datapoints-dev-tool-card-editor", HassRecordsDevToolCardEditor); + var _warnOnceKey = "__HASS_DATAPOINTS_DEPRECATED_TAG_WARNED__"; + function _warnDeprecatedTag(oldTag, newTag) { + const win = window; + if (!win[_warnOnceKey]) win[_warnOnceKey] = {}; + const warned = win[_warnOnceKey]; + if (warned[oldTag]) return; + warned[oldTag] = true; + console.warn(`[hass-datapoints] deprecated card tag "${oldTag}" in use; migrate to "${newTag}".`); + } + function _defineDeprecatedAlias({ oldTag, newTag, base }) { + if (customElements.get(oldTag)) return; + class DeprecatedAlias extends base { + constructor() { + super(); + _warnDeprecatedTag(oldTag, newTag); + } + } + customElements.define(oldTag, DeprecatedAlias); + } + var HassDatapointsRemovedStatisticsCard = class extends HTMLElement { + constructor(..._args) { + super(..._args); + _defineProperty(this, "_config", {}); + } + setConfig(config) { + this._config = config ?? {}; + this._render(); + } + set hass(_hass) {} + connectedCallback() { + this._render(); + } + _render() { + this.innerHTML = ` + +
${this._config?.title || "Statistics card"}
+
+

+ Datapoints statistics card removed. + Use custom:hass-datapoints-history-card instead. +

+

+ Deprecated types: custom:hass-datapoints-statistics-card, + custom:hass-records-statistics-card. +

+
+
+ `; + } + }; + var HassDatapointsRemovedStatisticsCardEditor = class extends HTMLElement { + setConfig(_config) { + this._render(); + } + set hass(_hass) {} + connectedCallback() { + this._render(); + } + _render() { + this.innerHTML = ` + +
+

+ Datapoints statistics card removed. + Replace with custom:hass-datapoints-history-card. +

+
+
+ `; + } + }; + if (!customElements.get("hass-datapoints-action-card")) customElements.define("hass-datapoints-action-card", HassDatapointsActionCard); + if (!customElements.get("hass-datapoints-quick-card")) customElements.define("hass-datapoints-quick-card", HassDatapointsQuickCard); + if (!customElements.get("hass-datapoints-history-card")) customElements.define("hass-datapoints-history-card", HassDatapointsHistoryCard); + if (!customElements.get("hass-datapoints-sensor-card")) customElements.define("hass-datapoints-sensor-card", HassDatapointsSensorCard); + if (!customElements.get("hass-datapoints-list-card")) customElements.define("hass-datapoints-list-card", HassDatapointsListCard); + if (!customElements.get("hass-datapoints-history-panel")) customElements.define("hass-datapoints-history-panel", HassDatapointsHistoryPanel); + if (!customElements.get("hass-datapoints-dev-tool-card")) customElements.define("hass-datapoints-dev-tool-card", HassDatapointsDevToolCard); + _defineDeprecatedAlias({ + oldTag: "hass-records-action-card", + newTag: "hass-datapoints-action-card", + base: HassDatapointsActionCard + }); + _defineDeprecatedAlias({ + oldTag: "hass-records-quick-card", + newTag: "hass-datapoints-quick-card", + base: HassDatapointsQuickCard + }); + _defineDeprecatedAlias({ + oldTag: "hass-records-history-card", + newTag: "hass-datapoints-history-card", + base: HassDatapointsHistoryCard + }); + _defineDeprecatedAlias({ + oldTag: "hass-records-sensor-card", + newTag: "hass-datapoints-sensor-card", + base: HassDatapointsSensorCard + }); + _defineDeprecatedAlias({ + oldTag: "hass-records-list-card", + newTag: "hass-datapoints-list-card", + base: HassDatapointsListCard + }); + _defineDeprecatedAlias({ + oldTag: "hass-records-dev-tool-card", + newTag: "hass-datapoints-dev-tool-card", + base: HassDatapointsDevToolCard + }); + if (!customElements.get("hass-datapoints-statistics-card")) customElements.define("hass-datapoints-statistics-card", HassDatapointsRemovedStatisticsCard); + if (!customElements.get("hass-records-statistics-card")) { + class HassRecordsRemovedStatisticsCard extends HassDatapointsRemovedStatisticsCard { + constructor() { + super(); + _warnDeprecatedTag("hass-records-statistics-card", "hass-datapoints-history-card"); + } + } + customElements.define("hass-records-statistics-card", HassRecordsRemovedStatisticsCard); + } + if (!customElements.get("hass-datapoints-action-card-editor")) customElements.define("hass-datapoints-action-card-editor", HassDatapointsActionCardEditor); + if (!customElements.get("hass-datapoints-quick-card-editor")) customElements.define("hass-datapoints-quick-card-editor", HassDatapointsQuickCardEditor); + if (!customElements.get("hass-datapoints-history-card-editor")) customElements.define("hass-datapoints-history-card-editor", HassDatapointsHistoryCardEditor); + if (!customElements.get("hass-datapoints-sensor-card-editor")) customElements.define("hass-datapoints-sensor-card-editor", HassDatapointsSensorCardEditor); + if (!customElements.get("hass-datapoints-list-card-editor")) customElements.define("hass-datapoints-list-card-editor", HassDatapointsListCardEditor); + if (!customElements.get("hass-datapoints-dev-tool-card-editor")) customElements.define("hass-datapoints-dev-tool-card-editor", HassDatapointsDevToolCardEditor); + _defineDeprecatedAlias({ + oldTag: "hass-records-action-card-editor", + newTag: "hass-datapoints-action-card-editor", + base: HassDatapointsActionCardEditor + }); + _defineDeprecatedAlias({ + oldTag: "hass-records-quick-card-editor", + newTag: "hass-datapoints-quick-card-editor", + base: HassDatapointsQuickCardEditor + }); + _defineDeprecatedAlias({ + oldTag: "hass-records-history-card-editor", + newTag: "hass-datapoints-history-card-editor", + base: HassDatapointsHistoryCardEditor + }); + _defineDeprecatedAlias({ + oldTag: "hass-records-sensor-card-editor", + newTag: "hass-datapoints-sensor-card-editor", + base: HassDatapointsSensorCardEditor + }); + _defineDeprecatedAlias({ + oldTag: "hass-records-list-card-editor", + newTag: "hass-datapoints-list-card-editor", + base: HassDatapointsListCardEditor + }); + _defineDeprecatedAlias({ + oldTag: "hass-records-dev-tool-card-editor", + newTag: "hass-datapoints-dev-tool-card-editor", + base: HassDatapointsDevToolCardEditor + }); + if (!customElements.get("hass-datapoints-statistics-card-editor")) customElements.define("hass-datapoints-statistics-card-editor", HassDatapointsRemovedStatisticsCardEditor); + if (!customElements.get("hass-records-statistics-card-editor")) { + class HassRecordsRemovedStatisticsCardEditor extends HassDatapointsRemovedStatisticsCardEditor { + constructor() { + super(); + _warnDeprecatedTag("hass-records-statistics-card-editor", "hass-datapoints-history-card"); + } + } + customElements.define("hass-records-statistics-card-editor", HassRecordsRemovedStatisticsCardEditor); + } window.customCards = window.customCards || []; var registeredTypes = new Set(window.customCards.map((card) => card.type)); [ { type: "hass-datapoints-action-card", - name: "Hass Records – Action Card", + name: "Datapoints – Action Card", description: "Full form to record a custom event with message, annotation, icon, colour and entity association.", preview: false }, { type: "hass-datapoints-quick-card", - name: "Hass Records – Quick Card", + name: "Datapoints – Quick Card", description: "Simple one-field card to quickly record a note with a bookmark icon.", preview: false }, { type: "hass-datapoints-history-card", - name: "Hass Records – History Card", + name: "Datapoints – History Card", description: "History line chart with coloured annotation markers for recorded events.", preview: false }, { type: "hass-datapoints-sensor-card", - name: "Hass Records – Sensor Card", + name: "Datapoints – Sensor Card", description: "Sensor card with line chart — annotations shown as icons on the data line.", preview: false }, { type: "hass-datapoints-list-card", - name: "Hass Records – List Card", + name: "Datapoints – List Card", description: "Activity-style datagrid to browse, search, edit and delete all recorded events.", preview: false }, { type: "hass-datapoints-dev-tool-card", - name: "Hass Records – Dev Tool", + name: "Datapoints – Dev Tool", description: "Generate demo datapoints from HA history and bulk-delete dev-flagged events.", preview: false } diff --git a/custom_components/hass_datapoints/src/__tests__/register-compat.spec.ts b/custom_components/hass_datapoints/src/__tests__/register-compat.spec.ts new file mode 100644 index 00000000..ff35dc31 --- /dev/null +++ b/custom_components/hass_datapoints/src/__tests__/register-compat.spec.ts @@ -0,0 +1,53 @@ +import { beforeAll, describe, expect, it, vi } from "vitest"; + +describe("register", () => { + beforeAll(async () => { + document.body.innerHTML = ""; + (window as unknown as { customCards?: unknown[] }).customCards = []; + + vi.spyOn(console, "groupCollapsed").mockImplementation(() => {}); + vi.spyOn(console, "log").mockImplementation(() => {}); + vi.spyOn(console, "groupEnd").mockImplementation(() => {}); + + await import("../register"); + }, 30_000); + + describe("GIVEN register.ts is loaded", () => { + describe("WHEN Lovelace custom card metadata is registered", () => { + it("THEN names use Datapoints prefix (not Hass Records/Hass Datapoints)", async () => { + expect.assertions(3); + + const cards = window.customCards || []; + const names = cards.map((c) => c.name).filter(Boolean) as string[]; + + expect(names.length).toBeGreaterThan(0); + expect(names.every((name) => name.startsWith("Datapoints – "))).toBe( + true + ); + expect( + names.some( + (name) => name.includes("Hass Records") || name.includes("Hass ") + ) + ).toBe(false); + }); + }); + + describe("WHEN old hass-records tags are referenced", () => { + it("THEN deprecated aliases exist for old dashboards", async () => { + expect.assertions(8); + + expect(customElements.get("hass-records-action-card")).toBeTruthy(); + expect(customElements.get("hass-records-quick-card")).toBeTruthy(); + expect(customElements.get("hass-records-history-card")).toBeTruthy(); + expect(customElements.get("hass-records-sensor-card")).toBeTruthy(); + expect(customElements.get("hass-records-list-card")).toBeTruthy(); + expect(customElements.get("hass-records-dev-tool-card")).toBeTruthy(); + + expect(customElements.get("hass-records-statistics-card")).toBeTruthy(); + expect( + customElements.get("hass-datapoints-statistics-card") + ).toBeTruthy(); + }); + }); + }); +}); diff --git a/custom_components/hass_datapoints/src/cards/action/__tests__/action.spec.ts b/custom_components/hass_datapoints/src/cards/action/__tests__/action.spec.ts index ca9fdda0..5bbbc179 100644 --- a/custom_components/hass_datapoints/src/cards/action/__tests__/action.spec.ts +++ b/custom_components/hass_datapoints/src/cards/action/__tests__/action.spec.ts @@ -1,9 +1,12 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createMockHass } from "@/test-support/mock-hass"; -import { HassRecordsActionCard } from "../action.ts"; +import { HassDatapointsActionCard } from "../action.ts"; if (!customElements.get("hass-datapoints-action-card")) { - customElements.define("hass-datapoints-action-card", HassRecordsActionCard); + customElements.define( + "hass-datapoints-action-card", + HassDatapointsActionCard + ); } function createCard(config: RecordWithUnknownValues = {}) { @@ -227,7 +230,7 @@ describe("action", () => { describe("GIVEN the static config methods", () => { describe("WHEN getStubConfig is called", () => { it("THEN it returns default config", () => { - expect(HassRecordsActionCard.getStubConfig()).toEqual({ + expect(HassDatapointsActionCard.getStubConfig()).toEqual({ title: "Record Event", }); }); @@ -235,7 +238,7 @@ describe("action", () => { describe("WHEN getConfigElement is called", () => { it("THEN it returns the editor element tag", () => { - const editorEl = HassRecordsActionCard.getConfigElement(); + const editorEl = HassDatapointsActionCard.getConfigElement(); expect(editorEl.tagName.toLowerCase()).toBe( "hass-datapoints-action-card-editor" ); diff --git a/custom_components/hass_datapoints/src/cards/action/action.ts b/custom_components/hass_datapoints/src/cards/action/action.ts index 04dcfbef..5dc57061 100644 --- a/custom_components/hass_datapoints/src/cards/action/action.ts +++ b/custom_components/hass_datapoints/src/cards/action/action.ts @@ -21,7 +21,7 @@ import { logger } from "@/lib/logger"; * - A ha-selector (target schema) lets the user add extra items per recording. * - On submit both are merged; the selector resets to empty afterwards. */ -export class HassRecordsActionCard extends LitElement { +export class HassDatapointsActionCard extends LitElement { static properties = { _config: { state: true }, _hass: { state: true }, diff --git a/custom_components/hass_datapoints/src/cards/action/editor.ts b/custom_components/hass_datapoints/src/cards/action/editor.ts index 7222424e..f0217d0f 100644 --- a/custom_components/hass_datapoints/src/cards/action/editor.ts +++ b/custom_components/hass_datapoints/src/cards/action/editor.ts @@ -16,7 +16,7 @@ type TargetPickerElement = Element & { value?: TargetPickerValue; }; -export class HassRecordsActionCardEditor extends EditorBase { +export class HassDatapointsActionCardEditor extends EditorBase { static styles: CSSResultGroup = [EditorBase.styles, styles]; private _configTarget(): TargetPickerValue { diff --git a/custom_components/hass_datapoints/src/cards/action/stories/action.stories.ts b/custom_components/hass_datapoints/src/cards/action/stories/action.stories.ts index 4c68b1ed..19b4e582 100644 --- a/custom_components/hass_datapoints/src/cards/action/stories/action.stories.ts +++ b/custom_components/hass_datapoints/src/cards/action/stories/action.stories.ts @@ -2,10 +2,13 @@ import type { Meta, StoryObj } from "@storybook/web-components-vite"; import { expect } from "@storybook/test"; import { html } from "lit"; import { createMockHass } from "@/test-support/mock-hass"; -import { HassRecordsActionCard } from "../action"; +import { HassDatapointsActionCard } from "../action"; if (!customElements.get("hass-datapoints-action-card")) { - customElements.define("hass-datapoints-action-card", HassRecordsActionCard); + customElements.define( + "hass-datapoints-action-card", + HassDatapointsActionCard + ); } type Story = StoryObj; @@ -17,10 +20,10 @@ const meta: Meta = { export default meta; -function getCard(canvasElement: HTMLElement): HassRecordsActionCard { +function getCard(canvasElement: HTMLElement): HassDatapointsActionCard { return canvasElement.querySelector( "hass-datapoints-action-card" - ) as HassRecordsActionCard; + ) as HassDatapointsActionCard; } const mockHass = createMockHass(); @@ -29,7 +32,7 @@ export const Default: Story = { render: () => html``, play: async ({ canvasElement }) => { - const card = getCard(canvasElement) as HassRecordsActionCard & { + const card = getCard(canvasElement) as HassDatapointsActionCard & { updateComplete: Promise; }; card.setConfig({ title: "Record Event" }); @@ -44,7 +47,7 @@ export const WithTargetsAndAnnotation: Story = { render: () => html``, play: async ({ canvasElement }) => { - const card = getCard(canvasElement) as HassRecordsActionCard & { + const card = getCard(canvasElement) as HassDatapointsActionCard & { updateComplete: Promise; }; card.setConfig({ diff --git a/custom_components/hass_datapoints/src/cards/dev-tool/__tests__/dev-tool.spec.ts b/custom_components/hass_datapoints/src/cards/dev-tool/__tests__/dev-tool.spec.ts index 5900fb7e..de9cceaa 100644 --- a/custom_components/hass_datapoints/src/cards/dev-tool/__tests__/dev-tool.spec.ts +++ b/custom_components/hass_datapoints/src/cards/dev-tool/__tests__/dev-tool.spec.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createMockHass } from "@/test-support/mock-hass"; -import { HassRecordsDevToolCard } from "../dev-tool.ts"; +import { HassDatapointsDevToolCard } from "../dev-tool.ts"; vi.mock("@/helpers.js", async (importOriginal) => { const mod = (await importOriginal()) as RecordWithUnknownValues; @@ -14,7 +14,7 @@ vi.mock("@/helpers.js", async (importOriginal) => { if (!customElements.get("hass-datapoints-dev-tool-card")) { customElements.define( "hass-datapoints-dev-tool-card", - HassRecordsDevToolCard + HassDatapointsDevToolCard ); } @@ -186,14 +186,16 @@ describe("dev-tool", () => { describe("WHEN getStubConfig is called", () => { it("THEN it returns default config", () => { expect.assertions(1); - expect(HassRecordsDevToolCard.getStubConfig()).toHaveProperty("title"); + expect(HassDatapointsDevToolCard.getStubConfig()).toHaveProperty( + "title" + ); }); }); describe("WHEN getConfigElement is called", () => { it("THEN it returns the dev-tool editor element tag", () => { expect.assertions(1); - const editorEl = HassRecordsDevToolCard.getConfigElement(); + const editorEl = HassDatapointsDevToolCard.getConfigElement(); expect(editorEl.tagName.toLowerCase()).toBe( "hass-datapoints-dev-tool-card-editor" ); diff --git a/custom_components/hass_datapoints/src/cards/dev-tool/dev-tool.ts b/custom_components/hass_datapoints/src/cards/dev-tool/dev-tool.ts index 74ed11e4..234acc69 100644 --- a/custom_components/hass_datapoints/src/cards/dev-tool/dev-tool.ts +++ b/custom_components/hass_datapoints/src/cards/dev-tool/dev-tool.ts @@ -9,12 +9,12 @@ import type { WindowConfig, WindowResult, } from "@/cards/dev-tool/types"; -import { HassRecordsDevToolCardEditor } from "./editor"; +import { HassDatapointsDevToolCardEditor } from "./editor"; import "@/atoms/display/feedback-banner/feedback-banner"; import "@/cards/dev-tool/dev-tool-results/dev-tool-results"; import "@/cards/dev-tool/dev-tool-windows/dev-tool-windows"; -export class HassRecordsDevToolCard extends HTMLElement { +export class HassDatapointsDevToolCard extends HTMLElement { _config: RecordWithUnknownValues = {}; _hass: Nullable = null; @@ -625,4 +625,4 @@ export class HassRecordsDevToolCard extends HTMLElement { } } -export { HassRecordsDevToolCardEditor }; +export { HassDatapointsDevToolCardEditor }; diff --git a/custom_components/hass_datapoints/src/cards/dev-tool/editor.ts b/custom_components/hass_datapoints/src/cards/dev-tool/editor.ts index e52bbaeb..1cdab67d 100644 --- a/custom_components/hass_datapoints/src/cards/dev-tool/editor.ts +++ b/custom_components/hass_datapoints/src/cards/dev-tool/editor.ts @@ -2,7 +2,7 @@ import { CSSResultGroup, html } from "lit"; import { msg } from "@/lib/i18n/localize"; import { EditorBase } from "@/molecules/editor-base/editor-base"; -export class HassRecordsDevToolCardEditor extends EditorBase { +export class HassDatapointsDevToolCardEditor extends EditorBase { static styles: CSSResultGroup = [EditorBase.styles]; render() { diff --git a/custom_components/hass_datapoints/src/cards/dev-tool/stories/dev-tool.stories.ts b/custom_components/hass_datapoints/src/cards/dev-tool/stories/dev-tool.stories.ts index ebf9d7b5..be27f197 100644 --- a/custom_components/hass_datapoints/src/cards/dev-tool/stories/dev-tool.stories.ts +++ b/custom_components/hass_datapoints/src/cards/dev-tool/stories/dev-tool.stories.ts @@ -2,12 +2,12 @@ import type { Meta, StoryObj } from "@storybook/web-components-vite"; import { expect } from "@storybook/test"; import { html } from "lit"; import { createMockHass } from "@/test-support/mock-hass"; -import { HassRecordsDevToolCard } from "../dev-tool"; +import { HassDatapointsDevToolCard } from "../dev-tool"; if (!customElements.get("hass-datapoints-dev-tool-card")) { customElements.define( "hass-datapoints-dev-tool-card", - HassRecordsDevToolCard + HassDatapointsDevToolCard ); } @@ -20,10 +20,10 @@ const meta: Meta = { export default meta; -function getCard(canvasElement: HTMLElement): HassRecordsDevToolCard { +function getCard(canvasElement: HTMLElement): HassDatapointsDevToolCard { return canvasElement.querySelector( "hass-datapoints-dev-tool-card" - ) as HassRecordsDevToolCard; + ) as HassDatapointsDevToolCard; } function makeMockHass() { @@ -87,7 +87,7 @@ export const WithResults: Story = { render: () => html``, play: async ({ canvasElement }) => { - const card = getCard(canvasElement) as HassRecordsDevToolCard & { + const card = getCard(canvasElement) as HassDatapointsDevToolCard & { _entities: string[]; _analyzeHistory: () => Promise; }; diff --git a/custom_components/hass_datapoints/src/cards/history/__tests__/editor.spec.ts b/custom_components/hass_datapoints/src/cards/history/__tests__/editor.spec.ts index 406b219c..7dec4880 100644 --- a/custom_components/hass_datapoints/src/cards/history/__tests__/editor.spec.ts +++ b/custom_components/hass_datapoints/src/cards/history/__tests__/editor.spec.ts @@ -1,18 +1,18 @@ import { afterEach, describe, expect, it } from "vitest"; import { createMockHass } from "@/test-support/mock-hass"; -import { HassRecordsHistoryCardEditor } from "../editor"; +import { HassDatapointsHistoryCardEditor } from "../editor"; if (!customElements.get("hass-datapoints-history-card-editor")) { customElements.define( "hass-datapoints-history-card-editor", - HassRecordsHistoryCardEditor + HassDatapointsHistoryCardEditor ); } function createEditor() { const el = document.createElement( "hass-datapoints-history-card-editor" - ) as HassRecordsHistoryCardEditor; + ) as HassDatapointsHistoryCardEditor; document.body.appendChild(el); el.hass = createMockHass(); el.setConfig({ @@ -34,7 +34,7 @@ function createEditor() { } describe("history editor", () => { - let el: Nullable = null; + let el: Nullable = null; afterEach(() => { el?.remove(); diff --git a/custom_components/hass_datapoints/src/cards/history/__tests__/history.spec.ts b/custom_components/hass_datapoints/src/cards/history/__tests__/history.spec.ts index 4616d276..fd9d16cc 100644 --- a/custom_components/hass_datapoints/src/cards/history/__tests__/history.spec.ts +++ b/custom_components/hass_datapoints/src/cards/history/__tests__/history.spec.ts @@ -1,5 +1,5 @@ /** - * history.spec.ts — GIVEN/WHEN/THEN tests for HassRecordsHistoryCard. + * history.spec.ts — GIVEN/WHEN/THEN tests for HassDatapointsHistoryCard. * * We test the public API surface: setConfig validation/normalisation, * _entityIds getter, static helpers, and basic rendering. @@ -55,10 +55,10 @@ vi.mock("@/lib/ha/navigation", async (importOriginal) => { }); // Import the card AFTER mocks are set up -let HassRecordsHistoryCard: typeof import("../history.ts").HassRecordsHistoryCard; +let HassDatapointsHistoryCard: typeof import("../history.ts").HassDatapointsHistoryCard; beforeAll(async () => { - ({ HassRecordsHistoryCard } = await import("../history.ts")); + ({ HassDatapointsHistoryCard } = await import("../history.ts")); }); // ── Helpers ─────────────────────────────────────────────────────────────────── @@ -86,7 +86,7 @@ function createMockHass(overrides = {}) { function createCard( config: RecordWithUnknownValues = { entity: "sensor.example" } ) { - const el = new HassRecordsHistoryCard(); + const el = new HassDatapointsHistoryCard(); el.setConfig(config); return el; } @@ -104,7 +104,7 @@ describe("history", () => { describe("WHEN setConfig is called", () => { it("THEN it throws an error", () => { expect.assertions(1); - const el = new HassRecordsHistoryCard(); + const el = new HassDatapointsHistoryCard(); expect(() => el.setConfig({})).toThrow(); }); }); @@ -418,7 +418,7 @@ describe("history", () => { describe("WHEN getStubConfig is called", () => { it("THEN it returns an object with entity and hours_to_show", () => { expect.assertions(2); - const stub = HassRecordsHistoryCard.getStubConfig(); + const stub = HassDatapointsHistoryCard.getStubConfig(); expect(stub).toHaveProperty("entity"); expect(stub).toHaveProperty("hours_to_show"); }); @@ -427,7 +427,7 @@ describe("history", () => { describe("WHEN getConfigElement is called", () => { it("THEN it returns the editor element tag name", () => { expect.assertions(1); - const editor = HassRecordsHistoryCard.getConfigElement(); + const editor = HassDatapointsHistoryCard.getConfigElement(); expect(editor.tagName.toLowerCase()).toBe( "hass-datapoints-history-card-editor" ); @@ -438,7 +438,7 @@ describe("history", () => { // ── Rendering ──────────────────────────────────────────────────────────── describe("GIVEN a card connected to the DOM with hass", () => { - let el: HassRecordsHistoryCard; + let el: HassDatapointsHistoryCard; beforeEach(async () => { el = createCard({ entity: "sensor.example", title: "Test Chart" }); diff --git a/custom_components/hass_datapoints/src/cards/history/editor.ts b/custom_components/hass_datapoints/src/cards/history/editor.ts index f1c35246..21ab3c2e 100644 --- a/custom_components/hass_datapoints/src/cards/history/editor.ts +++ b/custom_components/hass_datapoints/src/cards/history/editor.ts @@ -40,7 +40,7 @@ type AnalysisChangeDetail = { value?: unknown; }; -export class HassRecordsHistoryCardEditor extends EditorBase { +export class HassDatapointsHistoryCardEditor extends EditorBase { static styles: CSSResultGroup = [EditorBase.styles, styles]; private _configTarget(): TargetPickerValue { diff --git a/custom_components/hass_datapoints/src/cards/history/history-chart/__tests__/history-chart-height.spec.ts b/custom_components/hass_datapoints/src/cards/history/history-chart/__tests__/history-chart-height.spec.ts new file mode 100644 index 00000000..f9d3c7af --- /dev/null +++ b/custom_components/hass_datapoints/src/cards/history/history-chart/__tests__/history-chart-height.spec.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from "vitest"; +import { HistoryChart } from "../history-chart"; + +describe("history-chart height", () => { + describe("GIVEN ha-card has no measurable height", () => { + describe("WHEN available height is computed repeatedly", () => { + it("THEN it does not grow via host height feedback loop", () => { + expect.assertions(3); + + const card = document.createElement("ha-card") as HTMLElement; + const header = document.createElement("div"); + header.className = "card-header"; + card.appendChild(header); + + document.body.appendChild(card); + + const chart = new HistoryChart(); + card.appendChild(chart); + + // Simulate runaway host sizing from a previous render. + Object.defineProperty(chart, "clientHeight", { + configurable: true, + get() { + return 10_000; + }, + }); + + const first = ( + chart as unknown as { _getAvailableChartHeight: any } + )._getAvailableChartHeight(280) as number; + const second = ( + chart as unknown as { _getAvailableChartHeight: any } + )._getAvailableChartHeight(280) as number; + + expect(first).toBe(280); + expect(second).toBe(280); + expect(second).toBe(first); + }); + }); + }); +}); diff --git a/custom_components/hass_datapoints/src/cards/history/history-chart/history-chart.ts b/custom_components/hass_datapoints/src/cards/history/history-chart/history-chart.ts index de542741..82a8a2c7 100644 --- a/custom_components/hass_datapoints/src/cards/history/history-chart/history-chart.ts +++ b/custom_components/hass_datapoints/src/cards/history/history-chart/history-chart.ts @@ -440,7 +440,6 @@ export class HistoryChart extends HTMLElement { const scrollViewport = this._el( "chart-scroll-viewport" ) as Nullable; - const wrap = this as HTMLElement; const cardHeight = card?.clientHeight || 0; const occupiedHeight = @@ -451,12 +450,17 @@ export class HistoryChart extends HTMLElement { ? Math.max(0, cardHeight - occupiedHeight) : 0; const viewportHeight = scrollViewport?.clientHeight || 0; - const wrapHeight = wrap?.clientHeight || 0; - return Math.max( - minChartHeight, - cardDerivedHeight || viewportHeight || wrapHeight || 0 - ); + // Avoid using the host element height as a fallback. The host height can + // include previous chart sizing + padding, causing a feedback loop: + // ResizeObserver -> redraw -> measure host height -> grow -> resize -> … + if (cardDerivedHeight) { + return Math.max(minChartHeight, cardDerivedHeight); + } + if (viewportHeight) { + return Math.max(minChartHeight, viewportHeight); + } + return minChartHeight; } _syncTopSlotOffset(): void { diff --git a/custom_components/hass_datapoints/src/cards/history/history.ts b/custom_components/hass_datapoints/src/cards/history/history.ts index e840aa3a..f550d121 100644 --- a/custom_components/hass_datapoints/src/cards/history/history.ts +++ b/custom_components/hass_datapoints/src/cards/history/history.ts @@ -69,7 +69,7 @@ interface PartialLoadState { // ── Card ────────────────────────────────────────────────────────────────────── -export class HassRecordsHistoryCard extends ChartCardBase { +export class HassDatapointsHistoryCard extends ChartCardBase { static styles = styles; // ── State ────────────────────────────────────────────────────────────────── @@ -1641,4 +1641,7 @@ export class HassRecordsHistoryCard extends ChartCardBase { } } -customElements.define("hass-datapoints-history-card", HassRecordsHistoryCard); +customElements.define( + "hass-datapoints-history-card", + HassDatapointsHistoryCard +); diff --git a/custom_components/hass_datapoints/src/cards/history/stories/history.stories.ts b/custom_components/hass_datapoints/src/cards/history/stories/history.stories.ts index 573d1e51..46ed1fa9 100644 --- a/custom_components/hass_datapoints/src/cards/history/stories/history.stories.ts +++ b/custom_components/hass_datapoints/src/cards/history/stories/history.stories.ts @@ -8,11 +8,14 @@ */ import type { Meta, StoryObj } from "@storybook/web-components-vite"; import { html } from "lit"; -import { HassRecordsHistoryCard } from "../history"; +import { HassDatapointsHistoryCard } from "../history"; // Self-registers if not already defined (guards against HMR double-registration). if (!customElements.get("hass-datapoints-history-card")) { - customElements.define("hass-datapoints-history-card", HassRecordsHistoryCard); + customElements.define( + "hass-datapoints-history-card", + HassDatapointsHistoryCard + ); } type Story = StoryObj; diff --git a/custom_components/hass_datapoints/src/cards/list/__tests__/list.spec.ts b/custom_components/hass_datapoints/src/cards/list/__tests__/list.spec.ts index c3d8426f..6ee8533a 100644 --- a/custom_components/hass_datapoints/src/cards/list/__tests__/list.spec.ts +++ b/custom_components/hass_datapoints/src/cards/list/__tests__/list.spec.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createMockHass } from "@/test-support/mock-hass"; -import { HassRecordsListCard } from "../list.ts"; +import { HassDatapointsListCard } from "../list.ts"; import type { EventRecord } from "@/lib/types"; import { fetchEvents } from "@/lib/data/events-api"; @@ -25,7 +25,7 @@ vi.mock("@/lib/data/events-api", async (importOriginal) => { }); if (!customElements.get("hass-datapoints-list-card")) { - customElements.define("hass-datapoints-list-card", HassRecordsListCard); + customElements.define("hass-datapoints-list-card", HassDatapointsListCard); } const mockEvents: EventRecord[] = [ @@ -279,13 +279,13 @@ describe("list", () => { describe("GIVEN the static config methods", () => { describe("WHEN getStubConfig is called", () => { it("THEN it returns empty config", () => { - expect(HassRecordsListCard.getStubConfig()).toEqual({}); + expect(HassDatapointsListCard.getStubConfig()).toEqual({}); }); }); describe("WHEN getConfigElement is called", () => { it("THEN it returns the editor element tag", () => { - const editorEl = HassRecordsListCard.getConfigElement(); + const editorEl = HassDatapointsListCard.getConfigElement(); expect(editorEl.tagName.toLowerCase()).toBe( "hass-datapoints-list-card-editor" ); diff --git a/custom_components/hass_datapoints/src/cards/list/editor.ts b/custom_components/hass_datapoints/src/cards/list/editor.ts index d9069c9b..a9fab8f6 100644 --- a/custom_components/hass_datapoints/src/cards/list/editor.ts +++ b/custom_components/hass_datapoints/src/cards/list/editor.ts @@ -14,7 +14,7 @@ type TargetPickerElement = Element & { value?: TargetPickerValue; }; -export class HassRecordsListCardEditor extends EditorBase { +export class HassDatapointsListCardEditor extends EditorBase { static styles: CSSResultGroup = [EditorBase.styles, styles]; private _configTarget(): TargetPickerValue { diff --git a/custom_components/hass_datapoints/src/cards/list/list.ts b/custom_components/hass_datapoints/src/cards/list/list.ts index afa5cfd8..60f95cd9 100644 --- a/custom_components/hass_datapoints/src/cards/list/list.ts +++ b/custom_components/hass_datapoints/src/cards/list/list.ts @@ -22,7 +22,7 @@ import { logger } from "@/lib/logger"; * hass-datapoints-list-card – Activity-style datagrid with search, pagination, edit/delete. */ @localized() -export class HassRecordsListCard extends LitElement { +export class HassDatapointsListCard extends LitElement { static properties = { _config: { state: true }, _hass: { state: true }, diff --git a/custom_components/hass_datapoints/src/cards/list/stories/list.stories.ts b/custom_components/hass_datapoints/src/cards/list/stories/list.stories.ts index 4592a75c..e31c8ae7 100644 --- a/custom_components/hass_datapoints/src/cards/list/stories/list.stories.ts +++ b/custom_components/hass_datapoints/src/cards/list/stories/list.stories.ts @@ -2,10 +2,10 @@ import type { Meta, StoryObj } from "@storybook/web-components-vite"; import { expect } from "@storybook/test"; import { html } from "lit"; import { createMockHass } from "@/test-support/mock-hass"; -import { HassRecordsListCard } from "../list"; +import { HassDatapointsListCard } from "../list"; if (!customElements.get("hass-datapoints-list-card")) { - customElements.define("hass-datapoints-list-card", HassRecordsListCard); + customElements.define("hass-datapoints-list-card", HassDatapointsListCard); } type Story = StoryObj; @@ -17,10 +17,10 @@ const meta: Meta = { export default meta; -function getCard(canvasElement: HTMLElement): HassRecordsListCard { +function getCard(canvasElement: HTMLElement): HassDatapointsListCard { return canvasElement.querySelector( "hass-datapoints-list-card" - ) as HassRecordsListCard; + ) as HassDatapointsListCard; } function makeMockHass() { @@ -80,7 +80,7 @@ function makeMockHass() { export const Default: Story = { render: () => html``, play: async ({ canvasElement }) => { - const card = getCard(canvasElement) as HassRecordsListCard & { + const card = getCard(canvasElement) as HassDatapointsListCard & { updateComplete: Promise; _load: () => Promise; }; @@ -96,7 +96,7 @@ export const Default: Story = { export const WithoutSearch: Story = { render: () => html``, play: async ({ canvasElement }) => { - const card = getCard(canvasElement) as HassRecordsListCard & { + const card = getCard(canvasElement) as HassDatapointsListCard & { updateComplete: Promise; _load: () => Promise; }; diff --git a/custom_components/hass_datapoints/src/cards/quick/__tests__/quick.spec.ts b/custom_components/hass_datapoints/src/cards/quick/__tests__/quick.spec.ts index 3f4e5614..5a825459 100644 --- a/custom_components/hass_datapoints/src/cards/quick/__tests__/quick.spec.ts +++ b/custom_components/hass_datapoints/src/cards/quick/__tests__/quick.spec.ts @@ -1,9 +1,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createMockHass } from "@/test-support/mock-hass"; -import { HassRecordsQuickCard } from "../quick"; +import { HassDatapointsQuickCard } from "../quick"; if (!customElements.get("hass-datapoints-quick-card")) { - customElements.define("hass-datapoints-quick-card", HassRecordsQuickCard); + customElements.define("hass-datapoints-quick-card", HassDatapointsQuickCard); } function createCard(config: RecordWithUnknownValues = {}) { @@ -105,7 +105,7 @@ describe("quick", () => { describe("GIVEN a quick card with static config methods", () => { describe("WHEN getStubConfig is called", () => { it("THEN it returns default config", () => { - expect(HassRecordsQuickCard.getStubConfig()).toEqual({ + expect(HassDatapointsQuickCard.getStubConfig()).toEqual({ title: "Quick Record", }); }); @@ -113,7 +113,7 @@ describe("quick", () => { describe("WHEN getConfigElement is called", () => { it("THEN it returns the editor element tag", () => { - const editorEl = HassRecordsQuickCard.getConfigElement(); + const editorEl = HassDatapointsQuickCard.getConfigElement(); expect(editorEl.tagName.toLowerCase()).toBe( "hass-datapoints-quick-card-editor" ); diff --git a/custom_components/hass_datapoints/src/cards/quick/editor.ts b/custom_components/hass_datapoints/src/cards/quick/editor.ts index aae186df..cf460133 100644 --- a/custom_components/hass_datapoints/src/cards/quick/editor.ts +++ b/custom_components/hass_datapoints/src/cards/quick/editor.ts @@ -17,7 +17,7 @@ type TargetPickerElement = Element & { value?: TargetPickerValue; }; -export class HassRecordsQuickCardEditor extends EditorBase { +export class HassDatapointsQuickCardEditor extends EditorBase { static styles: CSSResultGroup = [EditorBase.styles, styles]; private _configTarget(): TargetPickerValue { diff --git a/custom_components/hass_datapoints/src/cards/quick/quick.ts b/custom_components/hass_datapoints/src/cards/quick/quick.ts index 9ec99d27..8a49162c 100644 --- a/custom_components/hass_datapoints/src/cards/quick/quick.ts +++ b/custom_components/hass_datapoints/src/cards/quick/quick.ts @@ -7,7 +7,7 @@ import type { HassLike } from "@/lib/types"; import "@/atoms/display/feedback-banner/feedback-banner"; import "@/cards/quick/quick-annotation/quick-annotation"; -export class HassRecordsQuickCard extends LitElement { +export class HassDatapointsQuickCard extends LitElement { static properties = { _config: { type: Object, state: true }, _hass: { type: Object, state: true }, diff --git a/custom_components/hass_datapoints/src/cards/quick/stories/quick.stories.ts b/custom_components/hass_datapoints/src/cards/quick/stories/quick.stories.ts index 1b1adbdd..4847f29b 100644 --- a/custom_components/hass_datapoints/src/cards/quick/stories/quick.stories.ts +++ b/custom_components/hass_datapoints/src/cards/quick/stories/quick.stories.ts @@ -2,10 +2,10 @@ import type { Meta, StoryObj } from "@storybook/web-components-vite"; import { expect } from "@storybook/test"; import { html } from "lit"; import { createMockHass } from "@/test-support/mock-hass"; -import { HassRecordsQuickCard } from "../quick"; +import { HassDatapointsQuickCard } from "../quick"; if (!customElements.get("hass-datapoints-quick-card")) { - customElements.define("hass-datapoints-quick-card", HassRecordsQuickCard); + customElements.define("hass-datapoints-quick-card", HassDatapointsQuickCard); } type Story = StoryObj; @@ -17,10 +17,10 @@ const meta: Meta = { export default meta; -function getCard(canvasElement: HTMLElement): HassRecordsQuickCard { +function getCard(canvasElement: HTMLElement): HassDatapointsQuickCard { return canvasElement.querySelector( "hass-datapoints-quick-card" - ) as HassRecordsQuickCard; + ) as HassDatapointsQuickCard; } const mockHass = createMockHass(); @@ -28,7 +28,7 @@ const mockHass = createMockHass(); export const Default: Story = { render: () => html``, play: async ({ canvasElement }) => { - const card = getCard(canvasElement) as HassRecordsQuickCard & { + const card = getCard(canvasElement) as HassDatapointsQuickCard & { updateComplete: Promise; }; card.setConfig({ title: "Quick Record" }); @@ -42,7 +42,7 @@ export const Default: Story = { export const WithAnnotation: Story = { render: () => html``, play: async ({ canvasElement }) => { - const card = getCard(canvasElement) as HassRecordsQuickCard & { + const card = getCard(canvasElement) as HassDatapointsQuickCard & { updateComplete: Promise; }; card.setConfig({ title: "Quick Record", show_annotation: true }); diff --git a/custom_components/hass_datapoints/src/cards/sensor/__tests__/sensor.spec.ts b/custom_components/hass_datapoints/src/cards/sensor/__tests__/sensor.spec.ts index f892b6c4..c2924770 100644 --- a/custom_components/hass_datapoints/src/cards/sensor/__tests__/sensor.spec.ts +++ b/custom_components/hass_datapoints/src/cards/sensor/__tests__/sensor.spec.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createMockHass } from "@/test-support/mock-hass"; -import { HassRecordsSensorCard } from "../sensor.ts"; +import { HassDatapointsSensorCard } from "../sensor.ts"; import type { EventRecord } from "@/lib/types"; import { fetchEvents } from "@/lib/data/events-api"; @@ -51,7 +51,10 @@ vi.mock("@/lib/data/events-api", async (importOriginal) => { }); if (!customElements.get("hass-datapoints-sensor-card")) { - customElements.define("hass-datapoints-sensor-card", HassRecordsSensorCard); + customElements.define( + "hass-datapoints-sensor-card", + HassDatapointsSensorCard + ); } const mockEvents: EventRecord[] = [ @@ -277,14 +280,14 @@ describe("sensor", () => { describe("GIVEN the static config methods", () => { describe("WHEN getStubConfig is called", () => { it("THEN it returns a config with an entity", () => { - const cfg = HassRecordsSensorCard.getStubConfig(); + const cfg = HassDatapointsSensorCard.getStubConfig(); expect(cfg).toHaveProperty("entity"); }); }); describe("WHEN getConfigElement is called", () => { it("THEN it returns the editor element tag", () => { - const editorEl = HassRecordsSensorCard.getConfigElement(); + const editorEl = HassDatapointsSensorCard.getConfigElement(); expect(editorEl.tagName.toLowerCase()).toBe( "hass-datapoints-sensor-card-editor" ); diff --git a/custom_components/hass_datapoints/src/cards/sensor/editor.ts b/custom_components/hass_datapoints/src/cards/sensor/editor.ts index 11891328..ebf7d5fa 100644 --- a/custom_components/hass_datapoints/src/cards/sensor/editor.ts +++ b/custom_components/hass_datapoints/src/cards/sensor/editor.ts @@ -10,7 +10,7 @@ import "@/atoms/form/editor-switch/editor-switch"; import "@/atoms/form/editor-entity-picker/editor-entity-picker"; import "@/atoms/form/editor-select/editor-select"; -export class HassRecordsSensorCardEditor extends EditorBase { +export class HassDatapointsSensorCardEditor extends EditorBase { static styles: CSSResultGroup = [EditorBase.styles, styles]; render() { diff --git a/custom_components/hass_datapoints/src/cards/sensor/sensor.ts b/custom_components/hass_datapoints/src/cards/sensor/sensor.ts index 56c7a7d4..757931b7 100644 --- a/custom_components/hass_datapoints/src/cards/sensor/sensor.ts +++ b/custom_components/hass_datapoints/src/cards/sensor/sensor.ts @@ -16,7 +16,7 @@ import "./sensor-records/sensor-records"; * hass-datapoints-sensor-card – Sensor card with inline annotation icons. * Canvas rendering is delegated to sensor-chart; annotation list to sensor-records. */ -export class HassRecordsSensorCard extends LitElement { +export class HassDatapointsSensorCard extends LitElement { static properties = { _config: { state: true }, _hass: { state: true }, diff --git a/custom_components/hass_datapoints/src/cards/sensor/stories/sensor.stories.ts b/custom_components/hass_datapoints/src/cards/sensor/stories/sensor.stories.ts index c939cc4f..a32ba5f1 100644 --- a/custom_components/hass_datapoints/src/cards/sensor/stories/sensor.stories.ts +++ b/custom_components/hass_datapoints/src/cards/sensor/stories/sensor.stories.ts @@ -13,10 +13,13 @@ */ import type { Meta, StoryObj } from "@storybook/web-components-vite"; import { html, type TemplateResult } from "lit"; -import { HassRecordsSensorCard } from "../sensor"; +import { HassDatapointsSensorCard } from "../sensor"; if (!customElements.get("hass-datapoints-sensor-card")) { - customElements.define("hass-datapoints-sensor-card", HassRecordsSensorCard); + customElements.define( + "hass-datapoints-sensor-card", + HassDatapointsSensorCard + ); } type Story = StoryObj; @@ -199,7 +202,7 @@ async function setupCard( ) { const card = canvasElement.querySelector( "hass-datapoints-sensor-card" - ) as HassRecordsSensorCard; + ) as HassDatapointsSensorCard; card.setConfig(config); if (hass) { card.hass = hass as any; diff --git a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-anomaly-overlap-mode.spec.ts b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-anomaly-overlap-mode.spec.ts index 9f5a18d6..ef9a07c9 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-anomaly-overlap-mode.spec.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-anomaly-overlap-mode.spec.ts @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { HassRecordsHistoryPanel } from "../datapoints"; +import { HassDatapointsHistoryPanel } from "../datapoints"; import { normalizeHistorySeriesAnalysis } from "@/lib/domain/history-series"; -describe("HassRecordsHistoryPanel anomaly overlap mode config", () => { +describe("HassDatapointsHistoryPanel anomaly overlap mode config", () => { afterEach(() => { vi.restoreAllMocks(); }); @@ -76,7 +76,7 @@ describe("HassRecordsHistoryPanel anomaly overlap mode config", () => { _hass: null, }; - HassRecordsHistoryPanel.prototype._renderContent.call(panel); + HassDatapointsHistoryPanel.prototype._renderContent.call(panel); expect(setConfig).toHaveBeenCalledTimes(1); expect(setConfig.mock.calls[0][0].anomaly_overlap_mode).toBe("only"); diff --git a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-bootstrap.spec.ts b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-bootstrap.spec.ts index 78905e2c..10c55cec 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-bootstrap.spec.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-bootstrap.spec.ts @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { HassRecordsHistoryPanel } from "../datapoints"; +import { HassDatapointsHistoryPanel } from "../datapoints"; import { normalizeHistorySeriesAnalysis } from "@/lib/domain/history-series"; -describe("HassRecordsHistoryPanel bootstrap", () => { +describe("HassDatapointsHistoryPanel bootstrap", () => { afterEach(() => { vi.restoreAllMocks(); }); @@ -28,7 +28,9 @@ describe("HassRecordsHistoryPanel bootstrap", () => { _updateUrl: updateUrl, }; - HassRecordsHistoryPanel.prototype._bootstrapAfterShellBuilt.call(panel); + HassDatapointsHistoryPanel.prototype._bootstrapAfterShellBuilt.call( + panel + ); expect(ensureHistoryBounds).toHaveBeenCalledTimes(1); expect(ensureUserPreferences).toHaveBeenCalledTimes(1); @@ -60,14 +62,15 @@ describe("HassRecordsHistoryPanel bootstrap", () => { }, ], _applyPreferredSeriesColors: - HassRecordsHistoryPanel.prototype._applyPreferredSeriesColors, + HassDatapointsHistoryPanel.prototype._applyPreferredSeriesColors, _mergeSavedSeriesRows: - HassRecordsHistoryPanel.prototype._mergeSavedSeriesRows, - _syncSeriesState: HassRecordsHistoryPanel.prototype._syncSeriesState, + HassDatapointsHistoryPanel.prototype._mergeSavedSeriesRows, + _syncSeriesState: + HassDatapointsHistoryPanel.prototype._syncSeriesState, _seriesColorQueryKey: () => "temperature", }; - HassRecordsHistoryPanel.prototype._applyPreferencePageState.call( + HassDatapointsHistoryPanel.prototype._applyPreferencePageState.call( panel, { series_rows: [ @@ -117,7 +120,7 @@ describe("HassRecordsHistoryPanel bootstrap", () => { ], _seriesColorQueryKey: () => "temperature", _applyPreferredSeriesColors: - HassRecordsHistoryPanel.prototype._applyPreferredSeriesColors, + HassDatapointsHistoryPanel.prototype._applyPreferredSeriesColors, _applyPreferencePageState: applyPreferencePageState, _saveUserPreferences: saveUserPreferences, _rendered: true, @@ -149,7 +152,7 @@ describe("HassRecordsHistoryPanel bootstrap", () => { }, }; - return HassRecordsHistoryPanel.prototype._ensureUserPreferences + return HassDatapointsHistoryPanel.prototype._ensureUserPreferences .call(panel) .then(() => { expect(applyPreferencePageState).not.toHaveBeenCalled(); @@ -207,7 +210,7 @@ describe("HassRecordsHistoryPanel bootstrap", () => { }, }; - HassRecordsHistoryPanel.prototype.disconnectedCallback.call(panel); + HassDatapointsHistoryPanel.prototype.disconnectedCallback.call(panel); expect(tabletMq.removeEventListener).toHaveBeenCalledTimes(1); expect(mobileMq.removeEventListener).toHaveBeenCalledTimes(1); diff --git a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-chart-resize.spec.ts b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-chart-resize.spec.ts index 1cfeded1..6034ca03 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-chart-resize.spec.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-chart-resize.spec.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { HassRecordsHistoryPanel } from "../datapoints"; +import { HassDatapointsHistoryPanel } from "../datapoints"; -describe("HassRecordsHistoryPanel", () => { +describe("HassDatapointsHistoryPanel", () => { beforeEach(() => { vi.restoreAllMocks(); }); @@ -39,7 +39,9 @@ describe("HassRecordsHistoryPanel", () => { }, }; - HassRecordsHistoryPanel.prototype._requestChartResizeRedraw.call(panel); + HassDatapointsHistoryPanel.prototype._requestChartResizeRedraw.call( + panel + ); expect(rafSpy).toHaveBeenCalledOnce(); expect(drawSpy).toHaveBeenCalledOnce(); @@ -65,7 +67,9 @@ describe("HassRecordsHistoryPanel", () => { _chartEl: null, }; - HassRecordsHistoryPanel.prototype._requestChartResizeRedraw.call(panel); + HassDatapointsHistoryPanel.prototype._requestChartResizeRedraw.call( + panel + ); expect(requestChartResizeRedraw).toHaveBeenCalledWith(null); }); diff --git a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-chart-zoom-highlight.spec.ts b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-chart-zoom-highlight.spec.ts index d6084e40..864a2026 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-chart-zoom-highlight.spec.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-chart-zoom-highlight.spec.ts @@ -1,7 +1,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { HassRecordsHistoryPanel } from "../datapoints"; +import { HassDatapointsHistoryPanel } from "../datapoints"; -describe("HassRecordsHistoryPanel chart zoom highlight", () => { +describe("HassDatapointsHistoryPanel chart zoom highlight", () => { afterEach(() => { vi.restoreAllMocks(); }); @@ -82,7 +82,7 @@ describe("HassRecordsHistoryPanel chart zoom highlight", () => { } ); - HassRecordsHistoryPanel.prototype._renderContent.call(panel); + HassDatapointsHistoryPanel.prototype._renderContent.call(panel); expect(panel._chartZoomCommittedRange).toEqual(zoomRange); expect(updateChartZoomHighlight).toHaveBeenCalled(); @@ -117,7 +117,7 @@ describe("HassRecordsHistoryPanel chart zoom highlight", () => { _renderComparisonTabs: renderComparisonTabs, }; - HassRecordsHistoryPanel.prototype._handleChartZoom.call(panel, { + HassDatapointsHistoryPanel.prototype._handleChartZoom.call(panel, { detail: { startTime: 100, endTime: 200, diff --git a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-collapsed-sidebar.spec.ts b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-collapsed-sidebar.spec.ts index 917d94f3..e99db74b 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-collapsed-sidebar.spec.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-collapsed-sidebar.spec.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { HassRecordsHistoryPanel } from "../datapoints"; +import { HassDatapointsHistoryPanel } from "../datapoints"; -describe("HassRecordsHistoryPanel collapsed sidebar interactions", () => { +describe("HassDatapointsHistoryPanel collapsed sidebar interactions", () => { let panel: RecordWithUnknownValues; beforeEach(() => { @@ -21,7 +21,7 @@ describe("HassRecordsHistoryPanel collapsed sidebar interactions", () => { describe("WHEN the collapsed rail background is clicked", () => { it("THEN it does not toggle the sidebar", () => { expect.assertions(1); - HassRecordsHistoryPanel.prototype._handleCollapsedSidebarClick.call( + HassDatapointsHistoryPanel.prototype._handleCollapsedSidebarClick.call( panel, { composedPath: () => [ @@ -41,7 +41,7 @@ describe("HassRecordsHistoryPanel collapsed sidebar interactions", () => { describe("WHEN the collapsed add button is clicked", () => { it("THEN it does not toggle the sidebar", () => { expect.assertions(1); - HassRecordsHistoryPanel.prototype._handleCollapsedSidebarClick.call( + HassDatapointsHistoryPanel.prototype._handleCollapsedSidebarClick.call( panel, { composedPath: () => [ @@ -76,7 +76,10 @@ describe("HassRecordsHistoryPanel collapsed sidebar interactions", () => { }, }; - HassRecordsHistoryPanel.prototype._openTargetPicker.call(panel, null); + HassDatapointsHistoryPanel.prototype._openTargetPicker.call( + panel, + null + ); expect(openTargetPicker).toHaveBeenCalledWith( panel._targetControl, @@ -104,7 +107,10 @@ describe("HassRecordsHistoryPanel collapsed sidebar interactions", () => { }, }; - HassRecordsHistoryPanel.prototype._openTargetPicker.call(panel, null); + HassDatapointsHistoryPanel.prototype._openTargetPicker.call( + panel, + null + ); expect(openTargetPicker).toHaveBeenCalledWith( panel._targetControl, diff --git a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-comparison-tabs.spec.ts b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-comparison-tabs.spec.ts index e4cb2e92..9e2db744 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-comparison-tabs.spec.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-comparison-tabs.spec.ts @@ -1,8 +1,8 @@ import { describe, expect, it, vi } from "vitest"; -import { HassRecordsHistoryPanel } from "../datapoints"; +import { HassDatapointsHistoryPanel } from "../datapoints"; import { createHistoryPageOrchestrationContext } from "../context/orchestration-context"; -describe("HassRecordsHistoryPanel comparison tabs", () => { +describe("HassDatapointsHistoryPanel comparison tabs", () => { describe("GIVEN the history card wraps an inner namespaced chart element", () => { describe("WHEN rendering comparison tabs", () => { it("THEN it mounts the tab rail into the inner chart top slot", () => { @@ -43,7 +43,7 @@ describe("HassRecordsHistoryPanel comparison tabs", () => { _deleteDateWindow: vi.fn(), }; - HassRecordsHistoryPanel.prototype._renderComparisonTabs.call(panel); + HassDatapointsHistoryPanel.prototype._renderComparisonTabs.call(panel); expect(panel._comparisonTabsHostEl).toBe(topSlot); expect(topSlot.hidden).toBe(false); @@ -89,7 +89,7 @@ describe("HassRecordsHistoryPanel comparison tabs", () => { _deleteDateWindow: vi.fn(), }; - HassRecordsHistoryPanel.prototype._renderComparisonTabs.call(panel); + HassDatapointsHistoryPanel.prototype._renderComparisonTabs.call(panel); expect(panel._comparisonTabsHostEl).toBe(topSlot); expect(topSlot.hidden).toBe(false); diff --git a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-event-visibility.spec.ts b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-event-visibility.spec.ts index 26aaf683..8ce7ee12 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-event-visibility.spec.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-event-visibility.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from "vitest"; -import { HassRecordsHistoryPanel } from "../datapoints"; +import { HassDatapointsHistoryPanel } from "../datapoints"; -describe("HassRecordsHistoryPanel", () => { +describe("HassDatapointsHistoryPanel", () => { describe("GIVEN a hidden event id list", () => { describe("WHEN toggling an already-hidden event", () => { it("THEN it removes the event id from the hidden list", () => { @@ -11,7 +11,7 @@ describe("HassRecordsHistoryPanel", () => { _renderContent: vi.fn(), }; - HassRecordsHistoryPanel.prototype._handleToggleEventVisibility.call( + HassDatapointsHistoryPanel.prototype._handleToggleEventVisibility.call( panel, { detail: { eventId: "evt-1" }, @@ -33,7 +33,7 @@ describe("HassRecordsHistoryPanel", () => { _renderContent: vi.fn(), }; - HassRecordsHistoryPanel.prototype._handleToggleEventVisibility.call( + HassDatapointsHistoryPanel.prototype._handleToggleEventVisibility.call( panel, { detail: { eventId: "evt-2" }, @@ -55,9 +55,12 @@ describe("HassRecordsHistoryPanel", () => { _renderContent: vi.fn(), }; - HassRecordsHistoryPanel.prototype._handleHoverEventRecord.call(panel, { - detail: { eventId: "evt-3", hovered: true }, - }); + HassDatapointsHistoryPanel.prototype._handleHoverEventRecord.call( + panel, + { + detail: { eventId: "evt-3", hovered: true }, + } + ); expect(panel._hoveredEventIds).toEqual(["evt-3"]); expect(panel._renderContent).toHaveBeenCalledOnce(); @@ -72,9 +75,12 @@ describe("HassRecordsHistoryPanel", () => { _renderContent: vi.fn(), }; - HassRecordsHistoryPanel.prototype._handleHoverEventRecord.call(panel, { - detail: { eventId: "evt-3", hovered: false }, - }); + HassDatapointsHistoryPanel.prototype._handleHoverEventRecord.call( + panel, + { + detail: { eventId: "evt-3", hovered: false }, + } + ); expect(panel._hoveredEventIds).toEqual([]); expect(panel._renderContent).toHaveBeenCalledOnce(); diff --git a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-update-url.spec.ts b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-update-url.spec.ts index 05f4325b..80e2369a 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-update-url.spec.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-update-url.spec.ts @@ -1,7 +1,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { HassRecordsHistoryPanel } from "../datapoints"; +import { HassDatapointsHistoryPanel } from "../datapoints"; -describe("HassRecordsHistoryPanel updateUrl", () => { +describe("HassDatapointsHistoryPanel updateUrl", () => { afterEach(() => { vi.restoreAllMocks(); }); @@ -56,10 +56,10 @@ describe("HassRecordsHistoryPanel updateUrl", () => { _hiddenEventIds: [], _selectedComparisonWindowId: null, _seriesColorQueryKey: - HassRecordsHistoryPanel.prototype._seriesColorQueryKey, + HassDatapointsHistoryPanel.prototype._seriesColorQueryKey, }; - HassRecordsHistoryPanel.prototype._updateUrl.call(panel, { + HassDatapointsHistoryPanel.prototype._updateUrl.call(panel, { push: false, }); diff --git a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-url-sync.spec.ts b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-url-sync.spec.ts index 97f211fe..327f314b 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-url-sync.spec.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/__tests__/datapoints-url-sync.spec.ts @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { HassRecordsHistoryPanel } from "../datapoints"; +import { HassDatapointsHistoryPanel } from "../datapoints"; import { normalizeHistorySeriesAnalysis } from "@/lib/domain/history-series"; -describe("HassRecordsHistoryPanel URL sync", () => { +describe("HassDatapointsHistoryPanel URL sync", () => { afterEach(() => { vi.restoreAllMocks(); }); @@ -23,7 +23,7 @@ describe("HassRecordsHistoryPanel URL sync", () => { _renderContent: renderContent, }; - HassRecordsHistoryPanel.prototype._setChartDatapointDisplayOption.call( + HassDatapointsHistoryPanel.prototype._setChartDatapointDisplayOption.call( panel, "hover_guides", true @@ -61,7 +61,9 @@ describe("HassRecordsHistoryPanel URL sync", () => { isConnected: true, }; - HassRecordsHistoryPanel.prototype._toggleSidebarCollapsed.call(panel); + HassDatapointsHistoryPanel.prototype._toggleSidebarCollapsed.call( + panel + ); expect(panel._sidebarCollapsed).toBe(true); expect(updateUrl).toHaveBeenCalledWith({ push: false }); @@ -95,7 +97,7 @@ describe("HassRecordsHistoryPanel URL sync", () => { _renderContent: renderContent, }; - HassRecordsHistoryPanel.prototype._setSeriesAnalysisOption.call( + HassDatapointsHistoryPanel.prototype._setSeriesAnalysisOption.call( panel, "sensor.temp", "show_trend_lines", diff --git a/custom_components/hass_datapoints/src/panels/datapoints/datapoints.ts b/custom_components/hass_datapoints/src/panels/datapoints/datapoints.ts index 08b2e71d..5c5d4715 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/datapoints.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/datapoints.ts @@ -256,7 +256,7 @@ type ResizablePanesElement = HTMLElement & { // Shared timeline, domain, and history-page helpers now live in dedicated subsystem files. -export class HassRecordsHistoryPanel extends HTMLElement { +export class HassDatapointsHistoryPanel extends HTMLElement { [key: string]: unknown; // Explicit declarations take precedence over the index signature so TypeScript diff --git a/custom_components/hass_datapoints/src/register.ts b/custom_components/hass_datapoints/src/register.ts index 933e247e..6b63b272 100644 --- a/custom_components/hass_datapoints/src/register.ts +++ b/custom_components/hass_datapoints/src/register.ts @@ -1,17 +1,18 @@ -/* eslint-disable no-console */ -import { HassRecordsActionCard } from "@/cards/action/action"; -import { HassRecordsActionCardEditor } from "@/cards/action/editor"; -import { HassRecordsDevToolCard } from "@/cards/dev-tool/dev-tool"; -import { HassRecordsDevToolCardEditor } from "@/cards/dev-tool/editor"; -import { HassRecordsHistoryCard } from "@/cards/history/history"; -import { HassRecordsHistoryCardEditor } from "@/cards/history/editor"; -import { HassRecordsHistoryPanel } from "@/panels/datapoints/datapoints"; -import { HassRecordsListCard } from "@/cards/list/list"; -import { HassRecordsListCardEditor } from "@/cards/list/editor"; -import { HassRecordsQuickCard } from "@/cards/quick/quick"; -import { HassRecordsQuickCardEditor } from "@/cards/quick/editor"; -import { HassRecordsSensorCard } from "@/cards/sensor/sensor"; -import { HassRecordsSensorCardEditor } from "@/cards/sensor/editor"; +/* eslint-disable no-console, max-classes-per-file */ +import { HassDatapointsActionCard } from "@/cards/action/action"; +import { HassDatapointsActionCardEditor } from "@/cards/action/editor"; +import { HassDatapointsDevToolCard } from "@/cards/dev-tool/dev-tool"; +import { HassDatapointsDevToolCardEditor } from "@/cards/dev-tool/editor"; +import { HassDatapointsHistoryCard } from "@/cards/history/history"; +import { HassDatapointsHistoryCardEditor } from "@/cards/history/editor"; +import { HassDatapointsHistoryPanel } from "@/panels/datapoints/datapoints"; +import { HassDatapointsListCard } from "@/cards/list/list"; +import { HassDatapointsListCardEditor } from "@/cards/list/editor"; +import { HassDatapointsQuickCard } from "@/cards/quick/quick"; +import { HassDatapointsQuickCardEditor } from "@/cards/quick/editor"; +import { HassDatapointsSensorCard } from "@/cards/sensor/sensor"; +import { HassDatapointsSensorCardEditor } from "@/cards/sensor/editor"; +import type { CardConfig } from "@/lib/types"; interface LovelaceCardRegistration { type: string; @@ -30,32 +31,204 @@ declare global { * Register all custom elements and advertise them to the Lovelace card picker. */ +// ── Deprecated element aliases (backwards compat) ─────────────────────────── + +const _warnOnceKey = "__HASS_DATAPOINTS_DEPRECATED_TAG_WARNED__"; +function _warnDeprecatedTag(oldTag: string, newTag: string): void { + const win = window as unknown as Record; + if (!win[_warnOnceKey]) { + win[_warnOnceKey] = {}; + } + const warned = win[_warnOnceKey] as Record; + if (warned[oldTag]) { + return; + } + warned[oldTag] = true; + + console.warn( + `[hass-datapoints] deprecated card tag "${oldTag}" in use; migrate to "${newTag}".` + ); +} + +function _defineDeprecatedAlias({ + oldTag, + newTag, + base, +}: { + oldTag: string; + newTag: string; + base: T; +}): void { + if (customElements.get(oldTag)) { + return; + } + + class DeprecatedAlias extends (base as unknown as CustomElementConstructor) { + constructor() { + super(); + _warnDeprecatedTag(oldTag, newTag); + } + } + customElements.define(oldTag, DeprecatedAlias); +} + +class HassDatapointsRemovedStatisticsCard extends HTMLElement { + private _config: CardConfig = {}; + + setConfig(config: CardConfig): void { + this._config = config ?? {}; + this._render(); + } + + set hass(_hass: import("@/lib/types").HassLike) { + // noop; exists so HA doesn't error when assigning hass. + } + + connectedCallback(): void { + this._render(); + } + + private _render(): void { + const title = + ((this._config as unknown as Record)?.title as + | string + | undefined) || "Statistics card"; + this.innerHTML = ` + +
${title}
+
+

+ Datapoints statistics card removed. + Use custom:hass-datapoints-history-card instead. +

+

+ Deprecated types: custom:hass-datapoints-statistics-card, + custom:hass-records-statistics-card. +

+
+
+ `; + } +} + +class HassDatapointsRemovedStatisticsCardEditor extends HTMLElement { + setConfig(_config: CardConfig): void { + this._render(); + } + + set hass(_hass: import("@/lib/types").HassLike) { + // noop + } + + connectedCallback(): void { + this._render(); + } + + private _render(): void { + this.innerHTML = ` + +
+

+ Datapoints statistics card removed. + Replace with custom:hass-datapoints-history-card. +

+
+
+ `; + } +} + // ── Card elements ────────────────────────────────────────────────────────── if (!customElements.get("hass-datapoints-action-card")) { - customElements.define("hass-datapoints-action-card", HassRecordsActionCard); + customElements.define( + "hass-datapoints-action-card", + HassDatapointsActionCard + ); } if (!customElements.get("hass-datapoints-quick-card")) { - customElements.define("hass-datapoints-quick-card", HassRecordsQuickCard); + customElements.define("hass-datapoints-quick-card", HassDatapointsQuickCard); } if (!customElements.get("hass-datapoints-history-card")) { - customElements.define("hass-datapoints-history-card", HassRecordsHistoryCard); + customElements.define( + "hass-datapoints-history-card", + HassDatapointsHistoryCard + ); } if (!customElements.get("hass-datapoints-sensor-card")) { - customElements.define("hass-datapoints-sensor-card", HassRecordsSensorCard); + customElements.define( + "hass-datapoints-sensor-card", + HassDatapointsSensorCard + ); } if (!customElements.get("hass-datapoints-list-card")) { - customElements.define("hass-datapoints-list-card", HassRecordsListCard); + customElements.define("hass-datapoints-list-card", HassDatapointsListCard); } if (!customElements.get("hass-datapoints-history-panel")) { customElements.define( "hass-datapoints-history-panel", - HassRecordsHistoryPanel + HassDatapointsHistoryPanel ); } if (!customElements.get("hass-datapoints-dev-tool-card")) { customElements.define( "hass-datapoints-dev-tool-card", - HassRecordsDevToolCard + HassDatapointsDevToolCard + ); +} + +// ── Backwards compat: hass-records-* tags ─────────────────────────────────── +// These were used by Hass Records versions; keep working for existing dashboards. +_defineDeprecatedAlias({ + oldTag: "hass-records-action-card", + newTag: "hass-datapoints-action-card", + base: HassDatapointsActionCard, +}); +_defineDeprecatedAlias({ + oldTag: "hass-records-quick-card", + newTag: "hass-datapoints-quick-card", + base: HassDatapointsQuickCard, +}); +_defineDeprecatedAlias({ + oldTag: "hass-records-history-card", + newTag: "hass-datapoints-history-card", + base: HassDatapointsHistoryCard, +}); +_defineDeprecatedAlias({ + oldTag: "hass-records-sensor-card", + newTag: "hass-datapoints-sensor-card", + base: HassDatapointsSensorCard, +}); +_defineDeprecatedAlias({ + oldTag: "hass-records-list-card", + newTag: "hass-datapoints-list-card", + base: HassDatapointsListCard, +}); +_defineDeprecatedAlias({ + oldTag: "hass-records-dev-tool-card", + newTag: "hass-datapoints-dev-tool-card", + base: HassDatapointsDevToolCard, +}); + +// ── Backwards compat: removed statistics card ─────────────────────────────── +if (!customElements.get("hass-datapoints-statistics-card")) { + customElements.define( + "hass-datapoints-statistics-card", + HassDatapointsRemovedStatisticsCard + ); +} +if (!customElements.get("hass-records-statistics-card")) { + class HassRecordsRemovedStatisticsCard extends HassDatapointsRemovedStatisticsCard { + constructor() { + super(); + _warnDeprecatedTag( + "hass-records-statistics-card", + "hass-datapoints-history-card" + ); + } + } + customElements.define( + "hass-records-statistics-card", + HassRecordsRemovedStatisticsCard ); } @@ -63,37 +236,92 @@ if (!customElements.get("hass-datapoints-dev-tool-card")) { if (!customElements.get("hass-datapoints-action-card-editor")) { customElements.define( "hass-datapoints-action-card-editor", - HassRecordsActionCardEditor + HassDatapointsActionCardEditor ); } if (!customElements.get("hass-datapoints-quick-card-editor")) { customElements.define( "hass-datapoints-quick-card-editor", - HassRecordsQuickCardEditor + HassDatapointsQuickCardEditor ); } if (!customElements.get("hass-datapoints-history-card-editor")) { customElements.define( "hass-datapoints-history-card-editor", - HassRecordsHistoryCardEditor + HassDatapointsHistoryCardEditor ); } if (!customElements.get("hass-datapoints-sensor-card-editor")) { customElements.define( "hass-datapoints-sensor-card-editor", - HassRecordsSensorCardEditor + HassDatapointsSensorCardEditor ); } if (!customElements.get("hass-datapoints-list-card-editor")) { customElements.define( "hass-datapoints-list-card-editor", - HassRecordsListCardEditor + HassDatapointsListCardEditor ); } if (!customElements.get("hass-datapoints-dev-tool-card-editor")) { customElements.define( "hass-datapoints-dev-tool-card-editor", - HassRecordsDevToolCardEditor + HassDatapointsDevToolCardEditor + ); +} + +// ── Backwards compat editors: hass-records-* ──────────────────────────────── +_defineDeprecatedAlias({ + oldTag: "hass-records-action-card-editor", + newTag: "hass-datapoints-action-card-editor", + base: HassDatapointsActionCardEditor, +}); +_defineDeprecatedAlias({ + oldTag: "hass-records-quick-card-editor", + newTag: "hass-datapoints-quick-card-editor", + base: HassDatapointsQuickCardEditor, +}); +_defineDeprecatedAlias({ + oldTag: "hass-records-history-card-editor", + newTag: "hass-datapoints-history-card-editor", + base: HassDatapointsHistoryCardEditor, +}); +_defineDeprecatedAlias({ + oldTag: "hass-records-sensor-card-editor", + newTag: "hass-datapoints-sensor-card-editor", + base: HassDatapointsSensorCardEditor, +}); +_defineDeprecatedAlias({ + oldTag: "hass-records-list-card-editor", + newTag: "hass-datapoints-list-card-editor", + base: HassDatapointsListCardEditor, +}); +_defineDeprecatedAlias({ + oldTag: "hass-records-dev-tool-card-editor", + newTag: "hass-datapoints-dev-tool-card-editor", + base: HassDatapointsDevToolCardEditor, +}); + +// ── Backwards compat editor: removed statistics card ──────────────────────── +if (!customElements.get("hass-datapoints-statistics-card-editor")) { + customElements.define( + "hass-datapoints-statistics-card-editor", + HassDatapointsRemovedStatisticsCardEditor + ); +} +if (!customElements.get("hass-records-statistics-card-editor")) { + class HassRecordsRemovedStatisticsCardEditor extends HassDatapointsRemovedStatisticsCardEditor { + constructor() { + super(); + _warnDeprecatedTag( + "hass-records-statistics-card-editor", + "hass-datapoints-history-card" + ); + } + } + customElements.define( + "hass-records-statistics-card-editor", + HassRecordsRemovedStatisticsCardEditor ); } @@ -103,42 +331,42 @@ const registeredTypes = new Set(window.customCards.map((card) => card.type)); const cardsToAdd: LovelaceCardRegistration[] = [ { type: "hass-datapoints-action-card", - name: "Hass Records – Action Card", + name: "Datapoints – Action Card", description: "Full form to record a custom event with message, annotation, icon, colour and entity association.", preview: false, }, { type: "hass-datapoints-quick-card", - name: "Hass Records – Quick Card", + name: "Datapoints – Quick Card", description: "Simple one-field card to quickly record a note with a bookmark icon.", preview: false, }, { type: "hass-datapoints-history-card", - name: "Hass Records – History Card", + name: "Datapoints – History Card", description: "History line chart with coloured annotation markers for recorded events.", preview: false, }, { type: "hass-datapoints-sensor-card", - name: "Hass Records – Sensor Card", + name: "Datapoints – Sensor Card", description: "Sensor card with line chart — annotations shown as icons on the data line.", preview: false, }, { type: "hass-datapoints-list-card", - name: "Hass Records – List Card", + name: "Datapoints – List Card", description: "Activity-style datagrid to browse, search, edit and delete all recorded events.", preview: false, }, { type: "hass-datapoints-dev-tool-card", - name: "Hass Records – Dev Tool", + name: "Datapoints – Dev Tool", description: "Generate demo datapoints from HA history and bulk-delete dev-flagged events.", preview: false, diff --git a/docs/cards.md b/docs/cards.md index 42fe1bc1..981497a0 100644 --- a/docs/cards.md +++ b/docs/cards.md @@ -15,6 +15,22 @@ Data Points ships six Lovelace cards and a dedicated history panel. All cards in | `hass-datapoints-history-card` | Multi-series analysis chart with target rows, anomaly overlays, date windows, zoom, timeline slider, and chart-created datapoints. | | `hass-datapoints-sensor-card` | Sensor-focused chart with inline datapoint markers. | +### Deprecated card types + +Older installations may still reference `hass-records-*` card types. These are supported as aliases and can be replaced directly in YAML: + +| Deprecated type | Replacement | +| ------------------------------ | ------------------------------- | +| `hass-records-action-card` | `hass-datapoints-action-card` | +| `hass-records-quick-card` | `hass-datapoints-quick-card` | +| `hass-records-history-card` | `hass-datapoints-history-card` | +| `hass-records-sensor-card` | `hass-datapoints-sensor-card` | +| `hass-records-list-card` | `hass-datapoints-list-card` | +| `hass-records-dev-tool-card` | `hass-datapoints-dev-tool-card` | +| `hass-records-statistics-card` | `hass-datapoints-history-card` | + +The old `hass-datapoints-statistics-card` is no longer shipped. If present in a dashboard, it renders a migration hint. + ### Visual editors All bundled cards include Lovelace visual editors. The dev-tool editor is intentionally minimal because the card itself does not expose configurable options.