From 5bb9e042cf7b3902eda6faa8f6a4b5e6be5100de Mon Sep 17 00:00:00 2001 From: Ajeyakrishna <98796547+Ajeyakrishna-k@users.noreply.github.com> Date: Sun, 22 Oct 2023 00:48:06 +0530 Subject: [PATCH] Adds a modal component (#954) * FEAT : adds a modal component * TEST : adds tests for modal component * Adds a task request form component (#955) * FEAT : adds a task request form * CHORE : refactored date call --------- Co-authored-by: Amit Prakash <34869115+iamitprakash@users.noreply.github.com> --- .../Issues/TaskRequestForm.test.tsx | 62 ++++++++ .../Unit/Components/modal/index.test.tsx | 50 ++++++ src/components/Modal/Modal | 0 src/components/Modal/index.tsx | 30 ++++ src/components/Modal/modal.module.scss | 31 ++++ src/components/issues/TaskRequestForm.tsx | 144 ++++++++++++++++++ 6 files changed, 317 insertions(+) create mode 100644 __tests__/Unit/Components/Issues/TaskRequestForm.test.tsx create mode 100644 __tests__/Unit/Components/modal/index.test.tsx create mode 100644 src/components/Modal/Modal create mode 100644 src/components/Modal/index.tsx create mode 100644 src/components/Modal/modal.module.scss create mode 100644 src/components/issues/TaskRequestForm.tsx diff --git a/__tests__/Unit/Components/Issues/TaskRequestForm.test.tsx b/__tests__/Unit/Components/Issues/TaskRequestForm.test.tsx new file mode 100644 index 000000000..0fbad8f33 --- /dev/null +++ b/__tests__/Unit/Components/Issues/TaskRequestForm.test.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { render, fireEvent, screen, waitFor } from '@testing-library/react'; +import TaskRequestForm from '@/components/issues/TaskRequestForm'; + +describe('TaskRequestForm Component', () => { + const date = new Date(); + const today = date.toISOString().split('T')[0]; + date.setDate(date.getDate() + 7); + const sevenDaysFromToday = date.toISOString().split('T')[0]; + test('renders form with default values', () => { + const createTaskRequestMock = jest.fn(); + render(); + const startDateInput = screen.getByLabelText( + /Start date:/i + ) as HTMLInputElement; + const endDateInput = screen.getByLabelText( + /End date:/i + ) as HTMLInputElement; + const descriptionTextarea = screen.getByLabelText( + /Description:/i + ) as HTMLTextAreaElement; + const submitButton = screen.getByRole('button', { + name: /Create Request/i, + }); + expect(startDateInput.value).toBe(today); + expect(endDateInput.value).toBe(sevenDaysFromToday); + expect(descriptionTextarea.value).toBe(''); + expect(submitButton).toBeInTheDocument(); + }); + test('updates state when values are entered', () => { + const createTaskRequestMock = jest.fn(); + render(); + const startDateInput = screen.getByLabelText( + /Start date:/i + ) as HTMLInputElement; + const endDateInput = screen.getByLabelText( + /End date:/i + ) as HTMLInputElement; + const descriptionTextarea = screen.getByLabelText( + /Description:/i + ) as HTMLTextAreaElement; + fireEvent.change(startDateInput, { target: { value: '2023-10-17' } }); + fireEvent.change(endDateInput, { target: { value: '2023-10-24' } }); + fireEvent.change(descriptionTextarea, { + target: { value: 'Test description' }, + }); + expect(startDateInput.value).toBe('2023-10-17'); + expect(endDateInput.value).toBe('2023-10-24'); + expect(descriptionTextarea.value).toBe('Test description'); + }); + test('submits form and calls createTaskRequest function', async () => { + const createTaskRequestMock = jest.fn(); + render(); + const submitButton = screen.getByRole('button', { + name: /Create Request/i, + }); + fireEvent.click(submitButton); + await waitFor(() => { + expect(createTaskRequestMock).toHaveBeenCalled(); + }); + }); +}); diff --git a/__tests__/Unit/Components/modal/index.test.tsx b/__tests__/Unit/Components/modal/index.test.tsx new file mode 100644 index 000000000..cda8b3bb4 --- /dev/null +++ b/__tests__/Unit/Components/modal/index.test.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import Modal from '@/components/Modal/index'; + +describe('Modal Component', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + test('renders modal when isOpen is true', () => { + const mockedFunction = jest.fn(); + const { getByTestId } = render( + + ); + const modalOverlay = getByTestId('modal-overlay'); + const modalBox = getByTestId('modal-box'); + expect(modalOverlay).toBeInTheDocument(); + expect(modalBox).toBeInTheDocument(); + }); + + test('does not render modal when isOpen is false', () => { + const mockedFunction = jest.fn(); + const { queryByTestId } = render( + + ); + const modalOverlay = queryByTestId('modal-overlay'); + const modalBox = queryByTestId('modal-box'); + expect(modalOverlay).toBeNull(); + expect(modalBox).toBeNull(); + }); + + test('toggle should be called when overlay is clicked', () => { + const toggleMock = jest.fn(); + const { getByTestId } = render( + + ); + const modalOverlay = getByTestId('modal-overlay'); + fireEvent.click(modalOverlay); + expect(toggleMock).toHaveBeenCalled(); + }); + + test('toggle should not be called when modal is clicked', () => { + const toggleMock = jest.fn(); + const { getByTestId } = render( + + ); + const modalBox = getByTestId('modal-box'); + fireEvent.click(modalBox); + expect(toggleMock).not.toHaveBeenCalled(); + }); +}); diff --git a/src/components/Modal/Modal b/src/components/Modal/Modal new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx new file mode 100644 index 000000000..04f36bbd7 --- /dev/null +++ b/src/components/Modal/index.tsx @@ -0,0 +1,30 @@ +import React, { ReactNode } from 'react'; +import styles from '@/components/Modal/modal.module.scss'; + +interface ModalType { + children?: ReactNode; + isOpen: boolean; + toggle: () => void; +} + +export default function Modal(props: ModalType) { + return ( + <> + {props.isOpen && ( +
+
e.stopPropagation()} + className={styles.modalBox} + data-testid="modal-box" + > + {props.children} +
+
+ )} + + ); +} diff --git a/src/components/Modal/modal.module.scss b/src/components/Modal/modal.module.scss new file mode 100644 index 000000000..a146406eb --- /dev/null +++ b/src/components/Modal/modal.module.scss @@ -0,0 +1,31 @@ +@keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} +.modalOverlay { + z-index: 9999; + width: 100%; + height: 100%; + position: fixed; + top: 0; + left: 0; + background: rgba(0, 0, 0, 0.7); + display: flex; + justify-content: center; + align-items: center; +} + +.modalBox { + display: block; + background: white; + width: max-content; + height: max-content; + padding: 1rem; + border-radius: 1rem; + animation: fadeIn ease-in-out 250ms; +} diff --git a/src/components/issues/TaskRequestForm.tsx b/src/components/issues/TaskRequestForm.tsx new file mode 100644 index 000000000..55bfd3f81 --- /dev/null +++ b/src/components/issues/TaskRequestForm.tsx @@ -0,0 +1,144 @@ +import { FC, MouseEvent, useReducer, useState } from 'react'; + +import styles from '@/components/issues/Card.module.scss'; + +import { reducerAction } from '@/types/ProgressUpdates'; +import { Loader } from '../tasks/card/Loader'; + +type ActionFormReducer = { + startedOn: number | string; + endsOn: number | string; + description: string | undefined; +}; + +type ActionFormProps = { + requestId?: string; + taskId?: string; + createTaskRequest: (data: ActionFormReducer) => Promise; +}; + +const date = new Date(); +const today = date.toISOString().split('T')[0]; +date.setDate(date.getDate() + 7); +const sevenDaysFromToday = date.toISOString().split('T')[0]; + +const initialState = { + endsOn: Date.now(), + startedOn: Date.now(), + description: ' ', +}; + +const reducer = (state: ActionFormReducer, action: reducerAction) => { + switch (action.type) { + case 'endsOn': + return { + ...state, + endsOn: new Date(`${action.value}`).getTime(), + }; + case 'startedOn': + return { + ...state, + startedOn: new Date(`${action.value}`).getTime(), + }; + case 'description': + return { + ...state, + description: action.value, + }; + default: + return state; + } +}; + +const TaskRequestForm: FC = ({ + requestId, + createTaskRequest, + taskId, +}) => { + console.log(requestId); + const [state, dispatch] = useReducer(reducer, initialState, undefined); + const [isLoading, setIsLoading] = useState(false); + + const handleSubmit = async (e: MouseEvent) => { + e.preventDefault(); + setIsLoading(true); + await createTaskRequest(state); + setIsLoading(false); + }; + + return ( +
+
+
+ + + dispatch({ + type: 'startedOn', + value: e.target.value, + }) + } + /> +
+
+ + + dispatch({ type: 'endsOn', value: e.target.value }) + } + /> +
+
+ +