Add FlightStatusButton component
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
// @vitest-environment jsdom
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import { FlightStatusButton } from "./FlightStatusButton.js";
|
||||
import type { ISimpleFlight } from "../../types.js";
|
||||
|
||||
vi.mock("@/i18n/provider.js", () => ({
|
||||
useTranslation: () => ({ t: (k: string) => k }),
|
||||
}));
|
||||
|
||||
function makeFlight(carrier = "SU"): ISimpleFlight {
|
||||
return {
|
||||
id: "X",
|
||||
routeType: "Direct",
|
||||
flyingTime: "1h",
|
||||
status: "Scheduled",
|
||||
flightId: { carrier, flightNumber: "0022", suffix: "", date: "20260417" },
|
||||
operatingBy: { carrier, flightNumber: "0022" },
|
||||
leg: {} as never,
|
||||
} as ISimpleFlight;
|
||||
}
|
||||
|
||||
describe("FlightStatusButton", () => {
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(window, "open", { value: vi.fn(), writable: true });
|
||||
});
|
||||
|
||||
it("renders label SHARED.DETAILS", () => {
|
||||
render(<FlightStatusButton flight={makeFlight()} locale="ru" />);
|
||||
expect(screen.getByText("SHARED.DETAILS")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("has data-testid flight-status-button", () => {
|
||||
render(<FlightStatusButton flight={makeFlight()} locale="ru" />);
|
||||
expect(screen.getByTestId("flight-status-button")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("SU click opens native onlineboard details URL", () => {
|
||||
render(<FlightStatusButton flight={makeFlight("SU")} locale="ru" />);
|
||||
fireEvent.click(screen.getByTestId("flight-status-button"));
|
||||
const openMock = window.open as ReturnType<typeof vi.fn>;
|
||||
expect(openMock).toHaveBeenCalledTimes(1);
|
||||
expect(openMock.mock.calls[0]![0]).toBe("/ru/onlineboard/SU0022-20260417");
|
||||
});
|
||||
|
||||
it("HZ click opens flyaurora URL", () => {
|
||||
render(<FlightStatusButton flight={makeFlight("HZ")} locale="ru" />);
|
||||
fireEvent.click(screen.getByTestId("flight-status-button"));
|
||||
const openMock = window.open as ReturnType<typeof vi.fn>;
|
||||
expect(openMock).toHaveBeenCalledTimes(1);
|
||||
expect(openMock.mock.calls[0]![0]).toContain("flyaurora.ru");
|
||||
});
|
||||
|
||||
it("small prop adds --small modifier class", () => {
|
||||
render(<FlightStatusButton flight={makeFlight()} locale="ru" small />);
|
||||
const btn = screen.getByTestId("flight-status-button");
|
||||
expect(btn.className).toContain("flight-action-btn--small");
|
||||
});
|
||||
|
||||
it("default (small=false) does not add --small class", () => {
|
||||
render(<FlightStatusButton flight={makeFlight()} locale="ru" />);
|
||||
const btn = screen.getByTestId("flight-status-button");
|
||||
expect(btn.className).not.toContain("flight-action-btn--small");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { FC } from "react";
|
||||
import { useTranslation } from "@/i18n/provider.js";
|
||||
import type { ISimpleFlight } from "../../types.js";
|
||||
import { AIRLINES } from "./airlines.js";
|
||||
import { buildOnlineBoardUrl } from "../../url.js";
|
||||
import "./actions.scss";
|
||||
|
||||
export interface FlightStatusButtonProps {
|
||||
flight: ISimpleFlight;
|
||||
locale: string;
|
||||
small?: boolean;
|
||||
}
|
||||
|
||||
export const FlightStatusButton: FC<FlightStatusButtonProps> = ({ flight, locale, small }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleClick = () => {
|
||||
const carrier = flight.operatingBy.carrier;
|
||||
if (!carrier) return;
|
||||
const config = AIRLINES[carrier];
|
||||
if (!config) return;
|
||||
|
||||
if (config.hasNativeStatus) {
|
||||
const path = buildOnlineBoardUrl({
|
||||
type: "details",
|
||||
carrier: flight.flightId.carrier,
|
||||
flightNumber: flight.flightId.flightNumber,
|
||||
...(flight.flightId.suffix ? { suffix: flight.flightId.suffix } : {}),
|
||||
date: flight.flightId.date,
|
||||
});
|
||||
window.open(`/${locale}/${path}`, "_blank", "noopener,noreferrer");
|
||||
} else if (config.statusUrl) {
|
||||
window.open(config.statusUrl, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
};
|
||||
|
||||
const classes = [
|
||||
"flight-action-btn",
|
||||
"flight-action-btn--blue-light",
|
||||
small ? "flight-action-btn--small" : "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={classes}
|
||||
data-testid="flight-status-button"
|
||||
title={t("SHARED.DETAILS-TOOLTIP")}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{t("SHARED.DETAILS")}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user