From 3de1f3f44a37be35c4f50702056551a4422c333f Mon Sep 17 00:00:00 2001 From: Hannah Purcell <69368883+hannahpurcell@users.noreply.github.com> Date: Wed, 8 May 2024 16:58:21 -0400 Subject: [PATCH] feat: Added route ladders to new minimal ladders page (#2583) * Added route ladders to new minimal ladders page * Removed unused `findSelectedVehicleOrGhost` --- assets/src/components/app.tsx | 4 +- assets/src/components/ladderPage.tsx | 44 +- assets/src/components/minimalLadderPage.tsx | 35 +- assets/src/components/routeLadders.tsx | 36 +- .../minimalLadderPage.test.tsx.snap | 173 +++++++ .../__snapshots__/routeLadders.test.tsx.snap | 435 +++++++++--------- assets/tests/components/ladderPage.test.tsx | 93 +--- .../components/minimalLadderPage.test.tsx | 74 +++ assets/tests/components/routeLadders.test.tsx | 49 +- 9 files changed, 557 insertions(+), 386 deletions(-) create mode 100644 assets/tests/components/__snapshots__/minimalLadderPage.test.tsx.snap create mode 100644 assets/tests/components/minimalLadderPage.test.tsx diff --git a/assets/src/components/app.tsx b/assets/src/components/app.tsx index f8cb21a43..e8eb0835b 100644 --- a/assets/src/components/app.tsx +++ b/assets/src/components/app.tsx @@ -52,7 +52,9 @@ export const AppRoutes = () => { }, [path, setPath]) const vehiclesByRouteIdNeeded = - openView === OpenView.Late || location.pathname === "/" + openView === OpenView.Late || + location.pathname === "/" || + location.pathname === "/minimal" const { socket } = useContext(SocketContext) const vehiclesByRouteId: ByRouteId<(VehicleInScheduledService | Ghost)[]> = diff --git a/assets/src/components/ladderPage.tsx b/assets/src/components/ladderPage.tsx index ff7918bbd..ee17c5a04 100644 --- a/assets/src/components/ladderPage.tsx +++ b/assets/src/components/ladderPage.tsx @@ -1,7 +1,5 @@ import React, { ReactElement, useContext, useState, useEffect } from "react" -import RoutesContext from "../contexts/routesContext" import { StateDispatchContext } from "../contexts/stateDispatchContext" -import useTimepoints from "../hooks/useTimepoints" import { RouteTab, currentRouteTab, @@ -10,10 +8,7 @@ import { isPreset, tabName, } from "../models/routeTab" -import { allVehiclesAndGhosts } from "../models/vehiclesByRouteId" import PickerContainer from "./pickerContainer" -import { Ghost, VehicleId, VehicleInScheduledService } from "../realtime.d" -import { ByRouteId, Route, RouteId, TimepointsByRouteId } from "../schedule.d" import { Notifications } from "./notifications" import Presets from "./presets" import RouteLadders from "./routeLadders" @@ -31,27 +26,11 @@ import { import OldCloseButton from "./oldCloseButton" import { SaveIcon, PlusThinIcon } from "../helpers/icon" import { tagManagerEvent } from "../helpers/googleTagManager" -import useAlerts from "../hooks/useAlerts" -import { SocketContext } from "../contexts/socketContext" import { fullStoryEvent } from "../helpers/fullStory" import { usePanelStateFromStateDispatchContext } from "../hooks/usePanelState" type DrawerContent = "route_picker" | "presets" -export const findRouteById = ( - routes: Route[] | null, - routeId: RouteId -): Route | undefined => (routes || []).find((route) => route.id === routeId) - -export const findSelectedVehicleOrGhost = ( - vehiclesByRouteId: ByRouteId<(VehicleInScheduledService | Ghost)[]>, - selectedVehicleId: VehicleId | undefined -): VehicleInScheduledService | Ghost | undefined => { - return allVehiclesAndGhosts(vehiclesByRouteId).find( - (bus) => bus.id === selectedVehicleId - ) -} - const LadderTab = ({ tab, selectTab, @@ -156,27 +135,8 @@ const LadderPage = (): ReactElement => { ladderCrowdingToggles: {}, } - const routes: Route[] | null = useContext(RoutesContext) - const timepointsByRouteId: TimepointsByRouteId = - useTimepoints(selectedRouteIds) - - const { socket } = useContext(SocketContext) - const alerts = useAlerts(socket, selectedRouteIds) - const routesWithAlerts = [] - - for (const routeId in alerts) { - if (alerts[routeId].length > 0) { - routesWithAlerts.push(routeId) - } - } - const [currentDrawerContent, setCurrentDrawerContent] = useState("route_picker") - - const selectedRoutes: Route[] = selectedRouteIds - .map((routeId) => findRouteById(routes, routeId)) - .filter((route) => route) as Route[] - const pickerContainerVisibleClass = pickerContainerIsVisible ? "c-ladder-page--picker-container-visible" : "c-ladder-page--picker-container-hidden" @@ -248,8 +208,7 @@ const LadderPage = (): ReactElement => { dispatch(deselectRouteInTab(routeId))} reverseLadder={(routeId) => dispatch(flipLadderInTab(routeId))} @@ -258,7 +217,6 @@ const LadderPage = (): ReactElement => { } ladderDirections={ladderDirections} ladderCrowdingToggles={ladderCrowdingToggles} - routesWithAlerts={routesWithAlerts} /> diff --git a/assets/src/components/minimalLadderPage.tsx b/assets/src/components/minimalLadderPage.tsx index 22845071b..768cdefe9 100644 --- a/assets/src/components/minimalLadderPage.tsx +++ b/assets/src/components/minimalLadderPage.tsx @@ -1,5 +1,36 @@ -import React from "react" +import React, { useContext } from "react" +import RouteLadders from "./routeLadders" +import { currentRouteTab } from "../models/routeTab" +import { StateDispatchContext } from "../contexts/stateDispatchContext" +import { + deselectRouteInTab, + flipLadderInTab, + toggleLadderCrowdingInTab, +} from "../state" export const MinimalLadderPage = () => { - return
Placeholder for Minimal Route Ladders Page
+ const [{ routeTabs }, dispatch] = useContext(StateDispatchContext) + + const { selectedRouteIds, ladderDirections, ladderCrowdingToggles } = + currentRouteTab(routeTabs) || { + selectedRouteIds: [] as string[], + ladderDirections: {}, + ladderCrowdingToggles: {}, + } + + return ( +
+ dispatch(deselectRouteInTab(routeId))} + reverseLadder={(routeId) => dispatch(flipLadderInTab(routeId))} + toggleCrowding={(routeId) => + dispatch(toggleLadderCrowdingInTab(routeId)) + } + ladderDirections={ladderDirections} + ladderCrowdingToggles={ladderCrowdingToggles} + /> +
+ ) } diff --git a/assets/src/components/routeLadders.tsx b/assets/src/components/routeLadders.tsx index d53df05d3..add81fb7a 100644 --- a/assets/src/components/routeLadders.tsx +++ b/assets/src/components/routeLadders.tsx @@ -5,29 +5,34 @@ import { ByRouteId, Route, TimepointsByRouteId, RouteId } from "../schedule.d" import RouteLadder from "./routeLadder" import { LadderDirections } from "../models/ladderDirection" import { LadderCrowdingToggles } from "../models/ladderCrowdingToggle" +import RoutesContext from "../contexts/routesContext" +import useTimepoints from "../hooks/useTimepoints" +import { SocketContext } from "../contexts/socketContext" +import useAlerts from "../hooks/useAlerts" + +export const findRouteById = ( + routes: Route[] | null, + routeId: RouteId +): Route | undefined => (routes || []).find((route) => route.id === routeId) interface Props { - routes: Route[] - timepointsByRouteId: TimepointsByRouteId + selectedRouteIds: string[] selectedVehicleId: VehicleId | undefined deselectRoute: (routeId: RouteId) => void reverseLadder: (routeId: RouteId) => void toggleCrowding: (routeId: RouteId) => void ladderDirections: LadderDirections ladderCrowdingToggles: LadderCrowdingToggles - routesWithAlerts: RouteId[] } const RouteLadders = ({ - routes, - timepointsByRouteId, + selectedRouteIds, selectedVehicleId, deselectRoute, reverseLadder, toggleCrowding, ladderDirections, ladderCrowdingToggles, - routesWithAlerts, }: Props) => { const vehiclesByRouteId: ByRouteId<(VehicleInScheduledService | Ghost)[]> = useContext(VehiclesByRouteIdContext) @@ -41,6 +46,23 @@ const RouteLadders = ({ } } + const routes: Route[] | null = useContext(RoutesContext) + const selectedRoutes: Route[] = selectedRouteIds + .map((routeId) => findRouteById(routes, routeId)) + .filter((route) => route) as Route[] + const timepointsByRouteId: TimepointsByRouteId = + useTimepoints(selectedRouteIds) + + const { socket } = useContext(SocketContext) + const alerts = useAlerts(socket, selectedRouteIds) + const routesWithAlerts: RouteId[] = [] + + for (const routeId in alerts) { + if (alerts[routeId].length > 0) { + routesWithAlerts.push(routeId) + } + } + return (
- {routes.map((route) => ( + {selectedRoutes.map((route) => ( +
+
+
+
+ +
+
+ 1 +
+
+
+ +
+
+ + + + + + +
+
+ WASMA Name +
+ + WASMA + +
+ + +
+
+ MELWA Name +
+ + MELWA + +
+ + +
+
+ HHGAT Name +
+ + HHGAT + +
+
+
+
+
+ +`; diff --git a/assets/tests/components/__snapshots__/routeLadders.test.tsx.snap b/assets/tests/components/__snapshots__/routeLadders.test.tsx.snap index ddd9f5abb..53e88b750 100644 --- a/assets/tests/components/__snapshots__/routeLadders.test.tsx.snap +++ b/assets/tests/components/__snapshots__/routeLadders.test.tsx.snap @@ -1,350 +1,327 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`RouteLadders renders a route ladder 1`] = ` -
+
+
+ +
+
+ 1 +
+
+
- 1 -
-
-
- -
-
- - - - - + + + + + +
WASMA Name
WASMA
MELWA Name
MELWA
HHGAT Name
HHGAT
- -
-
-
+
+
+
+ +
+
+ 28 +
+
+
- 28 -
-
-
- -
-
- - - - - + + + + + +
MATPN Name
MATPN
WELLH Name
WELLH
MORTN Name
MORTN
- +
+
-
-
+ `; diff --git a/assets/tests/components/ladderPage.test.tsx b/assets/tests/components/ladderPage.test.tsx index 3962de061..98ca3c91c 100644 --- a/assets/tests/components/ladderPage.test.tsx +++ b/assets/tests/components/ladderPage.test.tsx @@ -3,10 +3,7 @@ import React from "react" import { render, fireEvent, within } from "@testing-library/react" import "@testing-library/jest-dom/jest-globals" import { BrowserRouter } from "react-router-dom" -import LadderPage, { - findRouteById, - findSelectedVehicleOrGhost, -} from "../../src/components/ladderPage" +import LadderPage from "../../src/components/ladderPage" import { RoutesProvider } from "../../src/contexts/routesContext" import { StateDispatchProvider } from "../../src/contexts/stateDispatchContext" import useTimepoints from "../../src/hooks/useTimepoints" @@ -16,7 +13,7 @@ import { Notification, VehicleInScheduledService, } from "../../src/realtime" -import { ByRouteId, Route, TimepointsByRouteId } from "../../src/schedule.d" +import { Route, TimepointsByRouteId } from "../../src/schedule.d" import { initialState, State, @@ -530,63 +527,6 @@ describe("LadderPage", () => { }) }) -describe("findRouteById", () => { - test("finds a route in a list by its id", () => { - expect(findRouteById(routes, "28")).toEqual( - routeFactory.build({ - id: "28", - name: "28", - }) - ) - }) - - test("returns undefined if the route isn't found", () => { - expect(findRouteById(routes, "missing")).toEqual(undefined) - }) - - test("returns undefined if routes is null", () => { - expect(findRouteById(null, "does not matter")).toEqual(undefined) - }) -}) - -describe("findSelectedVehicleOrGhost", () => { - test("returns the requested vehicle if it is on the route", () => { - expect( - findSelectedVehicleOrGhost(vehiclesByRouteId, "on-route-39") - ).toEqual({ - id: "on-route-39", - routeStatus: "on_route", - }) - }) - - test("returns the requested vehicle if it is pulling out", () => { - expect( - findSelectedVehicleOrGhost(vehiclesByRouteId, "pulling-out-39") - ).toEqual({ - id: "pulling-out-39", - routeStatus: "pulling_out", - }) - }) - - test("returns the requested vehicle if it is a ghost bus", () => { - expect(findSelectedVehicleOrGhost(vehiclesByRouteId, "ghost-39")).toEqual({ - id: "ghost-39", - }) - }) - - test("returns undefined if the vehicle is not found", () => { - expect( - findSelectedVehicleOrGhost(vehiclesByRouteId, "missing-23") - ).toBeUndefined() - }) - - test("returns undefined if selectedVehicleId is undefined", () => { - expect( - findSelectedVehicleOrGhost(vehiclesByRouteId, undefined) - ).toBeUndefined() - }) -}) - const routes: Route[] = [ routeFactory.build({ id: "1", name: "1" }), routeFactory.build({ id: "28", name: "28" }), @@ -605,32 +545,3 @@ const timepointsByRouteId: TimepointsByRouteId = { "71": undefined, "73": null, } - -const vehiclesByRouteId: ByRouteId<(VehicleInScheduledService | Ghost)[]> = { - "23": [ - { - id: "on-route-23", - routeStatus: "on_route", - } as VehicleInScheduledService, - { - id: "pulling-out-23", - routeStatus: "pulling_out", - } as VehicleInScheduledService, - { - id: "ghost-23", - } as Ghost, - ], - "39": [ - { - id: "on-route-39", - routeStatus: "on_route", - } as VehicleInScheduledService, - { - id: "pulling-out-39", - routeStatus: "pulling_out", - } as VehicleInScheduledService, - { - id: "ghost-39", - } as Ghost, - ], -} diff --git a/assets/tests/components/minimalLadderPage.test.tsx b/assets/tests/components/minimalLadderPage.test.tsx new file mode 100644 index 000000000..98c33e5f9 --- /dev/null +++ b/assets/tests/components/minimalLadderPage.test.tsx @@ -0,0 +1,74 @@ +import { jest, describe, test, expect } from "@jest/globals" +import React from "react" +import { render } from "@testing-library/react" +import routeFactory from "../factories/route" +import { Route, TimepointsByRouteId } from "../../src/schedule.d" +import useTimepoints from "../../src/hooks/useTimepoints" +import { RoutesProvider } from "../../src/contexts/routesContext" +import { MinimalLadderPage } from "../../src/components/minimalLadderPage" +import { initialState } from "../../src/state" +import routeTabFactory from "../factories/routeTab" +import { BrowserRouter } from "react-router-dom" +import { StateDispatchProvider } from "../../src/contexts/stateDispatchContext" + +jest.mock("../../src/hooks/useTimepoints", () => ({ + __esModule: true, + default: jest.fn(() => ({})), +})) + +const routes: Route[] = [ + routeFactory.build({ id: "1", name: "1" }), + routeFactory.build({ id: "28", name: "28" }), +] +const timepointsByRouteId: TimepointsByRouteId = { + "1": [ + { id: "WASMA", name: "WASMA Name" }, + { id: "MELWA", name: "MELWA Name" }, + { id: "HHGAT", name: "HHGAT Name" }, + ], + "28": [ + { id: "MATPN", name: "MATPN Name" }, + { id: "WELLH", name: "WELLH Name" }, + { id: "MORTN", name: "MORTN Name" }, + ], + "71": undefined, + "73": null, +} + +describe("RouteLadders", () => { + test("renders a route ladder", () => { + jest.mocked(useTimepoints).mockImplementationOnce(() => timepointsByRouteId) + + const mockState = { + ...initialState, + routeTabs: [ + routeTabFactory.build({ + ordering: 0, + isCurrentTab: true, + selectedRouteIds: ["1"], + }), + routeTabFactory.build({ + ordering: undefined, + isCurrentTab: false, + selectedRouteIds: ["28"], + }), + routeTabFactory.build({ + ordering: 1, + isCurrentTab: false, + selectedRouteIds: ["39"], + }), + ], + } + const { asFragment } = render( + + + + + + + + ) + + expect(asFragment()).toMatchSnapshot() + }) +}) diff --git a/assets/tests/components/routeLadders.test.tsx b/assets/tests/components/routeLadders.test.tsx index 6af3b0c79..b7b09006f 100644 --- a/assets/tests/components/routeLadders.test.tsx +++ b/assets/tests/components/routeLadders.test.tsx @@ -1,10 +1,16 @@ import { jest, describe, test, expect } from "@jest/globals" import React from "react" -import renderer from "react-test-renderer" import { render, fireEvent } from "@testing-library/react" import routeFactory from "../factories/route" -import RouteLadders from "../../src/components/routeLadders" +import RouteLadders, { findRouteById } from "../../src/components/routeLadders" import { Route, TimepointsByRouteId } from "../../src/schedule.d" +import useTimepoints from "../../src/hooks/useTimepoints" +import { RoutesProvider } from "../../src/contexts/routesContext" + +jest.mock("../../src/hooks/useTimepoints", () => ({ + __esModule: true, + default: jest.fn(() => ({})), +})) const routes: Route[] = [ routeFactory.build({ id: "1", name: "1" }), @@ -27,37 +33,35 @@ const timepointsByRouteId: TimepointsByRouteId = { describe("RouteLadders", () => { test("renders a route ladder", () => { - const tree = renderer - .create( + jest.mocked(useTimepoints).mockImplementationOnce(() => timepointsByRouteId) + + const { asFragment } = render( + route.id)} selectedVehicleId={undefined} deselectRoute={jest.fn()} reverseLadder={jest.fn()} toggleCrowding={jest.fn()} ladderDirections={{}} ladderCrowdingToggles={{}} - routesWithAlerts={[]} /> - ) - .toJSON() + + ) - expect(tree).toMatchSnapshot() + expect(asFragment()).toMatchSnapshot() }) test("can scroll horizontally with mouse wheel", () => { const result = render( route.id)} selectedVehicleId={undefined} deselectRoute={jest.fn()} reverseLadder={jest.fn()} toggleCrowding={jest.fn()} ladderDirections={{}} ladderCrowdingToggles={{}} - routesWithAlerts={[]} /> ) @@ -68,4 +72,23 @@ describe("RouteLadders", () => { expect(routeLaddersDiv.scrollTo).toHaveBeenCalled() }) + + describe("findRouteById", () => { + test("finds a route in a list by its id", () => { + expect(findRouteById(routes, "28")).toEqual( + routeFactory.build({ + id: "28", + name: "28", + }) + ) + }) + + test("returns undefined if the route isn't found", () => { + expect(findRouteById(routes, "missing")).toEqual(undefined) + }) + + test("returns undefined if routes is null", () => { + expect(findRouteById(null, "does not matter")).toEqual(undefined) + }) + }) })