Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add visibility option to dashboard cards #20840

Merged
merged 9 commits into from
May 29, 2024
Merged

Add visibility option to dashboard cards #20840

merged 9 commits into from
May 29, 2024

Conversation

piitaya
Copy link
Member

@piitaya piitaya commented May 21, 2024

Proposed change

Attempt to add visibility to dashboard cards (Similar to #20805 for sections).

  • Load card and custom cards using hui-card component
  • Access to getCardSize and getLayoutOptions using hui-card (To be improved).
  • Add visibility to config
  • Add UI editor (maybe in another PR)

Type of change

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

Example configuration

Additional information

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue or discussion:
  • Link to documentation pull request:

Checklist

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

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

Summary by CodeRabbit

  • New Features

    • Introduced a new visibility property for Lovelace cards, enhancing customization based on conditions.
    • Added a new condition hasOnlyMediaQuery to improve visibility logic in Lovelace components.
  • Improvements

    • Enhanced card management by exclusively using the HuiCard type, simplifying the configuration and rendering process.
    • Improved visibility handling based on media queries and edit mode status.
  • Bug Fixes

    • Optimized media query listeners for better performance and reliability in visibility changes.
  • Refactor

    • Streamlined import statements and type declarations across multiple Lovelace components for better maintainability.

@piitaya piitaya marked this pull request as draft May 21, 2024 11:57
@piitaya piitaya force-pushed the card-visibility branch 2 times, most recently from d6f69f6 to 1a76eec Compare May 28, 2024 09:15
@piitaya piitaya force-pushed the card-visibility branch 2 times, most recently from 6468ff2 to 12069a1 Compare May 29, 2024 12:33
const mediaQueries = extractMediaQueries(conditions);

const listeners = mediaQueries.map((query) => {
const listener = listenMediaQuery(query, () => {
const visibility = checkConditionsMet(conditions, hass);
Copy link
Member Author

Choose a reason for hiding this comment

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

I moved checkConditionsMet outside of this function because hass value wasn't updated.

@piitaya piitaya marked this pull request as ready for review May 29, 2024 14:03
Copy link
Contributor

coderabbitai bot commented May 29, 2024

Warning

Rate Limit Exceeded

@piitaya has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 23 minutes and 52 seconds before requesting another review.

How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.
Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.
Please see our FAQ for further information.

Commits Files that changed from the base of the PR and between bc6cc9f and 9f0d5bb.

Walkthrough

The changes primarily focus on refactoring and consolidating the type usage for HuiCard across various components in the Lovelace UI of Home Assistant. This includes updating import statements, modifying type declarations for card arrays, and enhancing card handling logic. Additionally, new features like visibility conditions for cards and media query optimizations were introduced, improving the overall functionality and maintainability of the codebase.

Changes

File Path Change Summary
src/data/lovelace.ts Refactored imports to use type for HuiCard, adjusted cards types in interfaces to HuiCard[], removed HuiSection import.
src/data/lovelace/config/card.ts Added type imports for Condition and LovelaceLayoutOptions, introduced visibility property to LovelaceCardConfig.
src/panels/lovelace/cards/hui-card.ts Introduced HuiCard class with methods for rendering, updating, and visibility handling.
src/panels/lovelace/common/compute-card-size.ts Modified computeCardSize function to accept HuiCard type.
src/panels/lovelace/common/validate-condition.ts Optimized media query listeners and simplified visibility handling logic.
src/panels/lovelace/components/hui-conditional-base.ts Updated visibility logic to include hasOnlyMediaQuery condition, modified _listenMediaQueries method.
src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts Added and adjusted import statements, utilized functions for card and section manipulation.
src/panels/lovelace/editor/structs/base-card-struct.ts Added visibility property to baseLovelaceCardConfig.
src/panels/lovelace/sections/hui-grid-section.ts Refactored import statements, updated cards property type to HuiCard[], simplified layout options handling.
src/panels/lovelace/sections/hui-section.ts Modified imports, refactored card handling logic, updated visibility conditions, adjusted card element handling.
src/panels/lovelace/views/... Updated import statements to use HuiCard, modified cards property type to HuiCard[].

Sequence Diagram(s) (Beta)

sequenceDiagram
    participant User
    participant LovelaceView
    participant HuiCard
    participant MediaQuery

    User->>LovelaceView: Load View
    LovelaceView->>HuiCard: Create Card
    HuiCard->>MediaQuery: Listen for Conditions
    MediaQuery->>HuiCard: Update Visibility
    HuiCard->>LovelaceView: Render Card
    LovelaceView->>User: Display View
Loading
sequenceDiagram
    participant User
    participant CardEditor
    participant LovelaceCardConfig
    participant Condition

    User->>CardEditor: Open Card Editor
    CardEditor->>LovelaceCardConfig: Add/Edit Card
    LovelaceCardConfig->>Condition: Set Visibility Conditions
    Condition->>LovelaceCardConfig: Apply Conditions
    LovelaceCardConfig->>CardEditor: Save Config
    CardEditor->>User: Confirm Changes
Loading

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Outside diff range comments (19)
src/data/lovelace.ts (1)

Line range hint 1-12: Optimize imports to use type-only imports where appropriate.

- import { Connection, getCollection, HassEventBase } from "home-assistant-js-websocket";
- import { HuiCard } from "../panels/lovelace/cards/hui-card";
- import { HuiSection } from "../panels/lovelace/sections/hui-section";
- import { Lovelace, LovelaceBadge } from "../panels/lovelace/types";
- import { HomeAssistant } from "../types";
- import { LovelaceSectionConfig } from "./lovelace/config/section";
- import { fetchConfig, LegacyLovelaceConfig } from "./lovelace/config/types";
- import { LovelaceViewConfig } from "./lovelace/config/view";
+ import type { Connection, getCollection, HassEventBase } from "home-assistant-js-websocket";
+ import type { HuiCard } from "../panels/lovelace/cards/hui-card";
+ import type { HuiSection } from "../panels/lovelace/sections/hui-section";
+ import type { Lovelace, LovelaceBadge } from "../panels/lovelace/types";
+ import type { HomeAssistant } from "../types";
+ import type { LovelaceSectionConfig } from "./lovelace/config/section";
+ import type { fetchConfig, LegacyLovelaceConfig } from "./lovelace/config/types";
+ import type { LovelaceViewConfig } from "./lovelace/config/view";
src/panels/lovelace/components/hui-conditional-base.ts (1)

Line range hint 124-145: Avoid using non-null assertions. Consider checking for null or undefined before usage.

- this._config!.conditions,
- this.hass!
+ this._config?.conditions,
+ this.hass
src/panels/lovelace/views/hui-panel-view.ts (1)

Line range hint 70-119: Avoid using non-null assertions. Consider checking for null or undefined before usage.

- this.hass!.localize(
+ this.hass?.localize(
src/panels/lovelace/views/hui-sidebar-view.ts (2)

Line range hint 46-46: Avoid using non-null assertions as they bypass TypeScript's strict null checks.

- this._mql?.removeListener(this._mqlListenerRef!);
+ if (this._mqlListenerRef) {
+   this._mql?.removeListener(this._mqlListenerRef);
+ }

Also applies to: 94-94, 126-126, 137-137, 153-153, 164-164, 165-165, 166-166


Line range hint 1-9: Optimize imports by using import type for type-only imports to reduce runtime overhead and improve build performance.

- import { mdiArrowLeft, mdiArrowRight, mdiPlus } from "@mdi/js";
- import {
-   CSSResultGroup,
-   LitElement,
-   PropertyValues,
-   TemplateResult,
-   css,
-   html,
- } from "lit";
- import { property, state } from "lit/decorators";
+ import type { mdiArrowLeft, mdiArrowRight, mdiPlus } from "@mdi/js";
+ import type {
+   CSSResultGroup,
+   LitElement,
+   PropertyValues,
+   TemplateResult,
+   css,
+   html,
+ } from "lit";
+ import type { property, state } from "lit/decorators";

Also applies to: 14-15, 15-16

src/panels/lovelace/sections/hui-grid-section.ts (2)

Line range hint 53-53: Avoid using non-null assertions as they bypass TypeScript's strict null checks.

- const card = this.cards![idx];
+ if (this.cards) {
+   const card = this.cards[idx];
+   // Further logic involving `card`
+ }

Also applies to: 95-95, 149-149, 153-153


Line range hint 1-2: Optimize imports by using import type for type-only imports to reduce runtime overhead and improve build performance.

- import { mdiPlus } from "@mdi/js";
- import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
+ import type { mdiPlus } from "@mdi/js";
+ import type { CSSResultGroup, LitElement, css, html, nothing } from "lit";

Also applies to: 8-9, 9-10, 16-17

src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts (2)

Line range hint 66-66: Avoid using non-null assertions as they bypass TypeScript's strict null checks.

- this._yamlEditor.setValue(this._cardConfig);
+ if (this._yamlEditor && this._cardConfig) {
+   this._yamlEditor.setValue(this._cardConfig);
+ }

Also applies to: 108-108, 131-131, 132-132, 139-139, 158-158, 219-219, 220-220, 221-221, 222-222, 232-232, 249-249, 268-268


Line range hint 1-2: Optimize imports by using import type for type-only imports to reduce runtime overhead and improve build performance.

- import deepFreeze from "deep-freeze";
- import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
+ import type { deepFreeze } from "deep-freeze";
+ import type { css, CSSResultGroup, html, LitElement, nothing } from "lit";

Also applies to: 7-8, 8-9, 9-10, 12-13, 16-20, 22-23

src/panels/lovelace/common/validate-condition.ts (3)

Line range hint 209-216: Prefer using for...of for iterating over arrays for better readability and performance.

- [].forEach((state) => {
+ for (const state of []) {

Line range hint 123-123: Replace isNaN with Number.isNaN for safer checks without type coercion.

- if (isNaN(numericState)) {
+ if (Number.isNaN(numericState)) {

Also applies to: 129-129, 132-132


Line range hint 1-5: Optimize imports by using import type for type-only imports to reduce runtime overhead and improve build performance.

- import { ensureArray } from "../../../common/array/ensure-array";
- import {
-   MediaQueriesListener,
-   listenMediaQuery,
- } from "../../../common/dom/media_query";
+ import type { ensureArray } from "../../../common/array/ensure-array";
+ import type {
+   MediaQueriesListener,
+   listenMediaQuery,
+ } from "../../../common/dom/media_query";

Also applies to: 7-8

src/panels/lovelace/sections/hui-section.ts (2)

Line range hint 60-60: Avoid using non-null assertions as they bypass TypeScript's strict null checks.

- this._cards = this._cards!.map((curCardEl) =>
+ if (this._cards) {
+   this._cards = this._cards.map((curCardEl) =>

Also applies to: 116-122, 119-119, 127-134, 131-131, 146-146, 177-177, 198-198, 199-199, 200-200, 201-201, 202-202, 203-203, 259-259, 259-259, 261-261, 285-285, 287-287


Line range hint 1-1: Optimize imports by using import type for type-only imports to reduce runtime overhead and improve build performance.

- import { PropertyValues, ReactiveElement } from "lit";
- import { customElement, property, state } from "lit/decorators";
+ import type { PropertyValues, ReactiveElement } from "lit";
+ import type { customElement, property, state } from "lit/decorators";

Also applies to: 2-3

src/panels/lovelace/views/hui-masonry-view.ts (3)

Line range hint 70-72: Consider replacing forEach with for...of for better readability and performance in modern JavaScript.

- this._mqls?.forEach((mql) => {
-   mql.removeListener(this._mqlListenerRef!);
- });
+ for (const mql of this._mqls || []) {
+   mql.removeListener(this._mqlListenerRef!);
+ }

Line range hint 71-71: Avoid using non-null assertions. Consider adding checks or using optional chaining.

- this._mqlListenerRef!();
+ this._mqlListenerRef?.();

Also applies to: 91-91, 119-119, 134-134, 153-153, 154-154, 165-165, 239-239, 246-246, 260-260, 276-276


Line range hint 172-172: Replace forEach with for...of for iterating over arrays.

- columnElements.forEach((column) => {
-   if (!column.lastChild) {
-     column.parentElement!.removeChild(column);
-   }
- });
+ for (const column of columnElements) {
+   if (!column.lastChild) {
+     column.parentElement?.removeChild(column);
+   }
+ }

Also applies to: 244-248

src/panels/lovelace/views/hui-view.ts (2)

Line range hint 87-87: Avoid using non-null assertions. Consider adding checks or using optional chaining.

- this.lovelace!.editMode
+ this.lovelace?.editMode

Also applies to: 125-125, 277-277, 299-299, 300-300, 301-301, 302-302, 303-303, 304-304


Line range hint 178-184: Replace forEach with for...of for iterating over arrays to improve readability and performance.

- this._badges.forEach((badge) => {
-   try {
-     badge.hass = this.hass;
-   } catch (e: any) {
-     this._rebuildBadge(badge, createErrorBadgeConfig(e.message));
-   }
- });
+ for (const badge of this._badges) {
+   try {
+     badge.hass = this.hass;
+   } catch (e: any) {
+     this._rebuildBadge(badge, createErrorBadgeConfig(e.message));
+   }
+ }

Also applies to: 186-192, 194-200, 221-228, 229-236

src/panels/lovelace/views/hui-masonry-view.ts Show resolved Hide resolved
src/panels/lovelace/views/hui-view.ts Show resolved Hide resolved
@@ -4,4 +4,5 @@ export const baseLovelaceCardConfig = object({
type: string(),
view_layout: any(),
layout_options: any(),
visibility: any(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Specify a more appropriate type for visibility.

-  visibility: any(),
+  visibility: array(Condition),

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
visibility: any(),
visibility: array(Condition),

@@ -7,4 +8,5 @@ export interface LovelaceCardConfig {
layout_options?: LovelaceLayoutOptions;
type: string;
[key: string]: any;
Copy link
Contributor

Choose a reason for hiding this comment

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

Specify a more appropriate type for view_layout.

-  view_layout?: any,
+  view_layout?: LovelaceLayoutOptions,

Committable suggestion was skipped due low confidence.

Comment on lines 1 to 3
import { promiseTimeout } from "../../../common/util/promise-timeout";
import { HuiCard } from "../cards/hui-card";
import { LovelaceCard, LovelaceHeaderFooter } from "../types";
Copy link
Contributor

Choose a reason for hiding this comment

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

Optimize imports to use type-only imports where appropriate.

- import { HuiCard } from "../cards/hui-card";
- import { LovelaceCard, LovelaceHeaderFooter } from "../types";
+ import type { HuiCard } from "../cards/hui-card";
+ import type { LovelaceCard, LovelaceHeaderFooter } from "../types";

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
import { promiseTimeout } from "../../../common/util/promise-timeout";
import { HuiCard } from "../cards/hui-card";
import { LovelaceCard, LovelaceHeaderFooter } from "../types";
import { promiseTimeout } from "../../../common/util/promise-timeout";
import type { HuiCard } from "../cards/hui-card";
import type { LovelaceCard, LovelaceHeaderFooter } from "../types";

}

private _clearMediaQueries() {
this._listeners.forEach((unsub) => unsub());
Copy link
Contributor

Choose a reason for hiding this comment

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

Prefer using for...of for iterating over arrays for better readability and performance.

- this._listeners.forEach((unsub) => unsub());
+ for (const unsub of this._listeners) {
+   unsub();
+ }

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
this._listeners.forEach((unsub) => unsub());
for (const unsub of this._listeners) {
unsub();
}

while (this.lastChild) {
this.removeChild(this.lastChild);
}
this.appendChild(this._element!);
Copy link
Contributor

Choose a reason for hiding this comment

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

Avoid using non-null assertions as they bypass TypeScript's strict null checks.

- this.appendChild(this._element!);
+ if (this._element) {
+   this.appendChild(this._element);
+ }

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
this.appendChild(this._element!);
if (this._element) {
this.appendChild(this._element);
}

@@ -0,0 +1,141 @@
import { PropertyValues, ReactiveElement } from "lit";
Copy link
Contributor

Choose a reason for hiding this comment

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

Optimize imports by using import type for type-only imports to reduce runtime overhead and improve build performance.

- import { PropertyValues, ReactiveElement } from "lit";
- import { customElement, property, state } from "lit/decorators";
- import { MediaQueriesListener } from "../../../common/dom/media_query";
+ import type { PropertyValues, ReactiveElement } from "lit";
+ import type { customElement, property, state } from "lit/decorators";
+ import type { MediaQueriesListener } from "../../../common/dom/media_query";

Also applies to: 2-3, 4-5


Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
import { PropertyValues, ReactiveElement } from "lit";
import type { PropertyValues, ReactiveElement } from "lit";
import type { customElement, property, state } from "lit/decorators";
import type { MediaQueriesListener } from "../../../common/dom/media_query";

@@ -131,6 +124,14 @@ export class HuiSection extends ReactiveElement {
}
if (changedProperties.has("lovelace")) {
this._layoutElement.lovelace = this.lovelace;
this._cards.forEach((element) => {
try {
element.hass = this.hass;
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to set hass again? We already did that on line 118?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think so

@@ -131,6 +124,14 @@ export class HuiSection extends ReactiveElement {
}
if (changedProperties.has("lovelace")) {
this._layoutElement.lovelace = this.lovelace;
this._cards.forEach((element) => {
try {
Copy link
Member

Choose a reason for hiding this comment

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

This try catch should probably be moved to hui-card, same for the try catch when setting hass on line 118.

Copy link
Member Author

Choose a reason for hiding this comment

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

I will improve that in a next PR 👍

this._listeners = attachConditionMediaQueriesListeners(
this._config.visibility,
(matches) => {
this._updateElement(hasOnlyMediaQuery && matches);
Copy link
Member

Choose a reason for hiding this comment

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

Same here

const visible = visibility || this.lovelace!.editMode;
this._updateElement(visible);
(matches) => {
this._updateElement(hasOnlyMediaQuery && matches);
Copy link
Member

Choose a reason for hiding this comment

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

Same here

Copy link
Member Author

Choose a reason for hiding this comment

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

It's done in the updateElement but I agree, we should improve that.

return;
}
this._config = config;
this._element = createCardElement(config);
Copy link
Member

Choose a reason for hiding this comment

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

hui-card should probably also handle the rebuild logic, we can then remove it anywhere else.

@bramkragten bramkragten merged commit 13f0149 into dev May 29, 2024
13 checks passed
@bramkragten bramkragten deleted the card-visibility branch May 29, 2024 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants