Skip to content

Commit

Permalink
[#22] Create a confirm dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
manh-t committed Sep 13, 2023
1 parent 11d2ddf commit 7f353e3
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 6 deletions.
38 changes: 38 additions & 0 deletions src/components/Dialog/Confirm/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';

import { render, screen } from '@testing-library/react';

import ConfirmDialog, { confirmDialogDataTestIds } from '.';
import { dialogDataTestIds } from '..';

describe('ConfirmDialog', () => {
const emptyCallback = () => {
// Do nothing
};

describe('given the confirm dialog is opened', () => {
it('renders a confirm dialog', () => {
render(
<ConfirmDialog open title="Title" onClose={emptyCallback} onConfirm={emptyCallback}>
Test Content
</ConfirmDialog>
);

const confirmDialog = screen.getByTestId(confirmDialogDataTestIds.base);

expect(confirmDialog).toBeVisible();
});
});

describe('given the dialog is NOT opened', () => {
it('does NOT render a dialog', () => {
render(
<ConfirmDialog open={false} title="Title" onClose={emptyCallback} onConfirm={emptyCallback}>
Test Content
</ConfirmDialog>
);

expect(screen.queryByTestId(dialogDataTestIds.dialog)).not.toBeInTheDocument();
});
});
});
60 changes: 60 additions & 0 deletions src/components/Dialog/Confirm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';

import ElevatedButton from 'components/ElevatedButton';

import Dialog from '..';

export const confirmDialogDataTestIds = {
base: 'confirm-dialog__base',
positiveButton: 'confirm-dialog__positive-button',
negativeButton: 'confirm-dialog__negative-button',
};

interface ConfirmDialogProps {
title: string;
children: React.ReactNode;
open: boolean;
onClose: () => void;
onConfirm: () => void;
}
const ConfirmDialog = ({ open, onClose, title, children, onConfirm }: ConfirmDialogProps): JSX.Element => {
if (!open) {
return <></>;
}

return (
<div data-test-id={confirmDialogDataTestIds.base}>
<Dialog open={open}>
<h2 className="text-x-regular text-white font-extrabold">{title}</h2>
<div className="py-5 text-regular text-white opacity-70">{children}</div>
<div className="flex justify-end">
<div className="p-1">
<ElevatedButton
isFullWidth={false}
className="bg-white bg-opacity-[.18] text-white"
onClick={() => {
onClose();
onConfirm();
}}
data-test-id={confirmDialogDataTestIds.positiveButton}
>
Yes
</ElevatedButton>
</div>
<div className="p-1">
<ElevatedButton
isFullWidth={false}
className="bg-white text-black-chinese px-5"
onClick={() => onClose()}
data-test-id={confirmDialogDataTestIds.negativeButton}
>
Cancel
</ElevatedButton>
</div>
</div>
</Dialog>
</div>
);
};

export default ConfirmDialog;
25 changes: 25 additions & 0 deletions src/components/Dialog/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';

import { render, screen } from '@testing-library/react';

import Dialog, { dialogDataTestIds } from '.';

describe('Dialog', () => {
describe('given the dialog is opened', () => {
it('renders a dialog', () => {
render(<Dialog open>Test Content</Dialog>);

const dialog = screen.getByTestId(dialogDataTestIds.dialog);

expect(dialog).toBeVisible();
});
});

describe('given the dialog is NOT opened', () => {
it('does NOT render a dialog', () => {
render(<Dialog open={false}>Test Content</Dialog>);

expect(screen.queryByTestId(dialogDataTestIds.dialog)).not.toBeInTheDocument();
});
});
});
24 changes: 24 additions & 0 deletions src/components/Dialog/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';

export const dialogDataTestIds = {
dialog: 'dialog__base',
};
interface DialogProps {
children: React.ReactNode;
open: boolean;
}
const Dialog = (props: DialogProps): JSX.Element => {
const { open } = props;
if (!open) {
return <></>;
}
return (
<div className="fixed inset-0 z-50 overflow-auto bg-black bg-opacity-50 flex" data-test-id={dialogDataTestIds.dialog}>
<div className="relative p-6 bg-[#1E1E1E] w-full max-w-md m-auto flex-col flex rounded-[14px]">
<div>{props.children}</div>
</div>
</div>
);
};

export default Dialog;
16 changes: 13 additions & 3 deletions src/components/ElevatedButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,24 @@ import classNames from 'classnames';
interface ElevatedButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
isFullWidth: boolean;
className?: string;
}

