@elabs-ai/components-maps 4.0.0
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.
- package/LICENSE +21 -0
- package/README.md +121 -0
- package/dist/index.css +11 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +442 -0
- package/dist/index.js +1475 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
- package/src/index.ts +37 -0
- package/src/lib/arc-math.test.ts +41 -0
- package/src/lib/arc-math.ts +45 -0
- package/src/lib/merge-hover-paint.test.ts +36 -0
- package/src/lib/merge-hover-paint.ts +21 -0
- package/src/lib/use-token-color.ts +23 -0
- package/src/map-arc/index.ts +9 -0
- package/src/map-arc/map-arc.stories.tsx +58 -0
- package/src/map-arc/map-arc.tsx +294 -0
- package/src/map-canvas/index.ts +8 -0
- package/src/map-canvas/map-canvas-webgl-fallback.test.tsx +30 -0
- package/src/map-canvas/map-canvas.stories.tsx +54 -0
- package/src/map-canvas/map-canvas.test.tsx +93 -0
- package/src/map-canvas/map-canvas.tsx +349 -0
- package/src/map-canvas/map-context.ts +32 -0
- package/src/map-canvas/maps.css +15 -0
- package/src/map-canvas/use-resolved-basemap-theme.ts +77 -0
- package/src/map-cluster-layer/index.ts +1 -0
- package/src/map-cluster-layer/map-cluster-layer.stories.tsx +56 -0
- package/src/map-cluster-layer/map-cluster-layer.tsx +292 -0
- package/src/map-controls/index.ts +1 -0
- package/src/map-controls/map-controls.stories.tsx +43 -0
- package/src/map-controls/map-controls.test.tsx +56 -0
- package/src/map-controls/map-controls.tsx +220 -0
- package/src/map-geojson/index.ts +9 -0
- package/src/map-geojson/map-geojson.stories.tsx +124 -0
- package/src/map-geojson/map-geojson.tsx +274 -0
- package/src/map-marker/index.ts +12 -0
- package/src/map-marker/map-marker.stories.tsx +71 -0
- package/src/map-marker/map-marker.test.tsx +81 -0
- package/src/map-marker/map-marker.tsx +373 -0
- package/src/map-popup/index.ts +1 -0
- package/src/map-popup/map-popup.tsx +113 -0
- package/src/map-route/index.ts +1 -0
- package/src/map-route/map-route.stories.tsx +56 -0
- package/src/map-route/map-route.tsx +143 -0
- package/src/test-utils/maplibre-mock.ts +249 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any -- lightweight engine stub; the real surface is exercised by Storybook browser tests */
|
|
2
|
+
/**
|
|
3
|
+
* MapLibre can't render in jsdom (no WebGL) — unit tests mock the engine and
|
|
4
|
+
* assert the brand wrappers' own output, mirroring how @elabs-ai/components-flow mocks
|
|
5
|
+
* @xyflow/react. Real rendering + a11y come from the Storybook story tests.
|
|
6
|
+
*
|
|
7
|
+
* Usage in a test file:
|
|
8
|
+
* vi.mock("maplibre-gl", async () => {
|
|
9
|
+
* const { createMaplibreMock } = await import("../test-utils/maplibre-mock");
|
|
10
|
+
* return createMaplibreMock();
|
|
11
|
+
* });
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
type Handler = (...args: any[]) => void;
|
|
15
|
+
|
|
16
|
+
export class MockMap {
|
|
17
|
+
static instances: MockMap[] = [];
|
|
18
|
+
|
|
19
|
+
handlers = new Map<string, Set<Handler>>();
|
|
20
|
+
container: HTMLElement;
|
|
21
|
+
removed = false;
|
|
22
|
+
sources = new Map<string, any>();
|
|
23
|
+
layers = new Map<string, any>();
|
|
24
|
+
canvas = document.createElement("canvas");
|
|
25
|
+
zoomToCalls: unknown[] = [];
|
|
26
|
+
|
|
27
|
+
/** The full options object the component constructed the map with. */
|
|
28
|
+
options: Record<string, any>;
|
|
29
|
+
|
|
30
|
+
constructor(options: { container: HTMLElement } & Record<string, any>) {
|
|
31
|
+
this.container = options.container;
|
|
32
|
+
this.options = options;
|
|
33
|
+
MockMap.instances.push(this);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private key(event: string, layerId?: string) {
|
|
37
|
+
return layerId ? `${event}:${layerId}` : event;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
on(event: string, a: any, b?: any) {
|
|
41
|
+
const layerId = typeof a === "string" ? a : undefined;
|
|
42
|
+
const handler: Handler = typeof a === "function" ? a : b;
|
|
43
|
+
const key = this.key(event, layerId);
|
|
44
|
+
if (!this.handlers.has(key)) this.handlers.set(key, new Set());
|
|
45
|
+
this.handlers.get(key)!.add(handler);
|
|
46
|
+
// Fire lifecycle events immediately so `isLoaded` flips without a real engine.
|
|
47
|
+
if (event === "load" || event === "styledata") handler();
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
off(event: string, a: any, b?: any) {
|
|
52
|
+
const layerId = typeof a === "string" ? a : undefined;
|
|
53
|
+
const handler: Handler = typeof a === "function" ? a : b;
|
|
54
|
+
this.handlers.get(this.key(event, layerId))?.delete(handler);
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
remove() {
|
|
59
|
+
this.removed = true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getCenter() {
|
|
63
|
+
return { lng: 0, lat: 0 };
|
|
64
|
+
}
|
|
65
|
+
getZoom() {
|
|
66
|
+
return 1;
|
|
67
|
+
}
|
|
68
|
+
getBearing() {
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
getPitch() {
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
isMoving() {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
jumpTo() {}
|
|
78
|
+
easeTo() {}
|
|
79
|
+
flyTo() {}
|
|
80
|
+
zoomTo(zoom: number, options?: unknown) {
|
|
81
|
+
this.zoomToCalls.push([zoom, options]);
|
|
82
|
+
}
|
|
83
|
+
resetNorthPitch() {}
|
|
84
|
+
setStyle() {}
|
|
85
|
+
setProjection() {}
|
|
86
|
+
getContainer() {
|
|
87
|
+
return this.container;
|
|
88
|
+
}
|
|
89
|
+
getCanvas() {
|
|
90
|
+
return this.canvas;
|
|
91
|
+
}
|
|
92
|
+
addSource(id: string, source: any) {
|
|
93
|
+
this.sources.set(id, source);
|
|
94
|
+
}
|
|
95
|
+
getSource(id: string) {
|
|
96
|
+
const source = this.sources.get(id);
|
|
97
|
+
if (!source) return undefined;
|
|
98
|
+
return { ...source, setData: () => {}, getClusterExpansionZoom: async () => 2 };
|
|
99
|
+
}
|
|
100
|
+
removeSource(id: string) {
|
|
101
|
+
this.sources.delete(id);
|
|
102
|
+
}
|
|
103
|
+
addLayer(layer: { id: string }) {
|
|
104
|
+
this.layers.set(layer.id, layer);
|
|
105
|
+
}
|
|
106
|
+
getLayer(id: string) {
|
|
107
|
+
return this.layers.get(id);
|
|
108
|
+
}
|
|
109
|
+
removeLayer(id: string) {
|
|
110
|
+
this.layers.delete(id);
|
|
111
|
+
}
|
|
112
|
+
setPaintProperty() {}
|
|
113
|
+
setLayoutProperty() {}
|
|
114
|
+
setFeatureState() {}
|
|
115
|
+
queryRenderedFeatures() {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export class MockMarker {
|
|
121
|
+
static instances: MockMarker[] = [];
|
|
122
|
+
|
|
123
|
+
element: HTMLElement;
|
|
124
|
+
addedTo: MockMap | null = null;
|
|
125
|
+
popup: unknown = null;
|
|
126
|
+
lngLat = { lng: 0, lat: 0 };
|
|
127
|
+
|
|
128
|
+
constructor(options: { element?: HTMLElement } = {}) {
|
|
129
|
+
this.element = options.element ?? document.createElement("div");
|
|
130
|
+
MockMarker.instances.push(this);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
setLngLat(lngLat: [number, number]) {
|
|
134
|
+
this.lngLat = { lng: lngLat[0], lat: lngLat[1] };
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
getLngLat() {
|
|
138
|
+
return this.lngLat;
|
|
139
|
+
}
|
|
140
|
+
getElement() {
|
|
141
|
+
return this.element;
|
|
142
|
+
}
|
|
143
|
+
addTo(map: MockMap) {
|
|
144
|
+
this.addedTo = map;
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
remove() {
|
|
148
|
+
this.addedTo = null;
|
|
149
|
+
}
|
|
150
|
+
on() {
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
off() {
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
setPopup(popup: unknown) {
|
|
157
|
+
this.popup = popup;
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
isDraggable() {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
setDraggable() {
|
|
164
|
+
return this;
|
|
165
|
+
}
|
|
166
|
+
getOffset() {
|
|
167
|
+
return { x: 0, y: 0 };
|
|
168
|
+
}
|
|
169
|
+
setOffset() {
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
getRotation() {
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
175
|
+
setRotation() {
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
getRotationAlignment() {
|
|
179
|
+
return "auto";
|
|
180
|
+
}
|
|
181
|
+
setRotationAlignment() {
|
|
182
|
+
return this;
|
|
183
|
+
}
|
|
184
|
+
getPitchAlignment() {
|
|
185
|
+
return "auto";
|
|
186
|
+
}
|
|
187
|
+
setPitchAlignment() {
|
|
188
|
+
return this;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export class MockPopup {
|
|
193
|
+
static instances: MockPopup[] = [];
|
|
194
|
+
|
|
195
|
+
content: HTMLElement | null = null;
|
|
196
|
+
|
|
197
|
+
constructor() {
|
|
198
|
+
MockPopup.instances.push(this);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
setMaxWidth() {
|
|
202
|
+
return this;
|
|
203
|
+
}
|
|
204
|
+
setDOMContent(content: HTMLElement) {
|
|
205
|
+
this.content = content;
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
setLngLat() {
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
getLngLat() {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
setOffset() {
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
addTo() {
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
remove() {
|
|
221
|
+
return this;
|
|
222
|
+
}
|
|
223
|
+
isOpen() {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
on() {
|
|
227
|
+
return this;
|
|
228
|
+
}
|
|
229
|
+
off() {
|
|
230
|
+
return this;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Reset the per-class instance registries between tests. */
|
|
235
|
+
export function resetMaplibreMock() {
|
|
236
|
+
MockMap.instances = [];
|
|
237
|
+
MockMarker.instances = [];
|
|
238
|
+
MockPopup.instances = [];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** The module shape to return from `vi.mock("maplibre-gl", ...)`. */
|
|
242
|
+
export function createMaplibreMock() {
|
|
243
|
+
return {
|
|
244
|
+
default: { Map: MockMap, Marker: MockMarker, Popup: MockPopup },
|
|
245
|
+
Map: MockMap,
|
|
246
|
+
Marker: MockMarker,
|
|
247
|
+
Popup: MockPopup,
|
|
248
|
+
};
|
|
249
|
+
}
|