diff --git a/src/__tests__/WizardSavedObjectsList.test.js b/src/__tests__/WizardSavedObjectsList.test.js new file mode 100644 index 00000000..39af4aba --- /dev/null +++ b/src/__tests__/WizardSavedObjectsList.test.js @@ -0,0 +1,33 @@ +import React from "react" + +import "@testing-library/jest-dom/extend-expect" +import { render, screen } from "@testing-library/react" +import { Provider } from "react-redux" +import configureStore from "redux-mock-store" + +import WizardSavedObjectsList from "../components/NewDraftWizard/WizardComponents/WizardSavedObjectsList" + +const mockStore = configureStore([]) + +describe("WizardStepper", () => { + const store = mockStore({ + submissionType: "sample", + wizardStep: 1, + }) + + const submissions = [ + { accessionId: "EDAG1", schema: "sample" }, + { accessionId: "EDAG2", schema: "sample" }, + ] + + it("should have 'Added!' message rendered on item that has 'new' property", () => { + render( + + + + ) + submissions.forEach(item => { + expect(screen.getByText(item.accessionId)).toBeInTheDocument() + }) + }) +}) diff --git a/src/components/NewDraftWizard/WizardComponents/WizardSavedObjectsList.js b/src/components/NewDraftWizard/WizardComponents/WizardSavedObjectsList.js new file mode 100644 index 00000000..890bb8f2 --- /dev/null +++ b/src/components/NewDraftWizard/WizardComponents/WizardSavedObjectsList.js @@ -0,0 +1,102 @@ +//@flow +import React, { useEffect, useState, useRef } from "react" + +import IconButton from "@material-ui/core/IconButton" +import List from "@material-ui/core/List" +import ListItem from "@material-ui/core/ListItem" +import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction" +import ListItemText from "@material-ui/core/ListItemText" +import { makeStyles } from "@material-ui/core/styles" +import ClearIcon from "@material-ui/icons/Clear" +import { useSelector, useDispatch } from "react-redux" + +import { deleteObjectFromFolder } from "features/wizardSubmissionFolderSlice" + +const useStyles = makeStyles(theme => ({ + objectList: { + padding: "0 1rem", + width: "25%", + }, + header: { + marginBlockEnd: "0", + }, + objectListItems: { + border: "none", + borderRadius: 3, + margin: theme.spacing(1, 0), + boxShadow: "0px 3px 10px -5px rgba(0,0,0,0.49)", + alignItems: "flex-start", + padding: ".5rem", + }, + addedMessage: { + color: theme.palette.success.main, + visibility: "visible", + opacity: "1", + transition: "opacity 2s linear", + }, + hidden: { + color: theme.palette.success.main, + visibility: "hidden", + opacity: "0", + transition: "visibility 0s .2s, opacity .2s linear", + }, +})) + +const ToggleMessage = ({ delay, children }: { delay: number, children: any }) => { + const classes = useStyles() + const [visible, setVisible] = useState(true) + useEffect(() => { + setTimeout(() => { + setVisible(false) + }, delay) + }, [delay]) + + return {children} +} + +/** + * List objects by submission type. Enables deletion of objects + */ +const WizardSavedObjectsList = ({ submissionType, submissions }: { submissionType: string, submissions: any }) => { + const ref = useRef() + useEffect(() => { + ref.current = submissions + }) + const classes = useStyles() + const dispatch = useDispatch() + const objectType = useSelector(state => state.objectType) + const handleObjectDelete = objectId => { + dispatch(deleteObjectFromFolder(objectId, objectType)) + } + const newObject = submissions.filter(x => !ref.current?.includes(x)) + return ( +
+

Submitted {submissionType} items

