From f181ad53723cfc31d7142e6470b58caa41dfacf6 Mon Sep 17 00:00:00 2001 From: Andreas Helms Date: Wed, 11 Dec 2024 12:41:37 +0100 Subject: [PATCH] feat(leg-steps): add steps to bicycle and car legs --- app/component/BicycleLeg.js | 475 +++++++++++-------- app/component/CarLeg.js | 330 +++++++------ app/component/ItineraryCircleLineWithIcon.js | 13 +- app/component/ItineraryLegs.js | 2 + app/component/{WalkSteps.js => LegSteps.js} | 8 +- app/component/WalkLeg.js | 4 +- test/unit/component/BicycleLeg.test.js | 12 + 7 files changed, 490 insertions(+), 354 deletions(-) rename app/component/{WalkSteps.js => LegSteps.js} (96%) diff --git a/app/component/BicycleLeg.js b/app/component/BicycleLeg.js index bde3fe60dc..0d1bb228b6 100644 --- a/app/component/BicycleLeg.js +++ b/app/component/BicycleLeg.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { useState } from 'react'; import moment from 'moment-timezone'; import { FormattedMessage, intlShape } from 'react-intl'; import cx from 'classnames'; @@ -23,6 +23,7 @@ import CityBikeLeg from './CityBikeLeg'; import StopCode from './StopCode'; import PlatformNumber from './PlatformNumber'; import DelayedTime from './DelayedTime'; +import LegSteps from './LegSteps'; function BicycleLeg( { @@ -30,6 +31,7 @@ function BicycleLeg( index, leg, focusToLeg, + focusToStep, bicycleWalkLeg, arrivedAtDestinationWithRentedBicycle, startTime, @@ -37,6 +39,7 @@ function BicycleLeg( }, { config, intl }, ) { + const [showLegSteps, setShowLegSteps] = useState(false); let stopsDescription; let circleLine; const distance = displayDistance( @@ -138,137 +141,84 @@ function BicycleLeg( ? bicycleWalkLeg?.to.name : leg.to.name; return ( -
- - {leg.rentedBike === true && legDescription} - {(leg.mode === 'WALK' || leg.mode === 'BICYCLE_WALK') && - stopsDescription} - - - - {circleLine} - -
+ <> +
+ {leg.rentedBike === true && legDescription} + {(leg.mode === 'WALK' || leg.mode === 'BICYCLE_WALK') && + stopsDescription} - {!!arrivedAtDestinationWithRentedBicycle && ( - - )} - {isFirstLeg(index) || bicycleWalkLeg?.from.stop ? ( -
-
-
- {fromStop() ? ( - { - e.stopPropagation(); - }} - to={`/${PREFIX_STOPS}/${bicycleWalkLeg.from.stop.gtfsId}`} - > - {bicycleWalkLeg.from.name} -
+ {circleLine} + +
+ + + {!!arrivedAtDestinationWithRentedBicycle && ( + + )} + + {isFirstLeg(index) || bicycleWalkLeg?.from.stop ? ( +
+
+
+ {fromStop() ? ( + { + e.stopPropagation(); + }} + to={`/${PREFIX_STOPS}/${bicycleWalkLeg.from.stop.gtfsId}`} + > + {bicycleWalkLeg.from.name} + + + ) : ( + address + )} +
+ {bicycleWalkLeg?.from.stop?.code && ( + <> + + - - ) : ( - address + )} +
{place}
- {bicycleWalkLeg?.from.stop?.code && ( - <> - - - - )} -
{place}
-
-
isKeyboardSelectionEvent(e) && focusAction(e)} - role="button" - tabIndex="0" - aria-label={intl.formatMessage( - { id: 'itinerary-summary.show-on-map' }, - { target: leg.from.name || '' }, - )} - > - -
-
- ) : ( - - )} - {bicycleWalkLeg?.from.stop && ( -
-
- {bicycleWalkLeg.distance === -1 ? ( - - ), - }} - /> - ) : ( - - ), - duration: durationToString( - bicycleWalkLeg.endTime - bicycleWalkLeg.startTime, - ), - distance: displayDistance( - parseInt(bicycleWalkLeg.distance, 10), - config, - intl.formatNumber, - ), - }} - /> - )}
-
- )} - {leg.rentedBike && arrivedAtDestinationWithRentedBicycle && ( -
- -
- + )} + {bicycleWalkLeg?.from.stop && ( +
+
+ {bicycleWalkLeg.distance === -1 ? ( + + ), + }} + /> + ) : ( + + ), + duration: durationToString( + bicycleWalkLeg.endTime - bicycleWalkLeg.startTime, + ), + distance: displayDistance( + parseInt(bicycleWalkLeg.distance, 10), + config, + intl.formatNumber, + ), + }} + /> + )} + +
+ isKeyboardSelectionEvent(e) && focusAction(e) + } + role="button" + tabIndex="0" + aria-label={intl.formatMessage( + { id: 'itinerary-summary.show-on-map' }, + { target: leg.from.name || '' }, + )} + > + +
+
-
- )} - {rentalUri && ( - - - - )} -
-
- {stopsDescription} -
isKeyboardSelectionEvent(e) && focusToLeg(e)} - role="button" - tabIndex="0" - aria-label={intl.formatMessage({ - id: 'itinerary-summary-row.clickable-area-description', - })} - > - + +
+ +
-
-
- {bicycleWalkLeg && bicycleWalkLeg?.to.stop && ( -
+ )} + {rentalUri && ( + + + + )} +
- {bicycleWalkLeg.distance === -1 ? ( - - ), - }} - /> - ) : ( - - ), - duration: durationToString( - bicycleWalkLeg.endTime - bicycleWalkLeg.startTime, - ), - distance: displayDistance( - parseInt(bicycleWalkLeg.distance, 10), - config, - intl.formatNumber, - ), - }} - /> - )} +
+ {stopsDescription} + +
isKeyboardSelectionEvent(e) && focusAction(e)} + onClick={focusToLeg} + onKeyPress={e => isKeyboardSelectionEvent(e) && focusToLeg(e)} role="button" tabIndex="0" - aria-label={intl.formatMessage( - { id: 'itinerary-summary.show-on-map' }, - { target: leg.from.name || '' }, - )} + aria-label={intl.formatMessage({ + id: 'itinerary-summary-row.clickable-area-description', + })} >
- )} + {bicycleWalkLeg && bicycleWalkLeg?.to.stop && ( +
+
+ {bicycleWalkLeg.distance === -1 ? ( + + ), + }} + /> + ) : ( + + ), + duration: durationToString( + bicycleWalkLeg.endTime - bicycleWalkLeg.startTime, + ), + distance: displayDistance( + parseInt(bicycleWalkLeg.distance, 10), + config, + intl.formatNumber, + ), + }} + /> + )} +
+ isKeyboardSelectionEvent(e) && focusAction(e) + } + role="button" + tabIndex="0" + aria-label={intl.formatMessage( + { id: 'itinerary-summary.show-on-map' }, + { target: leg.from.name || '' }, + )} + > + +
+
+
+ )} +
-
+ {showLegSteps && ( +
+ + )} + ); } @@ -412,6 +463,15 @@ BicycleLeg.propTypes = { mode: PropTypes.string.isRequired, rentedBike: PropTypes.bool.isRequired, alerts: PropTypes.array, + steps: PropTypes.arrayOf( + PropTypes.shape({ + relativeDirection: PropTypes.string.isRequired, + streetName: PropTypes.string.isRequired, + distance: PropTypes.number.isRequired, + lat: PropTypes.number.isRequired, + lon: PropTypes.number.isRequired, + }), + ), }).isRequired, bicycleWalkLeg: PropTypes.shape({ endTime: PropTypes.number.isRequired, @@ -436,6 +496,7 @@ BicycleLeg.propTypes = { index: PropTypes.number.isRequired, focusAction: PropTypes.func.isRequired, focusToLeg: PropTypes.func.isRequired, + focusToStep: PropTypes.func.isRequired, arrivedAtDestinationWithRentedBicycle: PropTypes.bool, startTime: PropTypes.number.isRequired, previousLeg: PropTypes.shape({ diff --git a/app/component/CarLeg.js b/app/component/CarLeg.js index 793b1f9d1f..9f858db895 100644 --- a/app/component/CarLeg.js +++ b/app/component/CarLeg.js @@ -1,7 +1,8 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { useState } from 'react'; import moment from 'moment-timezone'; import { FormattedMessage, intlShape } from 'react-intl'; +import cx from 'classnames'; import Icon from './Icon'; import { displayDistance } from '../util/geo-utils'; @@ -12,9 +13,11 @@ import ServiceAlertIcon from './ServiceAlertIcon'; import { AlertSeverityLevelType } from '../constants'; import { replaceQueryParams } from '../util/queryUtils'; import DelayedTime from './DelayedTime'; +import LegSteps from './LegSteps'; function CarLeg(props, { config, intl, router, match, executeAction }) { const { leg } = props; + const [showLegSteps, setShowLegSteps] = useState(false); const distance = displayDistance( parseInt(props.leg.distance, 10), config, @@ -31,88 +34,66 @@ function CarLeg(props, { config, intl, router, match, executeAction }) { /* eslint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */ return ( -
- - - -