feat: Add UI optional UI locking when microphone is hot (#2484)
This commit is contained in:
committed by
dermotduffy
parent
9a763db1f3
commit
be7e7d79dc
@@ -386,7 +386,7 @@ describe('MediaGridController', () => {
|
||||
).toBe('3');
|
||||
});
|
||||
|
||||
it('should select cell when interacted with', () => {
|
||||
it('should dispatch a selection request when interacted with', () => {
|
||||
const children = createChildren();
|
||||
const parent = createParent({ children: children, width: 2000 });
|
||||
const controller = createController(parent);
|
||||
@@ -396,11 +396,18 @@ describe('MediaGridController', () => {
|
||||
const clickHandler = vi.fn();
|
||||
parent.addEventListener('click', clickHandler);
|
||||
|
||||
const selectedHandler = vi.fn();
|
||||
parent.addEventListener('advanced-camera-card:media-grid:selected', selectedHandler);
|
||||
|
||||
children[1].click();
|
||||
|
||||
// Click will not be allowed through.
|
||||
// Click is consumed; the controller dispatches the selection request but
|
||||
// does NOT mutate local state. The authoritative selection is applied by
|
||||
// the parent via `selectCell` once it propagates back.
|
||||
expect(clickHandler).not.toBeCalled();
|
||||
expect(controller.getSelected()).toBe('1');
|
||||
expect(selectedHandler).toBeCalledTimes(1);
|
||||
expect(selectedHandler.mock.calls[0][0].detail).toEqual({ selected: '1' });
|
||||
expect(controller.getSelected()).toBeNull();
|
||||
});
|
||||
|
||||
it('should ignore interaction events on already selected cell', () => {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { LockManager } from '../../src/card-controller/lock/manager';
|
||||
import type { LockManagerEpoch } from '../../src/card-controller/lock/types';
|
||||
import { MenuController } from '../../src/components-lib/menu-controller.js';
|
||||
import { SubmenuItem } from '../../src/components/submenu/types.js';
|
||||
import { MenuConfig, menuConfigSchema } from '../../src/config/schema/menu.js';
|
||||
@@ -12,6 +15,12 @@ const createMenuConfig = (config: unknown): MenuConfig => {
|
||||
return menuConfigSchema.parse(config);
|
||||
};
|
||||
|
||||
const createLock = (locked: boolean, actionsBlocked: boolean): LockManagerEpoch => {
|
||||
const lockManager = mock<LockManager>();
|
||||
lockManager.areAllActionsBlocked.mockReturnValue(actionsBlocked);
|
||||
return { manager: lockManager, locked };
|
||||
};
|
||||
|
||||
// @vitest-environment jsdom
|
||||
describe('MenuController', () => {
|
||||
const action = {
|
||||
@@ -71,6 +80,137 @@ describe('MenuController', () => {
|
||||
expect(host.getAttribute('expanded')).toBeNull();
|
||||
});
|
||||
|
||||
describe('setLockManagerEpoch', () => {
|
||||
it('should trigger update on first lock change', () => {
|
||||
const host = createLitElement();
|
||||
const controller = new MenuController(host);
|
||||
vi.mocked(host.requestUpdate).mockClear();
|
||||
|
||||
controller.setLockManagerEpoch(createLock(true, true));
|
||||
expect(host.requestUpdate).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should not trigger update when lock epoch is unchanged', () => {
|
||||
const host = createLitElement();
|
||||
const controller = new MenuController(host);
|
||||
const lock = createLock(false, true);
|
||||
controller.setLockManagerEpoch(lock);
|
||||
vi.mocked(host.requestUpdate).mockClear();
|
||||
|
||||
controller.setLockManagerEpoch({ manager: lock.manager, locked: lock.locked });
|
||||
expect(host.requestUpdate).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should reflect lock state in shouldButtonBeInert', () => {
|
||||
const controller = new MenuController(createLitElement());
|
||||
controller.setLockManagerEpoch(createLock(false, true));
|
||||
const button = {
|
||||
type: 'custom:advanced-camera-card-menu-icon' as const,
|
||||
icon: 'mdi:cow',
|
||||
tap_action: {
|
||||
action: 'fire-dom-event' as const,
|
||||
advanced_camera_card_action: 'camera_select' as const,
|
||||
camera: 'cam-1',
|
||||
},
|
||||
};
|
||||
|
||||
expect(controller.shouldButtonBeInert(button)).toBeFalsy();
|
||||
controller.setLockManagerEpoch(createLock(true, true));
|
||||
expect(controller.shouldButtonBeInert(button)).toBeTruthy();
|
||||
controller.setLockManagerEpoch(createLock(false, true));
|
||||
expect(controller.shouldButtonBeInert(button)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldButtonBeInert', () => {
|
||||
const blockedAction = {
|
||||
action: 'fire-dom-event' as const,
|
||||
advanced_camera_card_action: 'camera_select' as const,
|
||||
camera: 'cam-1',
|
||||
};
|
||||
const unblockedAction = {
|
||||
action: 'fire-dom-event' as const,
|
||||
advanced_camera_card_action: 'fullscreen' as const,
|
||||
};
|
||||
|
||||
it('should always return true when user explicitly set `inert: true`', () => {
|
||||
const controller = new MenuController(createLitElement());
|
||||
const button = {
|
||||
type: 'custom:advanced-camera-card-menu-icon' as const,
|
||||
icon: 'mdi:cow',
|
||||
inert: true,
|
||||
tap_action: unblockedAction,
|
||||
};
|
||||
|
||||
// Without the lock active.
|
||||
expect(controller.shouldButtonBeInert(button)).toBeTruthy();
|
||||
// With the lock active.
|
||||
controller.setLockManagerEpoch(createLock(true, false));
|
||||
expect(controller.shouldButtonBeInert(button)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return false when lock is inactive and button is not user-inert', () => {
|
||||
const controller = new MenuController(createLitElement());
|
||||
expect(
|
||||
controller.shouldButtonBeInert({
|
||||
type: 'custom:advanced-camera-card-menu-icon',
|
||||
icon: 'mdi:cow',
|
||||
tap_action: blockedAction,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should never mark submenu containers inert (even when all actions are blocked)', () => {
|
||||
const controller = new MenuController(createLitElement());
|
||||
controller.setLockManagerEpoch(createLock(true, true));
|
||||
|
||||
expect(
|
||||
controller.shouldButtonBeInert({
|
||||
type: 'custom:advanced-camera-card-menu-submenu',
|
||||
icon: 'mdi:menu',
|
||||
items: [],
|
||||
tap_action: blockedAction,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
controller.shouldButtonBeInert({
|
||||
type: 'custom:advanced-camera-card-menu-submenu-select',
|
||||
icon: 'mdi:menu',
|
||||
entity: 'select.foo',
|
||||
state_color: true,
|
||||
tap_action: blockedAction,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should mark icon buttons inert when locked and all actions are blocked', () => {
|
||||
const controller = new MenuController(createLitElement());
|
||||
controller.setLockManagerEpoch(createLock(true, true));
|
||||
|
||||
expect(
|
||||
controller.shouldButtonBeInert({
|
||||
type: 'custom:advanced-camera-card-menu-icon',
|
||||
icon: 'mdi:cow',
|
||||
tap_action: blockedAction,
|
||||
}),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should NOT mark icon buttons inert when locked and not all actions are blocked', () => {
|
||||
const controller = new MenuController(createLitElement());
|
||||
controller.setLockManagerEpoch(createLock(true, false));
|
||||
|
||||
expect(
|
||||
controller.shouldButtonBeInert({
|
||||
type: 'custom:advanced-camera-card-menu-icon',
|
||||
icon: 'mdi:cow',
|
||||
tap_action: blockedAction,
|
||||
hold_action: unblockedAction,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('should set and sort buttons', () => {
|
||||
it('without a hidden menu', () => {
|
||||
const controller = new MenuController(createLitElement());
|
||||
|
||||
Reference in New Issue
Block a user