diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b608d4fce..3c400c4cc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,15 +4,19 @@ name: test on: push: branches: - - master + - master pull_request: permissions: contents: read jobs: - test-node: + test-node-matrix: 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,16 @@ 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 }} + + 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" 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/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/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/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}; } diff --git a/test/src/exports.ts b/test/src/exports.ts index ccc6806ed..cb4e0d374 100644 --- a/test/src/exports.ts +++ b/test/src/exports.ts @@ -18,10 +18,20 @@ 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) { - if (!legacyComponents[key]) { + for (const key of components) { + if (!module[key]) { missingExports.push(key); } } @@ -30,7 +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.end(); });