Add StationChange component for multi-leg timeline

This commit is contained in:
2026-04-17 02:29:43 +03:00
parent 2d01e1a37e
commit 391db7c948
2 changed files with 114 additions and 0 deletions
@@ -0,0 +1,72 @@
// @vitest-environment jsdom
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import { StationChange } from "./StationChange.js";
import type { IFlightLegStation } from "../../types.js";
function makeStation(overrides: {
city?: string;
cityCode?: string;
airportCode?: string;
terminal?: string;
}): IFlightLegStation {
return {
scheduled: {
airport: "",
airportCode: overrides.airportCode ?? "SVO",
city: overrides.city ?? "Moscow",
cityCode: overrides.cityCode ?? "MOW",
countryCode: "RU",
},
latest: {
airport: "",
airportCode: overrides.airportCode ?? "SVO",
city: overrides.city ?? "Moscow",
cityCode: overrides.cityCode ?? "MOW",
countryCode: "RU",
},
dispatch: "",
gate: "",
terminal: overrides.terminal ?? "",
} as IFlightLegStation;
}
describe("StationChange", () => {
it("renders a single Station when there is no change (noChange)", () => {
const a = makeStation({ city: "Moscow", cityCode: "MOW", airportCode: "SVO", terminal: "D" });
const b = makeStation({ city: "Moscow", cityCode: "MOW", airportCode: "SVO", terminal: "D" });
const { container } = render(<StationChange from={a} to={b} />);
// Single Station rendered (no station-change wrapper)
expect(container.querySelector(".station-change")).toBeNull();
expect(container.querySelector(".station")).not.toBeNull();
});
it("renders two Stations separated by arrow when city differs", () => {
const a = makeStation({ city: "Moscow", cityCode: "MOW", airportCode: "SVO" });
const b = makeStation({ city: "Saint Petersburg", cityCode: "LED", airportCode: "LED" });
const { container } = render(<StationChange from={a} to={b} />);
expect(container.querySelector(".station-change--city")).not.toBeNull();
expect(screen.getByText("Moscow")).toBeTruthy();
expect(screen.getByText("Saint Petersburg")).toBeTruthy();
expect(container.querySelector(".station-change__arrow")).not.toBeNull();
});
it("renders airport change format when airports differ but city matches", () => {
const a = makeStation({ city: "Moscow", cityCode: "MOW", airportCode: "SVO", terminal: "D" });
const b = makeStation({ city: "Moscow", cityCode: "MOW", airportCode: "DME" });
const { container } = render(<StationChange from={a} to={b} />);
expect(container.querySelector(".station-change--airport")).not.toBeNull();
expect(screen.getByText("Moscow")).toBeTruthy();
expect(screen.getByText("SVO/D")).toBeTruthy();
expect(screen.getByText("DME")).toBeTruthy();
});
it("renders terminal change format when only terminal differs", () => {
const a = makeStation({ city: "Moscow", cityCode: "MOW", airportCode: "SVO", terminal: "D" });
const b = makeStation({ city: "Moscow", cityCode: "MOW", airportCode: "SVO", terminal: "F" });
const { container } = render(<StationChange from={a} to={b} />);
expect(container.querySelector(".station-change--terminal")).not.toBeNull();
expect(screen.getByText("SVO/D")).toBeTruthy();
expect(screen.getByText("SVO/F")).toBeTruthy();
});
});
@@ -0,0 +1,42 @@
import type { FC } from "react";
import type { IFlightLegStation } from "../../types.js";
import { Station } from "./Station.js";
import { detectStationChange } from "./detectStationChange.js";
export interface StationChangeProps {
from: IFlightLegStation;
to: IFlightLegStation;
}
function formatAirportWithTerminal(station: IFlightLegStation): string {
const code = station.scheduled.airportCode;
return station.terminal ? `${code}/${station.terminal}` : code;
}
export const StationChange: FC<StationChangeProps> = ({ from, to }) => {
const changeType = detectStationChange(from, to);
if (changeType === "noChange") {
return <Station station={from} />;
}
if (changeType === "city") {
return (
<div className="station-change station-change--city">
<Station station={from} />
<span className="station-change__arrow" aria-hidden="true">{"\u2192"}</span>
<Station station={to} />
</div>
);
}
// airport or terminal — same city
return (
<div className={`station-change station-change--${changeType}`}>
<span className="station-change__city">{from.scheduled.city}</span>
<span className="station-change__code">{formatAirportWithTerminal(from)}</span>
<span className="station-change__arrow" aria-hidden="true">{"\u2192"}</span>
<span className="station-change__code">{formatAirportWithTerminal(to)}</span>
</div>
);
};