Skip to content

Upgrade maplibre and react-map-gl #1452

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Aug 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7d35c98
chore(deps): Upgrade maplibre-gl and react-map-gl. Fix imports.
binh-dam-ibigroup Apr 18, 2025
d961b64
refactor(getFitBoundsPadding): Import from OTP-UI base-map ESM
binh-dam-ibigroup Apr 18, 2025
d2f2d22
refactor: Update imports for react-map-gl
binh-dam-ibigroup Apr 18, 2025
1d4090a
chore(deps): Use OTP-UI test packages
binh-dam-ibigroup Apr 23, 2025
0a4de65
chore(deps): Use tmp dependencies for test
binh-dam-ibigroup Jun 2, 2025
636d740
upgrade trip-form
miles-grant-ibigroup Jul 8, 2025
175b0ea
chore(deps): Update OTP-UI trip-form
binh-dam-ibigroup Jul 10, 2025
f4d84e1
fix(ResponsiveWebApp): Sort from/to coordinates before calling map.fi…
binh-dam-ibigroup Jul 11, 2025
3be3901
fix(RoutePreviewOverlay): Sort coordinates before calling map.fitBounds
binh-dam-ibigroup Jul 11, 2025
b42f5ae
refactor(webapp,rtprvw): Extract method to fit map to endpoints
binh-dam-ibigroup Jul 11, 2025
12ef610
refactor(RoutePreviewOverlay): Improve typing
binh-dam-ibigroup Jul 11, 2025
1e70eef
refactor(RoutePreviewOverlay): Remove console statement
binh-dam-ibigroup Jul 11, 2025
52a30fe
chore(deps): Update maplibre to 5.6.1
binh-dam-ibigroup Jul 15, 2025
55b77a4
refactor(util): Reuse fitMapToPoints with bounds sorting from base-map.
binh-dam-ibigroup Jul 15, 2025
991f56d
Merge branch 'dev' into new-maplibre-nopublish
binh-dam-ibigroup Jul 15, 2025
84b55f0
chore(deps): Finish updating maplibre to 5.6.1.
binh-dam-ibigroup Jul 15, 2025
6fe7dd6
fix(NearbyView): Throttle mouseleave events (Firefox bug)
binh-dam-ibigroup Jul 15, 2025
5779115
Merge branch 'dev' into new-maplibre-nopublish
binh-dam-ibigroup Aug 6, 2025
c9da4e9
chore(deps): Upgrade OTP-UI packages
binh-dam-ibigroup Aug 7, 2025
b8efe28
chore(deps): Update more OTP-UI packages that use new maplibre libraries
binh-dam-ibigroup Aug 8, 2025
8ca4dba
build(jest): Add alias mapping to react-map-gl.
binh-dam-ibigroup Aug 13, 2025
576417a
fix(RoutePreviewOverlay): Separate visible and mainPanelContent prop.
binh-dam-ibigroup Aug 13, 2025
341a7ff
fix(map.css): Underline map links for a11y tests.
binh-dam-ibigroup Aug 13, 2025
a6e77bc
Revert "fix(map.css): Underline map links for a11y tests."
binh-dam-ibigroup Aug 13, 2025
0796d02
test(a11y): Exclude .maplibregl-marker from a11y tests.
binh-dam-ibigroup Aug 13, 2025
0a0b320
refactor(map): Remove mapbox CSS references.
binh-dam-ibigroup Aug 13, 2025
1e4105f
Merge branch 'dev' into new-maplibre
binh-dam-ibigroup Aug 13, 2025
ef5bc72
Merge branch 'dev' into new-maplibre
binh-dam-ibigroup Aug 21, 2025
d5356e3
Merge branch 'dev' into new-maplibre
binh-dam-ibigroup Aug 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion a11y/a11y.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ async function runAxeTestOnPath(otpPath) {
page.waitForNavigation({ waitUntil: 'networkidle2' })
])

await expect(page).toPassAxeTests({ disabledRules })
await expect(page).toPassAxeTests({
disabledRules,
// FIXME: Send a PR to maplibre-gl 5.x to not populate aria-label on <div> elements.
// This occurs in their source code at https://github.com/maplibre/maplibre-gl-js/blob/b450876c1707ad7fc563a86b37a472578b4545dc/src/ui/marker.ts#L319.
exclude: '.maplibregl-marker'
})
return page
}

