Skip to content

Commit 81778e9

Browse files
Merge branch 'dev' into a11y-search-form
2 parents 8425aed + 43407f5 commit 81778e9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+728
-409
lines changed

Diff for: __tests__/components/viewers/__snapshots__/stop-viewer.js.snap

+8-8
Original file line numberDiff line numberDiff line change
@@ -1364,7 +1364,7 @@ exports[`components > viewers > stop viewer should render countdown times after
13641364
isOnColoredBackground={true}
13651365
>
13661366
<span
1367-
className="sc-ljRaeN ipRFHf"
1367+
className="sc-ljRaeN dFtn"
13681368
color="333333"
13691369
>
13701370
20
@@ -3678,7 +3678,7 @@ exports[`components > viewers > stop viewer should render countdown times for st
36783678
isOnColoredBackground={true}
36793679
>
36803680
<span
3681-
className="sc-ljRaeN ipRFHf"
3681+
className="sc-ljRaeN dFtn"
36823682
color="333333"
36833683
>
36843684
20
@@ -5682,7 +5682,7 @@ exports[`components > viewers > stop viewer should render times after midnight w
56825682
isOnColoredBackground={true}
56835683
>
56845684
<span
5685-
className="sc-ljRaeN ipRFHf"
5685+
className="sc-ljRaeN dFtn"
56865686
color="333333"
56875687
>
56885688
20
@@ -8920,7 +8920,7 @@ exports[`components > viewers > stop viewer should render with OTP transit index
89208920
isOnColoredBackground={true}
89218921
>
89228922
<span
8923-
className="sc-ljRaeN ipRFHf"
8923+
className="sc-ljRaeN dFtn"
89248924
color="333333"
89258925
>
89268926
20
@@ -9638,7 +9638,7 @@ exports[`components > viewers > stop viewer should render with OTP transit index
96389638
isOnColoredBackground={true}
96399639
>
96409640
<span
9641-
className="sc-ljRaeN ipRFHf"
9641+
className="sc-ljRaeN dFtn"
96429642
color="333333"
96439643
>
96449644
36
@@ -10356,7 +10356,7 @@ exports[`components > viewers > stop viewer should render with OTP transit index
1035610356
isOnColoredBackground={true}
1035710357
>
1035810358
<span
10359-
className="sc-ljRaeN ipRFHf"
10359+
className="sc-ljRaeN dFtn"
1036010360
color="333333"
1036110361
>
1036210362
94
@@ -11182,7 +11182,7 @@ exports[`components > viewers > stop viewer should render with OTP transit index
1118211182
isOnColoredBackground={true}
1118311183
>
1118411184
<span
11185-
className="sc-ljRaeN ipRFHf"
11185+
className="sc-ljRaeN dFtn"
1118611186
color="333333"
1118711187
>
1118811188
94
@@ -15446,7 +15446,7 @@ exports[`components > viewers > stop viewer should render with TriMet transit in
1544615446
isOnColoredBackground={true}
1544715447
>
1544815448
<span
15449-
className="sc-ljRaeN ipRFHf"
15449+
className="sc-ljRaeN dFtn"
1545015450
color="333333"
1545115451
>
1545215452
20

Diff for: __tests__/util/i18n.js

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/* globals describe, expect, it */
2+
3+
import { getLanguageOptions } from '../../lib/util/i18n'
4+
5+
describe('util > i18n', () => {
6+
describe('getLanguageOptions', () => {
7+
const testCases = [
8+
{
9+
expected: {
10+
'en-US': {
11+
name: 'English (US)'
12+
},
13+
fr: {
14+
name: 'French'
15+
}
16+
},
17+
input: {
18+
allLanguages: {
19+
name: 'All Languages'
20+
},
21+
'en-US': {
22+
name: 'English (US)'
23+
},
24+
fr: {
25+
name: 'French'
26+
}
27+
}
28+
},
29+
{
30+
expected: null,
31+
input: {
32+
allLanguages: {
33+
name: 'All Languages'
34+
},
35+
'en-US': {
36+
name: 'English (US)'
37+
},
38+
fr: {
39+
label: 'French'
40+
}
41+
}
42+
},
43+
{
44+
expected: null,
45+
input: {
46+
allLanguages: {
47+
name: 'All Languages'
48+
},
49+
'en-US': {
50+
name: 'English (US)'
51+
}
52+
}
53+
}
54+
]
55+
56+
testCases.forEach((testCase) => {
57+
it('should show at least two language options or null', () => {
58+
expect(getLanguageOptions(testCase.input)).toEqual(testCase.expected)
59+
})
60+
})
61+
})
62+
})

Diff for: i18n/en-US.yml

+4
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ components:
126126
other {# spaces available}
127127
}
128128
AppMenu:
129+
appMenu: App Menu
129130
callHistory: Call History
130131
closeMenu: Close Menu
131132
fieldTrip: Field Trip
@@ -206,6 +207,7 @@ components:
206207
FormNavigationButtons:
207208
ariaLabel: Form navigation
208209
ItinerarySummary:
210+
itineraryDetails: Itinerary details
209211
minMaxFare: "{minTotalFare} - {maxTotalFare}"
210212
LiveStopTimes:
211213
autoRefresh: Auto-refresh arrivals?
@@ -425,6 +427,8 @@ components:
425427
zoomToStop: Zoom to stop
426428

427429
SubNav:
430+
languageSelector: Select language
431+
activeLanguage: Active language
428432
languages: "Languages"
429433
myAccount: My account
430434
selectALanguage: Select a language

Diff for: i18n/fr.yml

+1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ components:
131131
other {# emplacements disponibles}
132132
}
133133
AppMenu:
134+
appMenu: App Menu
134135
callHistory: Historique des appels
135136
closeMenu: Fermer le menu
136137
fieldTrip: Groupes scolaires

Diff for: index.css

+6-1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ input[type="text"]::-ms-clear {
121121
outline-offset: -2px;
122122
}
123123

124+
.app-menu button[aria-selected="true"],
125+
#locale-selector li[aria-selected="true"] {
126+
font-weight: bold;
127+
}
128+
124129
.skip-nav-button {
125130
color: initial;
126131
position: fixed;
@@ -129,7 +134,7 @@ input[type="text"]::-ms-clear {
129134
.skip-nav-button:focus {
130135
padding: 7px 24px;
131136
top: 7px;
132-
z-index: 100
137+
z-index: 100;
133138
}
134139

135140
.view-switcher {

Diff for: lib/actions/api.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ import { FETCH_STATUS } from '../util/constants'
1212
import { getSecureFetchOptions } from '../util/middleware'
1313
import { getStopViewerConfig, queryIsValid } from '../util/state'
1414

15-
import { ItineraryView, setItineraryView, setViewedStop } from './ui'
15+
import {
16+
ItineraryView,
17+
MainPanelContent,
18+
setItineraryView,
19+
setViewedStop
20+
} from './ui'
1621
import { rememberPlace } from './user'
1722
import v1Actions from './apiV1'
1823
import v2Actions from './apiV2'
@@ -709,7 +714,9 @@ export const findRoutesError = createAction('FIND_ROUTES_ERROR')
709714
export function findRoutesIfNeeded(params) {
710715
return function (dispatch, getState) {
711716
if (
712-
getState().otp.transitIndex.routesFetchStatus === FETCH_STATUS.UNFETCHED
717+
getState().otp.transitIndex.routesFetchStatus ===
718+
FETCH_STATUS.UNFETCHED &&
719+
getState().otp.ui.mainPanelContent !== MainPanelContent.PATTERN_VIEWER
713720
) {
714721
dispatch(findingRoutes())
715722
dispatch(executeOTPAction('findRoutes', params))

Diff for: lib/actions/ui.js

+47-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { getPathFromParts } from '../util/ui'
1414

1515
import { clearActiveSearch, parseUrlQueryString, setActiveSearch } from './form'
1616
import { clearLocation, setMapCenter } from './map'
17+
import { createOrUpdateUser } from './user'
1718
import { findRouteIfNeeded, findRoutesIfNeeded, setUrlSearch } from './api'
1819
import { setActiveItinerary } from './narrative'
1920
import { setRouterId } from './config'
@@ -23,8 +24,10 @@ const updateLocale = createAction('UPDATE_LOCALE')
2324
// UI state enums
2425

2526
export const MainPanelContent = {
27+
PATTERN_VIEWER: 12,
2628
ROUTE_VIEWER: 1,
27-
STOP_VIEWER: 2
29+
STOP_VIEWER: 2,
30+
TRIP_VIEWER: 9
2831
}
2932

3033
export const MobileScreens = {
@@ -60,7 +63,7 @@ export const setMobileScreen = createAction('SET_MOBILE_SCREEN')
6063
export const clearPanel = createAction('CLEAR_MAIN_PANEL')
6164
const viewStop = createAction('SET_VIEWED_STOP')
6265
export const setHoveredStop = createAction('SET_HOVERED_STOP')
63-
export const setViewedTrip = createAction('SET_VIEWED_TRIP')
66+
const viewTrip = createAction('SET_VIEWED_TRIP')
6467
const viewRoute = createAction('SET_VIEWED_ROUTE')
6568
export const toggleAutoRefresh = createAction('TOGGLE_AUTO_REFRESH')
6669
const setPreviousItineraryView = createAction('SET_PREVIOUS_ITINERARY_VIEW')
@@ -120,6 +123,20 @@ export function setViewedRoute(payload) {
120123
}
121124
}
122125

126+
export function setViewedTrip(payload) {
127+
return function (dispatch, getState) {
128+
const { otp } = getState()
129+
const { viewedTrip } = otp.ui
130+
131+
dispatch(viewTrip({ ...viewedTrip, ...payload }))
132+
133+
if (payload?.tripId) {
134+
const path = getPathFromParts('trip', payload.tripId)
135+
dispatch(routeTo(path))
136+
}
137+
}
138+
}
139+
123140
/**
124141
* Sets the main panel content according to the payload (one of the enum values
125142
* of MainPanelContent) and routes the application to the correct path.
@@ -144,11 +161,14 @@ export function setMainPanelContent(payload) {
144161
case MainPanelContent.STOP_VIEWER:
145162
dispatch(routeTo('/stop'))
146163
break
164+
case MainPanelContent.TRIP_VIEWER:
165+
case MainPanelContent.PATTERN_VIEWER:
166+
break
147167
default:
148168
// Clear route, stop, and trip viewer focus and route to root
149169
dispatch(viewRoute(null))
150170
dispatch(viewStop(null))
151-
dispatch(setViewedTrip(null))
171+
dispatch(viewTrip(null))
152172
if (router.location.pathname !== '/') dispatch(routeTo('/'))
153173
break
154174
}
@@ -205,10 +225,17 @@ export function matchContentToUrl(map, location) {
205225
const patternId = subMatch?.params?.patternId
206226
// patternId may be undefined, which is OK as the route will still be routed
207227
dispatch(setViewedRoute({ patternId, routeId: id }))
228+
dispatch(
229+
setMainPanelContent(
230+
patternId
231+
? MainPanelContent.PATTERN_VIEWER
232+
: MainPanelContent.ROUTE_VIEWER
233+
)
234+
)
208235
} else {
209236
dispatch(setViewedRoute(null))
237+
dispatch(setMainPanelContent(MainPanelContent.ROUTE_VIEWER))
210238
}
211-
dispatch(setMainPanelContent(MainPanelContent.ROUTE_VIEWER))
212239
break
213240
case 'stop':
214241
if (id) {
@@ -218,6 +245,15 @@ export function matchContentToUrl(map, location) {
218245
dispatch(setMainPanelContent(MainPanelContent.STOP_VIEWER))
219246
}
220247
break
248+
case 'trip':
249+
if (id) {
250+
dispatch(setViewedTrip({ tripId: id }))
251+
dispatch(setMainPanelContent(MainPanelContent.TRIP_VIEWER))
252+
} else {
253+
dispatch(setViewedTrip(null))
254+
dispatch(setMainPanelContent(null))
255+
}
256+
break
221257
case 'start':
222258
case '@': {
223259
// Parse comma separated params (ensuring numbers are parsed correctly).
@@ -379,6 +415,13 @@ export function setLocale(locale) {
379415
// (The lang is the first portion of the locale.)
380416
const lang = effectiveLocale.split('-')[0]
381417
document.documentElement.lang = lang
418+
419+
window.localStorage.setItem('lang', matchedLocale)
420+
421+
if (loggedInUser) {
422+
loggedInUser.preferredLanguage = matchedLocale
423+
dispatch(createOrUpdateUser(loggedInUser, false))
424+
}
382425
}
383426
}
384427

Diff for: lib/actions/user.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ export function createOrUpdateUser(userData, silentOnSuccess = false, intl) {
323323

324324
// TODO: improve the UI feedback messages for this.
325325
if (status === 'success' && returnedUser) {
326-
if (!silentOnSuccess) {
326+
if (!silentOnSuccess && intl) {
327327
alert(intl.formatMessage({ id: 'actions.user.preferencesSaved' }))
328328
}
329329

@@ -332,12 +332,14 @@ export function createOrUpdateUser(userData, silentOnSuccess = false, intl) {
332332
// (This sorts saved places, and, for existing users, fetches trips.)
333333
dispatch(setUser(returnedUser, isCreatingUser))
334334
} else {
335-
alert(
336-
intl.formatMessage(
337-
{ id: 'actions.user.genericError' },
338-
{ err: JSON.stringify(message) }
335+
if (intl) {
336+
alert(
337+
intl.formatMessage(
338+
{ id: 'actions.user.genericError' },
339+
{ err: JSON.stringify(message) }
340+
)
339341
)
340-
)
342+
}
341343
}
342344
}
343345
}

Diff for: lib/components/app/app-menu-item.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ import React, { Component, HTMLAttributes, KeyboardEvent } from 'react'
66
interface Props extends HTMLAttributes<HTMLElement> {
77
href?: string
88
icon?: JSX.Element
9-
isDropdown?: boolean
109
onClick?: () => void
11-
subItems?: unknown[]
10+
subItems?: JSX.Element[]
1211
text: JSX.Element | string
1312
}
1413

@@ -76,11 +75,12 @@ export default class AppMenuItem extends Component<Props, State> {
7675
<Element
7776
aria-controls={subItems && containerId}
7877
aria-expanded={subItems && isExpanded}
78+
id={id}
7979
onClick={subItems ? this._toggleSubmenu : onClick}
8080
onKeyDown={this._handleKeyDown}
8181
{...otherProps}
8282
>
83-
<span>{icon}</span>
83+
<span aria-hidden>{icon}</span>
8484
<span>{text}</span>
8585
{subItems && (
8686
<span className="expand-menu-chevron">
@@ -90,7 +90,7 @@ export default class AppMenuItem extends Component<Props, State> {
9090
</Element>
9191
{subItems && (
9292
<AnimateHeight duration={500} height={isExpanded ? 'auto' : 0}>
93-
<div className="sub-menu-container" id={containerId}>
93+
<div className="sub-menu-container" id={containerId} role="group">
9494
{subItems}
9595
</div>
9696
</AnimateHeight>

0 commit comments

Comments
 (0)