diff --git a/src/assets.d.ts b/src/assets.d.ts new file mode 100644 index 00000000..a8590e5b --- /dev/null +++ b/src/assets.d.ts @@ -0,0 +1,29 @@ +declare module "*.svg" { + const src: string; + export default src; +} + +declare module "*.png" { + const src: string; + export default src; +} + +declare module "*.jpg" { + const src: string; + export default src; +} + +declare module "*.jpeg" { + const src: string; + export default src; +} + +declare module "*.gif" { + const src: string; + export default src; +} + +declare module "*.webp" { + const src: string; + export default src; +} diff --git a/src/features/online-board/components/details-panels/MealPanel.test.tsx b/src/features/online-board/components/details-panels/MealPanel.test.tsx new file mode 100644 index 00000000..12f1beb3 --- /dev/null +++ b/src/features/online-board/components/details-panels/MealPanel.test.tsx @@ -0,0 +1,48 @@ +// @vitest-environment jsdom +import { describe, it, expect, vi } from "vitest"; +import { render, screen } from "@testing-library/react"; +import { MealPanel } from "./MealPanel.js"; +import type { IMealItem } from "../../types.js"; + +vi.mock("@/i18n/provider.js", () => ({ + useTranslation: () => ({ t: (k: string) => k }), +})); + +describe("MealPanel", () => { + it("renders an icon for Economy meal", () => { + const meals: IMealItem[] = [{ type: "Economy" }]; + render(); + expect(screen.getByTestId("meal-icon-Economy")).toBeTruthy(); + }); + + it("renders icons for multiple meal types", () => { + const meals: IMealItem[] = [ + { type: "Economy" }, { type: "Comfort" }, { type: "Business" }, + ]; + render(); + expect(screen.getByTestId("meal-icon-Economy")).toBeTruthy(); + expect(screen.getByTestId("meal-icon-Comfort")).toBeTruthy(); + expect(screen.getByTestId("meal-icon-Business")).toBeTruthy(); + }); + + it("skips Special type (no link defined)", () => { + const meals: IMealItem[] = [{ type: "Special" }, { type: "Economy" }]; + render(); + expect(screen.queryByTestId("meal-icon-Special")).toBeNull(); + expect(screen.getByTestId("meal-icon-Economy")).toBeTruthy(); + }); + + it("meal icon is wrapped in a link to aeroflot.ru", () => { + const meals: IMealItem[] = [{ type: "Economy" }]; + render(); + const link = screen.getByTestId("meal-icon-Economy").closest("a"); + expect(link?.getAttribute("href")).toContain("aeroflot.ru"); + expect(link?.getAttribute("href")).toContain("meal-type_0"); + }); + + it("has panel data-testid", () => { + const meals: IMealItem[] = [{ type: "Economy" }]; + render(); + expect(screen.getByTestId("meal-panel")).toBeTruthy(); + }); +}); diff --git a/src/features/online-board/components/details-panels/MealPanel.tsx b/src/features/online-board/components/details-panels/MealPanel.tsx new file mode 100644 index 00000000..d2f4fdf1 --- /dev/null +++ b/src/features/online-board/components/details-panels/MealPanel.tsx @@ -0,0 +1,56 @@ +import type { FC } from "react"; +import { useTranslation } from "@/i18n/provider.js"; +import type { IMealItem, MealType } from "../../types.js"; +import { MEAL_LINKS } from "./shared.js"; +import econoIcon from "./icons/econom.svg"; +import comfortIcon from "./icons/comfort.svg"; +import businessIcon from "./icons/business.svg"; +import "./panels.scss"; + +const MEAL_ICON_URL: Record, string> = { + Economy: econoIcon, + Comfort: comfortIcon, + Business: businessIcon, +}; + +const MEAL_LABEL_KEYS: Record, string> = { + Economy: "DETAILS.MEAL_ECONOMY", + Comfort: "DETAILS.MEAL_COMFORT", + Business: "DETAILS.MEAL_BUSINESS", +}; + +const MEAL_ORDER = ["Economy", "Comfort", "Business"] as const; + +export interface MealPanelProps { + meals: IMealItem[]; +} + +export const MealPanel: FC = ({ meals }) => { + const { t } = useTranslation(); + const types = new Set(meals.map((m) => m.type)); + + return ( +
+
+ {MEAL_ORDER.map((type) => + types.has(type) ? ( + + {t(MEAL_LABEL_KEYS[type])} + {t(MEAL_LABEL_KEYS[type])} + + ) : null, + )} +
+
+ ); +};