feat: Add richer card theme support (#1809)
This commit is contained in:
@@ -98,7 +98,7 @@ describe('ConfigManager', () => {
|
||||
expect(api.getViewManager().reset).toBeCalled();
|
||||
expect(api.getMessageManager().reset).toBeCalled();
|
||||
expect(api.getAutomationsManager().addAutomations).toBeCalled();
|
||||
expect(api.getStyleManager().setPerformance).toBeCalled();
|
||||
expect(api.getStyleManager().updateFromConfig).toBeCalled();
|
||||
expect(api.getCardElementManager().update).toBeCalled();
|
||||
});
|
||||
|
||||
@@ -175,7 +175,7 @@ describe('ConfigManager', () => {
|
||||
manager.computeOverrideConfig();
|
||||
|
||||
expect(manager.getConfig()).toBeNull();
|
||||
expect(api.getStyleManager().setMinMaxHeight).not.toBeCalled();
|
||||
expect(api.getStyleManager().updateFromConfig).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should ignore overrides with same config', () => {
|
||||
@@ -188,12 +188,12 @@ describe('ConfigManager', () => {
|
||||
vi.mocked(getOverriddenConfig).mockReturnValue(config);
|
||||
|
||||
manager.setConfig(config);
|
||||
expect(api.getStyleManager().setMinMaxHeight).toBeCalled();
|
||||
expect(api.getStyleManager().updateFromConfig).toBeCalled();
|
||||
|
||||
vi.mocked(api.getStyleManager().setMinMaxHeight).mockClear();
|
||||
vi.mocked(api.getStyleManager().updateFromConfig).mockClear();
|
||||
manager.computeOverrideConfig();
|
||||
|
||||
expect(api.getStyleManager().setMinMaxHeight).not.toBeCalled();
|
||||
expect(api.getStyleManager().updateFromConfig).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should override', () => {
|
||||
@@ -204,7 +204,7 @@ describe('ConfigManager', () => {
|
||||
cameras: [{ camera_entity: 'camera.office' }],
|
||||
};
|
||||
manager.setConfig(config_1);
|
||||
vi.mocked(api.getStyleManager().setMinMaxHeight).mockClear();
|
||||
vi.mocked(api.getStyleManager().updateFromConfig).mockClear();
|
||||
|
||||
const config_2 = {
|
||||
type: 'custom:frigate-card',
|
||||
@@ -213,7 +213,7 @@ describe('ConfigManager', () => {
|
||||
vi.mocked(getOverriddenConfig).mockReturnValue(config_2);
|
||||
manager.computeOverrideConfig();
|
||||
|
||||
expect(api.getStyleManager().setMinMaxHeight).toBeCalled();
|
||||
expect(api.getStyleManager().updateFromConfig).toBeCalled();
|
||||
expect(api.getCardElementManager().update).toBeCalled();
|
||||
expect(manager.getConfig()).not.toEqual(manager.getNonOverriddenConfig());
|
||||
});
|
||||
|
||||
@@ -38,13 +38,13 @@ describe('HASSManager', () => {
|
||||
expect(manager.hasHASS()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should set light or dark mode upon setting hass', () => {
|
||||
it('should update theme upon setting hass', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new HASSManager(api);
|
||||
|
||||
manager.setHASS(createHASS());
|
||||
|
||||
expect(api.getStyleManager().setLightOrDarkMode).toBeCalled();
|
||||
expect(api.getStyleManager().applyTheme).toBeCalled();
|
||||
});
|
||||
|
||||
describe('should set condition manager state', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { add } from 'date-fns';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { InteractionManager } from '../../src/card-controller/interaction-manager';
|
||||
import { createCardAPI, createConfig } from '../test-utils';
|
||||
import { createCardAPI, createConfig, createLitElement } from '../test-utils';
|
||||
|
||||
vi.mock('lodash-es/throttle', () => ({
|
||||
default: vi.fn((fn) => fn),
|
||||
@@ -17,14 +17,20 @@ describe('InteractionManager', () => {
|
||||
|
||||
it('should initialize', () => {
|
||||
const api = createCardAPI();
|
||||
const element = createLitElement();
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
|
||||
const manager = new InteractionManager(api);
|
||||
|
||||
manager.initialize();
|
||||
expect(api.getConditionsManager().setState).toBeCalledWith({ interaction: false });
|
||||
expect(element.getAttribute('interaction')).toBeNull();
|
||||
});
|
||||
|
||||
it('should not take action without an interaction timeout', () => {
|
||||
it('should still report interaction without an interaction timeout', () => {
|
||||
const api = createCardAPI();
|
||||
const element = createLitElement();
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||
createConfig({
|
||||
view: {
|
||||
@@ -36,11 +42,15 @@ describe('InteractionManager', () => {
|
||||
|
||||
manager.reportInteraction();
|
||||
|
||||
expect(manager.hasInteraction()).toBeFalsy();
|
||||
expect(element.getAttribute('interaction')).not.toBeNull();
|
||||
expect(manager.hasInteraction()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should set condition state', () => {
|
||||
const api = createCardAPI();
|
||||
const element = createLitElement();
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||
createConfig({
|
||||
view: {
|
||||
@@ -55,12 +65,14 @@ describe('InteractionManager', () => {
|
||||
expect(api.getConditionsManager().setState).not.toBeCalled();
|
||||
|
||||
manager.reportInteraction();
|
||||
|
||||
expect(api.getConditionsManager().setState).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
interaction: true,
|
||||
}),
|
||||
);
|
||||
expect(manager.hasInteraction()).toBeTruthy();
|
||||
expect(element.getAttribute('interaction')).not.toBeNull();
|
||||
|
||||
vi.setSystemTime(add(start, { seconds: 10 }));
|
||||
vi.runOnlyPendingTimers();
|
||||
@@ -71,5 +83,6 @@ describe('InteractionManager', () => {
|
||||
}),
|
||||
);
|
||||
expect(manager.hasInteraction()).toBeFalsy();
|
||||
expect(element.getAttribute('interaction')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { StyleManager } from '../../src/card-controller/style-manager';
|
||||
import { FrigateCardView } from '../../src/config/types';
|
||||
import { createCardAPI, createConfig, createHASS, createView } from '../test-utils';
|
||||
import { FrigateCardView, ThemeName } from '../../src/config/types';
|
||||
import { createCardAPI, createConfig, createView } from '../test-utils';
|
||||
|
||||
// @vitest-environment jsdom
|
||||
describe('StyleManager', () => {
|
||||
@@ -9,97 +9,45 @@ describe('StyleManager', () => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('setLightOrDarkMode', () => {
|
||||
it('dark mode unspecified', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.setLightOrDarkMode();
|
||||
|
||||
expect(element.getAttribute('dark')).toBeNull();
|
||||
});
|
||||
|
||||
it('dark mode explicitly off', () => {
|
||||
describe('should set dimmable', () => {
|
||||
it('should be dimmable when dim is true', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||
createConfig({
|
||||
view: {
|
||||
dark_mode: 'off',
|
||||
dim: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.setLightOrDarkMode();
|
||||
manager.updateFromConfig();
|
||||
|
||||
expect(element.getAttribute('dark')).toBeNull();
|
||||
expect(element.getAttribute('dimmable')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('dark mode explicitly set', () => {
|
||||
it('should not be dimmable when dim is false', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||
createConfig({
|
||||
view: {
|
||||
dark_mode: 'on',
|
||||
dim: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.setLightOrDarkMode();
|
||||
manager.updateFromConfig();
|
||||
|
||||
expect(element.getAttribute('dark')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('dark mode auto without interaction', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||
createConfig({
|
||||
view: {
|
||||
dark_mode: 'auto',
|
||||
},
|
||||
}),
|
||||
);
|
||||
vi.mocked(api.getInteractionManager().hasInteraction).mockReturnValue(false);
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.setLightOrDarkMode();
|
||||
|
||||
expect(element.getAttribute('dark')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('dark mode auto with HA dark mode', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||
createConfig({
|
||||
view: {
|
||||
dark_mode: 'auto',
|
||||
},
|
||||
}),
|
||||
);
|
||||
vi.mocked(api.getInteractionManager().hasInteraction).mockReturnValue(true);
|
||||
const hass = createHASS();
|
||||
hass.themes.darkMode = true;
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.setLightOrDarkMode();
|
||||
|
||||
expect(element.getAttribute('dark')).not.toBeNull();
|
||||
expect(element.getAttribute('dimmable')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setExpandedMode', () => {
|
||||
describe('should set expanded mode', () => {
|
||||
it('with no view or known media', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
@@ -189,14 +137,14 @@ describe('StyleManager', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('setMinMaxHeight', () => {
|
||||
describe('should set min and max height', () => {
|
||||
it('without a config', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.setMinMaxHeight();
|
||||
manager.updateFromConfig();
|
||||
|
||||
expect(element.style.getPropertyValue('--frigate-card-max-height')).toBeFalsy();
|
||||
expect(element.style.getPropertyValue('--frigate-card-expand-height')).toBeFalsy();
|
||||
@@ -215,13 +163,13 @@ describe('StyleManager', () => {
|
||||
);
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.setMinMaxHeight();
|
||||
manager.updateFromConfig();
|
||||
|
||||
expect(element.style.getPropertyValue('--frigate-card-height')).toBe('800px');
|
||||
});
|
||||
});
|
||||
|
||||
describe('setPerformance', () => {
|
||||
describe('should set performance', () => {
|
||||
it('no styles set', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
@@ -229,7 +177,7 @@ describe('StyleManager', () => {
|
||||
vi.mocked(api.getConfigManager().getCardWideConfig).mockReturnValue({});
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.setPerformance();
|
||||
manager.updateFromConfig();
|
||||
|
||||
expect(
|
||||
element.style.getPropertyValue('--frigate-card-css-box-shadow'),
|
||||
@@ -255,7 +203,7 @@ describe('StyleManager', () => {
|
||||
);
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.setPerformance();
|
||||
manager.updateFromConfig();
|
||||
|
||||
expect(element.style.getPropertyValue('--frigate-card-css-box-shadow')).toEqual(
|
||||
'none',
|
||||
@@ -411,4 +359,111 @@ describe('StyleManager', () => {
|
||||
expect(manager.getAspectRatioStyle()).toEqual({ 'aspect-ratio': '4 / 3' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('should apply themes', () => {
|
||||
it('should not apply themes without a config', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.applyTheme();
|
||||
|
||||
expect(element.getAttribute('themes')).toBeNull();
|
||||
});
|
||||
|
||||
describe('should apply named theme', () => {
|
||||
it.each([
|
||||
['light' as const],
|
||||
['dark' as const],
|
||||
['traditional' as const],
|
||||
['ha' as const],
|
||||
])('%s', (theme: ThemeName) => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||
createConfig({
|
||||
view: {
|
||||
theme: {
|
||||
themes: [theme],
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.applyTheme();
|
||||
|
||||
expect(element.getAttribute('themes')).toBe(theme);
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply multiple themes', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||
createConfig({
|
||||
view: {
|
||||
theme: {
|
||||
themes: ['light', 'traditional'],
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.applyTheme();
|
||||
|
||||
expect(element.getAttribute('themes')).toBe('light traditional');
|
||||
});
|
||||
|
||||
it('should treat empty themes list as default', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||
createConfig({
|
||||
view: {
|
||||
theme: {
|
||||
themes: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.applyTheme();
|
||||
|
||||
expect(element.getAttribute('themes')).toBe('traditional');
|
||||
});
|
||||
|
||||
it('should apply overrides', () => {
|
||||
const api = createCardAPI();
|
||||
const element = document.createElement('div');
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||
createConfig({
|
||||
view: {
|
||||
theme: {
|
||||
overrides: {
|
||||
'--test-key': 'test-value',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const manager = new StyleManager(api);
|
||||
|
||||
manager.applyTheme();
|
||||
|
||||
expect(element.style.getPropertyValue('--test-key')).toBe('test-value');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user