Keep route drawings in sync
This commit is contained in:
+21
-3
@@ -570,6 +570,7 @@ export function App() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const clearRouteOutcome = useCallback(() => {
|
const clearRouteOutcome = useCallback(() => {
|
||||||
|
courseAssistant.stop();
|
||||||
routeRequestId.current += 1;
|
routeRequestId.current += 1;
|
||||||
routeWeatherRequestId.current += 1;
|
routeWeatherRequestId.current += 1;
|
||||||
setRoute(null);
|
setRoute(null);
|
||||||
@@ -580,7 +581,20 @@ export function App() {
|
|||||||
setRouteWeatherError(null);
|
setRouteWeatherError(null);
|
||||||
setRouteWeatherLoading(false);
|
setRouteWeatherLoading(false);
|
||||||
setRouteLoading(false);
|
setRouteLoading(false);
|
||||||
}, []);
|
}, [courseAssistant.stop]);
|
||||||
|
|
||||||
|
const editRoute = useCallback(() => {
|
||||||
|
clearRouteOutcome();
|
||||||
|
setPickMode(null);
|
||||||
|
}, [clearRouteOutcome]);
|
||||||
|
|
||||||
|
const cancelRoute = useCallback(() => {
|
||||||
|
setRouteStart(null);
|
||||||
|
setDestination(null);
|
||||||
|
setWaypoints([]);
|
||||||
|
clearRouteOutcome();
|
||||||
|
setPickMode(null);
|
||||||
|
}, [clearRouteOutcome]);
|
||||||
|
|
||||||
const saveCurrentBoat = useCallback((profile: BoatProfile) => {
|
const saveCurrentBoat = useCallback((profile: BoatProfile) => {
|
||||||
const stored = persistBoatProfile(profile);
|
const stored = persistBoatProfile(profile);
|
||||||
@@ -675,11 +689,12 @@ export function App() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
courseAssistant.stop();
|
||||||
setRoute(selected);
|
setRoute(selected);
|
||||||
if (activeVesselProfile) {
|
if (activeVesselProfile) {
|
||||||
loadRouteWeather(selected, activeVesselProfile);
|
loadRouteWeather(selected, activeVesselProfile);
|
||||||
}
|
}
|
||||||
}, [activeVesselProfile, loadRouteWeather, routeOptions]);
|
}, [activeVesselProfile, courseAssistant.stop, loadRouteWeather, routeOptions]);
|
||||||
|
|
||||||
const clearStart = useCallback(() => {
|
const clearStart = useCallback(() => {
|
||||||
setRouteStart(null);
|
setRouteStart(null);
|
||||||
@@ -716,6 +731,7 @@ export function App() {
|
|||||||
}, [clearRouteOutcome]);
|
}, [clearRouteOutcome]);
|
||||||
|
|
||||||
const loadOfflineVoyage = useCallback((voyage: OfflineVoyage) => {
|
const loadOfflineVoyage = useCallback((voyage: OfflineVoyage) => {
|
||||||
|
courseAssistant.stop();
|
||||||
routeRequestId.current += 1;
|
routeRequestId.current += 1;
|
||||||
routeWeatherRequestId.current += 1;
|
routeWeatherRequestId.current += 1;
|
||||||
setRouteStart(voyage.plan.start);
|
setRouteStart(voyage.plan.start);
|
||||||
@@ -737,7 +753,7 @@ export function App() {
|
|||||||
if (voyage.plan.vesselProfile && navigator.onLine) {
|
if (voyage.plan.vesselProfile && navigator.onLine) {
|
||||||
loadRouteWeather(offlineRoute, voyage.plan.vesselProfile);
|
loadRouteWeather(offlineRoute, voyage.plan.vesselProfile);
|
||||||
}
|
}
|
||||||
}, [loadRouteWeather]);
|
}, [courseAssistant.stop, loadRouteWeather]);
|
||||||
|
|
||||||
const uiMode = anchorWatch.phase !== "idle"
|
const uiMode = anchorWatch.phase !== "idle"
|
||||||
? "anchor"
|
? "anchor"
|
||||||
@@ -1022,6 +1038,8 @@ export function App() {
|
|||||||
onClearDestination={clearDestination}
|
onClearDestination={clearDestination}
|
||||||
onUseGpsAsStart={useGpsAsStart}
|
onUseGpsAsStart={useGpsAsStart}
|
||||||
onSelectRoute={selectRouteOption}
|
onSelectRoute={selectRouteOption}
|
||||||
|
onEditRoute={editRoute}
|
||||||
|
onCancelRoute={cancelRoute}
|
||||||
guidanceActive={courseAssistant.active}
|
guidanceActive={courseAssistant.active}
|
||||||
onStartGuidance={startCourseAssistant}
|
onStartGuidance={startCourseAssistant}
|
||||||
onEditBoat={() => {
|
onEditBoat={() => {
|
||||||
|
|||||||
@@ -417,8 +417,8 @@ export function MapView({
|
|||||||
map.addSource("route-guidance", {
|
map.addSource("route-guidance", {
|
||||||
type: "geojson",
|
type: "geojson",
|
||||||
data: guidanceFeatures(
|
data: guidanceFeatures(
|
||||||
guidanceActiveRef.current ? positionRef.current : null,
|
routeRef.current && guidanceActiveRef.current ? positionRef.current : null,
|
||||||
guidanceActiveRef.current ? guidanceTargetRef.current : null
|
routeRef.current && guidanceActiveRef.current ? guidanceTargetRef.current : null
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
map.addSource("anchor-watch", {
|
map.addSource("anchor-watch", {
|
||||||
@@ -950,13 +950,18 @@ export function MapView({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const map = mapRef.current;
|
const map = mapRef.current;
|
||||||
if (!map?.isStyleLoaded()) {
|
const source = map?.getSource("route-guidance") as GeoJSONSource | undefined;
|
||||||
|
if (!source) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = map.getSource("route-guidance") as GeoJSONSource | undefined;
|
source.setData(
|
||||||
source?.setData(guidanceFeatures(guidanceActive ? position : null, guidanceActive ? guidanceTarget : null));
|
guidanceFeatures(
|
||||||
}, [guidanceActive, guidanceTarget, position]);
|
route && guidanceActive ? position : null,
|
||||||
|
route && guidanceActive ? guidanceTarget : null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, [guidanceActive, guidanceTarget, position, route]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const map = mapRef.current;
|
const map = mapRef.current;
|
||||||
@@ -987,42 +992,45 @@ export function MapView({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const map = mapRef.current;
|
const map = mapRef.current;
|
||||||
if (!map?.isStyleLoaded()) {
|
const source = map?.getSource("start-point") as GeoJSONSource | undefined;
|
||||||
|
if (!source) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = map.getSource("start-point") as GeoJSONSource | undefined;
|
source.setData(startPoint ? pointFeature(startPoint) : emptyPoint());
|
||||||
source?.setData(startPoint ? pointFeature(startPoint) : emptyPoint());
|
|
||||||
}, [startPoint]);
|
}, [startPoint]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const map = mapRef.current;
|
const map = mapRef.current;
|
||||||
if (!map?.isStyleLoaded()) {
|
const source = map?.getSource("destination") as GeoJSONSource | undefined;
|
||||||
|
if (!source) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = map.getSource("destination") as GeoJSONSource | undefined;
|
source.setData(destination ? pointFeature(destination) : emptyPoint());
|
||||||
source?.setData(destination ? pointFeature(destination) : emptyPoint());
|
|
||||||
}, [destination]);
|
}, [destination]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const map = mapRef.current;
|
const map = mapRef.current;
|
||||||
if (!map?.isStyleLoaded()) {
|
const source = map?.getSource("waypoints") as GeoJSONSource | undefined;
|
||||||
|
if (!source) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = map.getSource("waypoints") as GeoJSONSource | undefined;
|
source.setData(waypointFeatures(waypoints));
|
||||||
source?.setData(waypointFeatures(waypoints));
|
|
||||||
}, [waypoints]);
|
}, [waypoints]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const map = mapRef.current;
|
const map = mapRef.current;
|
||||||
if (!map?.isStyleLoaded()) {
|
if (!map) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const source = map.getSource("route") as GeoJSONSource | undefined;
|
||||||
|
if (!source) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = map.getSource("route") as GeoJSONSource | undefined;
|
source.setData(route ? routeFeature(route) : emptyLine());
|
||||||
source?.setData(route ? routeFeature(route) : emptyLine());
|
|
||||||
fitRouteOnMap(map, route);
|
fitRouteOnMap(map, route);
|
||||||
}, [route]);
|
}, [route]);
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ type RoutePlannerProps = {
|
|||||||
onClearDestination: () => void;
|
onClearDestination: () => void;
|
||||||
onUseGpsAsStart: () => void;
|
onUseGpsAsStart: () => void;
|
||||||
onSelectRoute: (routeId: string) => void;
|
onSelectRoute: (routeId: string) => void;
|
||||||
|
onEditRoute?: () => void;
|
||||||
|
onCancelRoute?: () => void;
|
||||||
guidanceActive?: boolean;
|
guidanceActive?: boolean;
|
||||||
onStartGuidance?: () => void;
|
onStartGuidance?: () => void;
|
||||||
onEditBoat?: () => void;
|
onEditBoat?: () => void;
|
||||||
@@ -148,6 +150,8 @@ export function RoutePlanner({
|
|||||||
onClearDestination,
|
onClearDestination,
|
||||||
onUseGpsAsStart,
|
onUseGpsAsStart,
|
||||||
onSelectRoute,
|
onSelectRoute,
|
||||||
|
onEditRoute = () => undefined,
|
||||||
|
onCancelRoute = () => undefined,
|
||||||
guidanceActive = false,
|
guidanceActive = false,
|
||||||
onStartGuidance = () => undefined,
|
onStartGuidance = () => undefined,
|
||||||
onEditBoat = () => undefined,
|
onEditBoat = () => undefined,
|
||||||
@@ -612,13 +616,29 @@ export function RoutePlanner({
|
|||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div className="route-plan-actions">
|
||||||
<button
|
<button
|
||||||
className="secondary-action"
|
className="secondary-action"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setPlannerView("input")}
|
onClick={() => {
|
||||||
|
onEditRoute();
|
||||||
|
setPlannerView("input");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Plan ändern
|
Plan ändern
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
className="secondary-action route-cancel-action"
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
onCancelRoute();
|
||||||
|
setPlannerView("input");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<X size={15} aria-hidden="true" />
|
||||||
|
Route verwerfen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{result && (
|
{result && (
|
||||||
<section className="course-assistant-launch" aria-label="Navigation starten">
|
<section className="course-assistant-launch" aria-label="Navigation starten">
|
||||||
|
|||||||
@@ -1029,6 +1029,20 @@ button:disabled {
|
|||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.route-plan-actions {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-plan-actions > button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-cancel-action {
|
||||||
|
color: #8b3528;
|
||||||
|
}
|
||||||
|
|
||||||
.route-result[data-severity="ok"] {
|
.route-result[data-severity="ok"] {
|
||||||
background: #dceee6;
|
background: #dceee6;
|
||||||
color: #196f5c;
|
color: #196f5c;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ vi.mock("maplibre-gl", () => {
|
|||||||
layerDefinitions = new globalThis.Map<string, any>();
|
layerDefinitions = new globalThis.Map<string, any>();
|
||||||
renderedFeatures: any[] = [];
|
renderedFeatures: any[] = [];
|
||||||
zoom = 13;
|
zoom = 13;
|
||||||
|
styleLoaded = true;
|
||||||
container: HTMLElement;
|
container: HTMLElement;
|
||||||
fitBounds = vi.fn(() => this);
|
fitBounds = vi.fn(() => this);
|
||||||
easeTo = vi.fn(() => this);
|
easeTo = vi.fn(() => this);
|
||||||
@@ -110,7 +111,7 @@ vi.mock("maplibre-gl", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isStyleLoaded() {
|
isStyleLoaded() {
|
||||||
return true;
|
return this.styleLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
getZoom() {
|
getZoom() {
|
||||||
@@ -553,6 +554,128 @@ describe("MapView marine feature information", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("atomically replaces and clears route drawings while other map data is loading", async () => {
|
||||||
|
const stableOnPickCoordinate = vi.fn();
|
||||||
|
const stableOnMapReady = vi.fn();
|
||||||
|
const firstRoute: RouteResult = {
|
||||||
|
id: "first",
|
||||||
|
name: "Hauptroute",
|
||||||
|
geometry: {
|
||||||
|
type: "LineString",
|
||||||
|
coordinates: [
|
||||||
|
[7.1, 53.1],
|
||||||
|
[7.2, 53.2],
|
||||||
|
[7.3, 53.3]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
distanceNm: 10,
|
||||||
|
eta: null,
|
||||||
|
warnings: [],
|
||||||
|
dataSources: [],
|
||||||
|
routingMode: "fairway"
|
||||||
|
};
|
||||||
|
const secondRoute: RouteResult = {
|
||||||
|
...firstRoute,
|
||||||
|
id: "second",
|
||||||
|
name: "Alternative 1",
|
||||||
|
geometry: {
|
||||||
|
type: "LineString",
|
||||||
|
coordinates: [
|
||||||
|
[8.1, 54.1],
|
||||||
|
[8.2, 54.2],
|
||||||
|
[8.3, 54.3],
|
||||||
|
[8.4, 54.4]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const view = render(
|
||||||
|
<MapView
|
||||||
|
config={config}
|
||||||
|
position={{ lat: 53.1, lon: 7.1 }}
|
||||||
|
accuracyM={5}
|
||||||
|
startPoint={{ lat: 53.1, lon: 7.1 }}
|
||||||
|
destination={{ lat: 53.3, lon: 7.3 }}
|
||||||
|
waypoints={[{ lat: 53.2, lon: 7.2 }]}
|
||||||
|
pickMode={null}
|
||||||
|
route={firstRoute}
|
||||||
|
guidanceActive
|
||||||
|
guidanceTarget={{ lat: 53.2, lon: 7.2 }}
|
||||||
|
onPickCoordinate={stableOnPickCoordinate}
|
||||||
|
onMapReady={stableOnMapReady}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
await act(async () => maplibreState.current.emit("load"));
|
||||||
|
|
||||||
|
const routeSource = maplibreState.current.sources.get("route");
|
||||||
|
const guidanceSource = maplibreState.current.sources.get("route-guidance");
|
||||||
|
const startSource = maplibreState.current.sources.get("start-point");
|
||||||
|
const destinationSource = maplibreState.current.sources.get("destination");
|
||||||
|
const waypointSource = maplibreState.current.sources.get("waypoints");
|
||||||
|
maplibreState.current.styleLoaded = false;
|
||||||
|
|
||||||
|
view.rerender(
|
||||||
|
<MapView
|
||||||
|
config={config}
|
||||||
|
position={{ lat: 54.1, lon: 8.1 }}
|
||||||
|
accuracyM={5}
|
||||||
|
startPoint={{ lat: 54.1, lon: 8.1 }}
|
||||||
|
destination={{ lat: 54.4, lon: 8.4 }}
|
||||||
|
waypoints={[{ lat: 54.3, lon: 8.3 }]}
|
||||||
|
pickMode={null}
|
||||||
|
route={secondRoute}
|
||||||
|
guidanceActive
|
||||||
|
guidanceTarget={{ lat: 54.2, lon: 8.2 }}
|
||||||
|
onPickCoordinate={stableOnPickCoordinate}
|
||||||
|
onMapReady={stableOnMapReady}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(routeSource.setData).toHaveBeenLastCalledWith({
|
||||||
|
type: "Feature",
|
||||||
|
properties: {},
|
||||||
|
geometry: secondRoute.geometry
|
||||||
|
});
|
||||||
|
|
||||||
|
view.rerender(
|
||||||
|
<MapView
|
||||||
|
config={config}
|
||||||
|
position={{ lat: 54.1, lon: 8.1 }}
|
||||||
|
accuracyM={5}
|
||||||
|
startPoint={null}
|
||||||
|
destination={null}
|
||||||
|
waypoints={[]}
|
||||||
|
pickMode={null}
|
||||||
|
route={null}
|
||||||
|
guidanceActive={false}
|
||||||
|
guidanceTarget={null}
|
||||||
|
onPickCoordinate={stableOnPickCoordinate}
|
||||||
|
onMapReady={stableOnMapReady}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(routeSource.setData).toHaveBeenLastCalledWith({
|
||||||
|
type: "Feature",
|
||||||
|
properties: {},
|
||||||
|
geometry: { type: "LineString", coordinates: [] }
|
||||||
|
});
|
||||||
|
expect(guidanceSource.setData).toHaveBeenLastCalledWith({
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: []
|
||||||
|
});
|
||||||
|
expect(startSource.setData).toHaveBeenLastCalledWith({
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: []
|
||||||
|
});
|
||||||
|
expect(destinationSource.setData).toHaveBeenLastCalledWith({
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: []
|
||||||
|
});
|
||||||
|
expect(waypointSource.setData).toHaveBeenLastCalledWith({
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: []
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("focuses a route event requested by the shared navigation workspace", async () => {
|
it("focuses a route event requested by the shared navigation workspace", async () => {
|
||||||
const stableOnMapReady = vi.fn();
|
const stableOnMapReady = vi.fn();
|
||||||
const view = render(
|
const view = render(
|
||||||
@@ -636,7 +759,14 @@ describe("MapView marine feature information", () => {
|
|||||||
startPoint={{ lat: 52, lon: 7 }}
|
startPoint={{ lat: 52, lon: 7 }}
|
||||||
destination={{ lat: 52, lon: 7.1 }}
|
destination={{ lat: 52, lon: 7.1 }}
|
||||||
pickMode={null}
|
pickMode={null}
|
||||||
route={null}
|
route={{
|
||||||
|
geometry: { type: "LineString", coordinates: [[7, 52], [7.1, 52]] },
|
||||||
|
distanceNm: 4,
|
||||||
|
eta: null,
|
||||||
|
warnings: [],
|
||||||
|
dataSources: [],
|
||||||
|
routingMode: "fairway"
|
||||||
|
}}
|
||||||
guidanceActive
|
guidanceActive
|
||||||
guidanceTarget={{ lat: 52.001, lon: 7.01 }}
|
guidanceTarget={{ lat: 52.001, lon: 7.01 }}
|
||||||
onPickCoordinate={vi.fn()}
|
onPickCoordinate={vi.fn()}
|
||||||
|
|||||||
@@ -383,6 +383,7 @@ describe("RoutePlanner", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("opens the compact result view and keeps critical warnings above secondary route details", () => {
|
it("opens the compact result view and keeps critical warnings above secondary route details", () => {
|
||||||
|
const onEditRoute = vi.fn();
|
||||||
const result = {
|
const result = {
|
||||||
...routeOption("primary", "Hauptroute", 4),
|
...routeOption("primary", "Hauptroute", 4),
|
||||||
warnings: [
|
warnings: [
|
||||||
@@ -411,6 +412,7 @@ describe("RoutePlanner", () => {
|
|||||||
onClearDestination={vi.fn()}
|
onClearDestination={vi.fn()}
|
||||||
onUseGpsAsStart={vi.fn()}
|
onUseGpsAsStart={vi.fn()}
|
||||||
onSelectRoute={vi.fn()}
|
onSelectRoute={vi.fn()}
|
||||||
|
onEditRoute={onEditRoute}
|
||||||
onCollapse={vi.fn()}
|
onCollapse={vi.fn()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -422,9 +424,44 @@ describe("RoutePlanner", () => {
|
|||||||
expect(screen.queryByRole("button", { name: "Start auf Karte setzen" })).not.toBeInTheDocument();
|
expect(screen.queryByRole("button", { name: "Start auf Karte setzen" })).not.toBeInTheDocument();
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole("button", { name: "Plan ändern" }));
|
fireEvent.click(screen.getByRole("button", { name: "Plan ändern" }));
|
||||||
|
expect(onEditRoute).toHaveBeenCalledTimes(1);
|
||||||
expect(screen.getByText("Route planen")).toBeVisible();
|
expect(screen.getByText("Route planen")).toBeVisible();
|
||||||
expect(screen.getByRole("button", { name: "Start auf Karte setzen" })).toBeVisible();
|
expect(screen.getByRole("button", { name: "Start auf Karte setzen" })).toBeVisible();
|
||||||
expect(screen.getByRole("button", { name: "Zur Route" })).toBeVisible();
|
});
|
||||||
|
|
||||||
|
it("explicitly discards a planned route through the parent state", () => {
|
||||||
|
const onCancelRoute = vi.fn();
|
||||||
|
const result = routeOption("primary", "Hauptroute", 4);
|
||||||
|
|
||||||
|
render(
|
||||||
|
<RoutePlanner
|
||||||
|
startPoint={{ lat: 52, lon: 7 }}
|
||||||
|
gpsPosition={null}
|
||||||
|
destination={{ lat: 52, lon: 7.1 }}
|
||||||
|
result={result}
|
||||||
|
routeOptions={[result]}
|
||||||
|
weatherReport={null}
|
||||||
|
weatherLoading={false}
|
||||||
|
weatherError={null}
|
||||||
|
loading={false}
|
||||||
|
error={null}
|
||||||
|
pickMode={null}
|
||||||
|
onSubmit={vi.fn()}
|
||||||
|
onPickStart={vi.fn()}
|
||||||
|
onPickDestination={vi.fn()}
|
||||||
|
onClearStart={vi.fn()}
|
||||||
|
onClearDestination={vi.fn()}
|
||||||
|
onUseGpsAsStart={vi.fn()}
|
||||||
|
onSelectRoute={vi.fn()}
|
||||||
|
onCancelRoute={onCancelRoute}
|
||||||
|
onCollapse={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Route verwerfen" }));
|
||||||
|
|
||||||
|
expect(onCancelRoute).toHaveBeenCalledTimes(1);
|
||||||
|
expect(screen.getByText("Route planen")).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("filters retired fairway and depth warnings from route results", () => {
|
it("filters retired fairway and depth warnings from route results", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user