Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Base work to get detours in route ladder dropdown #2915

Merged
merged 41 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
b55e03f
feat: Set up detours channels
hannahpurcell Dec 11, 2024
59910e7
tweak: Mis-commit in detours.ex
hannahpurcell Dec 11, 2024
1df7bf2
fix: channel name for active detours by route id
hannahpurcell Dec 11, 2024
144976c
tweak: make a function private
hannahpurcell Dec 11, 2024
007ca7f
Merge branch 'main' into hp/skate-detours-channel
hannahpurcell Dec 19, 2024
6d5b7b0
tweak: have a default list in grouped_detours
hannahpurcell Dec 19, 2024
c084049
tweak: add value needed by db_detour_to_detour to detour_snapshot_fac…
hannahpurcell Dec 19, 2024
64662b8
tweak: delete unused line
hannahpurcell Dec 19, 2024
e7ad20b
tweak: make author_id a FK to detours
hannahpurcell Dec 19, 2024
0175b71
feat: Elixir tests for detoursChannel
hannahpurcell Dec 19, 2024
b954e54
tweak: build user differently to avoid test failure (from broadcast_d…
hannahpurcell Dec 19, 2024
090c05f
fix: Big fixes to useActiveDetoursByRoute to help channel joins / lea…
hannahpurcell Dec 19, 2024
014d5df
test: frontend tests for detour channels
hannahpurcell Dec 19, 2024
2c9023f
tweak: formatting / credo
hannahpurcell Dec 19, 2024
3456887
tweak: test simplification
hannahpurcell Dec 20, 2024
9914136
fix: spec typos fixed
hannahpurcell Dec 30, 2024
f7ee64c
tweak: Added hook for the recurrent pattern of loading one detour
hannahpurcell Dec 31, 2024
2736874
feat: New BSIcons for inbound/outbound symbol
hannahpurcell Jan 2, 2025
047a062
tweak: export DetoursMap interface
hannahpurcell Jan 2, 2025
592dec4
feat: Add skate detours to route ladder dropdown
hannahpurcell Jan 2, 2025
8acf707
fix: Channel name in broadcast for detours by route
hannahpurcell Jan 2, 2025
e269c7d
fix: need to parse backend data to frontend struct
hannahpurcell Jan 6, 2025
18605d4
fix: don't mix-match route name and id!
hannahpurcell Jan 6, 2025
071dcb2
fix: Tests needed updated with detour parsing, but also caught a bug
hannahpurcell Jan 6, 2025
01404bb
Merge branch 'hp/skate-detours-channel' into hp/hook-for-loading-sing…
hannahpurcell Jan 6, 2025
0e396ac
fix: Prevent double modals appearing
hannahpurcell Jan 6, 2025
8cf9e36
Merge branch 'hp/hook-for-loading-single-detour' into hp/detours-in-r…
hannahpurcell Jan 6, 2025
2fe6a69
fix: existing tests
hannahpurcell Jan 9, 2025
d34d285
tweak: formatting
hannahpurcell Jan 13, 2025
fcdcc31
test: new detourfactory + renaming old one
hannahpurcell Jan 13, 2025
82d50a1
test: add test for opening deatour from dropdown
hannahpurcell Jan 13, 2025
a8b9608
tweak: use mockReturnValue instead of mockImplementation
hannahpurcell Jan 13, 2025
d5d35ec
fix: test failing as a result of re-renders and how promises resolved
hannahpurcell Jan 15, 2025
e0907d5
test: remaining tests for populating detour dropdown and showing aler…
hannahpurcell Jan 15, 2025
f434be5
Merge branch 'main' into hp/detours-in-route-ladder-dropdown
hannahpurcell Jan 15, 2025
bbfd84d
fix: missed 1 test tweak after updating the text for the detour dropdown
hannahpurcell Jan 15, 2025
b381051
fix: use detourfactory in tests
hannahpurcell Jan 21, 2025
9d88297
Merge branch 'main' into hp/detours-in-route-ladder-dropdown
hannahpurcell Jan 21, 2025
f75ca52
Merge branch 'main' into hp/detours-in-route-ladder-dropdown
hannahpurcell Jan 21, 2025
054dbb1
tweak: include id when building detours
hannahpurcell Jan 22, 2025
ac49daa
tweak: formatting...
hannahpurcell Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions assets/css/_route_ladder.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,9 @@
padding-right: 0.5rem;
}

&.c-route-ladder__dropdown--non-skate-alert .dropdown-menu {
width: 20.5rem;
}

.dropdown-menu {
border-radius: 0.25rem;
max-width: 20.5rem;
min-width: 20.5rem;
hannahpurcell marked this conversation as resolved.
Show resolved Hide resolved
}

