From 46bf48554c3bebd6c63229d8588b7283561b7dea Mon Sep 17 00:00:00 2001 From: Alec M Date: Thu, 2 May 2024 12:45:29 -0400 Subject: [PATCH] CRDCDH-1091 Update Metadata Intention labels --- .../DataSubmissions/MetadataUpload.test.tsx | 68 +++++++++---------- .../DataSubmissions/MetadataUpload.tsx | 20 +++--- src/types/Submissions.d.ts | 17 ++++- 3 files changed, 58 insertions(+), 47 deletions(-) diff --git a/src/components/DataSubmissions/MetadataUpload.test.tsx b/src/components/DataSubmissions/MetadataUpload.test.tsx index 5a357849a..db9ddbbdc 100644 --- a/src/components/DataSubmissions/MetadataUpload.test.tsx +++ b/src/components/DataSubmissions/MetadataUpload.test.tsx @@ -68,7 +68,7 @@ const baseBatch: Batch = { displayID: 0, submissionID: "", type: "metadata", - metadataIntention: "New", + metadataIntention: "Add", fileCount: 0, files: [], status: "Uploading", @@ -212,7 +212,7 @@ describe("Basic Functionality", () => { data: { createBatch: { ...baseNewBatch, - metadataIntention: "New", + metadataIntention: "Add", fileCount: 1, files: [{ fileName: "metadata.txt", signedURL: "example-signed-url" }], }, @@ -251,7 +251,7 @@ describe("Basic Functionality", () => { ); const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - userEvent.click(getByLabelText(radio, "New")); + userEvent.click(getByLabelText(radio, "Add")); const file = new File(["unused-content"], "metadata.txt", { type: "text/plain" }); userEvent.upload(getByTestId("metadata-upload-file-input"), file); @@ -275,7 +275,7 @@ describe("Basic Functionality", () => { data: { createBatch: { ...baseNewBatch, - metadataIntention: "New", + metadataIntention: "Add", fileCount: 1, files: [{ fileName: "metadata.txt", signedURL: "example-signed-url" }], }, @@ -314,7 +314,7 @@ describe("Basic Functionality", () => { ); const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - userEvent.click(getByLabelText(radio, "New")); + userEvent.click(getByLabelText(radio, "Add")); const file = new File(["unused-content"], "metadata.txt", { type: "text/plain" }); userEvent.upload(getByTestId("metadata-upload-file-input"), file); @@ -372,7 +372,7 @@ describe("Basic Functionality", () => { ); const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - userEvent.click(getByLabelText(radio, "New")); + userEvent.click(getByLabelText(radio, "Add")); const file = new File(["unused-content"], "metadata.txt", { type: "text/plain" }); userEvent.upload(getByTestId("metadata-upload-file-input"), file); @@ -396,7 +396,7 @@ describe("Basic Functionality", () => { data: { createBatch: { ...baseNewBatch, - metadataIntention: "New", + metadataIntention: "Add", fileCount: 1, files: [{ fileName: "metadata.txt", signedURL: "example-signed-url" }], }, @@ -431,7 +431,7 @@ describe("Basic Functionality", () => { ); const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - userEvent.click(getByLabelText(radio, "New")); + userEvent.click(getByLabelText(radio, "Add")); const file = new File(["unused-content"], "metadata.txt", { type: "text/plain" }); userEvent.upload(getByTestId("metadata-upload-file-input"), file); @@ -459,7 +459,7 @@ describe("Basic Functionality", () => { data: { createBatch: { ...baseNewBatch, - metadataIntention: "New", + metadataIntention: "Add", fileCount: 1, files: [{ fileName: "metadata.txt", signedURL: "example-signed-url" }], }, @@ -498,7 +498,7 @@ describe("Basic Functionality", () => { ); const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - userEvent.click(getByLabelText(radio, "New")); + userEvent.click(getByLabelText(radio, "Add")); const file = new File(["unused-content"], "metadata.txt", { type: "text/plain" }); userEvent.upload(getByTestId("metadata-upload-file-input"), file); @@ -531,7 +531,7 @@ describe("Implementation Requirements", () => { data: { createBatch: { ...baseNewBatch, - metadataIntention: "New", + metadataIntention: "Add", fileCount: 1, files: [{ fileName: "metadata.txt", signedURL: "example-signed-url" }], }, @@ -547,7 +547,7 @@ describe("Implementation Requirements", () => { data: { updateBatch: { ...baseBatch, - metadataIntention: "New", + metadataIntention: "Add", status: "Uploaded", }, }, @@ -574,7 +574,7 @@ describe("Implementation Requirements", () => { ); const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - userEvent.click(getByLabelText(radio, "New")); + userEvent.click(getByLabelText(radio, "Add")); const file = new File(["unused-content"], "metadata.txt", { type: "text/plain" }); userEvent.upload(getByTestId("metadata-upload-file-input"), file); @@ -587,7 +587,7 @@ describe("Implementation Requirements", () => { ); }); - it("should disable 'Update' and 'Delete' if no metadata or data files are uploaded yet", () => { + it("should disable 'Add/Change' and 'Remove' if no metadata or data files are uploaded yet", () => { const { getByTestId } = render( { const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - expect(getByLabelText(radio, "New")).toBeEnabled(); - expect(getByLabelText(radio, "Update")).toBeDisabled(); - expect(getByLabelText(radio, "Delete")).toBeDisabled(); + expect(getByLabelText(radio, "Add")).toBeEnabled(); + expect(getByLabelText(radio, "Add/Change")).toBeDisabled(); + expect(getByLabelText(radio, "Remove")).toBeDisabled(); }); - it.each(["New", "Update"])( + it.each(["Add", "Add/Change"])( "should create a '%s' metadata batch when that intention is selected", async (intention) => { const variableMatcherMock = jest.fn().mockReturnValue(true); @@ -668,7 +668,7 @@ describe("Implementation Requirements", () => { ); // NOTE: this is separate because of the confirmation dialog step - it("should create a 'Delete' metadata batch when that intention is selected", async () => { + it("should create a 'Remove' metadata batch when that intention is selected", async () => { const variableMatcherMock = jest.fn().mockReturnValue(true); const mocks: MockedResponse[] = [ { @@ -680,7 +680,7 @@ describe("Implementation Requirements", () => { data: { createBatch: { ...baseNewBatch, - metadataIntention: "Delete", + metadataIntention: "Remove", fileCount: 1, files: [ { @@ -713,7 +713,7 @@ describe("Implementation Requirements", () => { ); const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - userEvent.click(getByLabelText(radio, "Delete")); + userEvent.click(getByLabelText(radio, "Remove")); const file = new File(["unused-content"], "metadata.txt", { type: "text/plain" }); userEvent.upload(getByTestId("metadata-upload-file-input"), file); @@ -724,12 +724,12 @@ describe("Implementation Requirements", () => { expect(variableMatcherMock).toHaveBeenCalledWith( expect.objectContaining({ - metadataIntention: "Delete", + metadataIntention: "Remove", }) ); }); - it("should show a confirmation dialog on click when the intention is 'Delete'", async () => { + it("should show a confirmation dialog on click when the intention is 'Remove'", async () => { const { getByTestId } = render( { ); const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - userEvent.click(getByLabelText(radio, "Delete")); + userEvent.click(getByLabelText(radio, "Remove")); const file = new File(["unused-content"], "metadata.txt", { type: "text/plain" }); userEvent.upload(getByTestId("metadata-upload-file-input"), file); @@ -775,7 +775,7 @@ describe("Implementation Requirements", () => { ); const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - userEvent.click(getByLabelText(radio, "New")); // NOTE: this is already selected by default + userEvent.click(getByLabelText(radio, "Add")); // NOTE: this is already selected by default const files = new Array(10).fill( new File(["unused-content"], "fake-metadata.txt", { type: "text/plain" }) @@ -801,7 +801,7 @@ describe("Implementation Requirements", () => { ); const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - userEvent.click(getByLabelText(radio, "New")); // NOTE: this is already selected by default + userEvent.click(getByLabelText(radio, "Add")); // NOTE: this is already selected by default const file = new File(["unused-content"], "fake-metadata.txt", { type: "text/plain" }); userEvent.upload(getByTestId("metadata-upload-file-input"), file); @@ -832,7 +832,7 @@ describe("Implementation Requirements", () => { const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - userEvent.click(getByLabelText(radio, "New")); // NOTE: this is already selected by default + userEvent.click(getByLabelText(radio, "Add")); // NOTE: this is already selected by default const file = new File(["unused-content"], `allowed-text${ext}`, { type }); userEvent.upload(getByTestId("metadata-upload-file-input"), file); @@ -861,7 +861,7 @@ describe("Implementation Requirements", () => { ); const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - userEvent.click(getByLabelText(radio, "New")); // NOTE: this is already selected by default + userEvent.click(getByLabelText(radio, "Add")); // NOTE: this is already selected by default const file = new File(["unused-content"], `NOT-text${ext}`, { type }); userEvent.upload(getByTestId("metadata-upload-file-input"), file); @@ -894,9 +894,9 @@ describe("Implementation Requirements", () => { const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - expect(getByLabelText(radio, "New")).toBeDisabled(); - expect(getByLabelText(radio, "Update")).toBeDisabled(); - expect(getByLabelText(radio, "Delete")).toBeDisabled(); + expect(getByLabelText(radio, "Add")).toBeDisabled(); + expect(getByLabelText(radio, "Add/Change")).toBeDisabled(); + expect(getByLabelText(radio, "Remove")).toBeDisabled(); expect(getByTestId("metadata-upload-file-select-button")).toBeDisabled(); }); @@ -928,9 +928,9 @@ describe("Implementation Requirements", () => { ); const radio = getByTestId("metadata-upload-batch-intention") as HTMLInputElement; - const newRadio = getByLabelText(radio, "New"); - const updateRadio = getByLabelText(radio, "Update"); - const deleteRadio = getByLabelText(radio, "Delete"); + const newRadio = getByLabelText(radio, "Add"); + const updateRadio = getByLabelText(radio, "Add/Change"); + const deleteRadio = getByLabelText(radio, "Remove"); if (expected === false) { expect(newRadio).toBeDisabled(); diff --git a/src/components/DataSubmissions/MetadataUpload.tsx b/src/components/DataSubmissions/MetadataUpload.tsx index 4f1d19979..061977cef 100644 --- a/src/components/DataSubmissions/MetadataUpload.tsx +++ b/src/components/DataSubmissions/MetadataUpload.tsx @@ -98,7 +98,7 @@ export const MetadataUpload = ({ submission, readOnly, onCreateBatch, onUpload } const { submissionId } = useParams(); const { user } = useAuthContext(); - const [metadataIntention, setMetadataIntention] = useState("New"); + const [metadataIntention, setMetadataIntention] = useState("Add"); const [selectedFiles, setSelectedFiles] = useState(null); const [isUploading, setIsUploading] = useState(false); const [openDeleteDialog, setOpenDeleteDialog] = useState(false); @@ -109,15 +109,15 @@ export const MetadataUpload = ({ submission, readOnly, onCreateBatch, onUpload } !submission?.metadataValidationStatus && !submission?.fileValidationStatus; const acceptedExtensions = [".tsv", ".txt"]; const metadataIntentionOptions = [ - { label: "New", value: "New", disabled: !canUpload }, + { label: "Add", value: "Add", disabled: !canUpload }, { - label: "Update", - value: "Update", + label: "Add/Change", + value: "Add/Change", disabled: !canUpload || isNewSubmission, }, { - label: "Delete", - value: "Delete", + label: "Remove", + value: "Remove", disabled: !canUpload || isNewSubmission, }, ]; @@ -182,7 +182,7 @@ export const MetadataUpload = ({ submission, readOnly, onCreateBatch, onUpload } const onUploadFail = (fileCount = 0) => { onUpload( `${fileCount} ${fileCount > 1 ? "Files" : "File"} failed to ${ - metadataIntention === "Delete" ? "delete" : "upload" + metadataIntention === "Remove" ? "delete" : "upload" }`, "error" ); @@ -250,7 +250,7 @@ export const MetadataUpload = ({ submission, readOnly, onCreateBatch, onUpload } // Batch upload completed successfully onUpload( `${selectedFiles.length} ${selectedFiles.length > 1 ? "Files" : "File"} successfully ${ - metadataIntention === "Delete" ? "deleted" : "uploaded" + metadataIntention === "Remove" ? "deleted" : "uploaded" }`, "success" ); @@ -326,7 +326,7 @@ export const MetadataUpload = ({ submission, readOnly, onCreateBatch, onUpload } variant="contained" color="info" onClick={() => - metadataIntention === "Delete" ? setOpenDeleteDialog(true) : handleUploadFiles() + metadataIntention === "Remove" ? setOpenDeleteDialog(true) : handleUploadFiles() } data-testid="metadata-upload-file-upload-button" disabled={readOnly || !selectedFiles?.length || !canUpload || isUploading} @@ -351,7 +351,7 @@ export const MetadataUpload = ({ submission, readOnly, onCreateBatch, onUpload } onChange={(_, value: MetadataIntention) => !readOnly && setMetadataIntention(value)} options={metadataIntentionOptions} gridWidth={4} - parentProps={{ sx: { minWidth: "400px" } }} + parentProps={{ sx: { minWidth: "430px" } }} readOnly={readOnly} inline row diff --git a/src/types/Submissions.d.ts b/src/types/Submissions.d.ts index 8559a24b9..579229931 100644 --- a/src/types/Submissions.d.ts +++ b/src/types/Submissions.d.ts @@ -110,7 +110,15 @@ type BatchFileInfo = { type BatchStatus = "Uploading" | "Uploaded" | "Failed"; -type MetadataIntention = "New" | "Update" | "Delete"; +/** + * The intention of the metadata upload. + * + * @note In MVP-2.1.0, the previous values were: + * - `New` => `Add` + * - `Update` => `Add/Change` + * - `Delete` => `Remove` + */ +type MetadataIntention = "Add" | "Add/Change" | "Remove"; type UploadType = "metadata" | "data file"; @@ -119,7 +127,10 @@ type Batch = { displayID: number; submissionID: string; // parent type: UploadType; - metadataIntention: MetadataIntention; // [New, Update, Delete], Update is meant for "Update or insert", metadata only! file batches are always treated as Update + /** + * See {@link MetadataIntention} for more information. + */ + metadataIntention: MetadataIntention; fileCount: number; // calculated by BE files: BatchFileInfo[]; status: BatchStatus; @@ -134,7 +145,7 @@ type NewBatch = { bucketName?: string; // S3 bucket of the submission, for file batch / CLI use filePrefix?: string; // prefix/path within S3 bucket, for file batch / CLI use type: UploadType; - metadataIntention: MetadataIntention; // [New, Update, Delete], Update is meant for "Update or insert", metadata only! file batches are always treated as Update + metadataIntention: MetadataIntention; fileCount: number; // calculated by BE files: FileURL[]; status: BatchStatus; // [New, Uploaded, Upload Failed, Loaded, Rejected] Loaded and Rejected are for metadata batch only