From 19a6b2a5d596c2f5b5b017a6d59931a027666057 Mon Sep 17 00:00:00 2001 From: Chris Gervang Date: Mon, 27 Jul 2026 23:30:40 -0700 Subject: [PATCH 1/4] ci: test MapLibre v4 and v5 --- .github/workflows/test.yml | 10 +++++++++- .../src/components/globe-control.ts | 15 ++++++++++++--- modules/react-maplibre/src/types/lib.ts | 13 ++++++++++--- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b608d4fce..412c40a73 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,7 +4,7 @@ name: test on: push: branches: - - master + - master pull_request: permissions: @@ -13,6 +13,10 @@ permissions: jobs: test-node: runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + maplibre: ['^4.0.0', '^5.0.0'] permissions: checks: write contents: read @@ -29,6 +33,9 @@ jobs: - name: Enable Corepack run: corepack enable yarn + - name: Install MapLibre GL JS ${{ matrix.maplibre }} + run: yarn workspace @vis.gl/react-maplibre add --dev "maplibre-gl@${{ matrix.maplibre }}" + - name: Install dependencies run: yarn bootstrap @@ -40,6 +47,7 @@ jobs: yarn test ci - name: Coveralls + if: matrix.maplibre == '^5.0.0' uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6 with: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/modules/react-maplibre/src/components/globe-control.ts b/modules/react-maplibre/src/components/globe-control.ts index 86e507307..07a1c6105 100644 --- a/modules/react-maplibre/src/components/globe-control.ts +++ b/modules/react-maplibre/src/components/globe-control.ts @@ -12,9 +12,18 @@ export type GlobeControlProps = { }; function _GlobeControl(props: GlobeControlProps) { - const ctrl = useControl(({mapLib}) => new mapLib.GlobeControl(props), { - position: props.position - }); + const ctrl = useControl( + ({mapLib}) => { + const GlobeControl = mapLib.GlobeControl; + if (!GlobeControl) { + throw new Error('GlobeControl is not supported by this version of MapLibre GL JS'); + } + return new GlobeControl(props); + }, + { + position: props.position + } + ); useEffect(() => { applyReactStyle(ctrl._container, props.style); diff --git a/modules/react-maplibre/src/types/lib.ts b/modules/react-maplibre/src/types/lib.ts index 7a58c459e..aa6536800 100644 --- a/modules/react-maplibre/src/types/lib.ts +++ b/modules/react-maplibre/src/types/lib.ts @@ -19,7 +19,7 @@ import type { TerrainSpecification, LogoControl, LogoControlOptions, - GlobeControl + IControl } from 'maplibre-gl'; export type { @@ -44,10 +44,17 @@ export type { TerrainControl as TerrainControlInstance, LogoControl as LogoControlInstance, LogoControlOptions, - GlobeControl as GlobeControlInstance, CustomLayerInterface } from 'maplibre-gl'; +type MaplibreModule = typeof import('maplibre-gl'); + +export type GlobeControlInstance = MaplibreModule extends { + GlobeControl: new () => infer Instance; +} + ? Instance + : IControl & {_container: HTMLElement}; + /** * A user-facing type that represents the minimal intersection between Mapbox and Maplibre * User provided `mapLib` is supposed to implement this interface @@ -76,5 +83,5 @@ export interface MapLib { LogoControl: {new (options: LogoControlOptions): LogoControl}; - GlobeControl: {new (options: any): GlobeControl}; + GlobeControl?: {new (options: any): GlobeControlInstance}; } From 31f199a215b03fba86adbf33aef5767b4fc00506 Mon Sep 17 00:00:00 2001 From: Chris Gervang Date: Tue, 28 Jul 2026 00:07:42 -0700 Subject: [PATCH 2/4] fix: export MapLibre GlobeControl --- docs/api-reference/maplibre/globe-control.md | 2 ++ modules/react-maplibre/src/index.ts | 2 ++ test/src/exports.ts | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/api-reference/maplibre/globe-control.md b/docs/api-reference/maplibre/globe-control.md index c1a999882..7f3b82544 100644 --- a/docs/api-reference/maplibre/globe-control.md +++ b/docs/api-reference/maplibre/globe-control.md @@ -2,6 +2,8 @@ React component that wraps maplibre-gl's [GlobeControl](https://maplibre.org/maplibre-gl-js/docs/API/classes/GlobeControl/) class. +Requires `maplibre-gl` v5 or later. + ```tsx import * as React from 'react'; import {Map, GlobeControl} from 'react-map-gl/maplibre'; diff --git a/modules/react-maplibre/src/index.ts b/modules/react-maplibre/src/index.ts index 4704dc9ce..da456a427 100644 --- a/modules/react-maplibre/src/index.ts +++ b/modules/react-maplibre/src/index.ts @@ -11,6 +11,7 @@ export {NavigationControl} from './components/navigation-control'; export {ScaleControl} from './components/scale-control'; export {TerrainControl} from './components/terrain-control'; export {LogoControl} from './components/logo-control'; +export {GlobeControl} from './components/globe-control'; export {Source} from './components/source'; export {Layer} from './components/layer'; export {useControl} from './components/use-control'; @@ -27,6 +28,7 @@ export type {NavigationControlProps} from './components/navigation-control'; export type {ScaleControlProps} from './components/scale-control'; export type {TerrainControlProps} from './components/terrain-control'; export type {LogoControlProps} from './components/logo-control'; +export type {GlobeControlProps} from './components/globe-control'; export type {SourceProps} from './components/source'; export type {LayerProps} from './components/layer'; diff --git a/test/src/exports.ts b/test/src/exports.ts index ccc6806ed..5c1be8e12 100644 --- a/test/src/exports.ts +++ b/test/src/exports.ts @@ -21,7 +21,7 @@ const Components = [ function getMissingExports(module: any): null | string[] { const missingExports: string[] = []; for (const key of Components) { - if (!legacyComponents[key]) { + if (!module[key]) { missingExports.push(key); } } @@ -32,5 +32,6 @@ test('Consistent component names#legacy', t => { t.notOk(getMissingExports(legacyComponents), 'Legacy endpoint contains all components'); t.notOk(getMissingExports(maplibreComponents), 'Maplibre endpoint contains all components'); t.notOk(getMissingExports(mapboxComponents), 'Mapbox endpoint contains all components'); + t.ok(maplibreComponents.GlobeControl, 'Maplibre endpoint exports GlobeControl'); t.end(); }); From b377e398a2f4ffb1e9e5d202cd2232f38a9bfe2a Mon Sep 17 00:00:00 2001 From: Chris Gervang Date: Tue, 28 Jul 2026 00:10:31 -0700 Subject: [PATCH 3/4] test: cover MapLibre-only exports --- test/src/exports.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/test/src/exports.ts b/test/src/exports.ts index 5c1be8e12..cb4e0d374 100644 --- a/test/src/exports.ts +++ b/test/src/exports.ts @@ -18,9 +18,19 @@ const Components = [ 'ScaleControl' ] as const; -function getMissingExports(module: any): null | string[] { +const MaplibreComponents = [ + ...Components, + 'TerrainControl', + 'LogoControl', + 'GlobeControl' +] as const; + +function getMissingExports( + module: any, + components = Components as readonly string[] +): null | string[] { const missingExports: string[] = []; - for (const key of Components) { + for (const key of components) { if (!module[key]) { missingExports.push(key); } @@ -30,8 +40,10 @@ function getMissingExports(module: any): null | string[] { test('Consistent component names#legacy', t => { t.notOk(getMissingExports(legacyComponents), 'Legacy endpoint contains all components'); - t.notOk(getMissingExports(maplibreComponents), 'Maplibre endpoint contains all components'); + t.notOk( + getMissingExports(maplibreComponents, MaplibreComponents), + 'Maplibre endpoint contains all components' + ); t.notOk(getMissingExports(mapboxComponents), 'Mapbox endpoint contains all components'); - t.ok(maplibreComponents.GlobeControl, 'Maplibre endpoint exports GlobeControl'); t.end(); }); From 4363759ff4b1cb5bc6472cba3be3496f84babe7a Mon Sep 17 00:00:00 2001 From: Chris Gervang Date: Tue, 28 Jul 2026 00:13:05 -0700 Subject: [PATCH 4/4] ci: add aggregate test job --- .github/workflows/test.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 412c40a73..3c400c4cc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ permissions: contents: read jobs: - test-node: + test-node-matrix: runs-on: ubuntu-22.04 strategy: fail-fast: false @@ -51,3 +51,12 @@ jobs: uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6 with: github-token: ${{ secrets.GITHUB_TOKEN }} + + test-node: + runs-on: ubuntu-22.04 + needs: test-node-matrix + if: ${{ always() }} + + steps: + - name: Check test matrix + run: test "${{ needs.test-node-matrix.result }}" = "success"