diff --git a/ui/src/components/modal/dynamic-modal.tsx b/ui/src/components/modal/dynamic-modal.tsx
new file mode 100644
index 00000000..d8933183
--- /dev/null
+++ b/ui/src/components/modal/dynamic-modal.tsx
@@ -0,0 +1,56 @@
+'use client';
+
+import {
+ AlertDialog,
+ AlertDialogHeader,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogTitle,
+ AlertDialogFooter,
+ AlertDialogCancel
+} from '@/components/ui/alert-dialog';
+import { ReactNode, useState } from 'react';
+import { Button } from '@/components/ui/button';
+
+const DynamicModal = ({
+ open,
+ title,
+ body,
+ buttons
+}: {
+ open: boolean;
+ title: string;
+ body: string;
+ buttons?: ReactNode[];
+}) => {
+ const [openDynamicModal, setOpenDynamicModal] = useState(open);
+
+ /**
+ * Closes the modal.
+ */
+ const close = () => {
+ setOpenDynamicModal(false);
+ };
+
+ return (
+
+
+
+ {title}
+ {body}
+
+
+ close()}>OK
+ {/*Any buttons that should lead the user to a different page.*/}
+ {buttons?.map((button, index) => (
+
+ ))}
+
+
+
+ );
+};
+
+export default DynamicModal;
diff --git a/ui/tests/components/modal/dynamic-modal.test.tsx b/ui/tests/components/modal/dynamic-modal.test.tsx
new file mode 100644
index 00000000..f52cef0b
--- /dev/null
+++ b/ui/tests/components/modal/dynamic-modal.test.tsx
@@ -0,0 +1,74 @@
+import { fireEvent, render, screen } from '@testing-library/react';
+import DynamicModal from '@/components/modal/dynamic-modal';
+import Link from 'next/link';
+
+describe('DynamicModal', () => {
+ it('should open an informational modal with test contents and no extra buttons', () => {
+ render();
+ fireEvent.focus(document);
+
+ expect(screen.getByRole('alertdialog', { name: 'A Dynamic Title' })).toBeInTheDocument();
+ expect(screen.getByRole('alertdialog')).toHaveTextContent('Some dynamic message here.');
+
+ expect(screen.getByRole('button', { name: 'OK' })).toBeInTheDocument();
+ });
+
+ it('should open an informational modal with test contents and extra buttons', () => {
+ render(
+ Button1>, <>Button2>]}
+ />
+ );
+ fireEvent.focus(document);
+
+ expect(screen.getByRole('alertdialog', { name: 'A Dynamic Title' })).toBeInTheDocument();
+ expect(screen.getByRole('alertdialog')).toHaveTextContent('Some dynamic message here.');
+
+ expect(screen.getByRole('button', { name: 'OK' })).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Button1' })).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Button2' })).toBeInTheDocument();
+ });
+
+ it('should close the modal upon clicking the OK button', () => {
+ render();
+ fireEvent.focus(document);
+
+ expect(screen.getByRole('alertdialog', { name: 'A Dynamic Title' })).toBeInTheDocument();
+ expect(screen.getByRole('heading', { name: 'A Dynamic Title' })).toBeInTheDocument();
+ expect(screen.getByRole('alertdialog')).toHaveTextContent('Some dynamic message here.');
+
+ expect(screen.getByRole('button', { name: 'OK' })).toBeInTheDocument();
+
+ fireEvent.click(screen.getByRole('button', { name: 'OK' }));
+ expect(screen.queryByRole('alertdialog', { name: 'A Dynamic Title' })).not.toBeInTheDocument();
+ });
+
+ it('should close the modal and route to the provided link (Feedback)', () => {
+ render(
+
+ Feedback
+
+ ]}
+ />
+ );
+ fireEvent.focus(document);
+
+ expect(screen.getByRole('alertdialog', { name: 'A Modal to the Feedback Page' })).toBeInTheDocument();
+ expect(screen.getByRole('alertdialog')).toHaveTextContent('Click Feedback to go to the Feedback Page.');
+
+ expect(screen.getByRole('button', { name: 'OK' })).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Feedback' })).toBeInTheDocument();
+ expect(screen.getByRole('link', { name: 'Feedback' })).toHaveAttribute('href', '/feedback');
+
+ fireEvent.click(screen.getByRole('button', { name: 'Feedback' }));
+ expect(screen.queryByRole('alertdialog', { name: 'A Modal to the Feedback Page' })).not.toBeInTheDocument();
+ });
+});