Skip to content

Commit

Permalink
Merge pull request opentripplanner#737 from opentripplanner/trip-view…
Browse files Browse the repository at this point in the history
…er-semantic-markup

Update trip viewer semantic HTML
  • Loading branch information
amy-corson-ibigroup authored Jan 9, 2023
2 parents 87110ef + c39b08b commit 8c1f4d8
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 84 deletions.
1 change: 1 addition & 0 deletions i18n/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ components:
accessible: Accessible
bicyclesAllowed: Allowed
header: Trip Viewer
listOfRouteStops: List of stops on this route
routeHeader: "Route: <strong>{routeShortName}</strong> {routeLongName}"
viewStop: View
UserAccountScreen:
Expand Down
198 changes: 115 additions & 83 deletions lib/components/viewers/trip-viewer.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
/* eslint-disable react/jsx-indent */
import { ArrowLeft } from '@styled-icons/fa-solid/ArrowLeft'
import { Bicycle } from '@styled-icons/fa-solid/Bicycle'
import { Label as BsLabel, Button } from 'react-bootstrap'
import { Circle } from '@styled-icons/fa-solid/Circle'
import { connect } from 'react-redux'
import { FormattedMessage } from 'react-intl'
import { FormattedMessage, injectIntl } from 'react-intl'
import { toDate } from 'date-fns-tz'
import { Wheelchair } from '@styled-icons/fa-solid/Wheelchair'
import coreUtils from '@opentripplanner/core-utils'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import styled from 'styled-components'

import * as apiActions from '../../actions/api'
import * as mapActions from '../../actions/map'
Expand All @@ -22,11 +24,26 @@ import ViewStopButton from './view-stop-button'

const { getCurrentDate } = coreUtils.time

const StopList = styled.ol`
padding-left: 0;
`
const Stop = styled.li`
list-style: none;
`
const RouteName = styled.h2`
margin-bottom: 20px;
font-size: inherit;
`
const HeaderText = styled.h1`
margin: 2px 0 0 0;
`

