@@ -52,9 +52,26 @@ const LegIconWrapper = styled.div`
52
52
}
53
53
`
54
54
55
+ const DetailsHintButton = styled . button `
56
+ &.visible {
57
+ display: contents;
58
+ }
59
+
60
+ /* the width/height/overflow trick still renders a tiny button,
61
+ so make it invisible and out of the way, but still visible to
62
+ screen readers */
63
+
64
+ background: transparent;
65
+ border: none;
66
+ height: 0;
67
+ overflow: hidden;
68
+ position: absolute;
69
+ width: 0;
70
+ `
55
71
const DetailsHint = styled . div `
56
72
clear: both;
57
73
color: #685c5c;
74
+ cursor: pointer;
58
75
font-size: small;
59
76
text-align: center;
60
77
`
@@ -254,6 +271,21 @@ class DefaultItinerary extends NarrativeItinerary {
254
271
. filter ( ( number ) => ! ! number ) [ 0 ]
255
272
}
256
273
274
+ const itineraryAttributeOptions = {
275
+ co2Config,
276
+ configCosts,
277
+ currency,
278
+ LegIcon
279
+ }
280
+
281
+ const renderItineraryAttributes = ( attribute ) => {
282
+ return attribute . render (
283
+ itinerary ,
284
+ itineraryAttributeOptions ,
285
+ defaultFareKey
286
+ )
287
+ }
288
+
257
289
return (
258
290
< div
259
291
className = { `option default-itin${ active ? ' active' : '' } ${
@@ -263,68 +295,73 @@ class DefaultItinerary extends NarrativeItinerary {
263
295
onMouseLeave = { this . _onMouseLeave }
264
296
role = "presentation"
265
297
>
266
- < button
298
+ < ItinerarySummaryWrapper
267
299
className = "header"
268
300
// _onHeaderClick comes from super component (NarrativeItinerary).
269
301
onClick = { this . _onHeaderClick }
270
302
>
271
- { /* FIXME: semantics - replace the divs with span (we are inside a button) */ }
272
- < ItinerarySummaryWrapper >
273
- < div className = "title" >
303
+ < div className = "title" >
304
+ < h3 >
274
305
< ItineraryDescription intl = { intl } itinerary = { itinerary } />
275
- < ItinerarySummary itinerary = { itinerary } LegIcon = { LegIcon } />
276
- { itineraryHasAccessibilityScores ( itinerary ) && (
277
- < AccessibilityRating
278
- gradationMap = { localizedGradationMapWithIcons }
279
- large
280
- score = { getAccessibilityScoreForItinerary ( itinerary ) }
281
- />
282
- ) }
283
- </ div >
284
- { isFlexItinerary && (
285
- < FlexIndicator
286
- isCallAhead = { isCallAhead }
287
- isContinuousDropoff = { isCoordinationRequired }
288
- phoneNumber = { phone }
306
+ </ h3 >
307
+ < ItinerarySummary itinerary = { itinerary } LegIcon = { LegIcon } />
308
+ { itineraryHasAccessibilityScores ( itinerary ) && (
309
+ < AccessibilityRating
310
+ gradationMap = { localizedGradationMapWithIcons }
311
+ large
312
+ score = { getAccessibilityScoreForItinerary ( itinerary ) }
289
313
/>
290
314
) }
291
- < ul className = "list-unstyled itinerary-attributes" >
292
- < FieldTripGroupSize itinerary = { itinerary } />
293
- { ITINERARY_ATTRIBUTES . sort ( ( a , b ) => {
294
- const aSelected = this . _isSortingOnAttribute ( a )
295
- const bSelected = this . _isSortingOnAttribute ( b )
296
- if ( aSelected ) return - 1
297
- if ( bSelected ) return 1
298
- return a . order - b . order
299
- } ) . map ( ( attribute ) => {
315
+ </ div >
316
+ { isFlexItinerary && (
317
+ < FlexIndicator
318
+ isCallAhead = { isCallAhead }
319
+ isContinuousDropoff = { isCoordinationRequired }
320
+ phoneNumber = { phone }
321
+ />
322
+ ) }
323
+ < ul
324
+ aria-label = { intl . formatMessage ( {
325
+ id : 'components.ItinerarySummary.itineraryDetails'
326
+ } ) }
327
+ className = "list-unstyled itinerary-attributes"
328
+ >
329
+ < FieldTripGroupSize itinerary = { itinerary } />
330
+ { ITINERARY_ATTRIBUTES . sort ( ( a , b ) => {
331
+ const aSelected = this . _isSortingOnAttribute ( a )
332
+ const bSelected = this . _isSortingOnAttribute ( b )
333
+ if ( aSelected ) return - 1
334
+ if ( bSelected ) return 1
335
+ return a . order - b . order
336
+ } )
337
+ . filter ( ( x ) => renderItineraryAttributes ( x ) !== undefined )
338
+ . map ( ( attribute ) => {
300
339
const isSelected = this . _isSortingOnAttribute ( attribute )
301
- const options = {
302
- co2Config,
303
- configCosts,
304
- currency,
305
- LegIcon
306
- }
307
340
if ( isSelected ) {
308
- options . isSelected = true
309
- options . selection = this . props . sort . type
341
+ itineraryAttributeOptions . isSelected = true
342
+ itineraryAttributeOptions . selection = this . props . sort . type
310
343
}
311
344
return (
312
345
< li
313
346
className = { `${ attribute . id } ${ isSelected ? ' main' : '' } ` }
314
347
key = { attribute . id }
315
348
>
316
- { attribute . render ( itinerary , options , defaultFareKey ) }
349
+ { renderItineraryAttributes ( attribute ) }
317
350
</ li >
318
351
)
319
352
} ) }
320
- </ ul >
321
- </ ItinerarySummaryWrapper >
322
- { active && ! expanded && (
353
+ </ ul >
354
+ </ ItinerarySummaryWrapper >
355
+ { ! expanded && (
356
+ < DetailsHintButton
357
+ className = { active && ! expanded && 'visible' }
358
+ onClick = { this . _onDirectClick }
359
+ >
323
360
< DetailsHint >
324
361
< FormattedMessage id = "components.DefaultItinerary.clickDetails" />
325
362
</ DetailsHint >
326
- ) }
327
- </ button >
363
+ </ DetailsHintButton >
364
+ ) }
328
365
{ active && expanded && (
329
366
< >
330
367
{ showRealtimeAnnotation && < SimpleRealtimeAnnotation /> }
0 commit comments