Wire FullRouteTimeline and TransferBar into OnlineBoardDetailsPage
Multi-leg flights now render a full-route timeline header and interleave a transfer-bar between consecutive legs, surfacing station change and intermediate-landing duration inline with the leg details.
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { OnlineBoardDetailsPage } from "./OnlineBoardDetailsPage.js";
|
||||
import type { IParsedFlightId, IDirectFlight } from "../types.js";
|
||||
import type { IParsedFlightId, IDirectFlight, IMultiLegFlight, IFlightLeg } from "../types.js";
|
||||
|
||||
const mockFlightId: IParsedFlightId = {
|
||||
carrier: "SU",
|
||||
@@ -75,8 +75,8 @@ const mockFlight: IDirectFlight = {
|
||||
|
||||
// Mutable state for test control
|
||||
let mockState = {
|
||||
flight: mockFlight as IDirectFlight | null,
|
||||
allFlights: [mockFlight] as IDirectFlight[],
|
||||
flight: mockFlight as IDirectFlight | IMultiLegFlight | null,
|
||||
allFlights: [mockFlight] as (IDirectFlight | IMultiLegFlight)[],
|
||||
daysOfFlight: ["20250115"] as string[],
|
||||
loading: false,
|
||||
error: null as Error | null,
|
||||
@@ -273,6 +273,70 @@ describe("OnlineBoardDetailsPage", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("multi-leg timeline integration", () => {
|
||||
function makeLeg(index: number, overrides: Partial<IFlightLeg> = {}): IFlightLeg {
|
||||
return {
|
||||
...mockFlight.leg,
|
||||
index,
|
||||
...overrides,
|
||||
} as IFlightLeg;
|
||||
}
|
||||
|
||||
it("does not render FullRouteTimeline for Direct flights", () => {
|
||||
render(
|
||||
<OnlineBoardDetailsPage flightId={mockFlightId} locale="ru" canonicalOrigin="https://example.com" />,
|
||||
);
|
||||
expect(screen.queryByTestId("full-route-timeline")).toBeNull();
|
||||
expect(screen.queryByTestId("transfer-bar")).toBeNull();
|
||||
});
|
||||
|
||||
it("renders FullRouteTimeline for MultiLeg flights", () => {
|
||||
const multiLeg: IMultiLegFlight = {
|
||||
id: "SU100-20250115",
|
||||
flightId: { carrier: "SU", flightNumber: "100", suffix: "", date: "20250115" },
|
||||
routeType: "MultiLeg",
|
||||
status: "Scheduled",
|
||||
flyingTime: "12:00",
|
||||
operatingBy: {},
|
||||
legs: [makeLeg(0), makeLeg(1)],
|
||||
};
|
||||
mockState = {
|
||||
flight: multiLeg,
|
||||
allFlights: [multiLeg],
|
||||
daysOfFlight: ["20250115"],
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
render(
|
||||
<OnlineBoardDetailsPage flightId={mockFlightId} locale="ru" canonicalOrigin="https://example.com" />,
|
||||
);
|
||||
expect(screen.getByTestId("full-route-timeline")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("interleaves one TransferBar between two legs", () => {
|
||||
const multiLeg: IMultiLegFlight = {
|
||||
id: "SU100-20250115",
|
||||
flightId: { carrier: "SU", flightNumber: "100", suffix: "", date: "20250115" },
|
||||
routeType: "MultiLeg",
|
||||
status: "Scheduled",
|
||||
flyingTime: "12:00",
|
||||
operatingBy: {},
|
||||
legs: [makeLeg(0), makeLeg(1)],
|
||||
};
|
||||
mockState = {
|
||||
flight: multiLeg,
|
||||
allFlights: [multiLeg],
|
||||
daysOfFlight: ["20250115"],
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
render(
|
||||
<OnlineBoardDetailsPage flightId={mockFlightId} locale="ru" canonicalOrigin="https://example.com" />,
|
||||
);
|
||||
expect(screen.getAllByTestId("transfer-bar")).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("flight schedule integration", () => {
|
||||
it("renders FlightSchedule when firstLeg.daysOfWeek is present", () => {
|
||||
const flightWithDaysOfWeek = {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @module
|
||||
*/
|
||||
|
||||
import { useCallback, type FC } from "react";
|
||||
import { Fragment, useCallback, type FC } from "react";
|
||||
import { useNavigate } from "@modern-js/runtime/router";
|
||||
import { useTranslation } from "@/i18n/provider.js";
|
||||
import "./OnlineBoardDetailsPage.scss";
|
||||
@@ -28,6 +28,8 @@ import { DayTabs } from "./DayTabs/index.js";
|
||||
import { BoardDetailsHeader } from "./BoardDetailsHeader/index.js";
|
||||
import { DetailsBackButton } from "./DetailsBackButton/index.js";
|
||||
import { FlightSchedule } from "./FlightSchedule/index.js";
|
||||
import { FullRouteTimeline } from "./FullRouteTimeline/index.js";
|
||||
import { TransferBar } from "./TransferBar/index.js";
|
||||
import type { IParsedFlightId, IFlightLeg } from "../types.js";
|
||||
|
||||
export interface OnlineBoardDetailsPageProps {
|
||||
@@ -41,12 +43,20 @@ export interface OnlineBoardDetailsPageProps {
|
||||
|
||||
/**
|
||||
* Render all legs of a flight with departure/arrival station details.
|
||||
* For multi-leg flights, interleaves a TransferBar between consecutive legs.
|
||||
*/
|
||||
function FlightLegs({ legs }: { legs: IFlightLeg[] }): JSX.Element {
|
||||
function FlightLegs({
|
||||
legs,
|
||||
viewType,
|
||||
}: {
|
||||
legs: IFlightLeg[];
|
||||
viewType: "Onlineboard" | "Schedule";
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div className="flight-details__legs" data-testid="flight-legs">
|
||||
{legs.map((leg) => (
|
||||
<div key={leg.index} className="flight-details__leg" data-testid={`flight-leg-${leg.index}`}>
|
||||
{legs.map((leg, i) => (
|
||||
<Fragment key={leg.index}>
|
||||
<div className="flight-details__leg" data-testid={`flight-leg-${leg.index}`}>
|
||||
<div className="flight-details__leg-header">
|
||||
<span className="flight-details__leg-index">Leg {leg.index + 1}</span>
|
||||
<span className="flight-details__leg-status">{leg.status}</span>
|
||||
@@ -124,6 +134,10 @@ function FlightLegs({ legs }: { legs: IFlightLeg[] }): JSX.Element {
|
||||
)}
|
||||
<FlightDetailsAccordion leg={leg} viewType="Onlineboard" />
|
||||
</div>
|
||||
{i < legs.length - 1 && (
|
||||
<TransferBar leg={leg} nextLeg={legs[i + 1]!} viewType={viewType} />
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
@@ -278,6 +292,10 @@ export const OnlineBoardDetailsPage: FC<OnlineBoardDetailsPageProps> = ({
|
||||
|
||||
<BoardDetailsHeader flight={displayFlight} locale={locale} />
|
||||
|
||||
{displayFlight.routeType === "MultiLeg" && (
|
||||
<FullRouteTimeline legs={displayFlight.legs} viewType="Onlineboard" />
|
||||
)}
|
||||
|
||||
{/* Summary card */}
|
||||
<FlightCard flight={displayFlight} />
|
||||
|
||||
@@ -292,7 +310,7 @@ export const OnlineBoardDetailsPage: FC<OnlineBoardDetailsPageProps> = ({
|
||||
)}
|
||||
|
||||
{/* Detailed leg information */}
|
||||
<FlightLegs legs={legs} />
|
||||
<FlightLegs legs={legs} viewType="Onlineboard" />
|
||||
|
||||
{/* Flying time */}
|
||||
<div className="flight-details__flying-time" data-testid="flying-time">
|
||||
|
||||
Reference in New Issue
Block a user