Skip to content

Commit

Permalink
Adds a modal component (#954)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
Ajeyakrishna-k and iamitprakash authored Oct 21, 2023
1 parent 1846697 commit 5bb9e04
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 0 deletions.
62 changes: 62 additions & 0 deletions __tests__/Unit/Components/Issues/TaskRequestForm.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<TaskRequestForm createTaskRequest={createTaskRequestMock} />);
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(<TaskRequestForm createTaskRequest={createTaskRequestMock} />);
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(<TaskRequestForm createTaskRequest={createTaskRequestMock} />);
const submitButton = screen.getByRole('button', {
name: /Create Request/i,
});
fireEvent.click(submitButton);
await waitFor(() => {
expect(createTaskRequestMock).toHaveBeenCalled();
});
});
});
50 changes: 50 additions & 0 deletions __tests__/Unit/Components/modal/index.test.tsx
Original file line number Diff line number Diff line change
@@ -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(
<Modal isOpen={true} toggle={mockedFunction} />
);
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(
<Modal isOpen={false} toggle={mockedFunction} />
);
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(
<Modal isOpen={true} toggle={toggleMock} />
);
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(
<Modal isOpen={true} toggle={toggleMock} />
);
const modalBox = getByTestId('modal-box');
fireEvent.click(modalBox);
expect(toggleMock).not.toHaveBeenCalled();
});
});
Empty file added src/components/Modal/Modal
Empty file.
30 changes: 30 additions & 0 deletions src/components/Modal/index.tsx
Original file line number Diff line number Diff line change
@@ -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 && (
<div
className={styles.modalOverlay}
onClick={props.toggle}
data-testid="modal-overlay"
>
<div
onClick={(e) => e.stopPropagation()}
className={styles.modalBox}
data-testid="modal-box"
>
{props.children}
</div>
</div>
)}
</>
);
}
31 changes: 31 additions & 0 deletions src/components/Modal/modal.module.scss
Original file line number Diff line number Diff line change
@@ -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;
}
144 changes: 144 additions & 0 deletions src/components/issues/TaskRequestForm.tsx
Original file line number Diff line number Diff line change
@@ -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<void>;
};

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<ActionFormProps> = ({
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 (
<form className={styles.request_form}>
<div className={styles.form_container}>
<div className={styles.inputContainer}>
<label htmlFor="starts-on" className={styles.assign_label}>
Start date:
</label>
<input
name="starts-on"
id="starts-on"
className={`${styles.assign} ${styles.input_date}`}
type="date"
required
defaultValue={today}
onChange={(e) =>
dispatch({
type: 'startedOn',
value: e.target.value,
})
}
/>
</div>
<div className={styles.inputContainer}>
<label htmlFor="ends-on" className={styles.assign_label}>
End date:
</label>
<input
name="ends-on"
id="ends-on"
className={` ${styles.assign} ${styles.input_date}`}
type="date"
defaultValue={sevenDaysFromToday}
required
onChange={(e) =>
dispatch({ type: 'endsOn', value: e.target.value })
}
/>
</div>
<div className={styles.inputContainer}>
<label
htmlFor="description"
className={styles.assign_label}
>
Description:
</label>
<textarea
name="description"
id="description"
placeholder="Why do you want this task?"
className={`${styles.assign} ${styles.description_box}`}
onChange={(e) =>
dispatch({
type: 'description',
value: e.target.value,
})
}
/>
</div>
</div>

{isLoading && <Loader />}
<div className={styles.form_container}>
<button
className={styles.card__top__button}
type="submit"
disabled={isLoading || !!requestId || !!taskId}
onClick={handleSubmit}
>
Create Request
</button>
</div>
</form>
);
};

export default TaskRequestForm;

0 comments on commit 5bb9e04

Please sign in to comment.