+ + {submissions.map(submission => { + return ( + + + + {newObject.length === 1 && newObject[0]?.accessionId === submission.accessionId && ( + Added! + )} + { + handleObjectDelete(submission.accessionId) + }} + edge="end" + aria-label="delete" + > + + + + + ) + })} + +
+ ) +} + +export default WizardSavedObjectsList diff --git a/src/components/NewDraftWizard/WizardSteps/WizardAddObjectStep.js b/src/components/NewDraftWizard/WizardSteps/WizardAddObjectStep.js index cdd8cd15..2708c421 100644 --- a/src/components/NewDraftWizard/WizardSteps/WizardAddObjectStep.js +++ b/src/components/NewDraftWizard/WizardSteps/WizardAddObjectStep.js @@ -7,6 +7,7 @@ import { useSelector } from "react-redux" import WizardAddObjectCard from "../WizardComponents/WizardAddObjectCard" import WizardHeader from "../WizardComponents/WizardHeader" import WizardObjectIndex from "../WizardComponents/WizardObjectIndex" +import WizardSavedObjectsList from "../WizardComponents/WizardSavedObjectsList" import WizardStepper from "../WizardComponents/WizardStepper" const useStyles = makeStyles(theme => ({ @@ -18,7 +19,10 @@ const useStyles = makeStyles(theme => ({ formBox: { display: "flex", justifyContent: "center", - width: "100%", + width: "60%", + }, + objectList: { + width: "40%", }, objectInfo: { margin: theme.spacing(2), @@ -31,6 +35,10 @@ const useStyles = makeStyles(theme => ({ const WizardAddObjectStep = () => { const classes = useStyles() const objectType = useSelector(state => state.objectType) + const currentSubmissionType = useSelector(state => state.objectType) + const folder = useSelector(state => state.submissionFolder) + const submissions = folder?.metadataObjects?.filter(obj => obj.schema === currentSubmissionType) + return ( <> @@ -47,6 +55,9 @@ const WizardAddObjectStep = () => { )} + {submissions?.length > 0 && ( + + )} ) diff --git a/src/features/wizardSubmissionFolderSlice.js b/src/features/wizardSubmissionFolderSlice.js index 77a459c5..2413f3e0 100644 --- a/src/features/wizardSubmissionFolderSlice.js +++ b/src/features/wizardSubmissionFolderSlice.js @@ -1,6 +1,7 @@ //@flow import { createSlice } from "@reduxjs/toolkit" import _extend from "lodash/extend" +import _reject from "lodash/reject" import objectAPIService from "../services/objectAPI" @@ -16,10 +17,15 @@ const wizardSubmissionFolderSlice = createSlice({ addObject: (state, action) => { state.metadataObjects.push(action.payload) }, + deleteObject: (state, action) => { + state.metadataObjects = _reject(state.metadataObjects, function (o) { + return o.accessionId === action.payload + }) + }, resetFolder: () => initialState, }, }) -export const { setFolder, addObject, resetFolder } = wizardSubmissionFolderSlice.actions +export const { setFolder, addObject, deleteObject, resetFolder } = wizardSubmissionFolderSlice.actions export default wizardSubmissionFolderSlice.reducer type FolderFromForm = { @@ -74,6 +80,14 @@ export const addObjectToFolder = (folderID: string, objectDetails: ObjectInFolde dispatch(addObject(objectDetails)) } +export const deleteObjectFromFolder = (objectId: string, objectType: string) => async (dispatch: any => void) => { + const response = await objectAPIService.deleteObjectByAccessionId(objectType, objectId) + if (!response.ok) { + return + } + dispatch(deleteObject(objectId)) +} + export const publishFolderContent = (folder: Folder) => async (dispatch: any => void) => { const changes = [{ op: "replace", path: "/published", value: true }] const response = await folderAPIService.patchFolderById(folder.id, changes) diff --git a/src/theme.js b/src/theme.js index 7e35ea59..8ca0d7c3 100644 --- a/src/theme.js +++ b/src/theme.js @@ -32,6 +32,9 @@ const CSCtheme = createMuiTheme({ background: { default: "#FFF", }, + success: { + main: "#62c480", + }, }, props: { MuiTextField: {