From a70dbd2546308c3cfcfefcb7828549b803cc8038 Mon Sep 17 00:00:00 2001 From: gnezim Date: Fri, 17 Apr 2026 01:28:54 +0300 Subject: [PATCH] Add FlightStatusButton component --- .../FlightStatusButton.test.tsx | 65 +++++++++++++++++++ .../BoardDetailsHeader/FlightStatusButton.tsx | 56 ++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 src/features/online-board/components/BoardDetailsHeader/FlightStatusButton.test.tsx create mode 100644 src/features/online-board/components/BoardDetailsHeader/FlightStatusButton.tsx diff --git a/src/features/online-board/components/BoardDetailsHeader/FlightStatusButton.test.tsx b/src/features/online-board/components/BoardDetailsHeader/FlightStatusButton.test.tsx new file mode 100644 index 00000000..36966463 --- /dev/null +++ b/src/features/online-board/components/BoardDetailsHeader/FlightStatusButton.test.tsx @@ -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(); + expect(screen.getByText("SHARED.DETAILS")).toBeTruthy(); + }); + + it("has data-testid flight-status-button", () => { + render(); + expect(screen.getByTestId("flight-status-button")).toBeTruthy(); + }); + + it("SU click opens native onlineboard details URL", () => { + render(); + fireEvent.click(screen.getByTestId("flight-status-button")); + const openMock = window.open as ReturnType; + expect(openMock).toHaveBeenCalledTimes(1); + expect(openMock.mock.calls[0]![0]).toBe("/ru/onlineboard/SU0022-20260417"); + }); + + it("HZ click opens flyaurora URL", () => { + render(); + fireEvent.click(screen.getByTestId("flight-status-button")); + const openMock = window.open as ReturnType; + expect(openMock).toHaveBeenCalledTimes(1); + expect(openMock.mock.calls[0]![0]).toContain("flyaurora.ru"); + }); + + it("small prop adds --small modifier class", () => { + render(); + 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(); + const btn = screen.getByTestId("flight-status-button"); + expect(btn.className).not.toContain("flight-action-btn--small"); + }); +}); diff --git a/src/features/online-board/components/BoardDetailsHeader/FlightStatusButton.tsx b/src/features/online-board/components/BoardDetailsHeader/FlightStatusButton.tsx new file mode 100644 index 00000000..52e78c30 --- /dev/null +++ b/src/features/online-board/components/BoardDetailsHeader/FlightStatusButton.tsx @@ -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 = ({ 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 ( + + ); +};