From 77ddf05d0f3ac2d6bb4cf5b3b73339ea66bd3dcf Mon Sep 17 00:00:00 2001 From: Yossi Saadi Date: Wed, 11 Sep 2024 16:14:45 +0300 Subject: [PATCH] feat(ModalHeader): header component to be used in modal (#2413) --- .../ModalHeader/ModalHeader.module.scss | 14 +++++ .../ModalNew/ModalHeader/ModalHeader.tsx | 52 ++++++++++++++++ .../ModalNew/ModalHeader/ModalHeader.types.ts | 19 ++++++ .../__tests__/ModalHeader.test.tsx | 59 +++++++++++++++++++ packages/core/src/tests/constants.ts | 1 + 5 files changed, 145 insertions(+) create mode 100644 packages/core/src/components/ModalNew/ModalHeader/ModalHeader.module.scss create mode 100644 packages/core/src/components/ModalNew/ModalHeader/ModalHeader.tsx create mode 100644 packages/core/src/components/ModalNew/ModalHeader/ModalHeader.types.ts create mode 100644 packages/core/src/components/ModalNew/ModalHeader/__tests__/ModalHeader.test.tsx diff --git a/packages/core/src/components/ModalNew/ModalHeader/ModalHeader.module.scss b/packages/core/src/components/ModalNew/ModalHeader/ModalHeader.module.scss new file mode 100644 index 0000000000..2da14a609b --- /dev/null +++ b/packages/core/src/components/ModalNew/ModalHeader/ModalHeader.module.scss @@ -0,0 +1,14 @@ +.header { + width: 100%; + align-self: flex-start; + + .title { + width: 100%; + } + + .descriptionIcon { + flex-shrink: 0; + margin-inline-end: var(--spacing-xs); + color: var(--icon-color); + } +} diff --git a/packages/core/src/components/ModalNew/ModalHeader/ModalHeader.tsx b/packages/core/src/components/ModalNew/ModalHeader/ModalHeader.tsx new file mode 100644 index 0000000000..e10a9207f8 --- /dev/null +++ b/packages/core/src/components/ModalNew/ModalHeader/ModalHeader.tsx @@ -0,0 +1,52 @@ +import React, { forwardRef } from "react"; +import cx from "classnames"; +import { getTestId } from "../../../tests/test-ids-utils"; +import { ComponentDefaultTestId } from "../../../tests/constants"; +import styles from "./ModalHeader.module.scss"; +import { ModalHeaderProps } from "./ModalHeader.types"; +import Flex from "../../Flex/Flex"; +import Heading from "../../Heading/Heading"; +import Text from "../../Text/Text"; +import Icon from "../../Icon/Icon"; + +const ModalHeader = forwardRef( + ( + { title, description, descriptionIcon, className, id, "data-testid": dataTestId }: ModalHeaderProps, + ref: React.ForwardedRef + ) => { + return ( + + + {title} + + {description && ( + + {descriptionIcon && ( + + )} + {typeof description === "string" ? ( + + {description} + + ) : ( + description + )} + + )} + + ); + } +); + +export default ModalHeader; diff --git a/packages/core/src/components/ModalNew/ModalHeader/ModalHeader.types.ts b/packages/core/src/components/ModalNew/ModalHeader/ModalHeader.types.ts new file mode 100644 index 0000000000..8e5f9a750f --- /dev/null +++ b/packages/core/src/components/ModalNew/ModalHeader/ModalHeader.types.ts @@ -0,0 +1,19 @@ +import React from "react"; +import { SubIcon, VibeComponentProps } from "../../../types"; + +interface WithoutDescription { + description?: never; + descriptionIcon?: never; +} + +interface WithDescription { + description: string | React.ReactNode; + descriptionIcon?: + | SubIcon + | { + name: SubIcon; + className?: string; + }; +} + +export type ModalHeaderProps = { title: string } & (WithoutDescription | WithDescription) & VibeComponentProps; diff --git a/packages/core/src/components/ModalNew/ModalHeader/__tests__/ModalHeader.test.tsx b/packages/core/src/components/ModalNew/ModalHeader/__tests__/ModalHeader.test.tsx new file mode 100644 index 0000000000..2ef1c295ed --- /dev/null +++ b/packages/core/src/components/ModalNew/ModalHeader/__tests__/ModalHeader.test.tsx @@ -0,0 +1,59 @@ +import React from "react"; +import { render } from "@testing-library/react"; +import ModalHeader from "../ModalHeader"; +import { Text as TextIcon } from "../../../Icon/Icons"; + +describe("ModalHeader", () => { + const title = "Test Modal Header"; + const simpleDescription = "This is a description"; + const descriptionIcon = TextIcon; + + it("renders the title correctly", () => { + const { getByText } = render(); + + expect(getByText(title)).toBeInTheDocument(); + }); + + it("renders the description correctly", () => { + const { getByText } = render(); + + expect(getByText(simpleDescription)).toBeInTheDocument(); + }); + + it("renders the description icon when provided", () => { + const { getByText, getByTestId } = render( + + ); + + expect(getByText(simpleDescription)).toBeInTheDocument(); + expect(getByTestId("icon")).toBeInTheDocument(); + }); + + it("renders custom description node", () => { + const customDescription = Custom description content; + + const { getByTestId } = render(); + + expect(getByTestId("custom-description")).toBeInTheDocument(); + }); + + it("does not render description when not provided", () => { + const { queryByText } = render(); + + expect(queryByText(simpleDescription)).not.toBeInTheDocument(); + }); + + it("renders with description icon when descriptionIcon is an object", () => { + const descriptionIconObject = { + name: TextIcon, + className: "with-custom-icon-class" + }; + + const { getByTestId } = render( + + ); + + const icon = getByTestId("icon"); + expect(icon).toHaveClass(descriptionIconObject.className); + }); +}); diff --git a/packages/core/src/tests/constants.ts b/packages/core/src/tests/constants.ts index 33cdfa8488..02edd008a2 100644 --- a/packages/core/src/tests/constants.ts +++ b/packages/core/src/tests/constants.ts @@ -103,6 +103,7 @@ export enum ComponentDefaultTestId { MODAL_CONTENT = "modal-content", MODAL_HEADER = "modal-header", MODAL_FOOTER_BUTTONS = "modal-footer-buttons", + MODAL_NEXT_HEADER = "modal-header", FORMATTED_NUMBER = "formatted-number", HIDDEN_TEXT = "hidden-text", DIALOG_CONTENT_CONTAINER = "dialog-content-container",