diff --git a/src/__tests__/WizardAddObjectStep.test.js b/src/__tests__/WizardAddObjectStep.test.js index d9b80f88..bc9e6b51 100644 --- a/src/__tests__/WizardAddObjectStep.test.js +++ b/src/__tests__/WizardAddObjectStep.test.js @@ -1,7 +1,7 @@ import React from "react" import "@testing-library/jest-dom/extend-expect" -import { render, screen } from "@testing-library/react" +import { render, screen, act } from "@testing-library/react" import { Provider } from "react-redux" import configureStore from "redux-mock-store" import { toMatchDiffSnapshot } from "snapshot-diff" @@ -33,25 +33,27 @@ describe("WizardAddObjectStep", () => { expect(screen.getByText("Add objects by clicking the name, then fill form or upload XML File.")).toBeInTheDocument() }) - it("should render appropriate card", () => { + it("should render appropriate card", async () => { const typeList = ["form", "xml", "existing"] - typeList.forEach(typeName => { - const store = mockStore({ - objectType: "study", - submissionType: typeName, - submissionFolder: { - description: "Test", - id: "FOL12341234", - name: "Testname", - published: false, - }, + await act(async () => { + typeList.forEach(typeName => { + const store = mockStore({ + objectType: "study", + submissionType: typeName, + submissionFolder: { + description: "Test", + id: "FOL12341234", + name: "Testname", + published: false, + }, + }) + render( + + + + ) + expect(screen.getByTestId(typeName)).toBeInTheDocument() }) - render( - - - - ) - expect(screen.getByTestId(typeName)).toBeInTheDocument() }) }) }) diff --git a/src/__tests__/WizardStepper.test.js b/src/__tests__/WizardStepper.test.js index a484e3c0..1e89ffa8 100644 --- a/src/__tests__/WizardStepper.test.js +++ b/src/__tests__/WizardStepper.test.js @@ -27,6 +27,7 @@ describe("WizardStepper", () => { const store = mockStore({ submissionType: "form", wizardStep: 1, + draftStatus: "notSaved", }) render( diff --git a/src/__tests__/WizardUploadObjectXMLForm.test.js b/src/__tests__/WizardUploadObjectXMLForm.test.js index c7ca0273..6c95427c 100644 --- a/src/__tests__/WizardUploadObjectXMLForm.test.js +++ b/src/__tests__/WizardUploadObjectXMLForm.test.js @@ -24,13 +24,14 @@ describe("WizardStepper", () => { }, }) - it("should have send button disabled when there's no validated xml file", () => { + it("should have send button disabled when there's no validated xml file", async () => { render( ) - expect(screen.getByRole("button", { name: /save/i })).toHaveAttribute("disabled") + const button = await screen.findByRole("button", { name: /save/i }) + expect(button).toHaveAttribute("disabled") }) it("should have uploaded file in input", async () => { @@ -40,7 +41,7 @@ describe("WizardStepper", () => { ) - const input = screen.getByRole("textbox") + const input = await screen.findByRole("textbox") userEvent.upload(input, file) expect(input.files[0]).toStrictEqual(file) diff --git a/src/components/NewDraftWizard/WizardComponents/WizardAddObjectCard.js b/src/components/NewDraftWizard/WizardComponents/WizardAddObjectCard.js index afd7f9de..3e17b4f0 100644 --- a/src/components/NewDraftWizard/WizardComponents/WizardAddObjectCard.js +++ b/src/components/NewDraftWizard/WizardComponents/WizardAddObjectCard.js @@ -84,15 +84,16 @@ const CustomCardHeader = ({ title }: { title: string }) => { const WizardAddObjectCard = () => { const classes = useStyles() const submissionType = useSelector(state => state.submissionType) + const objectType = useSelector(state => state.objectType) const cards = { form: { title: "Fill form", - component: , + component: , testId: "form", }, xml: { title: "Upload XML file", - component: , + component: , testId: "xml", }, existing: { diff --git a/src/components/NewDraftWizard/WizardComponents/WizardObjectIndex.js b/src/components/NewDraftWizard/WizardComponents/WizardObjectIndex.js index 4f459bcc..12ce3f03 100644 --- a/src/components/NewDraftWizard/WizardComponents/WizardObjectIndex.js +++ b/src/components/NewDraftWizard/WizardComponents/WizardObjectIndex.js @@ -14,6 +14,7 @@ import { useDispatch, useSelector } from "react-redux" import WizardAlert from "./WizardAlert" +import { resetDraftStatus } from "features/draftStatusSlice" import { setObjectType } from "features/wizardObjectTypeSlice" import { setSubmissionType } from "features/wizardSubmissionTypeSlice" @@ -137,6 +138,7 @@ const WizardObjectIndex = () => { const [cancelFormOpen, setCancelFormOpen] = useState(false) const currentObjectType = useSelector(state => state.objectType) const currentSubmissionType = useSelector(state => state.submissionType) + const draftStatus = useSelector(state => state.draftStatus) const handlePanelChange = panel => (event, newExpanded) => { setExpandedObjectType(newExpanded ? panel : false) @@ -147,8 +149,14 @@ const WizardObjectIndex = () => { dispatch(setSubmissionType(submissionType)) dispatch(setObjectType(expandedObjectType)) } else { - setClickedSubmissionType(submissionType) - setCancelFormOpen(true) + if (draftStatus === "notSaved") { + setClickedSubmissionType(submissionType) + setCancelFormOpen(true) + } else { + dispatch(resetDraftStatus()) + dispatch(setSubmissionType(submissionType)) + dispatch(setObjectType(expandedObjectType)) + } } } @@ -156,6 +164,7 @@ const WizardObjectIndex = () => { setClickedSubmissionType("") setCancelFormOpen(false) if (cancel) { + dispatch(resetDraftStatus()) dispatch(setSubmissionType(clickedSubmissionType)) dispatch(setObjectType(expandedObjectType)) } diff --git a/src/components/NewDraftWizard/WizardComponents/WizardSavedObjectsList.js b/src/components/NewDraftWizard/WizardComponents/WizardSavedObjectsList.js index 890bb8f2..2f9dc736 100644 --- a/src/components/NewDraftWizard/WizardComponents/WizardSavedObjectsList.js +++ b/src/components/NewDraftWizard/WizardComponents/WizardSavedObjectsList.js @@ -46,9 +46,10 @@ const ToggleMessage = ({ delay, children }: { delay: number, children: any }) => const classes = useStyles() const [visible, setVisible] = useState(true) useEffect(() => { - setTimeout(() => { + const toggle = setTimeout(() => { setVisible(false) }, delay) + return () => clearTimeout(toggle) }, [delay]) return {children} diff --git a/src/components/NewDraftWizard/WizardComponents/WizardStepper.js b/src/components/NewDraftWizard/WizardComponents/WizardStepper.js index 1ff6b0d9..5559300f 100644 --- a/src/components/NewDraftWizard/WizardComponents/WizardStepper.js +++ b/src/components/NewDraftWizard/WizardComponents/WizardStepper.js @@ -16,6 +16,7 @@ import { useDispatch, useSelector } from "react-redux" import WizardAlert from "./WizardAlert" import type { CreateFolderFormRef } from "components/NewDraftWizard/WizardSteps/WizardCreateFolderStep" +import { resetDraftStatus } from "features/draftStatusSlice" import { resetObjectType } from "features/wizardObjectTypeSlice" import { decrement, increment } from "features/wizardStepSlice" import { resetSubmissionType } from "features/wizardSubmissionTypeSlice" @@ -129,10 +130,12 @@ const WizardStepper = ({ createFolderFormRef }: { createFolderFormRef?: CreateFo const formState = useSelector(state => state.submissionType) const [alert, setAlert] = useState(false) const [direction, setDirection] = useState("") + const draftStatus = useSelector(state => state.draftStatus) const handleNavigation = (step: boolean) => { setDirection("") setAlert(false) + dispatch(resetDraftStatus()) if (step) { direction === "previous" ? dispatch(decrement()) : dispatch(increment()) dispatch(resetObjectType()) @@ -149,7 +152,7 @@ const WizardStepper = ({ createFolderFormRef }: { createFolderFormRef?: CreateFo variant="outlined" disabled={wizardStep < 1} onClick={() => { - if (wizardStep === 1 && formState.trim().length > 0) { + if (wizardStep === 1 && formState.trim().length > 0 && draftStatus === "notSaved") { setDirection("previous") setAlert(true) } else { @@ -182,7 +185,7 @@ const WizardStepper = ({ createFolderFormRef }: { createFolderFormRef?: CreateFo if (createFolderFormRef?.current) { await createFolderFormRef.current.dispatchEvent(new Event("submit", { cancelable: true })) } - if (wizardStep === 1 && formState.trim().length > 0) { + if (wizardStep === 1 && formState.trim().length > 0 && draftStatus === "notSaved") { setDirection("next") setAlert(true) } else if (wizardStep !== 2 && !createFolderFormRef?.current) { diff --git a/src/components/NewDraftWizard/WizardForms/WizardFillObjectDetailsForm.js b/src/components/NewDraftWizard/WizardForms/WizardFillObjectDetailsForm.js index 681b2070..a16ec82e 100644 --- a/src/components/NewDraftWizard/WizardForms/WizardFillObjectDetailsForm.js +++ b/src/components/NewDraftWizard/WizardForms/WizardFillObjectDetailsForm.js @@ -17,6 +17,7 @@ import { WizardAjvResolver } from "./WizardAjvResolver" import JSONSchemaParser from "./WizardJSONSchemaParser" import WizardStatusMessageHandler from "./WizardStatusMessageHandler" +import { setDraftStatus, resetDraftStatus } from "features/draftStatusSlice" import objectAPIService from "services/objectAPI" import schemaAPIService from "services/schemaAPI" @@ -72,6 +73,16 @@ type FormContentProps = { const FormContent = ({ resolver, formSchema, onSubmit }: FormContentProps) => { const classes = useStyles() const methods = useForm({ mode: "onBlur", resolver }) + const dispatch = useDispatch() + + const resetForm = () => { + methods.reset() + } + + useEffect(() => { + methods.formState.isDirty ? dispatch(setDraftStatus("notSaved")) : dispatch(setDraftStatus("")) + }, [methods.formState.isDirty, dispatch]) + return (
@@ -82,7 +93,7 @@ const FormContent = ({ resolver, formSchema, onSubmit }: FormContentProps) => { color="secondary" size="small" className={classes.formButton} - onClick={methods.reset} + onClick={() => resetForm()} > Clear form @@ -138,6 +149,7 @@ const WizardFillObjectDetailsForm = () => { } clearTimeout(waitForServertimer) setSubmitting(false) + dispatch(resetDraftStatus()) } /* diff --git a/src/components/NewDraftWizard/WizardForms/WizardUploadObjectXMLForm.js b/src/components/NewDraftWizard/WizardForms/WizardUploadObjectXMLForm.js index 1c76025d..f729227a 100644 --- a/src/components/NewDraftWizard/WizardForms/WizardUploadObjectXMLForm.js +++ b/src/components/NewDraftWizard/WizardForms/WizardUploadObjectXMLForm.js @@ -1,5 +1,5 @@ //@flow -import React, { useState } from "react" +import React, { useEffect, useState } from "react" import Button from "@material-ui/core/Button" import FormControl from "@material-ui/core/FormControl" @@ -12,6 +12,7 @@ import { useDispatch, useSelector } from "react-redux" import WizardStatusMessageHandler from "./WizardStatusMessageHandler" +import { setDraftStatus, resetDraftStatus } from "features/draftStatusSlice" import { resetErrorMessage } from "features/wizardErrorMessageSlice" import { addObjectToFolder } from "features/wizardSubmissionFolderSlice" import objectAPIService from "services/objectAPI" @@ -52,7 +53,11 @@ const WizardUploadObjectXMLForm = () => { const dispatch = useDispatch() const classes = useStyles() - const { register, errors, watch, handleSubmit } = useForm({ mode: "onChange" }) + const { register, errors, watch, handleSubmit, formState } = useForm({ mode: "onChange" }) + + useEffect(() => { + formState.isDirty && formState.isValid ? dispatch(setDraftStatus("notSaved")) : dispatch(resetDraftStatus()) + }, [formState.isDirty, formState.isValid, dispatch]) const watchFile = watch("fileUpload") @@ -79,6 +84,7 @@ const WizardUploadObjectXMLForm = () => { } clearTimeout(waitForServertimer) setSubmitting(false) + dispatch(resetDraftStatus()) } return ( @@ -125,7 +131,7 @@ const WizardUploadObjectXMLForm = () => { variant="outlined" color="primary" className={classes.submitButton} - disabled={isSubmitting || !watchFile || errors.fileUpload != null} + disabled={isSubmitting || watchFile?.length === 0 || errors.fileUpload != null} onClick={handleSubmit(onSubmit)} > Save diff --git a/src/features/draftStatusSlice.js b/src/features/draftStatusSlice.js new file mode 100644 index 00000000..3f514719 --- /dev/null +++ b/src/features/draftStatusSlice.js @@ -0,0 +1,16 @@ +//@flow +import { createSlice } from "@reduxjs/toolkit" + +const initialState = "" + +const draftStatusSlice = createSlice({ + name: "draftStatus", + initialState, + reducers: { + setDraftStatus: (state, action) => action.payload, + resetDraftStatus: () => initialState, + }, +}) + +export const { setDraftStatus, resetDraftStatus } = draftStatusSlice.actions +export default draftStatusSlice.reducer diff --git a/src/rootReducer.js b/src/rootReducer.js index 2c3ed6bc..239f3df3 100644 --- a/src/rootReducer.js +++ b/src/rootReducer.js @@ -2,6 +2,7 @@ import { combineReducers } from "@reduxjs/toolkit" +import draftStatusReducer from "features/draftStatusSlice" import wizardErrorMessageReducer from "features/wizardErrorMessageSlice" import objectTypeReducer from "features/wizardObjectTypeSlice" import wizardStepReducer from "features/wizardStepSlice" @@ -14,6 +15,7 @@ const rootReducer = combineReducers({ wizardStep: wizardStepReducer, submissionFolder: submissionFolderReducer, submissionType: submissionTypeReducer, + draftStatus: draftStatusReducer, }) export default rootReducer