@npm9912/v-map 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { afterEach, beforeAll, describe, expect, it } from "vitest";
|
|
2
|
+
import { createLiveMap, createTaggedElement, loadVMapBundle, waitFor, waitForHydration, } from "../../testing/browser-test-utils";
|
|
3
|
+
import { VMapEvents } from "../../utils/events";
|
|
4
|
+
function dispatchError(target, detail) {
|
|
5
|
+
target.dispatchEvent(new CustomEvent(VMapEvents.Error, {
|
|
6
|
+
detail,
|
|
7
|
+
bubbles: true,
|
|
8
|
+
composed: true,
|
|
9
|
+
}));
|
|
10
|
+
}
|
|
11
|
+
async function mountMapWithErrorComponent(attributes = {}) {
|
|
12
|
+
const map = createLiveMap();
|
|
13
|
+
const errorEl = createTaggedElement('v-map-error');
|
|
14
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
15
|
+
errorEl.setAttribute(name, value);
|
|
16
|
+
}
|
|
17
|
+
map.appendChild(errorEl);
|
|
18
|
+
document.body.appendChild(map);
|
|
19
|
+
await waitForHydration(map);
|
|
20
|
+
await waitForHydration(errorEl);
|
|
21
|
+
return { map, errorEl };
|
|
22
|
+
}
|
|
23
|
+
describe('v-map-error browser', () => {
|
|
24
|
+
beforeAll(async () => {
|
|
25
|
+
await loadVMapBundle();
|
|
26
|
+
await customElements.whenDefined('v-map');
|
|
27
|
+
await customElements.whenDefined('v-map-error');
|
|
28
|
+
});
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
document.body.innerHTML = '';
|
|
31
|
+
});
|
|
32
|
+
it('hydrates and renders an empty stack initially', async () => {
|
|
33
|
+
const { errorEl } = await mountMapWithErrorComponent({
|
|
34
|
+
position: 'top-right',
|
|
35
|
+
'auto-dismiss': '0',
|
|
36
|
+
});
|
|
37
|
+
const stack = errorEl.shadowRoot?.querySelector('.stack');
|
|
38
|
+
expect(stack).toBeTruthy();
|
|
39
|
+
expect(stack?.querySelectorAll('.toast').length).toBe(0);
|
|
40
|
+
});
|
|
41
|
+
it('renders a toast when its parent v-map dispatches vmap-error', async () => {
|
|
42
|
+
const { map, errorEl } = await mountMapWithErrorComponent({
|
|
43
|
+
'auto-dismiss': '0',
|
|
44
|
+
});
|
|
45
|
+
dispatchError(map, {
|
|
46
|
+
type: 'network',
|
|
47
|
+
message: 'WMS request failed',
|
|
48
|
+
});
|
|
49
|
+
await waitFor(() => (errorEl.shadowRoot?.querySelectorAll('.toast').length ?? 0) === 1);
|
|
50
|
+
const toast = errorEl.shadowRoot.querySelector('.toast');
|
|
51
|
+
expect(toast.textContent).toContain('WMS request failed');
|
|
52
|
+
expect(toast.textContent).toContain('network');
|
|
53
|
+
expect(toast.getAttribute('role')).toBe('alert');
|
|
54
|
+
});
|
|
55
|
+
it('positions the stack via the position attribute', async () => {
|
|
56
|
+
const { map, errorEl } = await mountMapWithErrorComponent({
|
|
57
|
+
position: 'bottom-left',
|
|
58
|
+
'auto-dismiss': '0',
|
|
59
|
+
});
|
|
60
|
+
dispatchError(map, { type: 'parse', message: 'positioned' });
|
|
61
|
+
await waitFor(() => (errorEl.shadowRoot?.querySelectorAll('.toast').length ?? 0) === 1);
|
|
62
|
+
const stack = errorEl.shadowRoot.querySelector('.stack');
|
|
63
|
+
const stackRect = stack.getBoundingClientRect();
|
|
64
|
+
const mapRect = map.getBoundingClientRect();
|
|
65
|
+
// bottom-left: stack should sit near map's bottom-left corner
|
|
66
|
+
expect(Math.abs(stackRect.left - mapRect.left)).toBeLessThan(40);
|
|
67
|
+
expect(Math.abs(stackRect.bottom - mapRect.bottom)).toBeLessThan(40);
|
|
68
|
+
});
|
|
69
|
+
it('overlays the map container with a high z-index host', async () => {
|
|
70
|
+
const { errorEl } = await mountMapWithErrorComponent({
|
|
71
|
+
'auto-dismiss': '0',
|
|
72
|
+
});
|
|
73
|
+
const computed = window.getComputedStyle(errorEl);
|
|
74
|
+
expect(computed.position).toBe('absolute');
|
|
75
|
+
expect(parseInt(computed.zIndex, 10)).toBeGreaterThanOrEqual(1000);
|
|
76
|
+
expect(computed.pointerEvents).toBe('none');
|
|
77
|
+
});
|
|
78
|
+
it('caps the toast stack at the configured max', async () => {
|
|
79
|
+
const { map, errorEl } = await mountMapWithErrorComponent({
|
|
80
|
+
'auto-dismiss': '0',
|
|
81
|
+
max: '2',
|
|
82
|
+
});
|
|
83
|
+
dispatchError(map, { type: 'network', message: 'first' });
|
|
84
|
+
dispatchError(map, { type: 'network', message: 'second' });
|
|
85
|
+
dispatchError(map, { type: 'network', message: 'third' });
|
|
86
|
+
await waitFor(() => (errorEl.shadowRoot?.querySelectorAll('.toast').length ?? 0) === 2);
|
|
87
|
+
const messages = Array.from(errorEl.shadowRoot.querySelectorAll('.message')).map(node => node.textContent?.trim());
|
|
88
|
+
expect(messages).toEqual(['second', 'third']);
|
|
89
|
+
});
|
|
90
|
+
it('removes a toast when the close button is clicked', async () => {
|
|
91
|
+
const { map, errorEl } = await mountMapWithErrorComponent({
|
|
92
|
+
'auto-dismiss': '0',
|
|
93
|
+
});
|
|
94
|
+
dispatchError(map, { type: 'parse', message: 'click me away' });
|
|
95
|
+
await waitFor(() => (errorEl.shadowRoot?.querySelectorAll('.toast').length ?? 0) === 1);
|
|
96
|
+
const closeBtn = errorEl.shadowRoot.querySelector('.close');
|
|
97
|
+
closeBtn.click();
|
|
98
|
+
await waitFor(() => (errorEl.shadowRoot?.querySelectorAll('.toast').length ?? 0) === 0);
|
|
99
|
+
});
|
|
100
|
+
it('auto-dismisses a toast after the configured timeout', async () => {
|
|
101
|
+
const { map, errorEl } = await mountMapWithErrorComponent({
|
|
102
|
+
'auto-dismiss': '200',
|
|
103
|
+
});
|
|
104
|
+
dispatchError(map, { type: 'network', message: 'fades' });
|
|
105
|
+
await waitFor(() => (errorEl.shadowRoot?.querySelectorAll('.toast').length ?? 0) === 1);
|
|
106
|
+
await waitFor(() => (errorEl.shadowRoot?.querySelectorAll('.toast').length ?? 0) === 0, 2_000);
|
|
107
|
+
});
|
|
108
|
+
it('targets a v-map by id via the for attribute', async () => {
|
|
109
|
+
const left = createLiveMap();
|
|
110
|
+
left.id = 'left';
|
|
111
|
+
const right = createLiveMap();
|
|
112
|
+
right.id = 'right';
|
|
113
|
+
const errorEl = createTaggedElement('v-map-error');
|
|
114
|
+
errorEl.setAttribute('for', 'right');
|
|
115
|
+
errorEl.setAttribute('auto-dismiss', '0');
|
|
116
|
+
document.body.appendChild(left);
|
|
117
|
+
document.body.appendChild(right);
|
|
118
|
+
document.body.appendChild(errorEl);
|
|
119
|
+
await waitForHydration(left);
|
|
120
|
+
await waitForHydration(right);
|
|
121
|
+
await waitForHydration(errorEl);
|
|
122
|
+
// dispatch on the wrong map - must NOT show
|
|
123
|
+
dispatchError(left, { type: 'network', message: 'wrong' });
|
|
124
|
+
await new Promise(resolve => requestAnimationFrame(() => resolve(undefined)));
|
|
125
|
+
expect(errorEl.shadowRoot?.querySelectorAll('.toast').length).toBe(0);
|
|
126
|
+
// dispatch on the right map - must show
|
|
127
|
+
dispatchError(right, { type: 'network', message: 'right' });
|
|
128
|
+
await waitFor(() => (errorEl.shadowRoot?.querySelectorAll('.toast').length ?? 0) === 1);
|
|
129
|
+
expect(errorEl.shadowRoot?.querySelector('.message')?.textContent).toBe('right');
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npm9912/v-map",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Provider-agnostische Web-Mapping-Komponentenbibliothek auf Basis von Stencil.js — unterstützt OpenLayers, Cesium, Leaflet und Deck.gl über ein einheitliches deklaratives Web-Component-API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"map",
|