diff --git a/apps/site/assets/ts/schedule/components/ScheduleDirection.tsx b/apps/site/assets/ts/schedule/components/ScheduleDirection.tsx index e08821d3ac..760b9741f4 100644 --- a/apps/site/assets/ts/schedule/components/ScheduleDirection.tsx +++ b/apps/site/assets/ts/schedule/components/ScheduleDirection.tsx @@ -19,7 +19,6 @@ import { } from "../../models/route"; import LineDiagram from "./line-diagram/LineDiagram"; import { fromStopTreeData } from "./ScheduleLoader"; -import { isEmptyTree } from "../../helpers/stop-tree"; export interface Props { route: EnhancedRoute; @@ -204,7 +203,12 @@ const ScheduleDirection = ({ ); }, [route, state.directionId, busVariantId, currentRoutePatternIdForData]); - const hasValidTree = lineState.data && !isEmptyTree(lineState.data.stopTree); + const routeStopList = + lineState.data && lineState.data.routeStopLists + ? (lineState.data.routeStopLists as RouteStop[][]).find( + rsList => !!rsList.find(rs => rs.branch === state.routePattern.name) + ) || [] + : []; return ( <>
@@ -225,9 +229,10 @@ const ScheduleDirection = ({ ) : null}
- {isSubwayRoute(route) && hasValidTree && ( + {isSubwayRoute(route) && ( )} - {!isSubwayRoute(route) && hasValidTree && ( + {!isSubwayRoute(route) && ( { const LineDiagram = ({ stopTree, + routeStopList, route, directionId, alerts }: Props): ReactElement => { - const stopTreeCoordStore = createStopTreeCoordStore(stopTree); + const stopTreeCoordStore = stopTree + ? createStopTreeCoordStore(stopTree) + : createStopTreeCoordStore(routeStopList); const liveData = useRealtime(route, directionId, true); const [query, setQuery] = useState(""); - const allStops: RouteStop[] = stopIds(stopTree).map(stopId => - stopForId(stopTree, stopId) - ); + const allStops: RouteStop[] = stopTree + ? stopIds(stopTree).map(stopId => stopForId(stopTree, stopId)) + : routeStopList; const filteredStops: RouteStop[] = allStops.filter(stop => stop.name.toLowerCase().includes(query.toLowerCase()) ); @@ -97,6 +101,7 @@ const LineDiagram = ({ key={stop.id} stopTree={stopTree} stopId={stop.id} + routeStopList={routeStopList} alerts={alertsByStop(alerts, stop.id)} onClick={handleStopClick} liveData={liveData?.[stop.id]} @@ -116,6 +121,7 @@ const LineDiagram = ({ => { // create a ref for each stop - we will use this to track the location of the stop so we can place the line diagram bubbles - const [stopRefsMap, updateAllStopCoords] = useTreeStopPositions(stopTree); + const [stopRefsMap, updateAllStopCoords] = useTreeStopPositions( + stopTree || routeStopList + ); const anyCrowding = Object.values( liveData || {} @@ -273,23 +282,50 @@ const LineDiagramWithStops = ({ !anyCrowding ? "u-no-crowding-data" : "" }`} > - -
    - + ) : ( + + )} +
      + {stopTree ? ( + + ) : ( + <> + {routeStopList.map((routeStop, index) => ( + + ))} + + )}
    diff --git a/apps/site/assets/ts/schedule/components/line-diagram/StopCard.tsx b/apps/site/assets/ts/schedule/components/line-diagram/StopCard.tsx index e4fd9100cd..b41d8cada3 100644 --- a/apps/site/assets/ts/schedule/components/line-diagram/StopCard.tsx +++ b/apps/site/assets/ts/schedule/components/line-diagram/StopCard.tsx @@ -30,7 +30,8 @@ import { StopRefContext } from "./LineDiagramWithStops"; import { LiveData } from "./__line-diagram"; interface Props { - stopTree: StopTree; + stopTree: StopTree | null; + routeStopList: RouteStop[]; stopId: StopId; alerts: Alert[]; onClick: (stop: RouteStop) => void; @@ -62,11 +63,6 @@ const lineName = ({ name, route: routeStopRoute }: RouteStop): string => { const hasLivePredictions = (liveData?: LiveData): boolean => !!liveData && liveData.headsigns.some(hasPredictionTime); -const showPrediction = ( - stopTree: StopTree, - stopId: StopId, - liveData?: LiveData -): boolean => hasLivePredictions(liveData) && !isEndNode(stopTree, stopId); const connectionsFor = ( routeStop: RouteStop, @@ -107,8 +103,7 @@ const hasUpcomingDeparturesIfSubway = ( return !!liveData && liveData.headsigns.length > 0; }; -const schedulesButtonLabel = (stopTree: StopTree, stopId: StopId): string => { - const route = routeForStop(stopTree, stopId); +const schedulesButtonLabel = (route: RouteStopRoute | null): string => { return route && isSubwayRoute(route) ? "View upcoming departures" : "View schedule"; @@ -125,29 +120,40 @@ const Alert = (): JSX.Element => ( const StopCard = ({ stopTree, stopId, + routeStopList, alerts, onClick, liveData, searchQuery }: Props): ReactElement => { const refs = useContext(StopRefContext)[0]; - const routeStop: RouteStop = stopForId(stopTree, stopId); + const routeStop = stopTree + ? stopForId(stopTree, stopId) + : routeStopList.find(rs => rs.id === stopId)!; + const routeStopIndex = routeStopList.indexOf(routeStop); + const isEnd = stopTree + ? isEndNode(stopTree, stopId) + : routeStopIndex === routeStopList.length - 1; const diversionAlert = alerts.find(isActiveDiversion); const showDiversion = - diversionAlert && - !(hasLivePredictions(liveData) && isEndNode(stopTree, stopId)); + diversionAlert && !(hasLivePredictions(liveData) && isEnd); + + const left = stopTree ? width(stopTree, stopId) : diagramWidth(1); + const connections = stopTree + ? connectionsFor(routeStop, stopTree) + : routeStop.connections; return (
  1. - {hasBranchLabel(stopTree, stopId) && ( + {stopTree && hasBranchLabel(stopTree, stopId) && (
    {lineName(routeStop)}
    )}
    - {StopConnections(stopId, connectionsFor(routeStop, stopTree))} - {showPrediction(stopTree, stopId, liveData) ? ( + {StopConnections(stopId, connections)} + {hasLivePredictions(liveData) && !isEnd ? ( - {!isEndNode(stopTree, stopId) && - hasUpcomingDeparturesIfSubway(stopTree, stopId, liveData) && ( + {!isEnd && + (stopTree + ? hasUpcomingDeparturesIfSubway(stopTree, stopId, liveData) + : true) && (
    )} diff --git a/apps/site/assets/ts/schedule/components/line-diagram/__tests__/DiagramTest.tsx b/apps/site/assets/ts/schedule/components/line-diagram/__tests__/DiagramTest.tsx index 5d9d480eef..27fff6470f 100644 --- a/apps/site/assets/ts/schedule/components/line-diagram/__tests__/DiagramTest.tsx +++ b/apps/site/assets/ts/schedule/components/line-diagram/__tests__/DiagramTest.tsx @@ -3,7 +3,7 @@ import * as redux from "react-redux"; import { mount, ReactWrapper } from "enzyme"; import { RouteStop, StopTree } from "../../__schedule"; import { createStopTreeCoordStore } from "../graphics/useTreeStopPositions"; -import Diagram from "../graphics/Diagram"; +import { Diagram } from "../graphics/Diagram"; import { Route, RouteType } from "../../../../__v3api"; import Stop from "../graphics/Stop"; import { LiveDataByStop } from "../__line-diagram"; diff --git a/apps/site/assets/ts/schedule/components/line-diagram/__tests__/LineDiagramTest.tsx b/apps/site/assets/ts/schedule/components/line-diagram/__tests__/LineDiagramTest.tsx index b418cf6a21..f084e2a0b0 100644 --- a/apps/site/assets/ts/schedule/components/line-diagram/__tests__/LineDiagramTest.tsx +++ b/apps/site/assets/ts/schedule/components/line-diagram/__tests__/LineDiagramTest.tsx @@ -48,7 +48,7 @@ const stopTree: StopTree = { }, startingNodes: ["a"] }; - +const testRouteStopList = Object.values(stopTree.byId).map(node => node.value); const route = { type: 3 as RouteType, name: "route 1", @@ -80,6 +80,7 @@ describe("LineDiagram", () => { wrapper = mount( { const subwayWrapper = mount( { render( { render( { render( node.value); const store = UseTreeStopPositions.createStopTreeCoordStore(stopTree); const route = { @@ -187,6 +189,7 @@ describe("LineDiagramWithStops", () => { { { node.value); const alertA: Alert = { id: "MOCK-ALERT-A", @@ -124,6 +125,7 @@ describe("StopCard", () => { { { { { { { { { {}} @@ -361,6 +371,7 @@ describe("StopCard", () => { {}} @@ -376,6 +387,7 @@ describe("StopCard", () => { {}} @@ -391,6 +403,7 @@ describe("StopCard", () => { {}} @@ -409,6 +422,7 @@ describe("StopCard", () => { - + - - - - - - - - - - - - + + + + + + + + + + + + Line diagram for diff --git a/apps/site/assets/ts/schedule/components/line-diagram/__tests__/__snapshots__/LineDiagramTest.tsx.snap b/apps/site/assets/ts/schedule/components/line-diagram/__tests__/__snapshots__/LineDiagramTest.tsx.snap index 57f840ddb5..ce6031c5aa 100644 --- a/apps/site/assets/ts/schedule/components/line-diagram/__tests__/__snapshots__/LineDiagramTest.tsx.snap +++ b/apps/site/assets/ts/schedule/components/line-diagram/__tests__/__snapshots__/LineDiagramTest.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`LineDiagram renders and matches snapshot 1`] = ` -" +"

    Stops

    @@ -17,12 +17,12 @@ exports[`LineDiagram renders and matches snapshot 1`] = ` - +
    - - - + + + Line diagram for @@ -55,7 +55,7 @@ exports[`LineDiagram renders and matches snapshot 1`] = `
      - +
    1. @@ -83,7 +83,7 @@ exports[`LineDiagram renders and matches snapshot 1`] = `
    2. - +
    3. @@ -111,7 +111,7 @@ exports[`LineDiagram renders and matches snapshot 1`] = `
    4. - +
    5. diff --git a/apps/site/assets/ts/schedule/components/line-diagram/__tests__/__snapshots__/LineDiagramWithStopsTest.tsx.snap b/apps/site/assets/ts/schedule/components/line-diagram/__tests__/__snapshots__/LineDiagramWithStopsTest.tsx.snap index 93b08bb5a3..8deccc5407 100644 --- a/apps/site/assets/ts/schedule/components/line-diagram/__tests__/__snapshots__/LineDiagramWithStopsTest.tsx.snap +++ b/apps/site/assets/ts/schedule/components/line-diagram/__tests__/__snapshots__/LineDiagramWithStopsTest.tsx.snap @@ -2,22 +2,22 @@ exports[`LineDiagramWithStops renders and matches snapshot 1`] = ` " - +
      - - - - - - - - - - - - - + + + + + + + + + + + + + Line diagram for @@ -111,7 +111,7 @@ exports[`LineDiagramWithStops renders and matches snapshot 1`] = `
        - +
      1. @@ -143,7 +143,7 @@ exports[`LineDiagramWithStops renders and matches snapshot 1`] = `
      2. - +
      3. @@ -175,7 +175,7 @@ exports[`LineDiagramWithStops renders and matches snapshot 1`] = `
      4. - +
      5. @@ -207,7 +207,7 @@ exports[`LineDiagramWithStops renders and matches snapshot 1`] = `
      6. - +
      7. @@ -271,7 +271,7 @@ exports[`LineDiagramWithStops renders and matches snapshot 1`] = ` - +
      8. @@ -303,7 +303,7 @@ exports[`LineDiagramWithStops renders and matches snapshot 1`] = `
      9. - +
      10. @@ -367,7 +367,7 @@ exports[`LineDiagramWithStops renders and matches snapshot 1`] = ` - +
      11. @@ -399,7 +399,7 @@ exports[`LineDiagramWithStops renders and matches snapshot 1`] = `
      12. - +
      13. @@ -431,7 +431,7 @@ exports[`LineDiagramWithStops renders and matches snapshot 1`] = `
      14. - +
      15. @@ -458,7 +458,7 @@ exports[`LineDiagramWithStops renders and matches snapshot 1`] = `
      16. - +
      17. @@ -490,7 +490,7 @@ exports[`LineDiagramWithStops renders and matches snapshot 1`] = `
      18. - +
      19. diff --git a/apps/site/assets/ts/schedule/components/line-diagram/__tests__/__snapshots__/StopCardTest.tsx.snap b/apps/site/assets/ts/schedule/components/line-diagram/__tests__/__snapshots__/StopCardTest.tsx.snap index 155552f0c9..39b9c91fc1 100644 --- a/apps/site/assets/ts/schedule/components/line-diagram/__tests__/__snapshots__/StopCardTest.tsx.snap +++ b/apps/site/assets/ts/schedule/components/line-diagram/__tests__/__snapshots__/StopCardTest.tsx.snap @@ -2,7 +2,7 @@ exports[`StopCard renders and matches snapshot 1`] = ` " - +
      20. diff --git a/apps/site/assets/ts/schedule/components/line-diagram/graphics/Diagram.tsx b/apps/site/assets/ts/schedule/components/line-diagram/graphics/Diagram.tsx index a91beaf6e3..f36931cf9c 100644 --- a/apps/site/assets/ts/schedule/components/line-diagram/graphics/Diagram.tsx +++ b/apps/site/assets/ts/schedule/components/line-diagram/graphics/Diagram.tsx @@ -19,7 +19,7 @@ import VehicleIcons from "../VehicleIcons"; import { LiveDataByStop } from "../__line-diagram"; import { BASE_LINE_WIDTH, DiagonalHatchPattern } from "./graphic-helpers"; import Stop from "./Stop"; -import Line from "./Line"; +import { Line, SimpleLine } from "./Line"; import Merges from "./Merges"; interface Props { @@ -34,11 +34,11 @@ const routeName = (route: Route): string => isAGreenLineRoute(route) ? "Green Line" : route.name; const diagramDescription = ( - stopTree: StopTree, + pathLength: number, route: Route, directionId: DirectionId ): string => { - const text = `${longestPathLength(stopTree)} stops`; + const text = `${pathLength} stops`; const { direction_destinations: destinations, direction_names: names @@ -71,30 +71,93 @@ const branchingDescription = (stopTree: StopTree): string => { }; const LiveVehicleIconSet = ({ - stopTree, - stopId, + isStart, + stop, liveData }: { - stopTree: StopTree; - stopId: StopId; + isStart: boolean; + stop: RouteStop; liveData?: LiveDataByStop; }): ReactElement | null => { + const stopId = stop.id; if (!liveData || !liveData[stopId]) return null; const vehicles = uniqBy(liveData[stopId].vehicles, "id"); // Hide vehicles arriving to the origin from 'off the line' - const vehicleData = isStartNode(stopTree, stopId) + const vehicleData = isStart ? vehicles.filter(vehicle => vehicle.status === "stopped") : vehicles; return ( ); }; +interface SimpleProps { + routeStopList: RouteStop[]; + route: Route; + directionId: DirectionId; + alerts: Alert[]; + liveData?: LiveDataByStop; +} + +const SimpleDiagram = ({ + routeStopList, + route, + directionId, + alerts, + liveData +}: SimpleProps): ReactElement => ( + <> + {routeStopList.map((routeStop, index) => ( + + ))} + + Line diagram for {routeName(route)} + + {diagramDescription(routeStopList.length, route, directionId)} + + {DiagonalHatchPattern()} + {/* Draw lines between stops */ + routeStopList.map((routeStop, index) => { + const nextStop = routeStopList[index + 1]; + if (!nextStop) return null; + return ( + + ); + })} + + {/* Draw circles for each stop */ + routeStopList.map((routeStop, index) => ( + // eslint-disable-next-line react/no-array-index-key + + ))} + + +); + const Diagram = ({ stopTree, route, @@ -106,8 +169,8 @@ const Diagram = ({ {stopIds(stopTree).map(stopId => ( ))} @@ -122,7 +185,7 @@ const Diagram = ({ > Line diagram for {routeName(route)} - {diagramDescription(stopTree, route, directionId)} + {diagramDescription(longestPathLength(stopTree), route, directionId)} {hasBranchLines(stopTree) && branchingDescription(stopTree)} {DiagonalHatchPattern()} @@ -153,4 +216,4 @@ const Diagram = ({ ); -export default Diagram; +export { Diagram, SimpleDiagram }; diff --git a/apps/site/assets/ts/schedule/components/line-diagram/graphics/Line.tsx b/apps/site/assets/ts/schedule/components/line-diagram/graphics/Line.tsx index 0997b788f9..e9c3191638 100644 --- a/apps/site/assets/ts/schedule/components/line-diagram/graphics/Line.tsx +++ b/apps/site/assets/ts/schedule/components/line-diagram/graphics/Line.tsx @@ -97,4 +97,37 @@ const Line = ({ ); }; -export default Line; +const SimpleLine = ({ + fromId, + toId, + alerts +}: Omit): ReactElement | null => { + const fromCoords: StopCoord | null = useSelector( + (state: CoordState) => state[fromId] + ); + const toCoords: StopCoord | null = useSelector( + (state: CoordState) => state[toId] + ); + if (!fromCoords || !toCoords) return null; + + const x = BASE_LINE_WIDTH + 1; + const [, y1] = fromCoords; + const [, y2] = toCoords; + const strokeProp = + hasAnActiveDiversion(fromId, alerts) && hasAnActiveDiversion(toId, alerts) + ? { stroke: "url(#diagonalHatch)" } + : {}; + return ( + + ); +}; + +export { Line, SimpleLine }; diff --git a/apps/site/assets/ts/schedule/components/line-diagram/graphics/Merges.tsx b/apps/site/assets/ts/schedule/components/line-diagram/graphics/Merges.tsx index fb98485621..e902f7ad09 100644 --- a/apps/site/assets/ts/schedule/components/line-diagram/graphics/Merges.tsx +++ b/apps/site/assets/ts/schedule/components/line-diagram/graphics/Merges.tsx @@ -16,7 +16,7 @@ import { MERGE_RADIUS, StopCoord } from "./graphic-helpers"; -import Line from "./Line"; +import { Line } from "./Line"; interface Props { stopTree: StopTree; diff --git a/apps/site/assets/ts/schedule/components/line-diagram/graphics/useTreeStopPositions.ts b/apps/site/assets/ts/schedule/components/line-diagram/graphics/useTreeStopPositions.ts index 69777bdf6d..535672e0f1 100644 --- a/apps/site/assets/ts/schedule/components/line-diagram/graphics/useTreeStopPositions.ts +++ b/apps/site/assets/ts/schedule/components/line-diagram/graphics/useTreeStopPositions.ts @@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef } from "react"; import { useDispatch } from "react-redux"; import { createStore, Store } from "redux"; import { stopIds } from "../../../../helpers/stop-tree"; -import { StopId, StopTree } from "../../__schedule"; +import { RouteStop, StopId, StopTree } from "../../__schedule"; import { branchPosition } from "../line-diagram-helpers"; import { BASE_LINE_WIDTH, @@ -14,10 +14,16 @@ import { export type RefMap = Map; +const isRouteStopList = ( + stopOrList: StopTree | RouteStop[] +): stopOrList is RouteStop[] => Array.isArray(stopOrList); + export const createStopTreeCoordStore = ( - stopTree: StopTree + stopTreeOrList: StopTree | RouteStop[] ): Store => { - const ids: StopId[] = stopIds(stopTree); + const ids: StopId[] = isRouteStopList(stopTreeOrList) + ? stopTreeOrList.map(rs => rs.id) + : stopIds(stopTreeOrList); const initialCoordState: CoordState = ids.reduce( (acc, id) => ({ ...acc, [id]: null }), @@ -39,14 +45,16 @@ const xCoordForStop = (stopTree: StopTree, id: StopId): number => BRANCH_SPACING * (branchPosition(stopTree, id) - 1) + BASE_LINE_WIDTH + 1; export default function useTreeStopPositions( - stopTree: StopTree + stopTreeOrList: StopTree | RouteStop[] ): [RefMap, () => void] { const stopRefsMap = useRef(new Map() as RefMap); const dispatchStopCoords = useDispatch(); const updateAllStops = useCallback((): void => { stopRefsMap.current.forEach((el, stopId) => { - const x = xCoordForStop(stopTree, stopId); + const x = isRouteStopList(stopTreeOrList) + ? BASE_LINE_WIDTH + 1 + : xCoordForStop(stopTreeOrList, stopId); let coordinates = null; if (el) { const { offsetTop, offsetHeight } = el; @@ -60,7 +68,7 @@ export default function useTreeStopPositions( coords: coordinates }); }); - }, [dispatchStopCoords, stopRefsMap, stopTree]); + }, [dispatchStopCoords, stopRefsMap, stopTreeOrList]); useEffect(() => { updateAllStops();