Expand Down
17 changes: 10 additions & 7 deletions lib/components/app/responsive-webapp.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { Col, Grid, Row } from 'react-bootstrap'
import { connect } from 'react-redux'
import { ConnectedRouter } from 'connected-react-router'
import { createHashHistory } from 'history'
import { getFitBoundsPadding } from '@opentripplanner/base-map/lib/util'
import { injectIntl, IntlProvider } from 'react-intl'
import { MapProvider } from 'react-map-gl'
import { MapProvider } from 'react-map-gl/maplibre'
import { QueryParamProvider } from 'use-query-params'
import { ReactRouter5Adapter } from 'use-query-params/adapters/react-router-5'
import { Route, Switch, withRouter } from 'react-router'
import { Toaster } from 'react-hot-toast'
import { util } from '@opentripplanner/base-map'
import coreUtils from '@opentripplanner/core-utils'
import isEqual from 'lodash.isequal'
import PropTypes from 'prop-types'
Expand Down Expand Up @@ -107,11 +107,14 @@ class ResponsiveWebapp extends Component {
autoFly !== false &&
activeItinerary === null
) {
if (query.from && query.to) {
map?.fitBounds([query.from, query.to], {
duration: 600,
padding: getFitBoundsPadding(map, 0.2)
})
if (
query.from &&
query.to &&
query.from !== prevProps.query.from &&
query.to !== prevProps.query.to &&
map
) {
util.fitMapToPoints(map, query.from, query.to, 0.2, 600)
} else if (query.from && !query.to) {
setMapCenter(map, query.from)
} else if (query.to && !query.from) {
Expand Down
5 changes: 2 additions & 3 deletions lib/components/map/default-map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import { connect } from 'react-redux'
import { GeolocateControl, NavigationControl } from 'react-map-gl'
import { GeolocateControl, NavigationControl } from 'react-map-gl/maplibre'
import { getCurrentDate } from '@opentripplanner/core-utils/lib/time'
import { injectIntl } from 'react-intl'
import BaseMap from '@opentripplanner/base-map'
Expand Down Expand Up @@ -55,8 +55,7 @@ const MapContainer = styled.div<{ hideLayerFilters: boolean }>`
box-sizing: unset;
}

.maplibregl-popup-content,
.mapboxgl-popup-content {
.maplibregl-popup-content {
border-radius: 10px;
box-shadow: 0 3px 14px 4px rgb(0 0 0 / 20%);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/components/map/elevation-point-marker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { connect } from 'react-redux'

import { ELEVATION_BLUE } from '../util/colors'
import { Leg } from '@opentripplanner/types'
import { Marker } from 'react-map-gl'
import { Marker } from 'react-map-gl/maplibre'
import coreUtils from '@opentripplanner/core-utils'
import React from 'react'
import styled from 'styled-components'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/map/itinerary-summary-overlay.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { connect } from 'react-redux'
import { Feature, lineString, LineString, Position } from '@turf/helpers'
import { Itinerary, Location } from '@opentripplanner/types'
import { Marker } from 'react-map-gl'
import { Marker } from 'react-map-gl/maplibre'
import centroid from '@turf/centroid'
import distance from '@turf/distance'
import polyline from '@mapbox/polyline'
Expand Down
6 changes: 2 additions & 4 deletions lib/components/map/map.css
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,11 @@
HACK: maplibre has a tendency to place the marker for the active stop in the stop viewer
at the bottom of the map (i.e. first). This change puts the active stop on top so it is easier to see.
*/
.maplibregl-marker:first-of-type,
.mapboxgl-marker:first-of-type {
.maplibregl-marker:first-of-type {
z-index: 100;
}

/* Make sure popups stay on top of markers above. */
.maplibregl-popup,
.mapboxgl-popup {
.maplibregl-popup {
z-index: 101;
}
2 changes: 1 addition & 1 deletion lib/components/map/nearby-view-dot-overlay.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { connect } from 'react-redux'
import { Location } from '@opentripplanner/types'
import { Marker } from 'react-map-gl'
import { Marker } from 'react-map-gl/maplibre'
import React from 'react'
import styled from 'styled-components'

Expand Down
2 changes: 1 addition & 1 deletion lib/components/map/point-popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FocusTrapWrapper } from '@opentripplanner/building-blocks'
import { Popup } from '@opentripplanner/base-map'
import { Search } from '@styled-icons/fa-solid/Search'
import { useIntl, WrappedComponentProps } from 'react-intl'
import { useMap } from 'react-map-gl'
import { useMap } from 'react-map-gl/maplibre'
import FromToLocationPicker from '@opentripplanner/from-to-location-picker'
import React, { useCallback } from 'react'
import styled from 'styled-components'
Expand Down
23 changes: 10 additions & 13 deletions lib/components/map/route-preview-overlay.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { connect } from 'react-redux'
import { getFitBoundsPadding } from '@opentripplanner/base-map/lib/util'
import { Itinerary, Location } from '@opentripplanner/types'
import { Layer, Source, useMap } from 'react-map-gl'
import { Layer, Source, useMap } from 'react-map-gl/maplibre'
import { util } from '@opentripplanner/base-map'
import polyline from '@mapbox/polyline'
import React, { useEffect } from 'react'

import { AppReduxState } from '../../util/state-types'
import { DARK_TEXT_GREY } from '../util/colors'
import {
getActiveItinerary,
Expand All @@ -15,7 +16,7 @@ import {
type Props = {
from: Location
geometries: string[]
mainPanelContent: number | null
hasNoMainPanelContent?: boolean
to: Location
visible?: boolean
}
Expand All @@ -26,20 +27,17 @@ type Props = {
const RoutePreviewOverlay = ({
from,
geometries,
mainPanelContent,
hasNoMainPanelContent,
to,
visible
}: Props) => {
// Center the map over the endpoints when this overlay is shown.
const { current: map } = useMap()
useEffect(() => {
if (visible && mainPanelContent === null) {
map?.fitBounds([from, to], {
duration: 600,
padding: getFitBoundsPadding(map, 0.2)
})
if (visible && hasNoMainPanelContent && map) {
util.fitMapToPoints(map, from, to, 0.2, 600)
}
}, [map, visible, from, to, mainPanelContent])
}, [map, visible, hasNoMainPanelContent, from, to])

if (!geometries || !visible) return <></>

Expand Down Expand Up @@ -82,8 +80,7 @@ const RoutePreviewOverlay = ({
}
}

// TODO: Typescript state
const mapStateToProps = (state: any) => {
const mapStateToProps = (state: AppReduxState) => {
const { activeSearchId, config, ui } = state.otp
// Only show this overlay if the metro UI is explicitly enabled
if (config.itinerary?.showFirstResultByDefault !== false) {
Expand All @@ -110,7 +107,7 @@ const mapStateToProps = (state: any) => {
return {
from,
geometries,
mainPanelContent: ui.mainPanelContent,
hasNoMainPanelContent: ui.mainPanelContent === null,
to,
visible:
// We need an explicit check for undefined and null because 0
Expand Down
2 changes: 1 addition & 1 deletion lib/components/map/simple-map.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { connect } from 'react-redux'
import { GeolocateControl, NavigationControl } from 'react-map-gl'
import { GeolocateControl, NavigationControl } from 'react-map-gl/maplibre'
import { Itinerary } from '@opentripplanner/types'
import { useIntl } from 'react-intl'
import BaseMap from '@opentripplanner/base-map'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/map/with-map.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MapRef, useMap } from 'react-map-gl'
import { MapRef, useMap } from 'react-map-gl/maplibre'
import React, { ComponentType, FC } from 'react'

/**
Expand Down
2 changes: 1 addition & 1 deletion lib/components/mobile/batch-results-screen.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Button } from 'react-bootstrap'
import { connect } from 'react-redux'
import { FormattedMessage } from 'react-intl'
import { ListUl } from '@styled-icons/fa-solid/ListUl'
import { useMap } from 'react-map-gl'
import { useMap } from 'react-map-gl/maplibre'
import coreUtils from '@opentripplanner/core-utils'
import React, { useEffect } from 'react'
import styled, { css } from 'styled-components'
Expand Down
2 changes: 1 addition & 1 deletion lib/components/util/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
Route,
Stop
} from '@opentripplanner/types'
import { MapRef } from 'react-map-gl'
import { MapRef } from 'react-map-gl/maplibre'

export interface StopTimeTrip {
blockId?: string
Expand Down
25 changes: 18 additions & 7 deletions lib/components/viewers/nearby/nearby-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { connect } from 'react-redux'
import { FormattedMessage, useIntl } from 'react-intl'
import { Location } from '@opentripplanner/types'
import { LonLatInput } from '@conveyal/lonlat'
import { MapRef, useMap } from 'react-map-gl'
import { MapRef, useMap, ViewStateChangeEvent } from 'react-map-gl/maplibre'
import { Search } from '@styled-icons/fa-solid/Search'
import { throttle } from '@tanstack/pacer'
import coreUtils from '@opentripplanner/core-utils'
import getGeocoder from '@opentripplanner/geocoder'
import LocationField from '@opentripplanner/location-field'
Expand Down Expand Up @@ -210,7 +211,8 @@ function NearbyView({
}, [location, setHighlightedLocation])

useEffect(() => {
const moveListener = (e: mapboxgl.EventData) => {
const moveListener = (e: ViewStateChangeEvent) => {
// @ts-expect-error TODO: What is this condition supposed to capture?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain how the ts error could be solved by answering this question?

if (e.geolocateSource) {
const coords = {
lat: e.viewState.latitude,
Expand All @@ -221,7 +223,7 @@ function NearbyView({
}
}

const dragListener = (e: mapboxgl.EventData) => {
const dragListener = (e: ViewStateChangeEvent) => {
const coords = {
lat: e.viewState.latitude,
lon: e.viewState.longitude
Expand Down Expand Up @@ -279,13 +281,22 @@ function NearbyView({
const onMouseEnter = useCallback(
(location: Location) => {
setHighlightedLocation(location)
map && zoomToPlace(map, location)
map && location && zoomToPlace(map, location)
},
[setHighlightedLocation, map, zoomToPlace]
)
const onMouseLeave = useCallback(() => {
setHighlightedLocation(null)
}, [setHighlightedLocation])

// onMouseLeave is throttled because of a bug on Firefox where
// two mouseleave events are triggered when moving cursor quickly from one card to the next.
const onMouseLeave = useCallback(
throttle(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any concerns with increasing bundle size for this? It looks like a very small package, just curious if you looked at any other solutions

(e) => {
setHighlightedLocation(null)
},
{ trailing: false, wait: 1000 }
),
[setHighlightedLocation]
)

// Determine whether the data we have is stale based on whether the coords match the URL
// Sometimes Redux could have data from a previous load of the nearby view
Expand Down
4 changes: 2 additions & 2 deletions lib/components/viewers/stop-schedule-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ExclamationCircle } from '@styled-icons/fa-solid/ExclamationCircle'
import { format, parse } from 'date-fns'
import { FormattedMessage, injectIntl, IntlShape } from 'react-intl'
import { Location } from '@styled-icons/fa-solid/Location'
import { MapRef } from 'react-map-gl'
import { MapRef } from 'react-map-gl/maplibre'
import { utcToZonedTime } from 'date-fns-tz'
import coreUtils from '@opentripplanner/core-utils'
import React, { Component, FormEvent } from 'react'
Expand All @@ -23,7 +23,7 @@ import PageTitle from '../util/page-title'
import ServiceTimeRangeRetriever from '../util/service-time-range-retriever'
import withMap from '../map/with-map'

import { CardBody, CardHeader } from './nearby/styled'
import { CardHeader } from './nearby/styled'
import FavoriteStopToggle from './favorite-stop-toggle'
import FromToPicker from './nearby/from-to-picker'
import StopCardHeader from './nearby/stop-card-header'
Expand Down
2 changes: 1 addition & 1 deletion lib/util/config-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
TransitOperator,
VehicleRentalMapOverlaySymbol
} from '@opentripplanner/types'
import { ControlPosition } from 'react-map-gl'
import { ControlPosition } from 'react-map-gl/maplibre'
import { GeocoderConfig as GeocoderConfigOtpUI } from '@opentripplanner/geocoder'

import { NearbyFilterKey } from './state-types'
Expand Down
34 changes: 18 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,32 +39,33 @@
"@bugsnag/js": "^7.17.0",
"@bugsnag/plugin-react": "^7.17.0",
"@floating-ui/react": "^0.19.2",
"@opentripplanner/base-map": "5.2.0",
"@opentripplanner/base-map": "6.0.0",
"@opentripplanner/building-blocks": "3.0.1",
"@opentripplanner/core-utils": "13.0.1",
"@opentripplanner/endpoints-overlay": "4.0.1",
"@opentripplanner/from-to-location-picker": "4.0.1",
"@opentripplanner/endpoints-overlay": "5.0.0",
"@opentripplanner/from-to-location-picker": "4.0.0",
"@opentripplanner/geocoder": "^3.0.5",
"@opentripplanner/humanize-distance": "^2.0.0",
"@opentripplanner/icons": "4.0.0",
"@opentripplanner/itinerary-body": "7.0.4",
"@opentripplanner/location-field": "4.0.4",
"@opentripplanner/location-icon": "^2.0.0",
"@opentripplanner/map-popup": "6.0.4",
"@opentripplanner/otp2-tile-overlay": "3.1.0",
"@opentripplanner/park-and-ride-overlay": "4.0.1",
"@opentripplanner/map-popup": "7.0.0",
"@opentripplanner/otp2-tile-overlay": "4.0.0",
"@opentripplanner/park-and-ride-overlay": "5.0.0",
"@opentripplanner/printable-itinerary": "4.0.1",
"@opentripplanner/route-viewer-overlay": "4.0.1",
"@opentripplanner/stop-viewer-overlay": "4.0.1",
"@opentripplanner/stops-overlay": "7.0.2",
"@opentripplanner/transit-vehicle-overlay": "6.0.2",
"@opentripplanner/transitive-overlay": "6.0.0",
"@opentripplanner/route-viewer-overlay": "5.0.0",
"@opentripplanner/stop-viewer-overlay": "5.0.0",
"@opentripplanner/stops-overlay": "8.0.0",
"@opentripplanner/transit-vehicle-overlay": "7.0.0",
"@opentripplanner/transitive-overlay": "7.0.0",
"@opentripplanner/trip-details": "^8.0.1",
"@opentripplanner/trip-form": "6.0.1",
"@opentripplanner/trip-viewer-overlay": "4.0.1",
"@opentripplanner/vehicle-rental-overlay": "4.0.2",
"@opentripplanner/trip-viewer-overlay": "5.0.0",
"@opentripplanner/vehicle-rental-overlay": "5.0.0",
"@styled-icons/fa-regular": "^10.34.0",
"@styled-icons/fa-solid": "^10.34.0",
"@tanstack/react-pacer": "^0.8.0",
"@turf/centroid": "^6.5.0",
"@turf/helpers": "^6.5.0",
"@types/react-transition-group": "^4.4.10",
Expand All @@ -91,7 +92,7 @@
"lodash.isempty": "^4.4.0",
"lodash.isequal": "^4.5.0",
"lodash.memoize": "^4.1.2",
"maplibre-gl": "^2.1.9",
"maplibre-gl": "^5.6.1",
"object-hash": "^3.0.0",
"object-path": "^0.11.8",
"object-to-formdata": "^4.1.0",
Expand All @@ -107,7 +108,7 @@
"react-hot-toast": "^2.4.1",
"react-intl": "^5.20.10",
"react-loading-skeleton": "^2.1.1",
"react-map-gl": "^7.0.15",
"react-map-gl": "^8.0.4",
"react-phone-number-input": "^3.1.0",
"react-redux": "^7.1.0",
"react-resize-detector": "^2.1.0",
Expand Down Expand Up @@ -136,7 +137,7 @@
"@graphql-tools/schema": "^10.0.0",
"@modyfi/vite-plugin-yaml": "^1.1.1",
"@opentripplanner/scripts": "^1.2.0",
"@opentripplanner/types": "^6.5.3",
"@opentripplanner/types": "7.0.0",
"@percy/cli": "^1.20.3",
"@percy/puppeteer": "^2.0.2",
"@types/clone": "^2.1.1",
Expand Down Expand Up @@ -205,6 +206,7 @@
},
"jest": {
"moduleNameMapper": {
"react-map-gl/maplibre": "@vis.gl/react-maplibre",
"i18n/(.*)\\.yml$": "<rootDir>__tests__/test-utils/mock-data/empty-yml.js",
"modeSettings.yml$": "<rootDir>__tests__/test-utils/mock-data/empty-yml.js",
"i18n-loader": "<rootDir>__tests__/test-utils/mock-data/i18n-loader.js",
Expand Down
Loading
Loading