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 Jul 25, 2023
1 parent 5b3e544 commit 931b0aa
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 9 deletions.
34 changes: 34 additions & 0 deletions src/components/Dialog/Confirm/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';

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

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

describe('ConfirmDialog', () => {
describe('given the confirm dialog is opened', () => {
it('renders a confirm dialog', () => {
render(
<ConfirmDialog open title="Title">
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">
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-diloa__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;
12 changes: 9 additions & 3 deletions src/components/ElevatedButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ import classNames from 'classnames';
interface ElevatedButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
isFullWidth: boolean;
className?: string;
}

const ElevatedButton = ({ children, isFullWidth, ...attributes }: 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 })}
className={classNames(DEFAULT_CLASS_NAMES, className, { 'w-full': isFullWidth, 'px-8': !isFullWidth })}
{...attributes}
>
{children}
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 @@ -2,6 +2,7 @@ import React from 'react';

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

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 @@ -123,7 +124,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 @@ -132,8 +133,50 @@ describe('QuestionScreen', () => {
closeButton.click();
});

expect(mockDispatch).toHaveBeenCalledWith({ type: 'survey/resetQuestions' });
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/resetQuestions' });
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
25 changes: 22 additions & 3 deletions src/screens/Question/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ReactComponent as ArrowRight } from 'assets/images/icons/arrow-right.sv
import { ReactComponent as CloseButton } from 'assets/images/icons/close-btn-white.svg';
import Answer from 'components/Answer';
import BackgroundImage from 'components/BackgroundImage';
import ConfirmDialog from 'components/Dialog/Confirm';
import ElevatedButton from 'components/ElevatedButton';
import LoadingDialog from 'components/LoadingDialog';
import { useAppDispatch, useAppSelector } from 'hooks';
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 @@ -57,10 +60,14 @@ const QuestionScreen = (): JSX.Element => {
);
};

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

const handleOnConfirm = () => {
dispatch(surveyAction.resetQuestions());
navigate(paths.root, { replace: true });
}
};

useEffect(() => {
if (isSubmitSuccess) {
Expand All @@ -77,9 +84,21 @@ 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>
<BackgroundImage backgroundUrl={currentQuestion?.coverImageUrl}>
<div className="flex flex-col h-full">
<button className="mt-8 mr-8 p-1 self-end" data-test-id={questionScreenTestIds.closeButton} onClick={handleOnClose}>
<button
className="mt-8 mr-8 p-1 self-end"
data-test-id={questionScreenTestIds.closeButton}
onClick={handleOnCloseButtonPressed}
>
<CloseButton />
</button>
<div className="w-1/2 self-center flex-1 flex flex-col justify-center">
Expand Down

0 comments on commit 931b0aa

Please sign in to comment.