Skip to content

Commit 23a380a

Browse files
authored
fix(maplibre): VectorTileSource/VectorLayer race condition (#349)
1 parent c164ff0 commit 23a380a

30 files changed

+1817
-933
lines changed

.github/workflows/deploy-map-icon-sprite.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: 'Build and deploy map icons'
1+
name: 'deploy map icons'
22
on:
33
workflow_dispatch:
44
push:

.github/workflows/deploy-sophora-components.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: 'Deploy sophora-components'
1+
name: 'deploy sophora-components'
22
on:
33
workflow_dispatch:
44
push:

.github/workflows/deploy-storybook.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: 'Deploy Storybook'
1+
name: 'deploy storybook'
22
on:
33
workflow_dispatch:
44
push:

.github/workflows/e2e.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: e2e tests
2+
on:
3+
workflow_dispatch:
4+
push:
5+
paths:
6+
- 'components/**'
7+
- 'mock-sveltekit/**'
8+
branches:
9+
- main
10+
pull_request:
11+
paths:
12+
- 'components/**'
13+
- 'mock-sveltekit/**'
14+
types: [opened, synchronize]
15+
16+
jobs:
17+
test:
18+
runs-on: ubuntu-latest
19+
timeout-minutes: 10
20+
steps:
21+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
22+
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
23+
with:
24+
node-version: lts/*
25+
- run: npm ci --workspace=components --include=dev
26+
- run: npm run sync --workspace=components
27+
- run: npm ci --workspace=mock-sveltekit
28+
- run: npm run sync --workspace=mock-sveltekit
29+
- run: npm run build --workspace=mock-sveltekit
30+
- run: npx playwright install --with-deps chromium
31+
- run: cd mock-sveltekit && npx playwright test

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Release to NPM
1+
name: release
22

33
on:
44
push:

.github/workflows/test-storybook.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: 'Run Storybook Tests'
1+
name: component tests
22
on:
33
workflow_dispatch:
44
push:

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ Svelte/kit component library for [SWR Data Lab](https://www.swr.de/home/swr-data
1313

1414
### Project structure
1515

16-
1. `/components`: Central repository for reusable components, utility scripts, and other reusable assets. We use [Storybook](https://storybook.js.org/) for previewing and component-level testing.
17-
2. `/mock-sveltekit`: Sample [SvelteKit](https://kit.svelte.dev/) application used to test our components and develop the configuration needed to build our applications for SWR.de, the SWR Aktuell native app and wherever else they need to go.
16+
1. `/components`: Central repository for components, utility scripts, and other reusable assets. We use [Storybook](https://storybook.js.org/) for previewing and component testing.
17+
2. `/mock-sveltekit`: Sample [SvelteKit](https://kit.svelte.dev/) application for e2e testing and developing the configuration needed to deploy our apps to SWR.de and the SWR Aktuell native app.
1818
3. `/mock-sophora`: Testing environment designed to mimic the SWR.de environment, featuring [`defunkt/jquery-pjax`](https://github.com/defunkt/jquery-pjax) navigation, global styles and server-side includes.
1919
4. `/sophora-components`: Experimental components that are intended to be used directly within SWR.de articles and pages via the "Datenjournalismus" module in the Sophora CMS.
2020

2121
### Release workflow
2222

23-
- We use [semantic-release](https://github.com/semantic-release/) to create releases and publish to the [NPM registry](https://www.npmjs.com/package/@swr-data-lab/components) on commit to `main`.
23+
- We use [semantic-release](https://github.com/semantic-release/) to create releases and publish to [npm](https://www.npmjs.com/package/@swr-data-lab/components) on commit to `main`.
2424
- Only [conventional commits](https://www.conventionalcommits.org/) trigger new releases. Prefix your commit message with `fix: ` for a patch, `feat: ` for a minor and `!: ` for a major version bump. See also [semantic-release docs](https://semantic-release.gitbook.io/semantic-release/support/faq).

components/src/maplibre/Maplibre.mdx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ This example initialises a map using the SWRDataLabLight style, adds an addition
3535
<VectorTileSource
3636
id='ev-infra-source'
3737
url='https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}'>
38+
<VectorLayer
39+
sourceId='ev-infra-source'
40+
sourceLayer='coverage'
41+
id='ev-infra-outline'
42+
type='line'
43+
paint={{'line-width': 1, 'line-color': 'red'}}>
44+
</VectorLayer>
3845
</VectorTileSource>
39-
<VectorLayer
40-
sourceId='ev-infra-source'
41-
sourceLayer='coverage'
42-
id='ev-infra-outline'
43-
type='line'
44-
paint={{'line-width': 1, 'line-color': 'red'}}>
45-
</VectorLayer>
4646
<AttributionControl />
4747
</Map>
4848
</div>
Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,51 @@
11
<script lang="ts">
2-
import { onDestroy, type Snippet } from 'svelte';
2+
import { onDestroy, onMount, type Snippet } from 'svelte';
33
import { type Map, type SourceSpecification } from 'maplibre-gl';
4-
import { getMapContext, createSourceContext, SourceContext } from '../context.svelte.js';
5-
6-
type Source = maplibregl.VectorTileSource | maplibregl.GeoJSONSource;
4+
import { getMapContext } from '../context.svelte.js';
75
86
interface MapSourceProps {
97
id: string;
8+
source?: maplibregl.VectorTileSource | maplibregl.GeoJSONSource;
109
sourceSpec: SourceSpecification;
11-
source?: Source;
1210
onLoad?: (map: Map, url?: string, data?: string) => undefined;
1311
children?: Snippet;
1412
}
1513
1614
let { id, sourceSpec, source = $bindable(), children }: MapSourceProps = $props();
1715
16+
const ctx = getMapContext();
1817
let firstRun = $state(true);
1918
20-
// Get map context
21-
const { map, styleLoaded } = $derived(getMapContext());
22-
23-
// Create source context
24-
const sourceContext = createSourceContext();
25-
26-
// 1. Add the source to the map using the spec, then get the
27-
// actual source object back from the map instance
2819
$effect(() => {
29-
if (map && styleLoaded && firstRun) {
30-
map.addSource(id, $state.snapshot(sourceSpec));
31-
source = map.getSource(id);
32-
firstRun = false;
33-
}
20+
ctx.waitForStyleLoaded(() => {
21+
ctx.addSource(id, sourceSpec);
22+
});
23+
firstRun = false;
3424
});
3525
3626
$effect(() => {
37-
if (source && sourceSpec.type === 'geojson') {
38-
if (firstRun === false) {
39-
source.setData(sourceSpec.data);
40-
}
27+
if (!firstRun && source && source.type === 'geojson') {
28+
source.setData(sourceSpec.data);
4129
}
4230
});
4331
4432
$effect(() => {
45-
if (!firstRun && source.setTiles) {
33+
if (!firstRun && source && source.type === 'vector') {
4634
source.setTiles(sourceSpec.tiles);
4735
}
4836
});
4937
5038
$effect(() => {
51-
if (!firstRun && source.setUrl) {
39+
if (!firstRun && source) {
5240
source.setUrl(sourceSpec.url);
5341
}
5442
});
5543
5644
onDestroy(() => {
57-
if (map && styleLoaded) {
58-
const layers = map?.getStyle().layers;
59-
layers
60-
.filter((l) => l.type !== 'background' && l.source == id)
61-
.forEach((l) => {
62-
map?.removeLayer(l.id);
63-
});
64-
map.removeSource(id);
65-
source = undefined;
66-
}
45+
ctx.removeSource(id);
6746
});
6847
</script>
6948

70-
{@render children?.()}
49+
{#if !firstRun}
50+
{@render children?.()}
51+
{/if}

components/src/maplibre/Tooltip/Tooltip.stories.svelte

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -36,46 +36,55 @@
3636
tiles={[
3737
`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`
3838
]}
39-
/>
40-
<VectorLayer
41-
sourceId="ev-infra-source"
42-
type="fill"
43-
id="coverage-fill"
44-
sourceLayer="coverage"
45-
onmousemove={(e) => {
46-
hovered = e.features?.[0];
47-
hoverCoords = e.lngLat;
48-
}}
49-
onmouseleave={() => (hovered = undefined)}
50-
paint={{
51-
'fill-color': ['step', ['get', 'coverage_2025'], 'white', 1, '#ffcfcc', 1.3, '#FF4D20']
52-
}}
53-
/>
54-
<VectorLayer
55-
{hovered}
56-
sourceId="ev-infra-source"
57-
sourceLayer="coverage"
58-
id="ev-infra-outline"
59-
type="line"
60-
layout={{
61-
'line-join': 'round'
62-
}}
63-
paint={{
64-
'line-width': [
65-
'case',
66-
['any', ['boolean', ['feature-state', 'hovered'], false]],
67-
1.5,
68-
0.5
69-
],
70-
'line-color': [
71-
'case',
72-
['any', ['boolean', ['feature-state', 'hovered'], false]],
73-
'#000',
74-
'#555'
75-
],
76-
'line-opacity': 1
77-
}}
78-
/>
39+
>
40+
<VectorLayer
41+
sourceId="ev-infra-source"
42+
type="fill"
43+
id="coverage-fill"
44+
sourceLayer="coverage"
45+
onmousemove={(e) => {
46+
hovered = e.features?.[0];
47+
hoverCoords = e.lngLat;
48+
}}
49+
onmouseleave={() => (hovered = undefined)}
50+
paint={{
51+
'fill-color': [
52+
'step',
53+
['get', 'coverage_2025'],
54+
'white',
55+
1,
56+
'#ffcfcc',
57+
1.3,
58+
'#FF4D20'
59+
]
60+
}}
61+
/>
62+
<VectorLayer
63+
{hovered}
64+
sourceId="ev-infra-source"
65+
sourceLayer="coverage"
66+
id="ev-infra-outline"
67+
type="line"
68+
layout={{
69+
'line-join': 'round'
70+
}}
71+
paint={{
72+
'line-width': [
73+
'case',
74+
['any', ['boolean', ['feature-state', 'hovered'], false]],
75+
1.5,
76+
0.5
77+
],
78+
'line-color': [
79+
'case',
80+
['any', ['boolean', ['feature-state', 'hovered'], false]],
81+
'#000',
82+
'#555'
83+
],
84+
'line-opacity': 1
85+
}}
86+
/>
87+
</VectorTileSource>
7988

8089
{#if hovered}
8190
<Tooltip

0 commit comments

Comments
 (0)