.dropdown-divider {
Expand Down
22 changes: 22 additions & 0 deletions assets/src/components/ladderPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { fullStoryEvent } from "../helpers/fullStory"
import { usePanelStateFromStateDispatchContext } from "../hooks/usePanelState"
import { DetourModal } from "./detours/detourModal"
import { Route } from "../schedule"
import { DetourId } from "../models/detoursList"
import { useLoadDetour } from "../hooks/useLoadDetour"

type DrawerContent = "route_picker" | "presets"

Expand Down Expand Up @@ -147,6 +149,8 @@ const LadderPage = (): ReactElement<HTMLDivElement> => {

const [showDetourModal, setShowDetourModal] = useState(false)
const [routeForDetour, setRouteForDetour] = useState<Route | null>(null)
const [detourId, setDetourId] = useState<DetourId | undefined>()
const detour = useLoadDetour(detourId)

return (
<div
Expand Down Expand Up @@ -226,6 +230,10 @@ const LadderPage = (): ReactElement<HTMLDivElement> => {
setRouteForDetour(route)
setShowDetourModal(true)
}}
onOpenDetour={(detourId) => {
setDetourId(detourId)
setShowDetourModal(true)
}}
/>
</div>
{routeForDetour && (
Expand All @@ -234,7 +242,21 @@ const LadderPage = (): ReactElement<HTMLDivElement> => {
show={showDetourModal}
onClose={() => {
setShowDetourModal(false)
setRouteForDetour(null)
}}
/>
)}
{showDetourModal && detour && (
<DetourModal
onClose={() => {
setShowDetourModal(false)
setDetourId(undefined)
}}
show
key={detourId ?? ""}
snapshot={detour.state}
author={detour.author}
updatedAt={detour.updatedAt}
/>
)}
</div>
Expand Down
65 changes: 52 additions & 13 deletions assets/src/components/routeLadder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,17 @@ import Tippy from "@tippyjs/react"
import { tagManagerEvent } from "../helpers/googleTagManager"
import inTestGroup, { TestGroups } from "../userInTestGroup"
import {
ArrowDownLeftSquare,
ArrowUpRightSquare,
ExclamationTriangleFill,
PlusSquare,
ThreeDotsVertical,
} from "../helpers/bsIcons"
import { RoutePill } from "./routePill"
import { Card, CloseButton, Dropdown } from "react-bootstrap"
import { joinClasses, joinTruthy } from "../helpers/dom"
import { DetourId, SimpleDetour } from "../models/detoursList"
import { DetoursMap } from "../hooks/useDetours"

interface Props {
route: Route
Expand All @@ -44,23 +48,26 @@ interface Props {
ladderCrowdingToggles: LadderCrowdingToggles
hasAlert: boolean
onAddDetour?: (route: Route) => void
onOpenDetour?: (detourId: DetourId) => void
skateDetoursForRoute?: DetoursMap
}

export const Header = ({
routeName,
onClose,
hasAlert,
showDropdown,

onClickAddDetour,
onOpenDetour,
skateDetoursForRoute,
}: {
routeName: string
onClose: () => void
hasAlert: boolean

showDropdown: boolean

onClickAddDetour?: () => void
onOpenDetour?: (detourId: DetourId) => void
skateDetoursForRoute?: DetoursMap
}) => {
const routePillId = "route-pill" + useId()
const routeOptionsToggleId = "route-options-toggle" + useId()
Expand Down Expand Up @@ -100,20 +107,48 @@ export const Header = ({
>
<PlusSquare /> Add detour
</Dropdown.Item>
<Dropdown.Divider className="border-top-0" />
<Dropdown.Header>
<div className="c-route-ladder__dropdown-header-text">
Active detours
</div>
</Dropdown.Header>
{hasAlert && (
<>
<Dropdown.Divider className="border-top-0" />
<Dropdown.Header>
<div className="c-route-ladder__dropdown-header-text">
Active detours
</div>
</Dropdown.Header>
<Dropdown.ItemText className="lh-base pb-4">
This route has an active detour. View detour details on{" "}
<a href="https://www.mbta.com/">mbta.com</a> or in IRIS.
</Dropdown.ItemText>
{skateDetoursForRoute &&
Object.values(skateDetoursForRoute).map(
(detour: SimpleDetour) => (
<Dropdown.Item
key={detour.id}
className="icon-link"
onClick={() => onOpenDetour?.(detour.id)}
>
{detour.direction === "Outbound" ? (
<ArrowDownLeftSquare />
) : (
<ArrowUpRightSquare />
)}
<div>
{detour.route} {detour.direction} -{" "}
{detour.intersection}
</div>
</Dropdown.Item>
)
)}
{(!skateDetoursForRoute ||
Object.values(skateDetoursForRoute).length == 0) && (
<Dropdown.ItemText className="lh-base pb-4">
This route has an active detour. View detour details on{" "}
<a href="https://www.mbta.com/">mbta.com</a> or in IRIS.
</Dropdown.ItemText>
)}
</>
)}
{!hasAlert && (
<Dropdown.ItemText className="lh-base pb-4">
No active detours
</Dropdown.ItemText>
)}
</Dropdown.Menu>
</Dropdown>
)}
Expand Down Expand Up @@ -223,6 +258,8 @@ const RouteLadder = ({
ladderCrowdingToggles,
hasAlert,
onAddDetour,
onOpenDetour,
skateDetoursForRoute,
}: Props) => {
const ladderDirection = getLadderDirectionForRoute(ladderDirections, route.id)

Expand Down Expand Up @@ -258,6 +295,8 @@ const RouteLadder = ({
onClickAddDetour={() => {
onAddDetour?.(route)
}}
onOpenDetour={onOpenDetour}
skateDetoursForRoute={skateDetoursForRoute}
/>
<Controls
displayCrowdingToggleIcon={displayCrowding}
Expand Down
13 changes: 13 additions & 0 deletions assets/src/components/routeLadders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import RoutesContext from "../contexts/routesContext"
import useTimepoints from "../hooks/useTimepoints"
import { SocketContext } from "../contexts/socketContext"
import useAlerts from "../hooks/useAlerts"
import { useActiveDetoursByRoute } from "../hooks/useDetours"
import { DetourId } from "../models/detoursList"

export const findRouteById = (
routes: Route[] | null,
Expand All @@ -24,6 +26,7 @@ interface Props {
ladderDirections: LadderDirections
ladderCrowdingToggles: LadderCrowdingToggles
onAddDetour?: (route: Route) => void
onOpenDetour?: (detourId: DetourId) => void
}

const RouteLadders = ({
Expand All @@ -35,6 +38,7 @@ const RouteLadders = ({
ladderDirections,
ladderCrowdingToggles,
onAddDetour,
onOpenDetour,
}: Props) => {
const vehiclesByRouteId: ByRouteId<(VehicleInScheduledService | Ghost)[]> =
useContext(VehiclesByRouteIdContext)
Expand All @@ -59,11 +63,18 @@ const RouteLadders = ({
const alerts = useAlerts(socket, selectedRouteIds)
const routesWithAlerts: RouteId[] = []

const skateDetours = useActiveDetoursByRoute(socket, selectedRouteIds)

for (const routeId in alerts) {
if (alerts[routeId].length > 0) {
routesWithAlerts.push(routeId)
}
}
for (const routeId in skateDetours) {
if (Object.keys(skateDetours[routeId]).length > 0) {
routesWithAlerts.push(routeId)
}
}

return (
<div
Expand All @@ -86,6 +97,8 @@ const RouteLadders = ({
ladderCrowdingToggles={ladderCrowdingToggles}
hasAlert={routesWithAlerts.includes(route.id)}
onAddDetour={onAddDetour}
onOpenDetour={onOpenDetour}
skateDetoursForRoute={skateDetours[route.id]}
/>
))}
</div>
Expand Down
42 changes: 42 additions & 0 deletions assets/src/helpers/bsIcons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,27 @@ export const ArrowClockwise = (props: SvgProps) => (
</svg>
)

/**
* @returns https://icons.getbootstrap.com/icons/arrow-down-left-square/
*/
export const ArrowDownLeftSquare = (props: SvgProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
className="bi bi-arrow-up-right-square"
viewBox="0 0 16 16"
aria-hidden
{...props}
>
<path
fillRule="evenodd"
d="M15 2a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1zM0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm5.854 8.803a.5.5 0 1 1-.708-.707L9.243 6H6.475a.5.5 0 1 1 0-1h3.975a.5.5 0 0 1 .5.5v3.975a.5.5 0 1 1-1 0V6.707z"
/>
</svg>
)

/**
* @returns https://icons.getbootstrap.com/icons/arrow-left-square/
*/
Expand Down Expand Up @@ -80,6 +101,27 @@ export const ArrowLeft = (props: SvgProps) => (
</svg>
)

/**
* @returns https://icons.getbootstrap.com/icons/arrow-up-right-square/
*/
export const ArrowUpRightSquare = (props: SvgProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
className="bi bi-arrow-up-right-square"
viewBox="0 0 16 16"
aria-hidden
{...props}
>
<path
fillRule="evenodd"
d="M15 2a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1zM0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm5.854 8.803a.5.5 0 1 1-.708-.707L9.243 6H6.475a.5.5 0 1 1 0-1h3.975a.5.5 0 0 1 .5.5v3.975a.5.5 0 1 1-1 0V6.707z"
/>
</svg>
)

/**
* @returns https://icons.getbootstrap.com/icons/box-arrow-right/
*/
Expand Down
2 changes: 1 addition & 1 deletion assets/src/hooks/useDetours.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ByRouteId, RouteId } from "../schedule"
import { equalByElements } from "../helpers/array"
import { array, create } from "superstruct"

interface DetoursMap {
export interface DetoursMap {
[key: number]: SimpleDetour
}

Expand Down
7 changes: 7 additions & 0 deletions assets/tests/components/app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { OpenView, PagePath } from "../../src/state/pagePanelState"
import { viewFactory } from "../factories/pagePanelStateFactory"
import userEvent from "@testing-library/user-event"
import { mockUsePanelState } from "../testHelpers/usePanelStateMocks"
import { useLoadDetour } from "../../src/hooks/useLoadDetour"

// Avoid Halloween
jest
Expand All @@ -48,9 +49,15 @@ jest.mock("userTestGroups", () => ({
}))

jest.mock("../../src/hooks/usePanelState")
jest.mock("../../src/hooks/useLoadDetour")

beforeEach(() => {
mockUsePanelState()
// we _should_ mock the promise, but this file already are mocking hooks here,
// and there are a lot of tests, so mocking the hook here too for now
jest.mocked(useLoadDetour).mockReturnValue(undefined)

// Also, `useVehicles` should be mocked in beforeEach. To be done in future PR
})

describe("App", () => {
Expand Down
18 changes: 14 additions & 4 deletions assets/tests/components/appStateWrapper.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { test, expect, jest } from "@jest/globals"
import { test, expect, jest, beforeEach } from "@jest/globals"
import React from "react"
import { render } from "@testing-library/react"
import { render, waitFor } from "@testing-library/react"
import AppStateWrapper from "../../src/components/appStateWrapper"
import { fetchDetour, fetchRoutes, putRouteTabs } from "../../src/api"
import { neverPromise } from "../testHelpers/mockHelpers"

// Avoid Halloween
jest
Expand All @@ -13,7 +15,15 @@ jest.mock("userTestGroups", () => ({
default: jest.fn(() => []),
}))

test("renders", () => {
jest.mock("../../src/api")

beforeEach(() => {
jest.mocked(fetchRoutes).mockReturnValue(neverPromise())
jest.mocked(putRouteTabs).mockReturnValue(neverPromise())
jest.mocked(fetchDetour).mockReturnValue(neverPromise())
})

test("renders", async () => {
const result = render(<AppStateWrapper />)
expect(result.asFragment()).toMatchSnapshot()
await waitFor(() => expect(result.asFragment()).toMatchSnapshot())
})
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { mockScreenSize, neverPromise } from "../../testHelpers/mockHelpers"
import getTestGroups from "../../../src/userTestGroups"
import { detourListFactory } from "../../factories/detourListFactory"
import { TestGroups } from "../../../src/userInTestGroup"
import { detourStateMachineFactory } from "../../factories/detourStateMachineFactory"
import { detourInProgressFactory } from "../../factories/detourStateMachineFactory"
import { viewDraftDetourHeading } from "../../testHelpers/selectors/components/detours/diversionPage"

jest
Expand Down Expand Up @@ -48,7 +48,7 @@ describe("Detours Page: Open a Detour", () => {
// even if it doesn't match the detour clicked
jest
.mocked(fetchDetour)
.mockResolvedValue(Ok(detourStateMachineFactory.build()))
.mockResolvedValue(Ok(detourInProgressFactory.build()))

const { baseElement } = render(<DetourListPage />)

Expand All @@ -69,7 +69,7 @@ describe("Detours Page: Open a Detour", () => {

jest
.mocked(fetchDetour)
.mockResolvedValue(Ok(detourStateMachineFactory.build()))
.mockResolvedValue(Ok(detourInProgressFactory.build()))

const { baseElement } = render(<DetourListPage />)

Expand All @@ -85,7 +85,7 @@ describe("Detours Page: Open a Detour", () => {

jest
.mocked(fetchDetour)
.mockResolvedValue(Ok(detourStateMachineFactory.build()))
.mockResolvedValue(Ok(detourInProgressFactory.build()))

render(<DetourListPage />)

Expand Down
Loading
Loading