const ElevatedButton = ({ children, isFullWidth, ...rest }: ElevatedButtonProps): JSX.Element => {
const ElevatedButton = ({
children,
isFullWidth,
className = 'bg-white text-black-chinese',
...attributes
}: ElevatedButtonProps): JSX.Element => {
const DEFAULT_CLASS_NAMES =
'bg-white text-black-chinese font-bold text-regular tracking-survey-tight rounded-[10px] focus:outline-none focus:shadow-outline h-14';
'font-bold text-regular tracking-survey-tight rounded-[10px] focus:outline-none focus:shadow-outline h-14';

return (
<button type="button" className={classNames(DEFAULT_CLASS_NAMES, { 'w-full': isFullWidth, 'px-8': !isFullWidth })} {...rest}>
<button
type="button"
className={classNames(DEFAULT_CLASS_NAMES, className, { 'w-full': isFullWidth, 'px-8': !isFullWidth })}
{...attributes}
>
{children}
</button>
);
Expand Down
49 changes: 46 additions & 3 deletions src/screens/Question/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import { act, render, screen } from '@testing-library/react';

import { answerDataTestIds } from 'components/Answer';
import { confirmDialogDataTestIds } from 'components/Dialog/Confirm';
import { useAppDispatch, useAppSelector } from 'hooks';
import { paths } from 'routes';
import { SurveyState } from 'store/reducers/Survey';
Expand Down Expand Up @@ -88,7 +89,7 @@ describe('QuestionScreen', () => {
});

describe('given the close button is clicked', () => {
it('navigates back to the Home screen', () => {
it('shows the confirm dialog', () => {
render(<TestComponent />);

const closeButton = screen.getByTestId(questionScreenTestIds.closeButton);
Expand All @@ -97,8 +98,50 @@ describe('QuestionScreen', () => {
closeButton.click();
});

expect(mockDispatch).toHaveBeenCalledWith({ type: 'survey/resetState' });
expect(mockUseNavigate).toHaveBeenCalledWith(paths.root, { replace: true });
expect(screen.getByTestId(confirmDialogDataTestIds.base)).toBeVisible();
});
});

describe('given the dialog is opened', () => {
describe('given the Yes button is clicked', () => {
it('navigates back to the Home screen', () => {
render(<TestComponent />);

const closeButton = screen.getByTestId(questionScreenTestIds.closeButton);

act(() => {
closeButton.click();
});

const dialogPositiveButton = screen.getByTestId(confirmDialogDataTestIds.positiveButton);

act(() => {
dialogPositiveButton.click();
});

expect(mockDispatch).toHaveBeenCalledWith({ type: 'survey/resetState' });
expect(mockUseNavigate).toHaveBeenCalledWith(paths.root, { replace: true });
});
});

describe('given the Cancel button is clicked', () => {
it('closes the dialog', () => {
render(<TestComponent />);

const closeButton = screen.getByTestId(questionScreenTestIds.closeButton);

act(() => {
closeButton.click();
});

const dialogNegativeButton = screen.getByTestId(confirmDialogDataTestIds.negativeButton);

act(() => {
dialogNegativeButton.click();
});

expect(screen.queryByTestId(confirmDialogDataTestIds.base)).not.toBeInTheDocument();
});
});
});

Expand Down
15 changes: 15 additions & 0 deletions src/screens/Question/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ToastContainer, toast } from 'react-toastify';
import { ReactComponent as ArrowRight } from 'assets/images/icons/arrow-right.svg';
import { ReactComponent as CloseButton } from 'assets/images/icons/close-btn.svg';
import Answer from 'components/Answer';
import ConfirmDialog from 'components/Dialog/Confirm';
import ElevatedButton from 'components/ElevatedButton';
import LoadingDialog from 'components/LoadingDialog';
import MainView from 'components/MainView';
Expand All @@ -30,6 +31,8 @@ const QuestionScreen = (): JSX.Element => {
const [currentQuestion, setCurrentQuestion] = useState(survey?.questions?.at(0));
const [questionIndex, setQuestionIndex] = useState(0);

const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false);

const handleAnswerChanged = (answers: AnswerRequest[]) => {
dispatch(
surveyAction.fillAnswers({
Expand Down Expand Up @@ -72,6 +75,10 @@ const QuestionScreen = (): JSX.Element => {
};

const handleOnClose = () => {
setIsConfirmDialogOpen(true);
};

const handleOnConfirm = () => {
dispatch(surveyAction.resetState());
navigate(paths.root, { replace: true });
};
Expand All @@ -93,6 +100,14 @@ const QuestionScreen = (): JSX.Element => {
return (
<div>
<ToastContainer />
<ConfirmDialog
title="Warning!"
open={isConfirmDialogOpen}
onClose={() => setIsConfirmDialogOpen(false)}
onConfirm={handleOnConfirm}
>
Are you sure you want to quit the survey?
</ConfirmDialog>
<MainView backgroundUrl={currentQuestion?.coverImageUrl}>
<div className="flex flex-col h-full">
<button
Expand Down

0 comments on commit 7f353e3

Please sign in to comment.