fix: Various editor / diagnostic improvements (#2363)
- Closes: #2360 - Closes: #2328
This commit is contained in:
@@ -20,7 +20,11 @@ assignees: ''
|
||||
**[REQUIRED] Card diagnostic information:**
|
||||
|
||||
<!--
|
||||
On the card, hold down the Iris (default) menu button for a few seconds then paste the diagnostics *between* the pair of triple backticks below. No help can be provided without this.
|
||||
No help can be provided without this information. Fetch the diagnostics and paste them *between* the pair of triple backticks below.
|
||||
|
||||
Use either of two ways to fetch the diagnostics:
|
||||
- Hold down the Iris (default) menu button for a few seconds.
|
||||
- Open the card editor and click the 'Toggle Diagnostics' button.
|
||||
-->
|
||||
|
||||
```yaml
|
||||
|
||||
+4
-2
@@ -19,8 +19,10 @@ incrementally improve, and I think time is better spent working on reproducible
|
||||
issues or new features, than guessing what versions/options you are using. The
|
||||
new issue template asks for the minimum possible information to support you:
|
||||
|
||||
- **Diagnostics**: On the card, hold down the Iris (default) menu button for a
|
||||
few seconds then paste the diagnostics into the issue between the backticks.
|
||||
- **Diagnostics**: On the card, fetch the diagnostics and paste into the issue
|
||||
between the backticks. You can fetch the diagnostics in either of two ways:
|
||||
- Hold down the Iris (default) menu button for a few seconds.
|
||||
- Open the card editor and click the 'Toggle Diagnostics' button.
|
||||
- **Description of the problem**: I need to understand the issue in sufficient
|
||||
detail.
|
||||
|
||||
|
||||
@@ -32,15 +32,7 @@ class ActionHandler extends HTMLElement implements ActionHandlerInterface {
|
||||
private started = false;
|
||||
|
||||
public connectedCallback(): void {
|
||||
[
|
||||
'touchcancel',
|
||||
'mouseout',
|
||||
'mouseup',
|
||||
'touchmove',
|
||||
'mousewheel',
|
||||
'wheel',
|
||||
'scroll',
|
||||
].forEach((ev) => {
|
||||
['mouseup', 'mousewheel', 'scroll', 'touchcancel', 'wheel'].forEach((ev) => {
|
||||
document.addEventListener(
|
||||
ev,
|
||||
() => {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { LitElement, ReactiveControllerHost } from 'lit';
|
||||
import { ActionEventTarget } from '../action-handler-directive';
|
||||
import { isCardInPanel } from '../ha/panel';
|
||||
import { LovelaceCard } from '../ha/types';
|
||||
import { setOrRemoveAttribute } from '../utils/basic';
|
||||
import { isBeingCasted } from '../utils/casting';
|
||||
import { isAncestorInEventPath } from '../utils/event-ancestor';
|
||||
import { CardMediaReviewEventTarget } from '../utils/review';
|
||||
import { ViewItem } from '../view/item';
|
||||
import { ActionExecutionRequestEventTarget } from './actions/utils/execution-request';
|
||||
@@ -12,7 +14,8 @@ import { CardElementAPI } from './types';
|
||||
export type ScrollCallback = () => void;
|
||||
export type MenuToggleCallback = () => void;
|
||||
|
||||
export type CardHTMLElement = LitElement &
|
||||
export type CardHTMLElement = LovelaceCard &
|
||||
LitElement &
|
||||
ReactiveControllerHost &
|
||||
ActionEventTarget &
|
||||
ActionExecutionRequestEventTarget &
|
||||
@@ -141,6 +144,10 @@ export class CardElementManager {
|
||||
'popstate',
|
||||
this._api.getQueryStringManager().requestExecution,
|
||||
);
|
||||
window.addEventListener(
|
||||
'advanced-camera-card:editor:diagnostics',
|
||||
this._editorDiagnosticsHandler,
|
||||
);
|
||||
|
||||
this._api.getConditionStateManager()?.setState({
|
||||
userAgent: navigator.userAgent,
|
||||
@@ -218,6 +225,10 @@ export class CardElementManager {
|
||||
'popstate',
|
||||
this._api.getQueryStringManager().requestExecution,
|
||||
);
|
||||
window.removeEventListener(
|
||||
'advanced-camera-card:editor:diagnostics',
|
||||
this._editorDiagnosticsHandler,
|
||||
);
|
||||
}
|
||||
|
||||
private _handleMediaReviewed = (ev: CustomEvent<ViewItem>): void => {
|
||||
@@ -230,4 +241,21 @@ export class CardElementManager {
|
||||
this.update();
|
||||
}
|
||||
};
|
||||
|
||||
protected _editorDiagnosticsHandler = (ev: Event): void => {
|
||||
const toggleDiagnostics = (): void => {
|
||||
const viewManager = this._api.getViewManager();
|
||||
if (viewManager.getView()?.view === 'diagnostics') {
|
||||
viewManager.setViewDefault();
|
||||
} else {
|
||||
viewManager.setViewByParameters({
|
||||
params: { view: 'diagnostics' },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (isAncestorInEventPath(this._element, ev, 'hui-dialog-edit-card')) {
|
||||
toggleDiagnostics();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+29
-20
@@ -271,6 +271,7 @@ import { localize } from './localize/localize.js';
|
||||
import editorStyle from './scss/editor.scss';
|
||||
import { arrayMove, prettifyTitle } from './utils/basic.js';
|
||||
import { getCameraID } from './utils/camera.js';
|
||||
import { fireAdvancedCameraCardEvent } from './utils/fire-advanced-camera-card-event.js';
|
||||
import { getFolderID } from './utils/folder.js';
|
||||
|
||||
const MENU_CAMERAS = 'cameras';
|
||||
@@ -2965,26 +2966,6 @@ export class AdvancedCameraCardEditor extends LitElement implements LovelaceCard
|
||||
[]) as RawAdvancedCameraCardConfigArray;
|
||||
|
||||
return html`
|
||||
${this._configUpgradeable
|
||||
? html` <div class="upgrade">
|
||||
<span>${localize('editor.upgrade_available')}</span>
|
||||
<span>
|
||||
<mwc-button
|
||||
raised
|
||||
label="${localize('editor.upgrade')}"
|
||||
@click=${() => {
|
||||
if (this._config) {
|
||||
const upgradedConfig = copyConfig(this._config);
|
||||
upgradeConfig(upgradedConfig);
|
||||
this._updateConfig(upgradedConfig);
|
||||
}
|
||||
}}
|
||||
>
|
||||
</mwc-button>
|
||||
</span>
|
||||
</div>
|
||||
<br />`
|
||||
: html``}
|
||||
<div class="card-config">
|
||||
${this._renderOptionSetHeader('cameras')}
|
||||
${this._expandedMenus[MENU_OPTIONS] === 'cameras'
|
||||
@@ -3597,6 +3578,34 @@ export class AdvancedCameraCardEditor extends LitElement implements LovelaceCard
|
||||
</div>`
|
||||
: ''}`
|
||||
: html``}
|
||||
<div class="action-buttons">
|
||||
${this._configUpgradeable
|
||||
? html`<ha-button
|
||||
appearance="filled"
|
||||
variant="warning"
|
||||
title=${localize('editor.upgrade_available')}
|
||||
aria-label=${localize('editor.upgrade_available')}
|
||||
@click=${() => {
|
||||
if (this._config) {
|
||||
const upgradedConfig = copyConfig(this._config);
|
||||
upgradeConfig(upgradedConfig);
|
||||
this._updateConfig(upgradedConfig);
|
||||
}
|
||||
}}
|
||||
>
|
||||
${localize('editor.upgrade')}
|
||||
</ha-button>`
|
||||
: ''}
|
||||
<ha-button
|
||||
title=${localize('editor.toggle_diagnostics')}
|
||||
aria-label=${localize('editor.toggle_diagnostics')}
|
||||
@click=${() => {
|
||||
fireAdvancedCameraCardEvent(this, 'editor:diagnostics');
|
||||
}}
|
||||
>
|
||||
${localize('editor.toggle_diagnostics')}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ export const sideLoadHomeAssistantElements = async (): Promise<boolean> => {
|
||||
'ha-spinner',
|
||||
'ha-state-icon',
|
||||
'ha-web-rtc-player',
|
||||
'mwc-button',
|
||||
'mwc-list-item',
|
||||
'state-badge',
|
||||
];
|
||||
|
||||
@@ -189,6 +189,7 @@ export interface LovelaceCard extends HTMLElement {
|
||||
hass?: HomeAssistant;
|
||||
isPanel?: boolean;
|
||||
editMode?: boolean;
|
||||
preview?: boolean;
|
||||
getCardSize(): number | Promise<number>;
|
||||
setConfig(config: LovelaceCardConfig): void;
|
||||
}
|
||||
|
||||
@@ -688,11 +688,12 @@
|
||||
"profiles_secondary": "Choose pre-configured sets of defaults",
|
||||
"remote_control": "Remote Control",
|
||||
"remote_control_secondary": "Options for remote controlling the card",
|
||||
"toggle_diagnostics": "Toggle diagnostics",
|
||||
"status_bar": "Status bar",
|
||||
"status_bar_secondary": "Status bar look & feel options",
|
||||
"timeline": "Timeline",
|
||||
"timeline_secondary": "Event timeline options",
|
||||
"upgrade": "Upgrade",
|
||||
"upgrade": "Automatic Upgrade",
|
||||
"upgrade_available": "An automatic card configuration upgrade is available",
|
||||
"view": "View",
|
||||
"view_secondary": "What the card should show and how to show it"
|
||||
|
||||
@@ -36,16 +36,9 @@
|
||||
.submenu + .option {
|
||||
margin-top: 10px;
|
||||
}
|
||||
div.upgrade {
|
||||
width: auto;
|
||||
border: 1px dotted var(--primary-color);
|
||||
margin: 10px;
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
div.upgrade span {
|
||||
padding: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.submenu-header {
|
||||
@@ -150,3 +143,7 @@ advanced-camera-card-message::part(icon) {
|
||||
margin-top: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
ha-button {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Walk up `element`'s ancestor chain (through shadow boundaries) looking for
|
||||
* an ancestor with the given tag name. Returns true if one is found and that
|
||||
* same element also appears in the event's composedPath — indicating the event
|
||||
* originated from within the same subtree as the element.
|
||||
*/
|
||||
export const isAncestorInEventPath = (
|
||||
element: Element,
|
||||
ev: Event,
|
||||
tagName: string,
|
||||
): boolean => {
|
||||
const composedPath = ev.composedPath();
|
||||
let node: Node | null = element;
|
||||
while (node) {
|
||||
if (node instanceof Element && node.tagName.toLowerCase() === tagName) {
|
||||
return composedPath.includes(node);
|
||||
}
|
||||
node = node instanceof ShadowRoot ? node.host : node.parentNode;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -1,13 +1,14 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { afterEach, assert, describe, expect, it, vi } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { CardElementManager } from '../../src/card-controller/card-element-manager';
|
||||
import { StateWatcher } from '../../src/card-controller/hass/state-watcher';
|
||||
import { QueryResults } from '../../src/view/query-results';
|
||||
import { View } from '../../src/view/view';
|
||||
import {
|
||||
callStateWatcherCallback,
|
||||
createCardAPI,
|
||||
createCardHTMLElement,
|
||||
createConfig,
|
||||
createLitElement,
|
||||
createStateEntity,
|
||||
createView,
|
||||
TestViewMedia,
|
||||
@@ -20,7 +21,7 @@ describe('CardElementManager', () => {
|
||||
});
|
||||
|
||||
it('should get element', () => {
|
||||
const element = createLitElement();
|
||||
const element = createCardHTMLElement();
|
||||
const manager = new CardElementManager(
|
||||
createCardAPI(),
|
||||
element,
|
||||
@@ -35,7 +36,7 @@ describe('CardElementManager', () => {
|
||||
const callback = vi.fn();
|
||||
const manager = new CardElementManager(
|
||||
createCardAPI(),
|
||||
createLitElement(),
|
||||
createCardHTMLElement(),
|
||||
callback,
|
||||
() => undefined,
|
||||
);
|
||||
@@ -49,7 +50,7 @@ describe('CardElementManager', () => {
|
||||
const callback = vi.fn();
|
||||
const manager = new CardElementManager(
|
||||
createCardAPI(),
|
||||
createLitElement(),
|
||||
createCardHTMLElement(),
|
||||
() => undefined,
|
||||
callback,
|
||||
);
|
||||
@@ -60,7 +61,7 @@ describe('CardElementManager', () => {
|
||||
});
|
||||
|
||||
it('should update', () => {
|
||||
const element = createLitElement();
|
||||
const element = createCardHTMLElement();
|
||||
const manager = new CardElementManager(
|
||||
createCardAPI(),
|
||||
element,
|
||||
@@ -73,7 +74,7 @@ describe('CardElementManager', () => {
|
||||
});
|
||||
|
||||
it('should get hasUpdated', () => {
|
||||
const element = createLitElement();
|
||||
const element = createCardHTMLElement();
|
||||
element.hasUpdated = true;
|
||||
const manager = new CardElementManager(
|
||||
createCardAPI(),
|
||||
@@ -89,7 +90,7 @@ describe('CardElementManager', () => {
|
||||
const windowAddEventListener = vi.spyOn(global.window, 'addEventListener');
|
||||
|
||||
const addEventListener = vi.fn();
|
||||
const element = createLitElement();
|
||||
const element = createCardHTMLElement();
|
||||
element.addEventListener = addEventListener;
|
||||
|
||||
const api = createCardAPI();
|
||||
@@ -132,6 +133,10 @@ describe('CardElementManager', () => {
|
||||
);
|
||||
expect(windowAddEventListener).toBeCalledWith('location-changed', expect.anything());
|
||||
expect(windowAddEventListener).toBeCalledWith('popstate', expect.anything());
|
||||
expect(windowAddEventListener).toBeCalledWith(
|
||||
'advanced-camera-card:editor:diagnostics',
|
||||
expect.anything(),
|
||||
);
|
||||
|
||||
expect(api.getInteractionManager().initialize).toBeCalled();
|
||||
expect(api.getFullscreenManager().initialize).toBeCalled();
|
||||
@@ -143,7 +148,7 @@ describe('CardElementManager', () => {
|
||||
it('should disconnect', () => {
|
||||
const windowRemoveEventListener = vi.spyOn(global.window, 'removeEventListener');
|
||||
|
||||
const element = createLitElement();
|
||||
const element = createCardHTMLElement();
|
||||
element.setAttribute('panel', '');
|
||||
element.setAttribute('casted', '');
|
||||
|
||||
@@ -195,6 +200,10 @@ describe('CardElementManager', () => {
|
||||
expect.anything(),
|
||||
);
|
||||
expect(windowRemoveEventListener).toBeCalledWith('popstate', expect.anything());
|
||||
expect(windowRemoveEventListener).toBeCalledWith(
|
||||
'advanced-camera-card:editor:diagnostics',
|
||||
expect.anything(),
|
||||
);
|
||||
|
||||
expect(api.getMediaLoadedInfoManager().clear).toBeCalled();
|
||||
expect(api.getFullscreenManager().disconnect).toBeCalled();
|
||||
@@ -217,7 +226,7 @@ describe('CardElementManager', () => {
|
||||
const stateWatcher = mock<StateWatcher>();
|
||||
vi.mocked(api.getHASSManager().getStateWatcher).mockReturnValue(stateWatcher);
|
||||
|
||||
const element = createLitElement();
|
||||
const element = createCardHTMLElement();
|
||||
const manager = new CardElementManager(
|
||||
api,
|
||||
element,
|
||||
@@ -245,7 +254,7 @@ describe('CardElementManager', () => {
|
||||
const stateWatcher = mock<StateWatcher>();
|
||||
vi.mocked(api.getHASSManager().getStateWatcher).mockReturnValue(stateWatcher);
|
||||
|
||||
const element = createLitElement();
|
||||
const element = createCardHTMLElement();
|
||||
const manager = new CardElementManager(
|
||||
api,
|
||||
element,
|
||||
@@ -275,7 +284,7 @@ describe('CardElementManager', () => {
|
||||
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
|
||||
|
||||
const element = createLitElement();
|
||||
const element = createCardHTMLElement();
|
||||
const manager = new CardElementManager(
|
||||
api,
|
||||
element,
|
||||
@@ -310,7 +319,7 @@ describe('CardElementManager', () => {
|
||||
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
|
||||
|
||||
const element = createLitElement();
|
||||
const element = createCardHTMLElement();
|
||||
const manager = new CardElementManager(
|
||||
api,
|
||||
element,
|
||||
@@ -334,4 +343,95 @@ describe('CardElementManager', () => {
|
||||
expect(element.requestUpdate).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('should handle diagnostics', () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
const createDialogWithCard = (element: HTMLElement) => {
|
||||
const dialog = document.createElement('hui-dialog-edit-card');
|
||||
dialog.attachShadow({ mode: 'open' });
|
||||
assert(dialog.shadowRoot);
|
||||
dialog.shadowRoot.append(element);
|
||||
return dialog;
|
||||
};
|
||||
|
||||
const fireFromDialog = (dialog: HTMLElement) => {
|
||||
const editorDiv = document.createElement('div');
|
||||
assert(dialog.shadowRoot);
|
||||
dialog.shadowRoot.append(editorDiv);
|
||||
editorDiv.dispatchEvent(
|
||||
new CustomEvent('advanced-camera-card:editor:diagnostics', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
it('sets view to diagnostics if card is in editor', () => {
|
||||
const api = createCardAPI();
|
||||
const element = createCardHTMLElement();
|
||||
const manager = new CardElementManager(
|
||||
api,
|
||||
element,
|
||||
() => undefined,
|
||||
() => undefined,
|
||||
);
|
||||
|
||||
const dialog = createDialogWithCard(element);
|
||||
document.body.append(dialog);
|
||||
manager.elementConnected();
|
||||
|
||||
fireFromDialog(dialog);
|
||||
|
||||
expect(api.getViewManager().setViewByParameters).toBeCalledWith({
|
||||
params: { view: 'diagnostics' },
|
||||
});
|
||||
});
|
||||
|
||||
it('resets to default view if already in diagnostics view', () => {
|
||||
const api = createCardAPI();
|
||||
const element = createCardHTMLElement();
|
||||
const manager = new CardElementManager(
|
||||
api,
|
||||
element,
|
||||
() => undefined,
|
||||
() => undefined,
|
||||
);
|
||||
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(
|
||||
new View({ view: 'diagnostics' }),
|
||||
);
|
||||
|
||||
const dialog = createDialogWithCard(element);
|
||||
document.body.append(dialog);
|
||||
manager.elementConnected();
|
||||
|
||||
fireFromDialog(dialog);
|
||||
|
||||
expect(api.getViewManager().setViewDefault).toBeCalled();
|
||||
});
|
||||
|
||||
it('does not set view to diagnostics if card is not in editor', () => {
|
||||
const api = createCardAPI();
|
||||
const element = createCardHTMLElement();
|
||||
const manager = new CardElementManager(
|
||||
api,
|
||||
element,
|
||||
() => undefined,
|
||||
() => undefined,
|
||||
);
|
||||
|
||||
manager.elementConnected();
|
||||
|
||||
// Event fired from a different dialog that does not contain the card
|
||||
const otherDialog = document.createElement('hui-dialog-edit-card');
|
||||
otherDialog.attachShadow({ mode: 'open' });
|
||||
document.body.append(otherDialog);
|
||||
fireFromDialog(otherDialog);
|
||||
|
||||
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+11
-1
@@ -24,7 +24,10 @@ import {
|
||||
import { ActionsManager } from '../src/card-controller/actions/actions-manager';
|
||||
import { AutomationsManager } from '../src/card-controller/automations-manager';
|
||||
import { CameraURLManager } from '../src/card-controller/camera-url-manager';
|
||||
import { CardElementManager } from '../src/card-controller/card-element-manager';
|
||||
import {
|
||||
CardElementManager,
|
||||
CardHTMLElement,
|
||||
} from '../src/card-controller/card-element-manager';
|
||||
import { ConfigManager } from '../src/card-controller/config/config-manager';
|
||||
import { CardController } from '../src/card-controller/controller';
|
||||
import { DefaultManager } from '../src/card-controller/default-manager';
|
||||
@@ -626,6 +629,13 @@ export const createParent = (options?: { children?: HTMLElement[] }): HTMLElemen
|
||||
return parent;
|
||||
};
|
||||
|
||||
export const createCardHTMLElement = (): CardHTMLElement => {
|
||||
const element = createLitElement() as CardHTMLElement;
|
||||
element.getCardSize = vi.fn();
|
||||
element.setConfig = vi.fn();
|
||||
return element;
|
||||
};
|
||||
|
||||
export const createLitElement = (): LitElement => {
|
||||
const element = document.createElement('div') as unknown as LitElement;
|
||||
element.addController = vi.fn();
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import { assert, describe, expect, it } from 'vitest';
|
||||
import { isAncestorInEventPath } from '../../src/utils/event-ancestor';
|
||||
|
||||
// @vitest-environment jsdom
|
||||
describe('isAncestorInEventPath', () => {
|
||||
const dispatch = (source: EventTarget, listener: (ev: Event) => void) => {
|
||||
window.addEventListener('test', listener, { once: true });
|
||||
source.dispatchEvent(new CustomEvent('test', { bubbles: true, composed: true }));
|
||||
};
|
||||
|
||||
it('returns true when element shares the ancestor with the event source', () => {
|
||||
const element = document.createElement('div');
|
||||
const ancestor = document.createElement('hui-dialog-edit-card');
|
||||
ancestor.attachShadow({ mode: 'open' });
|
||||
assert(ancestor.shadowRoot);
|
||||
ancestor.shadowRoot.append(element);
|
||||
document.body.append(ancestor);
|
||||
|
||||
const eventSource = document.createElement('div');
|
||||
ancestor.shadowRoot.append(eventSource);
|
||||
|
||||
let result: boolean | undefined;
|
||||
dispatch(eventSource, (ev) => {
|
||||
result = isAncestorInEventPath(element, ev, 'hui-dialog-edit-card');
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false when element and event source are in different ancestors with the same tag', () => {
|
||||
const element = document.createElement('div');
|
||||
const ancestor1 = document.createElement('hui-dialog-edit-card');
|
||||
ancestor1.attachShadow({ mode: 'open' });
|
||||
assert(ancestor1.shadowRoot);
|
||||
ancestor1.shadowRoot.append(element);
|
||||
document.body.append(ancestor1);
|
||||
|
||||
const ancestor2 = document.createElement('hui-dialog-edit-card');
|
||||
ancestor2.attachShadow({ mode: 'open' });
|
||||
assert(ancestor2.shadowRoot);
|
||||
const eventSource = document.createElement('div');
|
||||
ancestor2.shadowRoot.append(eventSource);
|
||||
document.body.append(ancestor2);
|
||||
|
||||
let result: boolean | undefined;
|
||||
dispatch(eventSource, (ev) => {
|
||||
result = isAncestorInEventPath(element, ev, 'hui-dialog-edit-card');
|
||||
});
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when element has no ancestor with the given tag', () => {
|
||||
const element = document.createElement('div');
|
||||
document.body.append(element);
|
||||
|
||||
const eventSource = document.createElement('div');
|
||||
document.body.append(eventSource);
|
||||
|
||||
let result: boolean | undefined;
|
||||
dispatch(eventSource, (ev) => {
|
||||
result = isAncestorInEventPath(element, ev, 'hui-dialog-edit-card');
|
||||
});
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('traverses shadow boundaries in the element ancestor chain', () => {
|
||||
const element = document.createElement('div');
|
||||
const inner = document.createElement('hui-card');
|
||||
inner.attachShadow({ mode: 'open' });
|
||||
assert(inner.shadowRoot);
|
||||
inner.shadowRoot.append(element);
|
||||
|
||||
const ancestor = document.createElement('hui-dialog-edit-card');
|
||||
ancestor.attachShadow({ mode: 'open' });
|
||||
assert(ancestor.shadowRoot);
|
||||
ancestor.shadowRoot.append(inner);
|
||||
document.body.append(ancestor);
|
||||
|
||||
const eventSource = document.createElement('div');
|
||||
ancestor.shadowRoot.append(eventSource);
|
||||
|
||||
let result: boolean | undefined;
|
||||
dispatch(eventSource, (ev) => {
|
||||
result = isAncestorInEventPath(element, ev, 'hui-dialog-edit-card');
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user