Skip to content

Commit

Permalink
Link VPC run number to VPP (#2263)
Browse files Browse the repository at this point in the history
* chore: add search-maps-on-mobile feature flag

* refactor: start lifting tab state into PropertiesPanel component

* refactor: move GhostPropertiesPanel tabmode to props

* refactor: StaleDataPropertiesPanel tabmode refactor

* refactor: move closing of properties panel into callback prop

* test: more tests for setTabMode callbacks

* refactor: remove some reliance on useState / useEffect

* refactor: modify mapModeForUser to prevent recursive import

* feat: ability to select VPP from VPC run number

* test: more tests for closePanel callback

* refactor: better name for selected vehicle state variable in MapPage

* refactor: consistent tense in callback names

* refactor(tests): better query for selected VPP tab

Co-authored-by: Kayla Firestack <[email protected]>

* refactor(tests): clean up default mocks in mapPage.test.tsx

* refactor(tests): clean up default mocks in propertiesPanel.test.tsx

* refactor: common type for shared properties panel props

* refactor: rename setTabMode to onChangeTabMode

* refactor: rename closePanel to onClosePanel

* refactor: also rename props on properties panel header component

* fix: focus state for run number in VPC

---------

Co-authored-by: Kayla Firestack <[email protected]>
  • Loading branch information
lemald and firestack authored Oct 26, 2023
1 parent e4f8fab commit 1d752ba
Show file tree
Hide file tree
Showing 24 changed files with 719 additions and 222 deletions.
16 changes: 16 additions & 0 deletions assets/css/_skate_ui.scss
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,22 @@ $font-weights: (
color: var(--font-color-on-light-700);
}

.kv-value--clickable {
color: var(--font-color-text-clickable);
font-weight: var(--font-weight-semi);
text-decoration: underline;

&:hover {
color: $color-eggplant-500;
}

&:focus {
color: $color-eggplant-500;
border: 1px solid $color-eggplant-700;
border-radius: 3px;
}
}

.headsign {
font-weight: var(--font-weight-semi);
font-size: var(--font-size-m);
Expand Down
8 changes: 6 additions & 2 deletions assets/src/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import Nav from "./nav"
import RightPanel from "./rightPanel"
import { mapModeForUser } from "../util/mapMode"
import { Ghost, VehicleInScheduledService } from "../realtime"
import MapPage from "./mapPage"
import SearchPage from "./searchPage"

export const AppRoutes = () => {
useAppcues()
Expand All @@ -48,6 +50,8 @@ export const AppRoutes = () => {

const mapMode = mapModeForUser()

const mapElement = mapMode.path === "/map" ? <MapPage /> : <SearchPage />

return (
<div className="l-app">
<div className="l-app__banner">
Expand Down Expand Up @@ -79,12 +83,12 @@ export const AppRoutes = () => {
/>
<BrowserRoute path="/settings" element={<SettingsPage />} />
{mapMode.supportsRightPanel ? (
<BrowserRoute path={mapMode.path} element={mapMode.element} />
<BrowserRoute path={mapMode.path} element={mapElement} />
) : null}
</Route>
<Route>
{!mapMode.supportsRightPanel ? (
<BrowserRoute path={mapMode.path} element={mapMode.element} />
<BrowserRoute path={mapMode.path} element={mapElement} />
) : null}
</Route>
</Routes>
Expand Down
115 changes: 71 additions & 44 deletions assets/src/components/mapPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import { LocationSearchResult } from "../models/locationSearchResult"
import LocationCard from "./mapPage/locationCard"
import { useLocationSearchResultById } from "../hooks/useLocationSearchResultById"
import { fullStoryEvent } from "../helpers/fullStory"
import PropertiesPanel from "./propertiesPanel"
import inTestGroup, { TestGroups } from "../userInTestGroup"

const SearchMode = ({
onSelectVehicleResult,
Expand Down Expand Up @@ -74,9 +76,11 @@ const SearchMode = ({
const SelectedVehicle = ({
vehicleId,
setSelection,
onRunClicked,
}: {
vehicleId: VehicleId
setSelection: (selectedEntity: SelectedEntity | null) => void
onRunClicked?: (vehicleOrGhost: Vehicle | Ghost) => void
}) => {
// TODO: When using socket from context, this doesn't work as-is
// Presumably because the useMostRecentVehicleById hook is being used twice, but
Expand Down Expand Up @@ -106,6 +110,7 @@ const SelectedVehicle = ({
vehicleOrGhost={selectedVehicleOrGhost}
key={selectedVehicleOrGhost.id}
onRouteVariantNameClicked={onRouteClicked || undefined}
onRunClick={onRunClicked}
/>
)
}
Expand Down Expand Up @@ -134,10 +139,12 @@ const Selection = ({
selectedEntity,
setSelection,
fetchedSelectedLocation,
onVehicleRunClicked,
}: {
selectedEntity: SelectedEntity
setSelection: (selectedEntity: SelectedEntity | null) => void
fetchedSelectedLocation: LocationSearchResult | null
onVehicleRunClicked?: (vehicleOrGhost: Vehicle | Ghost) => void
}): ReactElement => {
const [{ searchPageState }, dispatch] = useContext(StateDispatchContext)
const selectRoutePattern = (routePattern: RoutePattern) => {
Expand Down Expand Up @@ -186,6 +193,7 @@ const Selection = ({
<SelectedVehicle
vehicleId={selectedEntity.vehicleId}
setSelection={setSelection}
onRunClicked={onVehicleRunClicked}
/>
) : selectedEntity.type === SelectedEntityType.RoutePattern ? (
<SelectedRoute
Expand All @@ -211,6 +219,10 @@ const MapPage = (): ReactElement<HTMLDivElement> => {
const [{ searchPageState, openView }, dispatch] =
useContext(StateDispatchContext),
{ selectedEntity = null } = searchPageState
const [
selectedRightPanelVehicleOrGhost,
setSelectedRightPanelVehicleOrGhost,
] = useState<Vehicle | Ghost | null>(null)

useEffect(() => {
// don't dispatch closeView if the VPP is open
Expand Down Expand Up @@ -286,61 +298,76 @@ const MapPage = (): ReactElement<HTMLDivElement> => {
}

return (
<div
className="c-map-page inherit-box border-box"
aria-label="Search Map Page"
>
<>
<div
className={joinClasses([
"c-map-page__input-and-results",
searchOpen
? "c-map-page__input-and-results--visible"
: "c-map-page__input-and-results--hidden",
])}
aria-label="Map Search Panel"
className="c-map-page inherit-box border-box"
aria-label="Search Map Page"
>
<DrawerTab
isVisible={searchOpen}
toggleVisibility={toggleSearchDrawer}
/>
{selectedEntity ? (
<Selection
<div
className={joinClasses([
"c-map-page__input-and-results",
searchOpen
? "c-map-page__input-and-results--visible"
: "c-map-page__input-and-results--hidden",
])}
aria-label="Map Search Panel"
>
<DrawerTab
isVisible={searchOpen}
toggleVisibility={toggleSearchDrawer}
/>
{selectedEntity ? (
<Selection
selectedEntity={selectedEntity}
setSelection={(...args) => {
setFollowerShouldSetZoomLevel(false)
setVehicleSelection(...args)
}}
fetchedSelectedLocation={fetchedSelectedLocation}
onVehicleRunClicked={
inTestGroup(TestGroups.SearchMapsOnMobile)
? (vehicleOrGhost: Vehicle | Ghost) =>
setSelectedRightPanelVehicleOrGhost(vehicleOrGhost)
: undefined
}
/>
) : (
<SearchMode
onSelectVehicleResult={(...args) => {
setFollowerShouldSetZoomLevel(true)
selectVehicleResult(...args)
}}
onSelectLocationResult={selectLocationResult}
/>
)}
</div>
<div className="c-map-page__map">
<MapDisplay
selectedEntity={selectedEntity}
setSelection={(...args) => {
setFollowerShouldSetZoomLevel(false)
setVehicleSelection(...args)
}}
fetchedSelectedLocation={fetchedSelectedLocation}
initializeRouteFollowerEnabled={followerShouldSetZoomLevel === true}
vehicleUseCurrentZoom={followerShouldSetZoomLevel === false}
onInterruptVehicleFollower={
(followerShouldSetZoomLevel === false || undefined) &&
(() => {
setFollowerShouldSetZoomLevel(false)
})
}
/>
) : (
<SearchMode
onSelectVehicleResult={(...args) => {
setFollowerShouldSetZoomLevel(true)
selectVehicleResult(...args)
}}
onSelectLocationResult={selectLocationResult}
/>
)}
</div>
</div>
<div className="c-map-page__map">
<MapDisplay
selectedEntity={selectedEntity}
setSelection={(...args) => {
setFollowerShouldSetZoomLevel(false)
setVehicleSelection(...args)
}}
fetchedSelectedLocation={fetchedSelectedLocation}
initializeRouteFollowerEnabled={followerShouldSetZoomLevel === true}
vehicleUseCurrentZoom={followerShouldSetZoomLevel === false}
onInterruptVehicleFollower={
(followerShouldSetZoomLevel === false || undefined) &&
(() => {
setFollowerShouldSetZoomLevel(false)
})
}
{selectedRightPanelVehicleOrGhost && (
<PropertiesPanel
selectedVehicleOrGhost={selectedRightPanelVehicleOrGhost}
onClosePanel={() => setSelectedRightPanelVehicleOrGhost(null)}
initialTab="run"
/>
</div>
</div>
)}
</>
)
}

Expand Down
33 changes: 28 additions & 5 deletions assets/src/components/mapPage/vehiclePropertiesCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ interface TrNameValueProps {
children: ReactNode
idPrefix?: string
sensitivity?: HideSensitiveInfo
onValueClick?: () => void
}

const maskClass = "fs-mask"
Expand All @@ -84,6 +85,7 @@ const TrNameValue = ({
children: value,
idPrefix,
sensitivity: sensitive = HideSensitiveInfo.None,
onValueClick,
}: TrNameValueProps): React.ReactElement => {
const id = (idPrefix ?? name) + useId()
return (
Expand All @@ -102,15 +104,28 @@ const TrNameValue = ({
])}
aria-labelledby={id}
>
{value}
{onValueClick ? (
<button className="kv-value--clickable" onClick={onValueClick}>
{value}
</button>
) : (
<>{value}</>
)}
</td>
</tr>
)
}

type VehicleWorkInfoEventProps = {
onRunClick?: (vehicleOrGhost: Vehicle | Ghost) => void
}

type VehicleWorkInfoProps = VehicleOrGhostProp & VehicleWorkInfoEventProps

const VehicleWorkInfo = ({
vehicleOrGhost,
}: VehicleOrGhostProp): React.ReactElement => {
onRunClick,
}: VehicleWorkInfoProps): React.ReactElement => {
const isLoggedOutVehicle =
isVehicle(vehicleOrGhost) && isLoggedOut(vehicleOrGhost)
const noRunText = isLoggedOutVehicle ? "No run logged in" : "N/A"
Expand All @@ -120,7 +135,10 @@ const VehicleWorkInfo = ({
<>
<table className="c-vehicle-work-info">
<tbody className="c-vehicle-work-info__items">
<TrNameValue name="run">
<TrNameValue
name="run"
onValueClick={onRunClick && (() => onRunClick(vehicleOrGhost))}
>
{vehicleOrGhost.runId || noRunText}
</TrNameValue>
<TrNameValue name="vehicle">
Expand Down Expand Up @@ -193,11 +211,13 @@ const VehicleLocationStreetViewButton = ({ vehicle }: { vehicle: Vehicle }) => (

// #region Vehicle Properties Card
export type VehiclePropertiesCardProps = VehicleOrGhostProp &
VehicleRouteSummaryEventProps
VehicleRouteSummaryEventProps &
VehicleWorkInfoEventProps

const VehiclePropertiesCard = ({
vehicleOrGhost,
onRouteVariantNameClicked,
onRunClick,
}: VehiclePropertiesCardProps): React.ReactElement => {
return (
<div
Expand All @@ -224,7 +244,10 @@ const VehiclePropertiesCard = ({

<div className="c-vehicle-properties-card__body">
<div className="c-vehicle-properties-card__properties c-vehicle-properties-card__info-section">
<VehicleWorkInfo vehicleOrGhost={vehicleOrGhost} />
<VehicleWorkInfo
vehicleOrGhost={vehicleOrGhost}
onRunClick={onRunClick}
/>
</div>

<div
Expand Down
Loading

0 comments on commit 1d752ba

Please sign in to comment.