diff --git a/assets/css/map/markers/_vehicle_marker.scss b/assets/css/map/markers/_vehicle_marker.scss index 596c81561..6557c80ee 100644 --- a/assets/css/map/markers/_vehicle_marker.scss +++ b/assets/css/map/markers/_vehicle_marker.scss @@ -1,53 +1,61 @@ .c-vehicle-map__icon { - path { - fill: $color-primary-legacy; - stroke: $color-bg-base; - stroke-width: 2; + // Vehicle doesn't fit in marker as is. + overflow: visible; - &.on-time { - fill: $color-vehicle-ontime; - } + fill: $color-primary-legacy; + stroke: $color-bg-base; + stroke-width: 2; - &.early.early-red { - fill: $color-vehicle-red; - } - &.early.early-blue { - fill: $color-vehicle-blue; - } + .on-time { + fill: $color-vehicle-ontime; + } - &.late.early-red { - fill: $color-vehicle-blue; - } - &.late.early-blue { - fill: $color-vehicle-red; - } + .early { + fill: var(--color-early); + } - &.off-course { - fill: $color-vehicle-off-course; - } + .late { + fill: var(--color-late); + } - &.logged-out { - fill: $color-gray-300; - stroke: $color-gray-600; - stroke-width: 1px; - } + .off-course { + fill: $color-vehicle-off-course; + } - &.selected { - stroke: $color-eggplant-600; - stroke-width: 1.5; - filter: drop-shadow(1px 1px 4px $color-eggplant-400); - } + .logged-out { + fill: $color-gray-300; + stroke: $color-gray-600; + stroke-width: 1px; } - svg { - overflow: visible; + .selected { + stroke: $color-eggplant-600; + stroke-width: 1.5; + filter: drop-shadow(1px 1px 4px $color-eggplant-400); + } + + &.c-vehicle-marker--shuttle { + // Shuttle Colors + &.c-vehicle-marker--blue { + fill: var(--color-blue-line); + } + &.c-vehicle-marker--orange { + fill: var(--color-orange-line); + } + &.c-vehicle-marker--green { + fill: var(--color-green-line); + } + &.c-vehicle-marker--red { + fill: var(--color-red-line); + } + &.c-vehicle-marker--cr { + fill: var(--color-cr-line); + } } } .c-vehicle-map__label { - svg { - overflow: visible; - } + overflow: visible; &.primary { font-size: var(--font-size-s); diff --git a/assets/src/components/map/markers/vehicleMarker.tsx b/assets/src/components/map/markers/vehicleMarker.tsx index 9c48128a5..20efe3612 100644 --- a/assets/src/components/map/markers/vehicleMarker.tsx +++ b/assets/src/components/map/markers/vehicleMarker.tsx @@ -2,16 +2,19 @@ import { LatLngExpression, Marker } from "leaflet" import React, { PropsWithChildren, useContext, + useEffect, useRef, useState, - useEffect, } from "react" import { StateDispatchContext } from "../../../contexts/stateDispatchContext" import { joinClasses } from "../../../helpers/dom" import { vehicleLabel } from "../../../helpers/vehicleLabel" -import { statusClasses, drawnStatus } from "../../../models/vehicleStatus" - +import { + shuttleVariantFromRunId, + ShuttleVariant, +} from "../../../models/shuttle" +import { drawnStatus, statusClasses } from "../../../models/vehicleStatus" import { Vehicle } from "../../../realtime" import { ReactMarker } from "../utilities/reactMarker" @@ -24,6 +27,41 @@ interface VehicleMarkerProps extends PropsWithChildren { onShouldShowPopupChange?: (newValue: boolean) => void } +/** + * If the supplied {@linkcode vehicle} is a shuttle, returns + * classes to more specifically style shuttles matching certain conditions. + * For example, specific styles depending on Rapid Transit Line the shuttle is + * associated with. + * + * @param vehicle The vehicle to return styles for + * @returns Array of classes to add to a vehicle marker + */ +const stylesForShuttle = (vehicle: Vehicle) => { + // If this vehicle isn't a shuttle, return no styles + if (vehicle.isShuttle === false) { + return [] + } + + // Otherwise return a generic shuttle class and any more + // specific styles for the shuttle. + const classFor = (variant: string) => `c-vehicle-marker--${variant}` + const shuttleClasses = ["c-vehicle-marker--shuttle"] + switch (vehicle.runId && shuttleVariantFromRunId(vehicle.runId)) { + case ShuttleVariant.Blue: + return shuttleClasses.concat(classFor("blue")) + case ShuttleVariant.CommuterRail: + return shuttleClasses.concat(classFor("cr")) + case ShuttleVariant.Green: + return shuttleClasses.concat(classFor("green")) + case ShuttleVariant.Orange: + return shuttleClasses.concat(classFor("orange")) + case ShuttleVariant.Red: + return shuttleClasses.concat(classFor("red")) + default: + return shuttleClasses + } +} + export const VehicleMarker = ({ children, vehicle, @@ -90,10 +128,15 @@ export const VehicleMarker = ({ ref={markerRef} divIconSettings={{ iconAnchor: [0, 0], - className: "c-vehicle-map__icon", + // Disable default leaflet marker class + className: "", }} icon={ { + switch (runId) { + case "999-0501": + return ShuttleVariant.Blue + case "999-0502": + return ShuttleVariant.Green + case "999-0503": + return ShuttleVariant.Orange + case "999-0504": + return ShuttleVariant.Red + case "999-0505": + return ShuttleVariant.CommuterRail + case "999-0555": + return ShuttleVariant.Special + default: + return null + } +} export const formattedRunNumber = ({ runId }: Vehicle): string => { if (runId === null) { diff --git a/assets/tests/components/__snapshots__/mapPage.test.tsx.snap b/assets/tests/components/__snapshots__/mapPage.test.tsx.snap index aec1bb37f..77c849c7b 100644 --- a/assets/tests/components/__snapshots__/mapPage.test.tsx.snap +++ b/assets/tests/components/__snapshots__/mapPage.test.tsx.snap @@ -1739,12 +1739,13 @@ exports[` Snapshot renders vehicle data 1`] = `
Snapshot renders vehicle data 1`] = `
Snapshot renders vehicle data 1`] = `
Snapshot renders vehicle data 1`] = `
( @@ -38,4 +38,75 @@ describe("VehicleMarker", () => { expect(container.querySelector(".c-vehicle-map__icon")).toBeInTheDocument() expect(screen.getByText("101")).toBeInTheDocument() }) + + describe.each([ + { + forRunId: "999-0501", + assertSpecialClass: ".c-vehicle-marker--blue", + }, + { + forRunId: "999-0502", + assertSpecialClass: ".c-vehicle-marker--green", + }, + { + forRunId: "999-0503", + assertSpecialClass: ".c-vehicle-marker--orange", + }, + { + forRunId: "999-0504", + assertSpecialClass: ".c-vehicle-marker--red", + }, + { + forRunId: "999-0505", + assertSpecialClass: ".c-vehicle-marker--cr", + }, + { + forRunId: "999-0555", + assertSpecialClass: null, + }, + { + forRunId: "101", + assertSpecialClass: null, + }, + ])( + "when vehicle is shuttle with runId:`$forRunId`", + ({ forRunId, assertSpecialClass }) => { + test("should render with shuttle class", () => { + const { container } = render( + , + { + wrapper: TestMap, + } + ) + + expect( + container.querySelector(".c-vehicle-marker--shuttle") + ).toBeInTheDocument() + }) + + assertSpecialClass !== null && + test(`should render with class \`${assertSpecialClass}\``, () => { + const { container } = render( + , + { + wrapper: TestMap, + } + ) + + expect( + container.querySelector(assertSpecialClass) + ).toBeInTheDocument() + }) + } + ) }) diff --git a/assets/tests/components/mapPage.test.tsx b/assets/tests/components/mapPage.test.tsx index 62b54ce4e..fdf23c09b 100644 --- a/assets/tests/components/mapPage.test.tsx +++ b/assets/tests/components/mapPage.test.tsx @@ -1587,7 +1587,7 @@ describe("", () => { }) expect(selectedVehicleLabel).toBeInTheDocument() // this should be expressed via some accessibility property - expect(selectedVehicleLabel).toHaveClass("selected") + expect(selectedVehicleLabel.querySelector(".selected")).toBeVisible() expect( container.querySelectorAll(".c-vehicle-map__stop") diff --git a/assets/tests/components/mapPage/mapDisplay.test.tsx b/assets/tests/components/mapPage/mapDisplay.test.tsx index 28a1978c8..83510235a 100644 --- a/assets/tests/components/mapPage/mapDisplay.test.tsx +++ b/assets/tests/components/mapPage/mapDisplay.test.tsx @@ -624,7 +624,7 @@ describe("", () => { }) expect(selectedVehicleLabel).toBeInTheDocument() // this should be expressed via some accessibility property - expect(selectedVehicleLabel).toHaveClass("selected") + expect(selectedVehicleLabel.querySelector(".selected")).toBeVisible() expect( container.querySelectorAll(".c-vehicle-map__stop")