feat: show confirm dialog when editing case or steps

This commit is contained in:
Takeshi Kimata
2024-06-22 22:54:03 +09:00
parent f86b2a38cb
commit c652502277
6 changed files with 42 additions and 2 deletions

View File

@@ -216,7 +216,8 @@
"delete_file": "Delete file", "delete_file": "Delete file",
"click_to_upload": "Click to upload", "click_to_upload": "Click to upload",
"or_drag_and_drop": "or drag and drop", "or_drag_and_drop": "or drag and drop",
"max_file_size": "Max. file size" "max_file_size": "Max. file size",
"are_you_sure_leave": "Are you sure you want to leave the page?"
}, },
"Runs": { "Runs": {
"run_list": "Test Run List", "run_list": "Test Run List",

View File

@@ -215,7 +215,8 @@
"delete_file": "ファイルを削除", "delete_file": "ファイルを削除",
"click_to_upload": "クリックしてアップロード", "click_to_upload": "クリックしてアップロード",
"or_drag_and_drop": "またはドラッグアンドドロップ", "or_drag_and_drop": "またはドラッグアンドドロップ",
"max_file_size": "最大ファイルサイズ" "max_file_size": "最大ファイルサイズ",
"are_you_sure_leave": "ページを離れてもよろしいですか?"
}, },
"Runs": { "Runs": {
"run_list": "テストラン一覧", "run_list": "テストラン一覧",

View File

@@ -11,6 +11,7 @@ import { fetchCase, updateCase } from '@/utils/caseControl';
import { updateSteps } from './stepControl'; import { updateSteps } from './stepControl';
import { fetchCreateAttachments, fetchDownloadAttachment, fetchDeleteAttachment } from './attachmentControl'; import { fetchCreateAttachments, fetchDownloadAttachment, fetchDeleteAttachment } from './attachmentControl';
import { TokenContext } from '@/utils/TokenProvider'; import { TokenContext } from '@/utils/TokenProvider';
import { useFormGuard } from '@/utils/formGuard';
const defaultTestCase = { const defaultTestCase = {
id: 0, id: 0,
@@ -44,9 +45,12 @@ export default function CaseEditor({ projectId, folderId, caseId, messages, loca
const [isTitleInvalid, setIsTitleInvalid] = useState<boolean>(false); const [isTitleInvalid, setIsTitleInvalid] = useState<boolean>(false);
const [isUpdating, setIsUpdating] = useState<boolean>(false); const [isUpdating, setIsUpdating] = useState<boolean>(false);
const [plusCount, setPlusCount] = useState<number>(0); const [plusCount, setPlusCount] = useState<number>(0);
const [isDirty, setIsDirty] = useState(false);
const router = useRouter(); const router = useRouter();
useFormGuard(isDirty, messages.areYouSureLeave);
const onPlusClick = async (newStepNo: number) => { const onPlusClick = async (newStepNo: number) => {
setIsDirty(true);
const newStep: StepType = { const newStep: StepType = {
id: plusCount, id: plusCount,
step: '', step: '',
@@ -86,6 +90,8 @@ export default function CaseEditor({ projectId, folderId, caseId, messages, loca
}; };
const onDeleteClick = async (stepId: number) => { const onDeleteClick = async (stepId: number) => {
setIsDirty(true);
// find deletedStep's stepNo // find deletedStep's stepNo
if (testCase.Steps) { if (testCase.Steps) {
const deletedStep = testCase.Steps.find((step) => step.id === stepId); const deletedStep = testCase.Steps.find((step) => step.id === stepId);

View File

@@ -58,6 +58,7 @@ export default function Page({
clickToUpload: t('click_to_upload'), clickToUpload: t('click_to_upload'),
orDragAndDrop: t('or_drag_and_drop'), orDragAndDrop: t('or_drag_and_drop'),
maxFileSize: t('max_file_size'), maxFileSize: t('max_file_size'),
areYouSureLeave: t('are_you_sure_leave'),
}; };
return ( return (

View File

@@ -131,6 +131,7 @@ export type CaseMessages = {
clickToUpload: string; clickToUpload: string;
orDragAndDrop: string; orDragAndDrop: string;
maxFileSize: string; maxFileSize: string;
areYouSureLeave: string;
}; };
export { CaseType, StepType, AttachmentType, CaseTypeCountType, CasePriorityCountType, CasesMessages, CaseMessages }; export { CaseType, StepType, AttachmentType, CaseTypeCountType, CasePriorityCountType, CasesMessages, CaseMessages };

View File

@@ -0,0 +1,30 @@
import { useEffect } from 'react';
export const useFormGuard = (isDirty: boolean, confirmText: string) => {
useEffect(() => {
console.log(isDirty);
const handleClick = (event: MouseEvent) => {
if (isDirty && event.target instanceof Element && event.target.closest('a:not([target="_blank"]')) {
if (!window.confirm(confirmText)) {
event.preventDefault();
event.stopPropagation();
}
}
};
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
if (isDirty) {
event.preventDefault();
return (event.returnValue = '');
}
};
window.addEventListener('beforeunload', handleBeforeUnload);
window.addEventListener('click', handleClick, true);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
window.removeEventListener('click', handleClick, true);
};
}, [isDirty]);
};