diff --git a/example-config.yml b/example-config.yml
index 3dd4183d1..0312aa3ac 100644
--- a/example-config.yml
+++ b/example-config.yml
@@ -112,6 +112,11 @@ persistence:
# iconUrl: ''
# href: ''
+### These settings are only used for the field trip features.
+dateTime:
+ timeFormat: h:mm a
+ dateFormat: MM/dd/yyyy
+
map:
initLat: 45.52
initLon: -122.682
@@ -411,6 +416,8 @@ itinerary:
displayA11yError: false
# Whether to display itinerary info in the side of the preview or next to the departure times
showInlineItinerarySummary: false
+ # Whether to sync the sort type with the depart/arrive time in the date/time modal
+ syncSortWithDepartArrive: true
# The sort option to use by default
# Available sort options: 'BEST', 'DURATION', 'ARRIVALTIME', 'WALKTIME', 'COST', 'DEPARTURETIME'
# defaultSort: "BEST" # Default
diff --git a/lib/components/form/date-time-modal.js b/lib/components/form/date-time-modal.js
deleted file mode 100644
index 0936ebbe9..000000000
--- a/lib/components/form/date-time-modal.js
+++ /dev/null
@@ -1,76 +0,0 @@
-// TODO: TypeScript with props.
-/* eslint-disable react/prop-types */
-import { connect } from 'react-redux'
-import coreUtils from '@opentripplanner/core-utils'
-import PropTypes from 'prop-types'
-import React, { Component } from 'react'
-
-import { setQueryParam } from '../../actions/form'
-
-import { StyledDateTimeSelector } from './styled'
-
-class DateTimeModal extends Component {
- static propTypes = {
- setQueryParam: PropTypes.func
- }
-
- render() {
- const {
- config,
- date,
- dateFormatLegacy,
- departArrive,
- setQueryParam,
- time,
- timeFormatLegacy
- } = this.props
- const { homeTimezone, isTouchScreenOnDesktop } = config
- const touchClassName = isTouchScreenOnDesktop
- ? 'with-desktop-touchscreen'
- : ''
-
- return (
-
-
- `.
- // These props are not relevant in modern browsers,
- // where `` already
- // formats the time|date according to the OS settings.
- // eslint-disable-next-line react/jsx-sort-props
- timeFormatLegacy={timeFormatLegacy}
- timeZone={homeTimezone}
- />
-
-
- )
- }
-}
-
-const mapStateToProps = (state) => {
- const { date, departArrive, time } = state.otp.currentQuery
- const config = state.otp.config
- return {
- config,
- date,
- // This prop is for legacy browsers (see render method above).
- dateFormatLegacy: coreUtils.time.getDateFormat(config),
- departArrive,
- time,
- // This prop is for legacy browsers (see render method above).
- timeFormatLegacy: coreUtils.time.getTimeFormat(config)
- }
-}
-
-const mapDispatchToProps = {
- setQueryParam
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(DateTimeModal)
diff --git a/lib/components/form/date-time-modal.tsx b/lib/components/form/date-time-modal.tsx
new file mode 100644
index 000000000..e9f31407b
--- /dev/null
+++ b/lib/components/form/date-time-modal.tsx
@@ -0,0 +1,116 @@
+import { connect } from 'react-redux'
+import coreUtils from '@opentripplanner/core-utils'
+import React, { useCallback } from 'react'
+
+import * as formActions from '../../actions/form'
+import * as narrativeActions from '../../actions/narrative'
+import { AppConfig } from '../../util/config-types'
+import { AppReduxState, FilterType, SortType } from '../../util/state-types'
+
+import { StyledDateTimeSelector } from './styled'
+
+type Props = {
+ config: AppConfig
+ date: string
+ dateFormatLegacy?: string
+ departArrive: DepartArriveValue
+ setQueryParam: (params: any) => void
+ sort: SortType
+ time: string
+ timeFormatLegacy?: string
+ updateItineraryFilter: (payload: FilterType) => void
+}
+
+type DepartArriveValue = 'NOW' | 'DEPART' | 'ARRIVE'
+
+const DepartArriveTypeMap: Record<
+ DepartArriveValue,
+ FilterType['sort']['type']
+> = {
+ ARRIVE: 'ARRIVALTIME',
+ DEPART: 'DEPARTURETIME',
+ NOW: 'DURATION'
+}
+
+function DateTimeModal({
+ config,
+ date,
+ dateFormatLegacy,
+ departArrive,
+ setQueryParam,
+ sort,
+ time,
+ timeFormatLegacy,
+ updateItineraryFilter
+}: Props) {
+ const { homeTimezone, isTouchScreenOnDesktop } = config
+ const touchClassName = isTouchScreenOnDesktop
+ ? 'with-desktop-touchscreen'
+ : ''
+
+ const syncSortWithDepartArrive = config?.itinerary?.syncSortWithDepartArrive
+ // Note the side effect that this will resort the results of a previous query
+ // if the user changes the depart/arrive setting before the query is run.
+ const setQueryParamMiddleware = useCallback(
+ (params: any) => {
+ if (syncSortWithDepartArrive) {
+ updateItineraryFilter({
+ sort: {
+ ...sort,
+ type: DepartArriveTypeMap[params.departArrive as DepartArriveValue]
+ }
+ })
+ }
+ setQueryParam(params)
+ },
+ [setQueryParam, updateItineraryFilter, sort, syncSortWithDepartArrive]
+ )
+ return (
+
+
+ `.
+ // These props are not relevant in modern browsers,
+ // where `` already
+ // formats the time|date according to the OS settings.
+ // eslint-disable-next-line react/jsx-sort-props
+ timeFormatLegacy={timeFormatLegacy}
+ timeZone={homeTimezone}
+ />
+