From 181a73285f29d71c7a226b5e9e26840a50dd1d41 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Tue, 22 Oct 2024 08:40:16 -0400 Subject: [PATCH] feat(ts/components/vehicleMarker): implement shuttle colors (#2860) * fix(ts/components/vehicleMarker): move vehicle marker classes onto svg Right now if you were to pull out the icons into their own components, they wouldn't render correctly, so this fixes that the styling doesn't care if it's in a leaflet map or not. * fix(ts/components/vehicleMarker): replace color classes with css var * feat(ts/models/shuttle): create enum `ShuttleVariant` * feat(ts/models/shuttle): create `RunId` to `ShuttleVariant` "mapper" The `RunId` => `ShuttleVariant` relationship is "many to one", which makes this better suited for a function which returns the enum value from the `RunId`. --- assets/css/map/markers/_vehicle_marker.scss | 82 ++++++++++--------- .../components/map/markers/vehicleMarker.tsx | 63 ++++++++++++-- assets/src/models/shuttle.ts | 35 +++++++- .../__snapshots__/mapPage.test.tsx.snap | 12 ++- .../shuttleMapPage.test.tsx.snap | 24 ++++-- .../map/markers/vehicleMarker.test.tsx | 73 ++++++++++++++++- assets/tests/components/mapPage.test.tsx | 2 +- .../components/mapPage/mapDisplay.test.tsx | 2 +- 8 files changed, 231 insertions(+), 62 deletions(-) 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")