diff --git a/cypress/integration/draftTemplates.spec.js b/cypress/integration/draftTemplates.spec.js index 5b17f3186..359532061 100644 --- a/cypress/integration/draftTemplates.spec.js +++ b/cypress/integration/draftTemplates.spec.js @@ -97,7 +97,7 @@ describe("draft selections and templates", function () { cy.get("h1", { timeout: 10000 }).contains("Sample").should("be.visible") cy.get("[data-testid='title']").should("have.value", "Sample draft title") }), - it("should be able to select and delete draft templates and reuse them when creating a new folder", () => { + it("should be able to select, edit and delete draft templates and reuse them when creating a new folder", () => { // Select drafts inside the dialog cy.get("form").within(() => { cy.get("input[type='checkbox']").first().check() @@ -111,6 +111,28 @@ describe("draft selections and templates", function () { // Check if the drafts have been saved as user's templates in Home page cy.contains("Your Draft Templates", { timeout: 10000 }).should("be.visible") + // Edit template + cy.get("[data-testid='form-template-sample']").within(() => { + cy.get("button").first().click() + }) + + cy.get("[data-testid='edit-template']").click() + + cy.get("[role='presentation']").within(() => { + cy.get("input[name='title']").should("be.visible") + cy.get("input[name='title']").type(" edit", { force: true }) + cy.get("input[name='title']").should("have.value", "Sample draft title edit") + + cy.get("button[type='submit']").click() + }) + + cy.get("div[role=alert]", { timeout: 10000 }).contains("Template updated with") + + // Re-query template item + cy.get("[data-testid='template-sample-item']").first().as("firstSampleTemplate") + cy.get("@firstSampleTemplate").should("contain", "Sample draft title edit") + + // Count sample templates, delete template and test that template count has decreased const listAllSampleTemplates = () => { cy.get("[data-testid='template-sample-item']") .its("length") @@ -126,7 +148,6 @@ describe("draft selections and templates", function () { listAllSampleTemplates() - // Count sample templates, delete template and test that template count has decreased cy.get("[data-testid='template-sample-item']") .its("length") .then(lengthOfSampleTemplates => { @@ -198,6 +219,7 @@ describe("draft selections and templates", function () { cy.get("button[type=button]").contains("Next").click() cy.wait(500) + // Check that the new template is added as a draft cy.clickFillForm("Study") cy.get("[data-testid='Draft-objects']").children().should("have.length", 2) diff --git a/src/components/NewDraftWizard/WizardForms/WizardFillObjectDetailsForm.js b/src/components/NewDraftWizard/WizardForms/WizardFillObjectDetailsForm.js index 0b4240df1..02a6e13aa 100644 --- a/src/components/NewDraftWizard/WizardForms/WizardFillObjectDetailsForm.js +++ b/src/components/NewDraftWizard/WizardForms/WizardFillObjectDetailsForm.js @@ -27,6 +27,7 @@ import { setClearForm } from "features/clearFormSlice" import { setDraftStatus, resetDraftStatus } from "features/draftStatusSlice" import { resetFocus } from "features/focusSlice" import { updateStatus } from "features/statusMessageSlice" +import { replaceTemplate } from "features/userSlice" import { setCurrentObject, resetCurrentObject } from "features/wizardCurrentObjectSlice" import { deleteObjectFromFolder, replaceObjectInFolder } from "features/wizardSubmissionFolderSlice" import objectAPIService from "services/objectAPI" @@ -249,6 +250,7 @@ const FormContent = ({ const draftStatus = useSelector(state => state.draftStatus) const alert = useSelector(state => state.alert) const clearForm = useSelector(state => state.clearForm) + const user = useSelector(state => state.user) const methods = useForm({ mode: "onBlur", resolver }) @@ -438,14 +440,31 @@ const FormContent = ({ if (checkFormCleanedValuesEmpty(cleanedValues)) { const response = await templateAPI.patchTemplateFromJSON(objectType, currentObject.accessionId, cleanedValues) + const templates = user.templates + const index = templates.findIndex(item => item.accessionId === currentObject.accessionId) + const displayTitle = getObjectDisplayTitle(objectType, cleanedValues) + if (response.ok) { - dispatch( - updateStatus({ - status: ResponseStatus.success, - response: response, - helperText: "", + closeDialog() + dispatch(replaceTemplate(index, displayTitle, currentObject.accessionId)) + .then( + dispatch( + updateStatus({ + status: ResponseStatus.success, + response: response, + helperText: "", + }) + ) + ) + .catch(error => { + dispatch( + updateStatus({ + status: ResponseStatus.error, + response: error, + helperText: "Cannot replace template", + }) + ) }) - ) } else { dispatch( updateStatus({ @@ -587,6 +606,12 @@ const WizardFillObjectDetailsForm = (props: { closeDialog?: () => void }): React } if (objectType.length) fetchSchema() + + // Reset current object in state on unmount + return () => { + dispatch(resetCurrentObject()) + dispatch(resetDraftStatus()) + } }, [objectType]) // All Analysis AccessionIds diff --git a/src/features/userSlice.js b/src/features/userSlice.js index bc95550a2..2e572719c 100644 --- a/src/features/userSlice.js +++ b/src/features/userSlice.js @@ -19,6 +19,10 @@ const userSlice: any = createSlice({ initialState, reducers: { setUser: (state, action) => action.payload, + updateTemplateDisplayTitle: (state, action) => { + state.templates.find(item => item.accessionId === action.payload.accessionId).tags.displayTitle = + action.payload.displayTitle + }, deleteTemplateByAccessionId: (state, action) => { state.templates = _reject(state.templates, template => { return template.accessionId === action.payload @@ -28,7 +32,7 @@ const userSlice: any = createSlice({ }, }) -export const { setUser, resetUser, deleteTemplateByAccessionId } = userSlice.actions +export const { setUser, resetUser, updateTemplateDisplayTitle, deleteTemplateByAccessionId } = userSlice.actions export default userSlice.reducer export const fetchUserById = @@ -66,3 +70,21 @@ export const addDraftsToUser = } }) } + +export const replaceTemplate = + (index: number, displayTitle: string, accessionId: string): ((dispatch: (any) => void) => Promise) => + async (dispatch: any => void) => { + const tags: { displayTitle: string } = { displayTitle: displayTitle } + + const changes = [{ op: "replace", path: `/templates/${index}/tags`, value: tags }] + const response = await userAPIService.patchUserById("current", changes) + + return new Promise((resolve, reject) => { + if (response.ok) { + dispatch(updateTemplateDisplayTitle({ accessionId: accessionId, displayTitle: displayTitle })) + resolve(response) + } else { + reject(JSON.stringify(response)) + } + }) + } diff --git a/src/services/templateAPI.js b/src/services/templateAPI.js index c7e5cca46..214082698 100644 --- a/src/services/templateAPI.js +++ b/src/services/templateAPI.js @@ -5,6 +5,7 @@ import { omit } from "lodash" import { errorMonitor } from "./errorMonitor" import { OmitObjectValues } from "constants/wizardObject" +import { getObjectDisplayTitle } from "utils" const api = create({ baseURL: "/templates" }) api.addMonitor(errorMonitor) @@ -18,7 +19,8 @@ const getTemplateByAccessionId = async (objectType: string, accessionId: string) } const patchTemplateFromJSON = async (objectType: string, accessionId: any, JSONContent: any): Promise => { - return await api.patch(`/${objectType}/${accessionId}`, omit(JSONContent, OmitObjectValues)) + const draftTags = { tags: { displayTitle: getObjectDisplayTitle(objectType, JSONContent) } } + return await api.patch(`/${objectType}/${accessionId}`, { ...omit(JSONContent, OmitObjectValues), draftTags }) } const deleteTemplateByAccessionId = async (objectType: string, accessionId: string): Promise => {