From 33fc966e9431d50419f42598ae7d6c88b88b5944 Mon Sep 17 00:00:00 2001 From: Eddie Maldonado Date: Wed, 3 Jan 2024 10:29:38 -0500 Subject: [PATCH] Detour pilot: basic detour route drawing (#2334) * refactor: remove redundant fragment * feat: dummy detour page with ability to select starting point * feat: allow adding points to the detour and finishing the detour * fixup! feat: allow adding points to the detour and finishing the detour * refactor: move some state up into DetourMap component * fix: lock in endpoint once selected * feat: add ability to remove most recent waypoint * feat: style shapes and start / end markers * feat: marker for intermediate points on detour * fix: titles for start and end markers * test: basic unit tests for route shape drawing * fix: fix CSS class names * fixup! fix: fix CSS class names * test: more unit tests for detour drawing * refactor: change props to take shape, remove dummy page * fixup! refactor: change props to take shape, remove dummy page * feat: place route drawing component in diversions page * fix: remove last remnants of dummy detours page * fix: UI details better match route shape in story Co-authored-by: Josh Larson * refactor: rename `shape` prop to `originalShape` * fix: prevent duplicate positions on detour shape --------- Co-authored-by: Josh Larson --- assets/css/app.scss | 1 + assets/css/bootstrap.scss | 2 +- assets/css/detours/_detour_map.scss | 17 + assets/src/components/detours/detourMap.tsx | 204 ++++ .../src/components/detours/diversionPage.tsx | 12 +- assets/src/components/mapPage/mapDisplay.tsx | 54 +- .../skate-components/__story-data__/shape.ts | 995 ++++++++++++++++++ .../detours/diversionPage.stories.tsx | 3 + .../components/detours/detourMap.test.tsx | 106 ++ 9 files changed, 1358 insertions(+), 36 deletions(-) create mode 100644 assets/css/detours/_detour_map.scss create mode 100644 assets/src/components/detours/detourMap.tsx create mode 100644 assets/stories/skate-components/__story-data__/shape.ts create mode 100644 assets/tests/components/detours/detourMap.test.tsx diff --git a/assets/css/app.scss b/assets/css/app.scss index e93489354..ff6e9678a 100644 --- a/assets/css/app.scss +++ b/assets/css/app.scss @@ -47,6 +47,7 @@ $z-properties-panel-context: ( @import "crowding"; @import "cutout_overlay"; @import "data_status_banner"; +@import "detours/detour_map"; @import "directions_button"; @import "disconnected_modal"; @import "diversion_page"; diff --git a/assets/css/bootstrap.scss b/assets/css/bootstrap.scss index cc5093215..1275e9fea 100644 --- a/assets/css/bootstrap.scss +++ b/assets/css/bootstrap.scss @@ -28,7 +28,7 @@ // @import "../node_modules/bootstrap/scss/badge"; // @import "../node_modules/bootstrap/scss/breadcrumb"; // @import "../node_modules/bootstrap/scss/button-group"; -// @import "../node_modules/bootstrap/scss/buttons"; +@import "../node_modules/bootstrap/scss/buttons"; // @import "../node_modules/bootstrap/scss/card"; // @import "../node_modules/bootstrap/scss/carousel"; // @import "../node_modules/bootstrap/scss/close"; diff --git a/assets/css/detours/_detour_map.scss b/assets/css/detours/_detour_map.scss new file mode 100644 index 000000000..221bdbbe5 --- /dev/null +++ b/assets/css/detours/_detour_map.scss @@ -0,0 +1,17 @@ +.c-detour_map--original-route-shape { + stroke: $color-kiwi-500; +} + +.c-detour_map--detour-route-shape { + stroke: $color-lemon-500; +} + +.c-detour_map-circle-marker--start { + stroke: $color-kiwi-400; + fill: $color-kiwi-400; +} + +.c-detour_map-circle-marker--end { + stroke: $color-strawberry-400; + fill: $color-strawberry-400; +} diff --git a/assets/src/components/detours/detourMap.tsx b/assets/src/components/detours/detourMap.tsx new file mode 100644 index 000000000..b90df4de4 --- /dev/null +++ b/assets/src/components/detours/detourMap.tsx @@ -0,0 +1,204 @@ +import React, { useState } from "react" +import { Shape } from "../../schedule" +import { LatLngExpression } from "leaflet" +import { Polyline, useMap, useMapEvent } from "react-leaflet" +import Leaflet, { Map as LeafletMap } from "leaflet" +import Map from "../map" +import { CustomControl } from "../map/controls/customControl" +import { Button } from "react-bootstrap" +import { ReactMarker } from "../map/utilities/reactMarker" + +export const DetourMap = ({ shape }: { shape: Shape }) => { + const [startPoint, setStartPoint] = useState(null) + const [endPoint, setEndPoint] = useState(null) + const [detourPositions, setDetourPositions] = useState([]) + + const onAddDetourPosition = (p: LatLngExpression) => + setDetourPositions((positions) => [...positions, p]) + + return ( + + + + + + + ) +} + +const RouteShapeWithDetour = ({ + originalShape, + startPoint, + onSetStartPoint, + endPoint, + onSetEndPoint, + detourPositions, + onAddDetourPosition, +}: { + originalShape: Shape + startPoint: LatLngExpression | null + onSetStartPoint: (p: LatLngExpression | null) => void + endPoint: LatLngExpression | null + onSetEndPoint: (p: LatLngExpression | null) => void + detourPositions: LatLngExpression[] + onAddDetourPosition: (p: LatLngExpression) => void +}) => { + const routeShapePositions: LatLngExpression[] = originalShape.points.map( + (point) => [point.lat, point.lon] + ) + + const map = useMap() + + useMapEvent("click", (e) => { + if (startPoint !== null && endPoint === null) { + onAddDetourPosition(e.latlng) + } + }) + + // points on the detour not already represented by the start and end + const uniqueDetourPositions = + detourPositions.length === 0 + ? [] + : endPoint === null + ? detourPositions.slice(1) + : detourPositions.slice(1, -2) + + return ( + <> + { + if (startPoint === null) { + const position = closestPosition( + routeShapePositions, + e.latlng, + map + ) + onSetStartPoint(position) + position && onAddDetourPosition(position) + } else if (endPoint === null) { + const position = closestPosition( + routeShapePositions, + e.latlng, + map + ) + onSetEndPoint(position) + position && onAddDetourPosition(position) + } + }, + }} + bubblingMouseEvents={false} + /> + {startPoint && } + {endPoint && } + + {uniqueDetourPositions.map((position) => ( + + ))} + + ) +} + +const StartMarker = ({ position }: { position: LatLngExpression }) => ( + +) + +const EndMarker = ({ position }: { position: LatLngExpression }) => ( + +) + +const StartOrEndMarker = ({ + classSuffix, + title, + position, +}: { + classSuffix: string + title: string + position: LatLngExpression +}) => ( + + + + } + /> +) + +const DetourPointMarker = ({ position }: { position: LatLngExpression }) => ( + + + + } + /> +) + +const closestPosition = ( + positions: LatLngExpression[], + position: LatLngExpression, + map: LeafletMap +): LatLngExpression | null => { + const [closestPosition] = positions.reduce< + [LatLngExpression | null, number | null] + >( + ([closestPosition, closestDistance], currentPosition) => { + const distance = map.distance(position, currentPosition) + if (closestDistance === null || distance < closestDistance) { + return [position, distance] + } else { + return [closestPosition, closestDistance] + } + }, + [null, null] + ) + + return closestPosition +} diff --git a/assets/src/components/detours/diversionPage.tsx b/assets/src/components/detours/diversionPage.tsx index 507bbfd17..6aaee2f26 100644 --- a/assets/src/components/detours/diversionPage.tsx +++ b/assets/src/components/detours/diversionPage.tsx @@ -1,6 +1,7 @@ import React from "react" import { DiversionPanel, DiversionPanelProps } from "./diversionPanel" -import MapDisplay from "../mapPage/mapDisplay" +import { DetourMap } from "./detourMap" +import { Shape } from "../../schedule" export const DiversionPage = ({ directions, @@ -9,7 +10,8 @@ export const DiversionPage = ({ routeDescription, routeDirection, routeOrigin, -}: DiversionPanelProps) => ( + shape, +}: DiversionPanelProps & { shape: Shape }) => (

Create Detour

@@ -25,11 +27,7 @@ export const DiversionPage = ({ />
- {}} - fetchedSelectedLocation={null} - /> +
) diff --git a/assets/src/components/mapPage/mapDisplay.tsx b/assets/src/components/mapPage/mapDisplay.tsx index fd5e1c8fc..e246e9b25 100644 --- a/assets/src/components/mapPage/mapDisplay.tsx +++ b/assets/src/components/mapPage/mapDisplay.tsx @@ -200,35 +200,33 @@ const RoutePatternLayers = ({ isSelected: boolean }): JSX.Element => { return routePattern.shape ? ( - <> - - {(zoomLevel) => { - return ( - <> - {routePattern.shape && ( - <> - + {(zoomLevel) => { + return ( + <> + {routePattern.shape && ( + <> + + + - - - - - )} - - ) - }} - - + + + )} + + ) + }} + ) : ( <> ) diff --git a/assets/stories/skate-components/__story-data__/shape.ts b/assets/stories/skate-components/__story-data__/shape.ts new file mode 100644 index 000000000..77d371675 --- /dev/null +++ b/assets/stories/skate-components/__story-data__/shape.ts @@ -0,0 +1,995 @@ +import { Shape } from "../../../src/schedule" + +export const route39shape: Shape = { + id: "390152", + points: [ + { + lat: 42.347455, + lon: -71.074539, + }, + { + lat: 42.347419, + lon: -71.074522, + }, + { + lat: 42.347371, + lon: -71.074401, + }, + { + lat: 42.347386, + lon: -71.074115, + }, + { + lat: 42.347459, + lon: -71.074015, + }, + { + lat: 42.34757, + lon: -71.073921, + }, + { + lat: 42.347594, + lon: -71.073685, + }, + { + lat: 42.347471, + lon: -71.073635, + }, + { + lat: 42.347248, + lon: -71.073532, + }, + { + lat: 42.347945, + lon: -71.072659, + }, + { + lat: 42.348637, + lon: -71.071817, + }, + { + lat: 42.349008, + lon: -71.072001, + }, + { + lat: 42.349519, + lon: -71.072247, + }, + { + lat: 42.350396, + lon: -71.072661, + }, + { + lat: 42.349889, + lon: -71.074627, + }, + { + lat: 42.349889, + lon: -71.074627, + }, + { + lat: 42.349846, + lon: -71.074793, + }, + { + lat: 42.349598, + lon: -71.075875, + }, + { + lat: 42.349465, + lon: -71.076415, + }, + { + lat: 42.349412, + lon: -71.076586, + }, + { + lat: 42.349412, + lon: -71.076586, + }, + { + lat: 42.349381, + lon: -71.076685, + }, + { + lat: 42.349324, + lon: -71.076825, + }, + { + lat: 42.349266, + lon: -71.076934, + }, + { + lat: 42.349149, + lon: -71.077221, + }, + { + lat: 42.349031, + lon: -71.077432, + }, + { + lat: 42.348464, + lon: -71.078098, + }, + { + lat: 42.348254, + lon: -71.078355, + }, + { + lat: 42.347899, + lon: -71.078748, + }, + { + lat: 42.347047, + lon: -71.079717, + }, + { + lat: 42.346268, + lon: -71.08068, + }, + { + lat: 42.345927, + lon: -71.081084, + }, + { + lat: 42.345532, + lon: -71.08165, + }, + { + lat: 42.345399, + lon: -71.081822, + }, + { + lat: 42.345287, + lon: -71.081959, + }, + { + lat: 42.345287, + lon: -71.081959, + }, + { + lat: 42.344448, + lon: -71.082982, + }, + { + lat: 42.344291, + lon: -71.083165, + }, + { + lat: 42.343813, + lon: -71.083804, + }, + { + lat: 42.342811, + lon: -71.085047, + }, + { + lat: 42.341916, + lon: -71.086092, + }, + { + lat: 42.34146, + lon: -71.086615, + }, + { + lat: 42.341389, + lon: -71.086724, + }, + { + lat: 42.341389, + lon: -71.086724, + }, + { + lat: 42.341219, + lon: -71.086982, + }, + { + lat: 42.341141, + lon: -71.087149, + }, + { + lat: 42.340701, + lon: -71.088278, + }, + { + lat: 42.340481, + lon: -71.088859, + }, + { + lat: 42.340481, + lon: -71.088859, + }, + { + lat: 42.340441, + lon: -71.088967, + }, + { + lat: 42.339769, + lon: -71.090647, + }, + { + lat: 42.339595, + lon: -71.091027, + }, + { + lat: 42.339443, + lon: -71.091377, + }, + { + lat: 42.339153, + lon: -71.092122, + }, + { + lat: 42.339153, + lon: -71.092122, + }, + { + lat: 42.339126, + lon: -71.092193, + }, + { + lat: 42.339124, + lon: -71.092234, + }, + { + lat: 42.339019, + lon: -71.092463, + }, + { + lat: 42.338707, + lon: -71.093272, + }, + { + lat: 42.338104, + lon: -71.094754, + }, + { + lat: 42.338074, + lon: -71.094822, + }, + { + lat: 42.337714, + lon: -71.095747, + }, + { + lat: 42.337616, + lon: -71.095998, + }, + { + lat: 42.337616, + lon: -71.095998, + }, + { + lat: 42.337246, + lon: -71.096949, + }, + { + lat: 42.336769, + lon: -71.098135, + }, + { + lat: 42.336349, + lon: -71.099252, + }, + { + lat: 42.336241, + lon: -71.099565, + }, + { + lat: 42.336001, + lon: -71.100136, + }, + { + lat: 42.336001, + lon: -71.100136, + }, + { + lat: 42.33592, + lon: -71.100326, + }, + { + lat: 42.335759, + lon: -71.100684, + }, + { + lat: 42.335628, + lon: -71.100996, + }, + { + lat: 42.335511, + lon: -71.101359, + }, + { + lat: 42.335135, + lon: -71.102428, + }, + { + lat: 42.335049, + lon: -71.102679, + }, + { + lat: 42.335049, + lon: -71.102679, + }, + { + lat: 42.334913, + lon: -71.103077, + }, + { + lat: 42.334642, + lon: -71.103776, + }, + { + lat: 42.334372, + lon: -71.104454, + }, + { + lat: 42.334177, + lon: -71.104912, + }, + { + lat: 42.333972, + lon: -71.105316, + }, + { + lat: 42.333763, + lon: -71.105618, + }, + { + lat: 42.333617, + lon: -71.105882, + }, + { + lat: 42.333617, + lon: -71.105882, + }, + { + lat: 42.333474, + lon: -71.106141, + }, + { + lat: 42.333383, + lon: -71.106334, + }, + { + lat: 42.333328, + lon: -71.10647, + }, + { + lat: 42.33328, + lon: -71.106635, + }, + { + lat: 42.333257, + lon: -71.106791, + }, + { + lat: 42.333251, + lon: -71.107056, + }, + { + lat: 42.333242, + lon: -71.107884, + }, + { + lat: 42.333251, + lon: -71.108617, + }, + { + lat: 42.333249, + lon: -71.108757, + }, + { + lat: 42.333233, + lon: -71.10903, + }, + { + lat: 42.333216, + lon: -71.109265, + }, + { + lat: 42.333216, + lon: -71.109265, + }, + { + lat: 42.333209, + lon: -71.109363, + }, + { + lat: 42.333185, + lon: -71.109592, + }, + { + lat: 42.333146, + lon: -71.10987, + }, + { + lat: 42.333083, + lon: -71.1101, + }, + { + lat: 42.33303, + lon: -71.110241, + }, + { + lat: 42.332945, + lon: -71.110418, + }, + { + lat: 42.332743, + lon: -71.110807, + }, + { + lat: 42.33264, + lon: -71.110982, + }, + { + lat: 42.332495, + lon: -71.111212, + }, + { + lat: 42.33207, + lon: -71.111836, + }, + { + lat: 42.332011, + lon: -71.111945, + }, + { + lat: 42.331964, + lon: -71.112046, + }, + { + lat: 42.331817, + lon: -71.111992, + }, + { + lat: 42.331817, + lon: -71.111992, + }, + { + lat: 42.33137, + lon: -71.111828, + }, + { + lat: 42.330773, + lon: -71.111594, + }, + { + lat: 42.330053, + lon: -71.111298, + }, + { + lat: 42.329625, + lon: -71.111102, + }, + { + lat: 42.329385, + lon: -71.110984, + }, + { + lat: 42.329385, + lon: -71.110984, + }, + { + lat: 42.32923, + lon: -71.110908, + }, + { + lat: 42.329059, + lon: -71.110833, + }, + { + lat: 42.328916, + lon: -71.110783, + }, + { + lat: 42.32863, + lon: -71.110714, + }, + { + lat: 42.328469, + lon: -71.110715, + }, + { + lat: 42.328238, + lon: -71.110748, + }, + { + lat: 42.327969, + lon: -71.110809, + }, + { + lat: 42.327643, + lon: -71.110902, + }, + { + lat: 42.327042, + lon: -71.111138, + }, + { + lat: 42.326679, + lon: -71.111314, + }, + { + lat: 42.326601, + lon: -71.111354, + }, + { + lat: 42.326601, + lon: -71.111354, + }, + { + lat: 42.326521, + lon: -71.111397, + }, + { + lat: 42.326428, + lon: -71.111446, + }, + { + lat: 42.325704, + lon: -71.1119, + }, + { + lat: 42.325475, + lon: -71.112012, + }, + { + lat: 42.325194, + lon: -71.11211, + }, + { + lat: 42.32495, + lon: -71.112187, + }, + { + lat: 42.324724, + lon: -71.112234, + }, + { + lat: 42.324538, + lon: -71.11226, + }, + { + lat: 42.324309, + lon: -71.112272, + }, + { + lat: 42.323892, + lon: -71.112259, + }, + { + lat: 42.323815, + lon: -71.112255, + }, + { + lat: 42.323815, + lon: -71.112255, + }, + { + lat: 42.323573, + lon: -71.112241, + }, + { + lat: 42.323111, + lon: -71.112203, + }, + { + lat: 42.322304, + lon: -71.112151, + }, + { + lat: 42.322304, + lon: -71.112151, + }, + { + lat: 42.322122, + lon: -71.11214, + }, + { + lat: 42.321588, + lon: -71.112091, + }, + { + lat: 42.321429, + lon: -71.112052, + }, + { + lat: 42.320971, + lon: -71.111956, + }, + { + lat: 42.320927, + lon: -71.111929, + }, + { + lat: 42.320588, + lon: -71.111861, + }, + { + lat: 42.319899, + lon: -71.111698, + }, + { + lat: 42.319744, + lon: -71.111669, + }, + { + lat: 42.319624, + lon: -71.111655, + }, + { + lat: 42.319526, + lon: -71.11167, + }, + { + lat: 42.319427, + lon: -71.111731, + }, + { + lat: 42.319427, + lon: -71.111731, + }, + { + lat: 42.319092, + lon: -71.111935, + }, + { + lat: 42.318791, + lon: -71.112147, + }, + { + lat: 42.318451, + lon: -71.112388, + }, + { + lat: 42.317903, + lon: -71.112765, + }, + { + lat: 42.317471, + lon: -71.113086, + }, + { + lat: 42.317239, + lon: -71.113243, + }, + { + lat: 42.317073, + lon: -71.113356, + }, + { + lat: 42.316997, + lon: -71.113408, + }, + { + lat: 42.316997, + lon: -71.113408, + }, + { + lat: 42.316684, + lon: -71.113625, + }, + { + lat: 42.316466, + lon: -71.113763, + }, + { + lat: 42.316365, + lon: -71.113839, + }, + { + lat: 42.316192, + lon: -71.113946, + }, + { + lat: 42.316009, + lon: -71.114018, + }, + { + lat: 42.315866, + lon: -71.114059, + }, + { + lat: 42.315693, + lon: -71.114091, + }, + { + lat: 42.315485, + lon: -71.114092, + }, + { + lat: 42.315306, + lon: -71.114093, + }, + { + lat: 42.314625, + lon: -71.114117, + }, + { + lat: 42.314123, + lon: -71.114115, + }, + { + lat: 42.313656, + lon: -71.114096, + }, + { + lat: 42.313625, + lon: -71.114103, + }, + { + lat: 42.31343, + lon: -71.114094, + }, + { + lat: 42.313305, + lon: -71.114098, + }, + { + lat: 42.313305, + lon: -71.114098, + }, + { + lat: 42.313216, + lon: -71.114101, + }, + { + lat: 42.313013, + lon: -71.114102, + }, + { + lat: 42.312508, + lon: -71.11415, + }, + { + lat: 42.312294, + lon: -71.114182, + }, + { + lat: 42.312144, + lon: -71.114233, + }, + { + lat: 42.311607, + lon: -71.114398, + }, + { + lat: 42.311457, + lon: -71.114436, + }, + { + lat: 42.311457, + lon: -71.114436, + }, + { + lat: 42.311217, + lon: -71.114496, + }, + { + lat: 42.310969, + lon: -71.114573, + }, + { + lat: 42.310736, + lon: -71.114656, + }, + { + lat: 42.310534, + lon: -71.114763, + }, + { + lat: 42.31038, + lon: -71.114855, + }, + { + lat: 42.310097, + lon: -71.115134, + }, + { + lat: 42.309922, + lon: -71.115292, + }, + { + lat: 42.309922, + lon: -71.115292, + }, + { + lat: 42.309846, + lon: -71.115361, + }, + { + lat: 42.309661, + lon: -71.115473, + }, + { + lat: 42.309584, + lon: -71.115511, + }, + { + lat: 42.309284, + lon: -71.115583, + }, + { + lat: 42.308923, + lon: -71.11558, + }, + { + lat: 42.30846, + lon: -71.115557, + }, + { + lat: 42.30838, + lon: -71.115552, + }, + { + lat: 42.30838, + lon: -71.115552, + }, + { + lat: 42.308043, + lon: -71.115532, + }, + { + lat: 42.307641, + lon: -71.115507, + }, + { + lat: 42.307374, + lon: -71.115478, + }, + { + lat: 42.30724, + lon: -71.11545, + }, + { + lat: 42.307063, + lon: -71.115409, + }, + { + lat: 42.306825, + lon: -71.115341, + }, + { + lat: 42.306619, + lon: -71.115246, + }, + { + lat: 42.306284, + lon: -71.115097, + }, + { + lat: 42.306147, + lon: -71.11503, + }, + { + lat: 42.306147, + lon: -71.11503, + }, + { + lat: 42.305928, + lon: -71.114922, + }, + { + lat: 42.305845, + lon: -71.114867, + }, + { + lat: 42.305744, + lon: -71.114812, + }, + { + lat: 42.305653, + lon: -71.114772, + }, + { + lat: 42.305592, + lon: -71.114743, + }, + { + lat: 42.305478, + lon: -71.114671, + }, + { + lat: 42.305304, + lon: -71.114649, + }, + { + lat: 42.30511, + lon: -71.11465, + }, + { + lat: 42.30484, + lon: -71.114671, + }, + { + lat: 42.304547, + lon: -71.114698, + }, + { + lat: 42.304394, + lon: -71.114704, + }, + { + lat: 42.304394, + lon: -71.114704, + }, + { + lat: 42.304213, + lon: -71.114711, + }, + { + lat: 42.30389, + lon: -71.114702, + }, + { + lat: 42.303623, + lon: -71.114704, + }, + { + lat: 42.303529, + lon: -71.1147, + }, + { + lat: 42.303016, + lon: -71.114586, + }, + { + lat: 42.302848, + lon: -71.114542, + }, + { + lat: 42.302502, + lon: -71.114474, + }, + { + lat: 42.302319, + lon: -71.114451, + }, + { + lat: 42.302153, + lon: -71.114448, + }, + { + lat: 42.301895, + lon: -71.114477, + }, + { + lat: 42.301581, + lon: -71.114528, + }, + { + lat: 42.30098, + lon: -71.11461, + }, + { + lat: 42.300538, + lon: -71.114662, + }, + { + lat: 42.300291, + lon: -71.114734, + }, + { + lat: 42.299927, + lon: -71.114898, + }, + { + lat: 42.299841, + lon: -71.114939, + }, + { + lat: 42.299854, + lon: -71.114767, + }, + { + lat: 42.299895, + lon: -71.11454, + }, + { + lat: 42.299977, + lon: -71.114484, + }, + ], +} diff --git a/assets/stories/skate-components/detours/diversionPage.stories.tsx b/assets/stories/skate-components/detours/diversionPage.stories.tsx index e7dafd0e9..ce12a4f05 100644 --- a/assets/stories/skate-components/detours/diversionPage.stories.tsx +++ b/assets/stories/skate-components/detours/diversionPage.stories.tsx @@ -4,6 +4,7 @@ import DiversionPanelMeta, { WithDirections as DiversionPanelWithDirections, WithStops as DiversionPanelWithStops, } from "./diversionPanel.stories" +import { route39shape } from "../__story-data__/shape" const meta = { component: DiversionPage, @@ -14,6 +15,8 @@ const meta = { args: { // Provide default route settings ...DiversionPanelMeta.args, + routeName: "39", + shape: route39shape, }, argTypes: { ...DiversionPanelMeta.argTypes, diff --git a/assets/tests/components/detours/detourMap.test.tsx b/assets/tests/components/detours/detourMap.test.tsx new file mode 100644 index 000000000..6b2181ba9 --- /dev/null +++ b/assets/tests/components/detours/detourMap.test.tsx @@ -0,0 +1,106 @@ +import { jest, describe, test, expect, beforeEach } from "@jest/globals" +import { render, screen } from "@testing-library/react" +import React from "react" +import { DetourMap } from "../../../src/components/detours/detourMap" +import userEvent from "@testing-library/user-event" +import "@testing-library/jest-dom/jest-globals" +import shapeFactory from "../../factories/shape" + +beforeEach(() => { + jest.spyOn(global, "scrollTo").mockImplementationOnce(jest.fn()) +}) + +describe("DetourMap", () => { + test("can click on route shape to start detour", async () => { + const { container } = render() + + await userEvent.click( + container.querySelector(".c-detour_map--original-route-shape")! + ) + + expect(screen.getByTitle("Detour Start")).not.toBeNull() + expect(screen.queryByTitle("Detour End")).toBeNull() + }) + + test("can click on route shape again to end detour", async () => { + const { container } = render() + + await userEvent.click( + container.querySelector(".c-detour_map--original-route-shape")! + ) + + await userEvent.click( + container.querySelector(".c-detour_map--original-route-shape")! + ) + + expect(screen.getByTitle("Detour End")).not.toBeNull() + }) + + test("clicking on map while drawing a detour adds a shape", async () => { + const { container } = render() + + await userEvent.click( + container.querySelector(".c-detour_map--original-route-shape")! + ) + + await userEvent.click(container.querySelector(".c-vehicle-map")!) + + expect( + container.querySelectorAll(".c-detour_map-circle-marker--detour-point") + ).toHaveLength(1) + }) + + test("clicking on 'Clear Last Waypoint' removes last point from detour", async () => { + const { container } = render() + + await userEvent.click( + container.querySelector(".c-detour_map--original-route-shape")! + ) + + await userEvent.click(container.querySelector(".c-vehicle-map")!) + + await userEvent.click( + screen.getByRole("button", { name: "Clear Last Waypoint" }) + ) + + expect( + container.querySelectorAll(".c-detour_map-circle-marker--detour-point") + ).toHaveLength(0) + }) + + test("'Clear Last Waypoint' is disabled before detour drawing is started", () => { + render() + + expect( + screen.getByRole("button", { name: "Clear Last Waypoint" }) + ).toBeDisabled() + }) + + test("'Clear Last Waypoint' is disabled when detour drawing has started but no waypoints are present", async () => { + const { container } = render() + + await userEvent.click( + container.querySelector(".c-detour_map--original-route-shape")! + ) + + expect( + screen.getByRole("button", { name: "Clear Last Waypoint" }) + ).toBeDisabled() + }) + + test("'Clear Last Waypoint' is disabled when detour drawing has finished", async () => { + const { container } = render() + + await userEvent.click( + container.querySelector(".c-detour_map--original-route-shape")! + ) + + await userEvent.click( + container.querySelector(".c-detour_map--original-route-shape")! + ) + + expect( + screen.getByRole("button", { name: "Clear Last Waypoint" }) + ).toBeDisabled() + }) +})