Skip to content

Commit

Permalink
Merge branch 'main' into cmdct-4139-ltss-1
Browse files Browse the repository at this point in the history
  • Loading branch information
ailZhou committed Dec 4, 2024
2 parents 71b8b08 + b98aad3 commit 8fe1890
Show file tree
Hide file tree
Showing 3 changed files with 242 additions and 0 deletions.
2 changes: 2 additions & 0 deletions services/ui-src/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export { ExportedReportPage } from "./pages/Export/ExportedReportPage";
// menus
export { Menu } from "./menus/Menu";
export { MenuOption } from "./menus/MenuOption";
// modals
export { Modal } from "./modals/Modal";
// Redirects
export { PostLogoutRedirect } from "./PostLogoutRedirect/index";
// tables
Expand Down
57 changes: 57 additions & 0 deletions services/ui-src/src/components/modals/Modal.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { fireEvent, render, screen } from "@testing-library/react";
import { axe } from "jest-axe";
//components
import { Text } from "@chakra-ui/react";
import { Modal } from "components";

const mockCloseHandler = jest.fn();
const mockConfirmationHandler = jest.fn();

const content = {
heading: "Dialog Heading",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed accumsan diam vitae metus lacinia, eget tempor purus placerat.",
actionButtonText: "Dialog Action",
closeButtonText: "Cancel",
};

const modalComponent = (
<Modal
onConfirmHandler={mockConfirmationHandler}
modalDisclosure={{
isOpen: true,
onClose: mockCloseHandler,
}}
content={content}
>
<Text>{content.body}</Text>
</Modal>
);

describe("Test Modal", () => {
beforeEach(() => {
render(modalComponent);
});

test("Modal shows the contents", () => {
expect(screen.getByText(content.heading)).toBeTruthy();
expect(screen.getByText(content.body)).toBeTruthy();
});

test("Modals action button can be clicked", () => {
fireEvent.click(screen.getByText(/Dialog Action/i));
expect(mockConfirmationHandler).toHaveBeenCalledTimes(1);
});

test("Modals close button can be clicked", () => {
fireEvent.click(screen.getByText(/Cancel/i));
expect(mockCloseHandler).toHaveBeenCalledTimes(1);
});
});

describe("Test Modal accessibility", () => {
it("Should not have basic accessibility issues", async () => {
const { container } = render(modalComponent);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});
183 changes: 183 additions & 0 deletions services/ui-src/src/components/modals/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import { ReactNode } from "react";
import {
Box,
Button,
Flex,
Heading,
Image,
Modal as ChakraModal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Spinner,
} from "@chakra-ui/react";
import closeIcon from "assets/icons/close/icon_close_primary.svg";

export const Modal = ({
modalDisclosure,
content,
onConfirmHandler,
submitting,
formId,
children,
}: Props) => {
return (
<ChakraModal
isOpen={modalDisclosure.isOpen}
onClose={modalDisclosure.onClose}
preserveScrollBarGap={true}
>
<ModalOverlay />
<ModalContent sx={sx.modalContent}>
<ModalHeader sx={sx.modalHeader}>
<Heading as="h1" sx={sx.modalHeaderText}>
{content.heading}
</Heading>
</ModalHeader>
{content.subheading && (
<Box sx={sx.modalSubheader}>{content.subheading}</Box>
)}
<Flex sx={sx.modalCloseContainer}>
<Button
sx={sx.modalClose}
leftIcon={<Image src={closeIcon} alt="Close" sx={sx.closeIcon} />}
variant="link"
onClick={modalDisclosure.onClose}
>
Close
</Button>
</Flex>
<ModalBody sx={sx.modalBody}>{children}</ModalBody>
<ModalFooter sx={sx.modalFooter}>
{formId && (
<Button
sx={sx.action}
form={formId}
type="submit"
data-testid="modal-submit-button"
>
{submitting ? <Spinner size="md" /> : content.actionButtonText}
</Button>
)}
{onConfirmHandler && (
<Button
sx={sx.action}
onClick={() => onConfirmHandler()}
data-testid="modal-submit-button"
>
{submitting ? <Spinner size="md" /> : content.actionButtonText}
</Button>
)}
{content.closeButtonText && (
<Button
sx={sx.close}
variant="link"
onClick={modalDisclosure.onClose}
>
{content.closeButtonText}
</Button>
)}
</ModalFooter>
</ModalContent>
</ChakraModal>
);
};

interface Props {
modalDisclosure: {
isOpen: boolean;
onClose: any;
};
content: {
heading: string;
subheading?: string;
actionButtonText: string | ReactNode;
closeButtonText?: string;
};
submitting?: boolean;
onConfirmHandler?: Function;
formId?: string;
children?: ReactNode;
[key: string]: any;
}

const sx = {
modalContent: {
boxShadow: ".125rem .125rem .25rem",
borderRadius: "0",
maxWidth: "30rem",
marginX: "4rem",
padding: "2rem",
},
modalHeader: {
padding: "0",
},
modalHeaderText: {
padding: "0 4rem 0 0",
fontSize: "2xl",
fontWeight: "bold",
},
modalSubheader: {
margin: "0.5rem auto -1rem auto",
},
modalCloseContainer: {
alignItems: "center",
justifycontent: "center",
flexShrink: "0",
position: "absolute",
top: "2rem",
right: "2rem",
},
modalClose: {
span: {
margin: "0.25rem",
paddingTop: "0.06rem",
svg: {
fontSize: "xs",
width: "xs",
height: "xs",
},
},
},
modalBody: {
paddingX: "0",
paddingY: "1rem",
},
modalFooter: {
justifyContent: "flex-start",
padding: "0",
paddingTop: "2rem",
},
action: {
justifyContent: "center",
marginRight: "2rem",
minWidth: "10rem",
span: {
marginLeft: "0.5rem",
marginRight: "-0.25rem",
"&.ds-c-spinner": {
marginLeft: 0,
},
},
".mobile &": {
fontSize: "sm",
},
},
close: {
justifyContent: "start",
padding: ".5rem 1rem",
span: {
marginLeft: "0rem",
marginRight: "0.5rem",
},
".mobile &": {
fontSize: "sm",
marginRight: "0",
},
},
closeIcon: {
width: "0.75rem",
},
};

0 comments on commit 8fe1890

Please sign in to comment.