fix: Use HA style and coloring for all icons (#1790)

This commit is contained in:
Dermot Duffy
2024-12-23 15:22:27 -08:00
committed by GitHub
parent a9e5889ca7
commit 9d812af9f4
68 changed files with 557 additions and 1366 deletions
@@ -206,7 +206,11 @@ describe('GenericCameraManagerEngine', () => {
expect(
createEngine().getCameraMetadata(createHASS(), createGenericCameraConfig()),
).toEqual({
icon: 'mdi:video',
icon: {
entity: undefined,
icon: undefined,
fallback: 'mdi:video',
},
title: '',
});
});
@@ -217,10 +221,11 @@ describe('GenericCameraManagerEngine', () => {
createHASS(),
createGenericCameraConfig({ id: 'https://go2rtc#stream' }),
),
).toEqual({
icon: 'mdi:video',
title: 'https://go2rtc#stream',
});
).toEqual(
expect.objectContaining({
title: 'https://go2rtc#stream',
}),
);
});
it('with configured title', async () => {
@@ -231,10 +236,11 @@ describe('GenericCameraManagerEngine', () => {
title: 'My Camera',
}),
),
).toEqual({
icon: 'mdi:video',
title: 'My Camera',
});
).toEqual(
expect.objectContaining({
title: 'My Camera',
}),
);
});
describe('with entity title', () => {
@@ -250,10 +256,11 @@ describe('GenericCameraManagerEngine', () => {
camera_entity: 'camera.test',
}),
),
).toEqual({
icon: 'mdi:video',
title: 'My Entity Camera',
});
).toEqual(
expect.objectContaining({
title: 'My Entity Camera',
}),
);
});
it('webrtc_card.entity', async () => {
@@ -270,10 +277,11 @@ describe('GenericCameraManagerEngine', () => {
},
}),
),
).toEqual({
icon: 'mdi:video',
title: 'My Entity Camera',
});
).toEqual(
expect.objectContaining({
title: 'My Entity Camera',
}),
);
});
});
});
@@ -259,8 +259,12 @@ describe('ReolinkCameraManagerEngine', () => {
});
const engine = createEngine();
expect(engine.getCameraMetadata(createHASS(), cameraConfig)).toEqual({
engineLogo: '/src/camera-manager/reolink/assets/reolink.svg',
icon: 'mdi:camera',
engineIcon: 'reolink',
icon: {
icon: 'mdi:camera',
entity: 'camera.office',
fallback: 'mdi:video',
},
title: 'Office',
});
});
@@ -59,7 +59,7 @@ describe('StatusBarItemManager', () => {
const cameraManager = createCameraManager(store);
vi.mocked(cameraManager.getCameraMetadata).mockReturnValue({
title: 'Camera Title',
icon: 'mdi:camera',
icon: { icon: 'mdi:camera' },
});
expect(
@@ -225,8 +225,10 @@ describe('StatusBarItemManager', () => {
const cameraManager = createCameraManager(store);
vi.mocked(cameraManager.getCameraMetadata).mockReturnValue({
title: 'Camera Title',
icon: 'mdi:camera',
engineLogo: 'IMAGE_LOGO',
icon: {
icon: 'mdi:camera',
},
engineIcon: 'ENGINE_ICON',
});
expect(
@@ -235,8 +237,8 @@ describe('StatusBarItemManager', () => {
view: createView({ view: 'live', camera: 'camera-1' }),
}),
).toContainEqual({
type: 'custom:frigate-card-status-bar-image' as const,
image: 'IMAGE_LOGO',
type: 'custom:frigate-card-status-bar-icon' as const,
icon: 'ENGINE_ICON',
});
});
});
@@ -0,0 +1,73 @@
import { describe, expect, it } from 'vitest';
import { IconController } from '../../src/components-lib/icon-controller';
import { createHASS, createStateEntity } from '../test-utils';
describe('IconController', () => {
describe('should get custom icon', () => {
it('should return frigate SVG for frigate icon', () => {
expect(new IconController().getCustomIcon({ icon: 'frigate' })).toMatch(
/frigate.svg$/,
);
});
it('should return motioneye SVG for motioneye icon', () => {
expect(new IconController().getCustomIcon({ icon: 'motioneye' })).toMatch(
/motioneye.svg$/,
);
});
it('should return reolink SVG for reolink icon', () => {
expect(new IconController().getCustomIcon({ icon: 'reolink' })).toMatch(
/reolink.svg$/,
);
});
it('should return null for mdi icon', () => {
expect(new IconController().getCustomIcon({ icon: 'mdi:car' })).toBeNull();
});
it('should return null for undefined icon', () => {
expect(new IconController().getCustomIcon()).toBeNull();
});
});
describe('should create state object for state badge', () => {
it('should return null for non-existent entity', () => {
expect(
new IconController().createStateObjectForStateBadge(
createHASS(),
'sensor.DOES_NOT_EXIST',
),
).toBeNull();
});
it('should return modified state object for existing entity', () => {
expect(
new IconController().createStateObjectForStateBadge(
createHASS({
'sensor.existing': createStateEntity({
entity_id: 'sensor.existing',
attributes: {
friendly_name: 'Existing',
icon: 'mdi:car',
entity_picture: 'http://example.com/image.jpg',
entity_picture_local: 'local.jpg',
},
}),
}),
'sensor.existing',
),
).toEqual(
expect.objectContaining({
entity_id: 'sensor.existing',
attributes: expect.objectContaining({
friendly_name: 'Existing',
icon: 'mdi:car',
entity_picture: undefined,
entity_picture_local: undefined,
}),
}),
);
});
});
});
@@ -127,7 +127,9 @@ describe('MenuButtonController', () => {
);
vi.mocked(cameraManager).getCameraMetadata.mockReturnValue({
title: 'title',
icon: 'icon',
icon: {
icon: 'icon',
},
});
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
@@ -178,7 +180,9 @@ describe('MenuButtonController', () => {
);
vi.mocked(cameraManager).getCameraMetadata.mockReturnValue({
title: 'title',
icon: 'icon',
icon: {
icon: 'icon',
},
});
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
@@ -332,7 +336,10 @@ describe('MenuButtonController', () => {
return cameraID === 'camera-1'
? {
title: 'title',
icon: 'icon',
icon: {
icon: 'icon',
entity: 'entity',
},
}
: null;
},
@@ -351,7 +358,7 @@ describe('MenuButtonController', () => {
{
enabled: true,
icon: 'icon',
entity: 'camera.1',
entity: 'entity',
state_color: true,
title: 'title',
selected: true,
@@ -364,7 +371,7 @@ describe('MenuButtonController', () => {
{
enabled: true,
icon: undefined,
entity: 'camera.2',
entity: undefined,
state_color: true,
title: undefined,
selected: false,
@@ -377,7 +384,7 @@ describe('MenuButtonController', () => {
{
enabled: true,
icon: undefined,
entity: 'camera.3',
entity: undefined,
state_color: true,
title: undefined,
selected: false,
@@ -443,7 +450,7 @@ describe('MenuButtonController', () => {
{
enabled: true,
icon: undefined,
entity: 'camera.1',
entity: undefined,
state_color: true,
title: undefined,
selected: false,
@@ -456,7 +463,7 @@ describe('MenuButtonController', () => {
{
enabled: true,
icon: undefined,
entity: 'camera.2',
entity: undefined,
state_color: true,
title: undefined,
// camera-2 is selected in this test scenario because of the view
@@ -471,7 +478,7 @@ describe('MenuButtonController', () => {
{
enabled: true,
icon: undefined,
entity: 'camera.3',
entity: undefined,
state_color: true,
title: undefined,
selected: false,
@@ -1212,7 +1219,6 @@ describe('MenuButtonController', () => {
{
enabled: true,
selected: false,
icon: 'mdi:cast',
entity: 'media_player.tv',
state_color: false,
title: 'media_player.tv',
@@ -1266,7 +1272,6 @@ describe('MenuButtonController', () => {
{
enabled: true,
selected: false,
icon: 'mdi:bookmark',
entity: 'not_a_real_player',
state_color: false,
title: 'not_a_real_player',
+1 -35
View File
@@ -2,9 +2,7 @@ import { handleActionConfig } from '@dermotduffy/custom-card-helpers';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { MenuController } from '../../src/components-lib/menu-controller';
import { MenuConfig, menuConfigSchema } from '../../src/config/types';
import { StateParameters } from '../../src/types';
import { refreshDynamicStateParameters } from '../../src/utils/ha';
import { createInteractionEvent, createHASS, createLitElement } from '../test-utils';
import { createInteractionEvent, createLitElement } from '../test-utils';
vi.mock('@dermotduffy/custom-card-helpers');
vi.mock('../../src/utils/ha');
@@ -362,38 +360,6 @@ describe('MenuController', () => {
});
});
describe('should get fresh button state', () => {
it('on state icon', () => {
const controller = new MenuController(createLitElement());
const stateButton = {
type: 'custom:frigate-card-menu-state-icon' as const,
icon: 'mdi:sheep',
entity: 'switch.foo',
state_color: true,
};
const stateParameters: StateParameters = {};
vi.mocked(refreshDynamicStateParameters).mockReturnValue(stateParameters);
expect(controller.getFreshButtonState(createHASS(), stateButton)).toBe(
stateParameters,
);
expect(vi.mocked(refreshDynamicStateParameters)).toBeCalled();
});
it('on non state icon', () => {
const controller = new MenuController(createLitElement());
const button = {
type: 'custom:frigate-card-menu-icon' as const,
icon: 'mdi:sheep',
};
expect(controller.getFreshButtonState(createHASS(), button)).toEqual(button);
expect(vi.mocked(refreshDynamicStateParameters)).not.toBeCalled();
});
});
describe('should handle actions', () => {
it('should bail without config', () => {
const controller = new MenuController(createLitElement());
+1 -1
View File
@@ -285,7 +285,7 @@ describe('config defaults', () => {
},
},
status_bar: {
height: 46,
height: 30,
items: {
engine: {
enabled: true,
-27
View File
@@ -1,27 +0,0 @@
import { describe, expect, it } from 'vitest';
import frigateSVG from '../../src/camera-manager/frigate/assets/frigate.svg';
import motioneyeSVG from '../../src/camera-manager/motioneye/assets/motioneye.svg';
import reolinkSVG from '../../src/camera-manager/reolink/assets/reolink.svg';
import { getCustomIconURL } from '../../src/utils/custom-icons';
describe('getCustomIconURL', () => {
it('should return frigate SVG for frigate icon', () => {
expect(getCustomIconURL('frigate')).toBe(frigateSVG);
});
it('should return motioneye SVG for motioneye icon', () => {
expect(getCustomIconURL('motioneye')).toBe(motioneyeSVG);
});
it('should return reolink SVG for reolink icon', () => {
expect(getCustomIconURL('reolink')).toBe(reolinkSVG);
});
it('should return null for mdi icon', () => {
expect(getCustomIconURL('mdi:car')).toBeNull();
});
it('should return null for undefined icon', () => {
expect(getCustomIconURL()).toBeNull();
});
});
+1 -23
View File
@@ -2,11 +2,10 @@ import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
import { describe, expect, it, vi } from 'vitest';
import {
canonicalizeHAURL,
getEntityIcon,
hasHAConnectionStateChanged,
isHARelativeURL,
} from '../../../src/utils/ha/index.js';
import { createHASS, createStateEntity } from '../../test-utils.js';
import { createHASS } from '../../test-utils.js';
const createConnected = (connected: boolean): HomeAssistant => {
const hass = createHASS();
@@ -49,27 +48,6 @@ describe('hasHAConnectionStateChanged', () => {
});
});
describe('getEntityIcon', () => {
it('should get icon from attributes', () => {
expect(
getEntityIcon(
createHASS({
'camera.test': createStateEntity({
attributes: {
icon: 'mdi:cow',
},
}),
}),
'camera.test',
),
).toBe('mdi:cow');
});
it('should get icon from domain', () => {
expect(getEntityIcon(createHASS(), 'camera.test')).toBe('mdi:video');
});
});
describe('isHARelativeURL', () => {
it('should return true when URL is HA relative', () => {
expect(isHARelativeURL('/api/foo')).toBeTruthy();