diff --git a/lib/components/form/call-taker/advanced-options.js b/lib/components/form/call-taker/advanced-options.js
index 450c8c67f..dcf81ee32 100644
--- a/lib/components/form/call-taker/advanced-options.js
+++ b/lib/components/form/call-taker/advanced-options.js
@@ -2,9 +2,13 @@
// FIXME: Remove the following eslint rule exception.
/* eslint-disable jsx-a11y/label-has-for */
import * as TripFormClasses from '@opentripplanner/trip-form/lib/styled'
+import {
+ DropdownSelector,
+ SliderSelector,
+ SubmodeSelector
+} from '@opentripplanner/trip-form'
import { FormattedMessage, injectIntl } from 'react-intl'
import { hasBike } from '@opentripplanner/core-utils/lib/itinerary'
-import { SliderSelector, SubmodeSelector } from '@opentripplanner/trip-form'
import isEmpty from 'lodash.isempty'
import React, { Component, lazy, Suspense } from 'react'
import styled from 'styled-components'
@@ -155,9 +159,9 @@ class AdvancedOptions extends Component {
})
}
- _setWaklTolerance = ({ walkTolerance }) => {
+ _setWalkTolerance = ({ walkReluctance }) => {
this.props.setUrlSearch({
- walkTolerance
+ walkReluctance
})
}
@@ -224,17 +228,19 @@ class AdvancedOptions extends Component {
justifyContent: 'space-between'
}}
>
-
{hasBike(currentModes?.map((m) => m.mode).join(',') || '') ? (
diff --git a/lib/components/narrative/metro/attribute-utils.tsx b/lib/components/narrative/metro/attribute-utils.tsx
index 36a12b90b..c48afd0c8 100644
--- a/lib/components/narrative/metro/attribute-utils.tsx
+++ b/lib/components/narrative/metro/attribute-utils.tsx
@@ -9,7 +9,7 @@ export const getFirstTransitLegStop = (
itinerary.legs?.find((leg: Leg) => leg?.from?.vertexType === 'TRANSIT')?.from
?.name
-export const getFlexAttirbutes = (
+export const getFlexAttributes = (
itinerary: Itinerary
): {
isCallAhead: boolean
diff --git a/lib/components/narrative/metro/metro-itinerary.tsx b/lib/components/narrative/metro/metro-itinerary.tsx
index e86a593e3..cbd0f7194 100644
--- a/lib/components/narrative/metro/metro-itinerary.tsx
+++ b/lib/components/narrative/metro/metro-itinerary.tsx
@@ -28,7 +28,7 @@ import ItineraryBody from '../line-itin/connected-itinerary-body'
import NarrativeItinerary from '../narrative-itinerary'
import SimpleRealtimeAnnotation from '../simple-realtime-annotation'
-import { getFirstTransitLegStop, getFlexAttirbutes } from './attribute-utils'
+import { getFlexAttributes } from './attribute-utils'
import DepartureTimesList, {
SetActiveItineraryHandler
} from './departure-times-list'
@@ -202,23 +202,17 @@ class MetroItinerary extends NarrativeItinerary {
static ModesAndRoutes = MetroItineraryRoutes
_onMouseEnter = () => {
- const { active, index, setVisibleItinerary, visibleItinerary } = this.props
+ const { active, index, setVisibleItinerary, visible } = this.props
// Set this itinerary as visible if not already visible.
- const visibleNotSet =
- visibleItinerary === null || visibleItinerary === undefined
- const isVisible =
- visibleItinerary === index || (active === index && visibleNotSet)
+ const isVisible = visible || active
if (typeof setVisibleItinerary === 'function' && !isVisible) {
setVisibleItinerary({ index })
}
}
_onMouseLeave = () => {
- const { index, setVisibleItinerary, visibleItinerary } = this.props
- if (
- typeof setVisibleItinerary === 'function' &&
- visibleItinerary === index
- ) {
+ const { setVisibleItinerary, visible } = this.props
+ if (typeof setVisibleItinerary === 'function' && visible) {
setVisibleItinerary({ index: null })
}
}
@@ -266,7 +260,7 @@ class MetroItinerary extends NarrativeItinerary {
const { SvgIcon } = this.context
const { isCallAhead, isContinuousDropoff, isFlexItinerary, phone } =
- getFlexAttirbutes(itinerary)
+ getFlexAttributes(itinerary)
const { fareCurrency, transitFare } = getFare(itinerary, defaultFareType)
diff --git a/lib/components/narrative/narrative-itineraries.js b/lib/components/narrative/narrative-itineraries.js
index 30200a5cf..23d1d96fe 100644
--- a/lib/components/narrative/narrative-itineraries.js
+++ b/lib/components/narrative/narrative-itineraries.js
@@ -43,6 +43,15 @@ import Loading from './loading'
import NarrativeItinerariesErrors from './narrative-itineraries-errors'
import NarrativeItinerariesHeader from './narrative-itineraries-header'
+/** Creates a start time object for the given itinerary. */
+function makeStartTime(itinerary) {
+ return {
+ itinerary,
+ legs: itinerary.legs,
+ realtime: firstTransitLegIsRealtime(itinerary)
+ }
+}
+
function doMergeItineraries(itineraries) {
const mergedItineraries = itineraries
.reduce((prev, cur, curIndex) => {
@@ -60,46 +69,51 @@ function doMergeItineraries(itineraries) {
// Only process itineraries less than 24 hours in the future
differenceInDays(updatedItinerary.startTime, Date.now()) < 1
) {
- const duplicateItin = updatedItineraries[duplicateIndex]
+ const duplicateFoundItin = updatedItineraries[duplicateIndex]
// TODO: MERGE ROUTE NAMES
- // Add only new start time to existing itinerary
+ // Add only new start time to existing itinerary.
+ // The existing itinerary is the earliest between
+ // this itinerary (updatedItinerary) and duplicateItin.
+ // This is because alternate routes are only added to the first non-duplicate itinerary,
+ // and we show alternate routes for the first (i.e. earliest) non-duplicate itinerary found.
+ let duplicateItin = duplicateFoundItin
+ let itinCopyToAdd = updatedItinerary
+ if (duplicateFoundItin.startTime > updatedItinerary.startTime) {
+ duplicateItin = updatedItinerary
+ duplicateItin.startTimes = duplicateFoundItin.allStartTimes
+ updatedItineraries[duplicateIndex] = updatedItinerary
+ itinCopyToAdd = duplicateFoundItin
+ }
+
if (!duplicateItin.allStartTimes) {
- duplicateItin.allStartTimes = [
- {
- itinerary: duplicateItin,
- legs: duplicateItin.legs,
- realtime: firstTransitLegIsRealtime(duplicateItin)
- }
- ]
+ duplicateItin.allStartTimes = [makeStartTime(duplicateItin)]
}
// Only add new time if it doesn't already exist. It would be better to use
// the uniqueness feature of Set, but unfortunately objects are never equal
if (
!duplicateItin.allStartTimes.find(
- (time) => getFirstLegStartTime(time.legs) === cur.startTime
+ (time) =>
+ getFirstLegStartTime(time.legs) === itinCopyToAdd.startTime
)
) {
- duplicateItin.allStartTimes.push({
- itinerary: updatedItinerary,
- legs: cur.legs,
- realtime: firstTransitLegIsRealtime(cur)
- })
+ duplicateItin.allStartTimes.push(makeStartTime(itinCopyToAdd))
}
// Some legs will be the same, but have a different route
// This map catches those and stores the alternate routes so they can be displayed
duplicateItin.legs = duplicateItin.legs.map((leg, index) => {
const newLeg = clone(leg)
- if (leg?.routeId !== cur.legs[index]?.routeId) {
+ const curLeg = itinCopyToAdd.legs[index]
+ const curLegRouteId = curLeg?.routeId
+ if (curLegRouteId && leg?.routeId && leg?.routeId !== curLegRouteId) {
if (!newLeg.alternateRoutes) {
newLeg.alternateRoutes = {}
}
- const { routeId } = cur.legs?.[index]
- newLeg.alternateRoutes[routeId] = {
+ newLeg.alternateRoutes[curLegRouteId] = {
// We save the entire leg to the alternateRoutes object so in
// the future, we can draw the leg on the map as an alternate route
- ...cur.legs?.[index]
+ ...curLeg
}
}
return newLeg
diff --git a/lib/components/user/sequential-pane-display.tsx b/lib/components/user/sequential-pane-display.tsx
index 1939d67af..e41a953b7 100644
--- a/lib/components/user/sequential-pane-display.tsx
+++ b/lib/components/user/sequential-pane-display.tsx
@@ -50,6 +50,12 @@ class SequentialPaneDisplay extends Component> {
routeTo(`${parentPath}/${nextId}`)
}
+ h1Ref = React.createRef()
+
+ _focusHeader = () => {
+ this.h1Ref?.current?.focus()
+ }
+
_handleToNextPane = async (e: MouseEvent