diff --git a/jest.config.ts b/jest.config.ts index ad208fa..f815810 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -6,6 +6,7 @@ export default { "src/components/**/*.{js,jsx,ts,tsx}", "src/pages/**/*.{js,jsx,ts,tsx}", "src/utils/**/*.{js,jsx,ts,tsx}", + "src/modals/**/*.{js,jsx,ts,tsx}", ], // Automatically clear mock calls and instances between every test diff --git a/src/components/FlightTicket/FlightTicket.definitions.tsx b/src/components/FlightTicket/FlightTicket.definitions.tsx index 6a2c79a..65d38ee 100644 --- a/src/components/FlightTicket/FlightTicket.definitions.tsx +++ b/src/components/FlightTicket/FlightTicket.definitions.tsx @@ -1,11 +1,9 @@ /* eslint-disable autofix/no-unused-vars */ +import type { FlightLegData } from "../../interfaces/flight-leg.interface"; + export interface FlightTicketProps { - date: string; - departingAirport: string; - arrivingAirport: string; - airline: string; - legType: LegType; + flight: FlightLegData; colorVariant: FlightTicketColorVariant; isLastElement: boolean; } diff --git a/src/components/FlightTicket/FlightTicket.module.css b/src/components/FlightTicket/FlightTicket.module.css index 8b31a71..47bd475 100644 --- a/src/components/FlightTicket/FlightTicket.module.css +++ b/src/components/FlightTicket/FlightTicket.module.css @@ -4,6 +4,16 @@ font-family: "Roboto", sans-serif; box-shadow: 0px 5px 5px #949494; border-radius: 20px; + transition: 0.2s ease-in-out; +} + +.ticketBase:hover { + flex-direction: column; + width: 270px; + font-family: "Roboto", sans-serif; + box-shadow: 0px 5px 5px #373636; + border-radius: 20px; + cursor: pointer; } .ticketBorder { diff --git a/src/components/FlightTicket/FlightTicket.tsx b/src/components/FlightTicket/FlightTicket.tsx index 7ee1b8c..94d2bc7 100644 --- a/src/components/FlightTicket/FlightTicket.tsx +++ b/src/components/FlightTicket/FlightTicket.tsx @@ -1,5 +1,6 @@ import styles from "./FlightTicket.module.css"; import { FlightTicketColorVariant, LegType } from "./FlightTicket.definitions"; +import FlightDetailsModal from "../../modals/FlightDetailsModal/FlightDetailsModal"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCircleArrowRight, @@ -7,18 +8,15 @@ import { faPlaneDeparture, faPlane, } from "@fortawesome/free-solid-svg-icons"; +import { useState } from "react"; import type { FlightTicketProps } from "./FlightTicket.definitions"; const FlightTicket = ({ - date, - departingAirport, - arrivingAirport, - airline, - legType, + flight, colorVariant, isLastElement, }: FlightTicketProps) => { - const renderLegTypeIcon = (legType: LegType) => { + const renderLegTypeIcon = (legType: string) => { if (legType === LegType.DEPARTURE) { return ; } else if (legType === LegType.CONNECTING) { @@ -28,55 +26,83 @@ const FlightTicket = ({ } }; + const [isModalOpen, setIsModalOpen] = useState(false); + + const closeModal = () => { + setIsModalOpen(false); + }; + + const openModal = () => { + setIsModalOpen(true); + }; + return ( -
-
-
{date}
-
-
-
-
{departingAirport}
- -
{arrivingAirport}
+
+
+
+
+ {flight.fields["Departure Date/Time"].split("T")[0]} +
+
+
+
+ {flight.fields["Departure Airport"]} +
+ +
+ {flight.fields["Arrival Airport"]} +
+
-
-
-
{legType.toLocaleUpperCase()}
- {renderLegTypeIcon(legType)} +
+
+
+ {flight.fields["Leg Type"].toLocaleUpperCase()} +
+ {renderLegTypeIcon(flight.fields["Leg Type"])} +
+
+ {flight.fields.Airline.toLocaleUpperCase()} +
-
{airline.toLocaleUpperCase()}
+
+
+
click to see more details
-
-
click to see more details
-
+ {isModalOpen && ( + + )}
); }; diff --git a/src/components/FlightTicket/__tests__/FlightTicket.test.tsx b/src/components/FlightTicket/__tests__/FlightTicket.test.tsx index e8b515c..9c99e20 100644 --- a/src/components/FlightTicket/__tests__/FlightTicket.test.tsx +++ b/src/components/FlightTicket/__tests__/FlightTicket.test.tsx @@ -1,5 +1,6 @@ +import { createTestFlightLegData } from "../../../util/test-data.util"; import FlightTicket from "../FlightTicket"; -import { LegType, FlightTicketColorVariant } from "../FlightTicket.definitions"; +import { FlightTicketColorVariant } from "../FlightTicket.definitions"; import { render } from "@testing-library/react"; describe("FlightTicket Tests", () => { @@ -7,141 +8,163 @@ describe("FlightTicket Tests", () => { // test color variance, test different flight types test("Test example component with border and blue color", () => { - const component = render( - , - ); + const mockProps = { + flight: createTestFlightLegData(), + colorVariant: FlightTicketColorVariant.BLUE, + isLastElement: false, + }; + + const component = render(); // Check that the component renders expect(component).toBeTruthy(); //check that date is the same - const date = component.getByText("11/23/2023"); + const date = component.getByText( + mockProps.flight.fields["Departure Date/Time"].split("T")[0], + ); expect(date).toBeTruthy(); //check that the departing airport is the same - const departingAirport = component.getByText("LAX"); + const departingAirport = component.getByText( + mockProps.flight.fields["Departure Airport"], + ); expect(departingAirport).toBeTruthy(); - const arrivingAirport = component.getByText("BNA"); + const arrivingAirport = component.getByText( + mockProps.flight.fields["Arrival Airport"], + ); expect(arrivingAirport).toBeTruthy(); - const airline = component.getByText("SOUTHWEST"); + const airline = component.getByText( + mockProps.flight.fields.Airline.toLocaleUpperCase(), + ); expect(airline).toBeTruthy(); - const legType = component.getByText("CONNECTING"); + const legType = component.getByText( + mockProps.flight.fields["Leg Type"].toLocaleUpperCase(), + ); expect(legType).toBeTruthy(); }); /**************************SECOND TEST*******************************/ test("Test example component with no border and red color", () => { - const component = render( - , - ); + const mockProps = { + flight: createTestFlightLegData(), + colorVariant: FlightTicketColorVariant.RED, + isLastElement: true, + }; + + const component = render(); // Check that the component renders expect(component).toBeTruthy(); //check that date is the same - const date = component.getByText("11/23/2023"); + const date = component.getByText( + mockProps.flight.fields["Departure Date/Time"].split("T")[0], + ); expect(date).toBeTruthy(); //check that the departing airport is the same - const departingAirport = component.getByText("LAX"); + const departingAirport = component.getByText( + mockProps.flight.fields["Departure Airport"], + ); expect(departingAirport).toBeTruthy(); - const arrivingAirport = component.getByText("BNA"); + const arrivingAirport = component.getByText( + mockProps.flight.fields["Arrival Airport"], + ); expect(arrivingAirport).toBeTruthy(); - const airline = component.getByText("SOUTHWEST"); + const airline = component.getByText( + mockProps.flight.fields.Airline.toLocaleUpperCase(), + ); expect(airline).toBeTruthy(); - const legType = component.getByText("DEPARTURE"); + const legType = component.getByText( + mockProps.flight.fields["Leg Type"].toLocaleUpperCase(), + ); expect(legType).toBeTruthy(); }); /**************************THIRD TEST*******************************/ test("Test example component with no border and blue color", () => { - const component = render( - , - ); + const mockProps = { + flight: createTestFlightLegData(), + colorVariant: FlightTicketColorVariant.BLUE, + isLastElement: true, + }; + const component = render(); // Check that the component renders expect(component).toBeTruthy(); //check that date is the same - const date = component.getByText("11/23/2023"); + const date = component.getByText( + mockProps.flight.fields["Departure Date/Time"].split("T")[0], + ); expect(date).toBeTruthy(); //check that the departing airport is the same - const departingAirport = component.getByText("LAX"); + const departingAirport = component.getByText( + mockProps.flight.fields["Departure Airport"], + ); expect(departingAirport).toBeTruthy(); - const arrivingAirport = component.getByText("BNA"); + const arrivingAirport = component.getByText( + mockProps.flight.fields["Arrival Airport"], + ); expect(arrivingAirport).toBeTruthy(); - const airline = component.getByText("SOUTHWEST"); + const airline = component.getByText( + mockProps.flight.fields.Airline.toLocaleUpperCase(), + ); expect(airline).toBeTruthy(); - const legType = component.getByText("RETURN"); + const legType = component.getByText( + mockProps.flight.fields["Leg Type"].toLocaleUpperCase(), + ); expect(legType).toBeTruthy(); }); /**************************Fourth TEST*******************************/ test("Test example component with no border and red color", () => { - const component = render( - , - ); + const mockProps = { + flight: createTestFlightLegData(), + colorVariant: FlightTicketColorVariant.RED, + isLastElement: true, + }; + const component = render(); // Check that the component renders expect(component).toBeTruthy(); //check that date is the same - const date = component.getByText("11/23/2023"); + const date = component.getByText( + mockProps.flight.fields["Departure Date/Time"].split("T")[0], + ); expect(date).toBeTruthy(); //check that the departing airport is the same - const departingAirport = component.getByText("LAX"); + const departingAirport = component.getByText( + mockProps.flight.fields["Departure Airport"], + ); expect(departingAirport).toBeTruthy(); - const arrivingAirport = component.getByText("BNA"); + const arrivingAirport = component.getByText( + mockProps.flight.fields["Arrival Airport"], + ); expect(arrivingAirport).toBeTruthy(); - const airline = component.getByText("SOUTHWEST"); + const airline = component.getByText( + mockProps.flight.fields.Airline.toLocaleUpperCase(), + ); expect(airline).toBeTruthy(); - const legType = component.getByText("RETURN"); + const legType = component.getByText( + mockProps.flight.fields["Leg Type"].toLocaleUpperCase(), + ); expect(legType).toBeTruthy(); }); }); diff --git a/src/interfaces/flight-leg.interface.ts b/src/interfaces/flight-leg.interface.ts index 915959e..ac8a770 100644 --- a/src/interfaces/flight-leg.interface.ts +++ b/src/interfaces/flight-leg.interface.ts @@ -14,18 +14,19 @@ export interface FlightLegData { "BL - Treatment Type": string; "BL - Site 1": string; Passengers: string[]; - "Departure Airport": string[]; - "Arrival Airport": string[]; + "Departure Airport": string; + "Arrival Airport": string; "BL - Site 1 Links": string[]; "Leg ID": string; + "Leg Type": string; "# of Linked PAX": number; "# of PAX": number; "Total Miles": number; - "Passenger Names": string; + "Passenger Names": string | string[]; "Total Cost": number; "Cost per PAX": number; "AirTable Record ID": string; - "Passenger AirTable Record IDs": string; + "Passenger AirTable Record IDs": string | string[]; "Log Airline Credit": { label: string; url: string; diff --git a/src/modals/FlightDetailsModal/FlightDetailsModal.definitions.tsx b/src/modals/FlightDetailsModal/FlightDetailsModal.definitions.tsx new file mode 100644 index 0000000..d6a1d62 --- /dev/null +++ b/src/modals/FlightDetailsModal/FlightDetailsModal.definitions.tsx @@ -0,0 +1,6 @@ +import type { FlightLegData } from "../../interfaces/flight-leg.interface"; + +export interface FlightDetailsModalProps { + onClose: () => void; + flight: FlightLegData; +} diff --git a/src/modals/FlightDetailsModal/FlightDetailsModal.module.css b/src/modals/FlightDetailsModal/FlightDetailsModal.module.css new file mode 100644 index 0000000..5fba7cd --- /dev/null +++ b/src/modals/FlightDetailsModal/FlightDetailsModal.module.css @@ -0,0 +1,39 @@ +.modalBackdrop { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + font-family: "Inter", sans-serif; + z-index: 1000; +} + +.modal { + background-color: white; + padding: 20px; + margin-bottom: 200px; + border-radius: 5px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + top: 10%; +} + +.exitIcon { + width: 30px; + height: 30px; +} + +.exitIcon:hover { + cursor: pointer; +} + +.modalContent { + padding-top: 10px; + padding-left: 30px; + padding-right: 30px; +} diff --git a/src/modals/FlightDetailsModal/FlightDetailsModal.tsx b/src/modals/FlightDetailsModal/FlightDetailsModal.tsx new file mode 100644 index 0000000..6cef86f --- /dev/null +++ b/src/modals/FlightDetailsModal/FlightDetailsModal.tsx @@ -0,0 +1,30 @@ +import styles from "./FlightDetailsModal.module.css"; +import { faXmark } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import type { FlightDetailsModalProps } from "./FlightDetailsModal.definitions"; + +const FlightDetailsModal = ({ onClose, flight }: FlightDetailsModalProps) => { + return ( +
+
+ +
+ {[ + flight.fields["Departure Date/Time"].substring(0, 9), + flight.fields["Departure Airport"], + flight.fields["Arrival Airport"], + flight.fields.Airline, + flight.fields["Leg Type"], + ]} +
+
+
+ ); +}; + +export default FlightDetailsModal; diff --git a/src/modals/FlightDetailsModal/__tests__/FlightDetailsModal.test.tsx b/src/modals/FlightDetailsModal/__tests__/FlightDetailsModal.test.tsx new file mode 100644 index 0000000..0d9b95b --- /dev/null +++ b/src/modals/FlightDetailsModal/__tests__/FlightDetailsModal.test.tsx @@ -0,0 +1,29 @@ +import { createTestFlightLegData } from "../../../util/test-data.util"; +import FlightDetailsModal from "../FlightDetailsModal"; +import { fireEvent, render } from "@testing-library/react"; + +describe("FlightModal Tests", () => { + /**************************FIRST TEST*******************************/ + // test that the flight modal renders and closes + + test("Test that modal renders and the close button works", () => { + const onClose = jest.fn; + const component = render( + , + ); + + // Check that the component renders + expect(component).toBeTruthy(); + + //check that close icon clicks + + const buttonToClose = component.container.querySelector("closeIcon"); + if (buttonToClose != null) { + fireEvent.click(buttonToClose); + expect(onClose).toHaveBeenCalledTimes(1); + } + }); +}); diff --git a/src/pages/MyFlights/MyFlights.tsx b/src/pages/MyFlights/MyFlights.tsx index 92e9092..47dca71 100644 --- a/src/pages/MyFlights/MyFlights.tsx +++ b/src/pages/MyFlights/MyFlights.tsx @@ -1,23 +1,23 @@ import FlightTicket from "../../components/FlightTicket/FlightTicket"; -import { - FlightTicketColorVariant, - LegType, -} from "../../components/FlightTicket/FlightTicket.definitions"; +import { FlightTicketColorVariant } from "../../components/FlightTicket/FlightTicket.definitions"; +import { createTestFlightLegData } from "../../util/test-data.util"; const MyFlights = () => { // MyFlights tab - console.log("MyFlights"); return ( - +
+ + +
); }; diff --git a/src/util/test-data.util.ts b/src/util/test-data.util.ts index 5b804b3..c54d547 100644 --- a/src/util/test-data.util.ts +++ b/src/util/test-data.util.ts @@ -248,20 +248,8 @@ export const createTestFlightLegData = ( max: 3, }), ), - "Departure Airport": faker.helpers.arrayElements( - [faker.location.city(), faker.location.city(), faker.location.city()], - faker.number.int({ - min: 1, - max: 2, - }), - ), - "Arrival Airport": faker.helpers.arrayElements( - [faker.location.city(), faker.location.city(), faker.location.city()], - faker.number.int({ - min: 1, - max: 2, - }), - ), + "Departure Airport": faker.string.alpha(3).toLocaleUpperCase(), + "Arrival Airport": faker.string.alpha(3).toLocaleUpperCase(), "BL - Site 1 Links": faker.helpers.arrayElements( [faker.internet.url(), faker.internet.url(), faker.internet.url()], faker.number.int({ @@ -274,6 +262,11 @@ export const createTestFlightLegData = ( min: 1, max: 3, }), + "Leg Type": faker.helpers.arrayElement([ + "Departure", + "Connecting", + "Return", + ]), "# of PAX": faker.number.int({ min: 1, max: 3,