class TripViewer extends Component {
static propTypes = {
findTrip: apiActions.findTrip.type,
hideBackButton: PropTypes.bool,
homeTimezone: PropTypes.string,
intl: PropTypes.object,
setViewedTrip: uiActions.setViewedTrip.type,
tripData: PropTypes.object,
viewedTrip: PropTypes.object
Expand All @@ -43,7 +60,8 @@ class TripViewer extends Component {
}

render() {
const { hideBackButton, homeTimezone, tripData, viewedTrip } = this.props
const { hideBackButton, homeTimezone, intl, tripData, viewedTrip } =
this.props
const startOfDay = toDate(getCurrentDate(homeTimezone), {
timeZone: homeTimezone
})
Expand All @@ -65,18 +83,17 @@ class TripViewer extends Component {
)}

{/* Header Text */}
<div className="header-text">
<HeaderText className="header-text">
<FormattedMessage id="components.TripViewer.header" />
</div>
</HeaderText>
<div style={{ clear: 'both' }} />
</div>

<div className="trip-viewer-body">
{/* Basic Trip Info */}
{tripData && (
<div>
{/* Route name */}
<div>
<RouteName>
{tripData.route && (
<FormattedMessage
id="components.TripViewer.routeHeader"
Expand All @@ -87,89 +104,101 @@ class TripViewer extends Component {
}}
/>
)}
</div>
</RouteName>

{/* Wheelchair/bike accessibility badges, if applicable */}
<h4>
{tripData.wheelchairAccessible === 1 && (
<BsLabel bsStyle="primary">
<IconWithText Icon={Wheelchair}>
<FormattedMessage id="components.TripViewer.accessible" />
</IconWithText>
</BsLabel>
)}
<SpanWithSpace margin={0.25} />
{tripData.bikesAllowed === 1 && (
<BsLabel bsStyle="success">
<IconWithText Icon={Bicycle}>
<FormattedMessage id="components.TripViewer.bicyclesAllowed" />
</IconWithText>
</BsLabel>
)}
</h4>
{(tripData.wheelchairAccessible === 1 ||
tripData.bikesAllowed === 1) && (
<div>
{tripData.wheelchairAccessible === 1 && (
// TODO: these labels are currently insufficient for screen readers
<BsLabel bsStyle="primary">
<IconWithText Icon={Wheelchair}>
<FormattedMessage id="components.TripViewer.accessible" />
</IconWithText>
</BsLabel>
)}
<SpanWithSpace margin={0.25} />
{tripData.bikesAllowed === 1 && (
<BsLabel bsStyle="success">
<IconWithText Icon={Bicycle}>
<FormattedMessage id="components.TripViewer.bicyclesAllowed" />
</IconWithText>
</BsLabel>
)}
</div>
)}
</div>
)}

{/* Stop Listing */}
{tripData &&
tripData.stops &&
tripData.stopTimes &&
tripData.stops.map((stop, i) => {
// determine whether to use special styling for first/last stop
let stripMapLineClass = 'strip-map-line'
if (i === 0) stripMapLineClass = 'strip-map-line-first'
else if (i === tripData.stops.length - 1) {
stripMapLineClass = 'strip-map-line-last'
}

// determine whether to show highlight in strip map
let highlightClass
if (i === viewedTrip.fromIndex) {
highlightClass = 'strip-map-highlight-first'
} else if (i > viewedTrip.fromIndex && i < viewedTrip.toIndex) {
highlightClass = 'strip-map-highlight'
} else if (i === viewedTrip.toIndex) {
highlightClass = 'strip-map-highlight-last'
}

return (
<div key={i}>
{/* the departure time */}
<div className="stop-time">
<DepartureTime
originDate={startOfDay}
stopTime={tripData.stopTimes[i]}
/>
</div>

{/* the vertical strip map */}
<div className="strip-map-container">
{highlightClass && <div className={highlightClass} />}
<div className={stripMapLineClass} />
<div className="strip-map-icon">
<StyledIconWrapper>
<Circle />
</StyledIconWrapper>
</div>
</div>

{/* the stop-viewer button */}
<div className="stop-button-container">
<ViewStopButton
stopId={stop.id}
text={
<FormattedMessage id="components.TripViewer.viewStop" />
}
/>
</div>

{/* the main stop label */}
<div className="stop-name">{stop.name}</div>

<div style={{ clear: 'both' }} />
</div>
)
<StopList
aria-label={intl.formatMessage({
id: 'components.TripViewer.listOfRouteStops'
})}
>
{tripData &&
tripData.stops &&
tripData.stopTimes &&
tripData.stops.map((stop, i) => {
// determine whether to use special styling for first/last stop
let stripMapLineClass = 'strip-map-line'
if (i === 0) stripMapLineClass = 'strip-map-line-first'
else if (i === tripData.stops.length - 1) {
stripMapLineClass = 'strip-map-line-last'
}

// determine whether to show highlight in strip map
let highlightClass
if (i === viewedTrip.fromIndex) {
highlightClass = 'strip-map-highlight-first'
} else if (i > viewedTrip.fromIndex && i < viewedTrip.toIndex) {
highlightClass = 'strip-map-highlight'
} else if (i === viewedTrip.toIndex) {
highlightClass = 'strip-map-highlight-last'
}

return (
<Stop key={i}>
{/* the departure time */}
<div className="stop-time">
<DepartureTime
originDate={startOfDay}
stopTime={tripData.stopTimes[i]}
/>
</div>

{/* the vertical strip map */}
<div className="strip-map-container">
{highlightClass && <div className={highlightClass} />}
<div className={stripMapLineClass} />
<div className="strip-map-icon">
<StyledIconWrapper>
<Circle />
</StyledIconWrapper>
</div>
</div>

{/* the stop-viewer button */}
<div className="stop-button-container">
{/* TODO: To a screen reader, "View" appears before the stop name.
A separate label for screen readers will need to be set up. */}
<ViewStopButton
stopId={stop.id}
text={
<FormattedMessage id="components.TripViewer.viewStop" />
}
/>
</div>

{/* the main stop label */}
<div className="stop-name">{stop.name}</div>

<div style={{ clear: 'both' }} />
</Stop>
)
})}
</StopList>
</div>
</div>
)
Expand All @@ -191,4 +220,7 @@ const mapDispatchToProps = {
setViewedTrip: uiActions.setViewedTrip
}

export default connect(mapStateToProps, mapDispatchToProps)(TripViewer)
export default connect(
mapStateToProps,
mapDispatchToProps
)(injectIntl(TripViewer))
2 changes: 1 addition & 1 deletion percy/percy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ test('OTP-RR', async () => {

// Open stop viewer from trip viewer
await page.click(
'div.trip-viewer-body > div:nth-child(3) > div.stop-button-container > button'
'div.trip-viewer-body > ol > li:nth-child(3) > div.stop-button-container > button'
)
await page.waitForSelector('.stop-viewer')

Expand Down

0 comments on commit 8c1f4d8

Please sign in to comment.