diff --git a/source/frontend/src/components/common/GenericModal.tsx b/source/frontend/src/components/common/GenericModal.tsx index 108f19b997..427ebc2645 100644 --- a/source/frontend/src/components/common/GenericModal.tsx +++ b/source/frontend/src/components/common/GenericModal.tsx @@ -27,6 +27,7 @@ export interface ModalContent { handleCancel?: Function; /** Optional function to control behaviour of ok button. Default is to reload the app. */ handleOk?: Function; + handleOkDisabled?: boolean; /** Optional text to display on the cancel button. Default is Cancel. */ cancelButtonText?: string; /** Optional variant that will override the default variant of warning. */ @@ -98,6 +99,7 @@ export const GenericModal = (props: Omit & ModalProps) = display, setDisplay, handleOk, + handleOkDisabled, handleCancel, title, message, @@ -215,12 +217,19 @@ export const GenericModal = (props: Omit & ModalProps) = title="cancel-modal" variant={cancelButtonVariant ?? 'secondary'} onClick={close} + data-testid="cancel-modal-button" > {cancelButtonText} )} - diff --git a/source/frontend/src/components/common/Section/Section.tsx b/source/frontend/src/components/common/Section/Section.tsx index 021a90f61b..608d5ff49d 100644 --- a/source/frontend/src/components/common/Section/Section.tsx +++ b/source/frontend/src/components/common/Section/Section.tsx @@ -9,18 +9,29 @@ interface SectionProps { header?: React.ReactNode; isCollapsable?: boolean; initiallyExpanded?: boolean; + noPadding?: boolean; } export const Section: React.FC< React.PropsWithChildren> & { 'data-testid'?: string; } -> = ({ header, children, title, isCollapsable, initiallyExpanded, className, ...rest }) => { +> = ({ + header, + children, + title, + isCollapsable, + initiallyExpanded, + noPadding, + className, + ...rest +}) => { const [isCollapsed, setIsCollapsed] = useState(!initiallyExpanded && true); return ( {header && ( @@ -66,9 +77,9 @@ const StyledSectionHeader = styled.h2` margin-bottom: 2rem; `; -const StyledFormSection = styled.div` - margin: 1.5rem; - padding: 1.5rem; +const StyledFormSection = styled.div<{ noPadding?: boolean }>` + margin: ${props => (props.noPadding === true ? '' : '1.5rem')}; + padding: ${props => (props.noPadding === true ? '' : '1.5rem')}; background-color: white; text-align: left; border-radius: 0.5rem; diff --git a/source/frontend/src/components/common/form/FileDragAndDrop.tsx b/source/frontend/src/components/common/form/FileDragAndDrop.tsx new file mode 100644 index 0000000000..484f3f77ba --- /dev/null +++ b/source/frontend/src/components/common/form/FileDragAndDrop.tsx @@ -0,0 +1,152 @@ +import { ChangeEvent, FunctionComponent, useState } from 'react'; +import styled from 'styled-components'; + +import { SectionField } from '../Section/SectionField'; + +interface IFileDragAndDropProps { + onSelectFile: (file: File | null) => void; + selectedFile: File | null; + validExtensions: string[]; +} + +const FileDragAndDrop: FunctionComponent> = ({ + onSelectFile, + selectedFile, + validExtensions, +}) => { + const validDocumentExtensions: string = validExtensions.map(x => `.${x}`).join(','); + + const [isDragging, setIsDragging] = useState(false); + + const handleFileInput = (changeEvent: ChangeEvent) => { + // handle validations + if (changeEvent.target !== null) { + var target = changeEvent.target; + if (target.files !== null && target.files.length > 0) { + onSelectFile(target.files[0]); + } + } + }; + + const handleDragEnter = (event: React.DragEvent) => { + event.preventDefault(); + event.stopPropagation(); + + setIsDragging(true); + }; + + const handleDragLeave = (event: React.DragEvent) => { + event.preventDefault(); + event.stopPropagation(); + + setIsDragging(false); + }; + + const handleDragOver = (event: React.DragEvent) => { + event.preventDefault(); + event.stopPropagation(); + + setIsDragging(true); + }; + + const handleDrop = (event: React.DragEvent) => { + event.preventDefault(); + event.stopPropagation(); + + let files = [...event.dataTransfer.files]; + + if (files && files.length > 0) { + onSelectFile(files[0]); + event.dataTransfer.clearData(); + } + + setIsDragging(false); + }; + + const shortenString = (text: string, maxLength: number, terminator: string = '...'): string => { + if (text.length > maxLength) { + return text.substring(0, maxLength - terminator.length) + terminator; + } + + return text; + }; + + return ( +
+ + + Drag files here to attach or{' '} + + Browse + + + + + {selectedFile !== null && ( + + + {shortenString(selectedFile.name || '', 20)} + + + )} +
+ ); +}; + +export default FileDragAndDrop; + +const DragDropZone = styled.div<{ isDragging: boolean }>` + border: 1px solid ${({ theme }) => theme.css.lightVariantColor}; + + border-style: ${({ isDragging }) => (isDragging ? 'solid' : 'dashed')}; + + border: ${props => (props.isDragging ? `1px solid ${props.theme.css.draftColor}` : '')}; + + background-color: ${props => (props.isDragging ? props.theme.css.filterBoxColor : '')}; + + height: 10rem; + line-height: 10rem; + text-align: center; +`; + +const StyledContent = styled.div` + width: 100%; + display: inline-block; + vertical-align: middle; + line-height: normal; +`; + +const StyledUploadLabel = styled.label` + display: inline-block; + color: ${({ theme }) => theme.css.linkColor}; + cursor: pointer; + + &:hover { + color: ${({ theme }) => theme.css.linkHoverColor}; + text-decoration: underline; + } +`; + +const StyledFileInput = styled.input` + display: none; +`; + +const StyledSelectedFile = styled.div` + padding-top: 2rem; + overflow: hide; + word-wrap: break-word; +`; diff --git a/source/frontend/src/features/documents/ComposedDocument.ts b/source/frontend/src/features/documents/ComposedDocument.ts index 7f7c7c85d5..b89742b551 100644 --- a/source/frontend/src/features/documents/ComposedDocument.ts +++ b/source/frontend/src/features/documents/ComposedDocument.ts @@ -86,6 +86,8 @@ export class DocumentUploadFormData { public documentTypeId: string; public documentStatusCode: string; public documentMetadata: Record; + public isDocumentTypeChanged: boolean = false; + public isSelectedFile: boolean = false; public constructor( initialStatus: string, @@ -125,6 +127,7 @@ export class DocumentUpdateFormData { public mayanDocumentId: number; public documentStatusCode: string; public documentMetadata: Record; + public documentTypeId: string = ''; public static fromApi( composedDocument: ComposedDocument, @@ -144,6 +147,9 @@ export class DocumentUpdateFormData { model.documentMetadata[metaType.metadata_type?.id?.toString() || '-'] = foundMetadata?.value ?? ''; }); + const documentTypeLabel = composedDocument.pimsDocumentRelationship?.document?.documentType?.id; + + model.documentTypeId = documentTypeLabel?.toString() || ''; return model; } diff --git a/source/frontend/src/features/documents/DocumentMetadataYupSchema.tsx b/source/frontend/src/features/documents/DocumentMetadataYupSchema.tsx index ce098ef307..5bde93525f 100644 --- a/source/frontend/src/features/documents/DocumentMetadataYupSchema.tsx +++ b/source/frontend/src/features/documents/DocumentMetadataYupSchema.tsx @@ -14,5 +14,8 @@ export const getDocumentMetadataYupSchema = ( } } - return Yup.object().shape({ documentMetadata: Yup.object().shape(metadataSchema) }); + return Yup.object().shape({ + documentMetadata: Yup.object().shape(metadataSchema), + documentTypeId: Yup.string().required('Document type is required'), + }); }; diff --git a/source/frontend/src/features/documents/documentDetail/DocumentDetailForm.tsx b/source/frontend/src/features/documents/documentDetail/DocumentDetailForm.tsx index 67ee0be875..e7f65926ce 100644 --- a/source/frontend/src/features/documents/documentDetail/DocumentDetailForm.tsx +++ b/source/frontend/src/features/documents/documentDetail/DocumentDetailForm.tsx @@ -4,6 +4,7 @@ import { Col, Row } from 'react-bootstrap'; import { Button } from '@/components/common/buttons/Button'; import { Select } from '@/components/common/form'; import LoadingBackdrop from '@/components/common/LoadingBackdrop'; +import { Section } from '@/components/common/Section/Section'; import { SectionField } from '@/components/common/Section/SectionField'; import TooltipIcon from '@/components/common/TooltipIcon'; import * as API from '@/constants/API'; @@ -13,13 +14,7 @@ import useLookupCodeHelpers from '@/hooks/useLookupCodeHelpers'; import { Api_DocumentUpdateRequest } from '@/models/api/Document'; import { Api_Storage_DocumentTypeMetadataType } from '@/models/api/DocumentStorage'; -import { - StyledGreySection, - StyledH2, - StyledH3, - StyledHeader, - StyledScrollable, -} from '../commonStyles'; +import { StyledH3, StyledScrollable } from '../commonStyles'; import { ComposedDocument, DocumentUpdateFormData } from '../ComposedDocument'; import { DocumentMetadataView } from '../DocumentMetadataView'; import { getDocumentMetadataYupSchema } from '../DocumentMetadataYupSchema'; @@ -53,20 +48,20 @@ export const DocumentDetailForm: React.FunctionComponent< {hasClaim(Claims.DOCUMENT_VIEW) && ( <> - - - - - Document information - - - - +
+ Document information + {' '} + + } + > innerRef={props.formikRef} @@ -95,15 +90,22 @@ export const DocumentDetailForm: React.FunctionComponent< mayanMetadata={props.mayanMetadataTypes} formikProps={formikProps} /> +
Do you want to proceed?
+
- - @@ -111,7 +113,7 @@ export const DocumentDetailForm: React.FunctionComponent< )}
- +
)} diff --git a/source/frontend/src/features/documents/documentDetail/DocumentDetailHeader.tsx b/source/frontend/src/features/documents/documentDetail/DocumentDetailHeader.tsx index f9aebb5c09..0097783333 100644 --- a/source/frontend/src/features/documents/documentDetail/DocumentDetailHeader.tsx +++ b/source/frontend/src/features/documents/documentDetail/DocumentDetailHeader.tsx @@ -18,7 +18,7 @@ const DocumentDetailHeader: React.FunctionComponent< const mayanDocumentId = props.document.pimsDocumentRelationship?.document?.mayanDocumentId || -1; return ( - <> +
- +
); }; diff --git a/source/frontend/src/features/documents/documentDetail/DocumentDetailView.tsx b/source/frontend/src/features/documents/documentDetail/DocumentDetailView.tsx index 38c05b98ea..3e82af41ae 100644 --- a/source/frontend/src/features/documents/documentDetail/DocumentDetailView.tsx +++ b/source/frontend/src/features/documents/documentDetail/DocumentDetailView.tsx @@ -1,21 +1,15 @@ -import { Col, Row } from 'react-bootstrap'; import { FaEdit } from 'react-icons/fa'; +import styled from 'styled-components'; import { LinkButton } from '@/components/common/buttons'; import LoadingBackdrop from '@/components/common/LoadingBackdrop'; +import { Section } from '@/components/common/Section/Section'; import { SectionField } from '@/components/common/Section/SectionField'; import TooltipIcon from '@/components/common/TooltipIcon'; import Claims from '@/constants/claims'; import useKeycloakWrapper from '@/hooks/useKeycloakWrapper'; -import { - StyledGreySection, - StyledH2, - StyledH3, - StyledHeader, - StyledNoData, - StyledScrollable, -} from '../commonStyles'; +import { StyledH3, StyledNoData, StyledScrollable } from '../commonStyles'; import { ComposedDocument } from '../ComposedDocument'; import { StyledContainer } from '../list/styles'; import DocumentDetailHeader from './DocumentDetailHeader'; @@ -40,31 +34,32 @@ export const DocumentDetailView: React.FunctionComponent< {hasClaim(Claims.DOCUMENT_VIEW) && ( <> - - - - - Document information - - - - {hasClaim(Claims.DOCUMENT_EDIT) && ( - - {' '} - { - props.setIsEditable(true); - }} - > - - - - )} - + +
+ Document information + {' '} + + } + > + {hasClaim(Claims.DOCUMENT_EDIT) && ( + + { + props.setIsEditable(true); + }} + > + + + + )} {props.document.pimsDocumentRelationship?.document?.statusTypeCode?.description} @@ -83,9 +78,14 @@ export const DocumentDetailView: React.FunctionComponent< ))} - +
)} ); }; + +const RightFlexDiv = styled.div` + display: flex; + flex-direction: row-reverse; +`; diff --git a/source/frontend/src/features/documents/documentDetail/__snapshots__/DocumentDetailForm.test.tsx.snap b/source/frontend/src/features/documents/documentDetail/__snapshots__/DocumentDetailForm.test.tsx.snap index d22e73e3e9..055e2b21c2 100644 --- a/source/frontend/src/features/documents/documentDetail/__snapshots__/DocumentDetailForm.test.tsx.snap +++ b/source/frontend/src/features/documents/documentDetail/__snapshots__/DocumentDetailForm.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`DocumentDetailForm component renders as expected 1`] = ` -.c9.btn { +.c8.btn { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -31,49 +31,49 @@ exports[`DocumentDetailForm component renders as expected 1`] = ` cursor: pointer; } -.c9.btn:hover { +.c8.btn:hover { -webkit-text-decoration: underline; text-decoration: underline; opacity: 0.8; } -.c9.btn:focus { +.c8.btn:focus { outline-width: 0.4rem; outline-style: solid; outline-offset: 1px; box-shadow: none; } -.c9.btn.btn-primary { +.c8.btn.btn-primary { border: none; } -.c9.btn.btn-secondary { +.c8.btn.btn-secondary { background: none; } -.c9.btn.btn-info { +.c8.btn.btn-info { border: none; background: none; padding-left: 0.6rem; padding-right: 0.6rem; } -.c9.btn.btn-info:hover, -.c9.btn.btn-info:active, -.c9.btn.btn-info:focus { +.c8.btn.btn-info:hover, +.c8.btn.btn-info:active, +.c8.btn.btn-info:focus { background: none; } -.c9.btn.btn-light { +.c8.btn.btn-light { border: none; } -.c9.btn.btn-dark { +.c8.btn.btn-dark { border: none; } -.c9.btn.btn-link { +.c8.btn.btn-link { font-size: 1.6rem; font-weight: 400; background: none; @@ -94,9 +94,9 @@ exports[`DocumentDetailForm component renders as expected 1`] = ` padding: 0; } -.c9.btn.btn-link:hover, -.c9.btn.btn-link:active, -.c9.btn.btn-link:focus { +.c8.btn.btn-link:hover, +.c8.btn.btn-link:active, +.c8.btn.btn-link:focus { -webkit-text-decoration: underline; text-decoration: underline; border: none; @@ -105,14 +105,14 @@ exports[`DocumentDetailForm component renders as expected 1`] = ` outline: none; } -.c9.btn.btn-link:disabled, -.c9.btn.btn-link.disabled { +.c8.btn.btn-link:disabled, +.c8.btn.btn-link.disabled { background: none; pointer-events: none; } -.c9.btn:disabled, -.c9.btn:disabled:hover { +.c8.btn:disabled, +.c8.btn:disabled:hover { box-shadow: none; -webkit-user-select: none; -moz-user-select: none; @@ -123,18 +123,30 @@ exports[`DocumentDetailForm component renders as expected 1`] = ` opacity: 0.65; } -.c9.Button .Button__icon { +.c8.Button .Button__icon { margin-right: 1.6rem; } -.c9.Button--icon-only:focus { +.c8.Button--icon-only:focus { outline: none; } -.c9.Button--icon-only .Button__icon { +.c8.Button--icon-only .Button__icon { margin-right: 0; } +.c4 { + font-weight: bold; + border-bottom: 0.2rem solid; + margin-bottom: 2rem; +} + +.c3 { + background-color: white; + text-align: left; + border-radius: 0.5rem; +} + .c2.required::before { content: '*'; position: absolute; @@ -146,7 +158,7 @@ exports[`DocumentDetailForm component renders as expected 1`] = ` font-weight: bold; } -.c6 { +.c5 { width: 100%; -webkit-box-flex: 1; -webkit-flex-grow: 1; @@ -155,15 +167,7 @@ exports[`DocumentDetailForm component renders as expected 1`] = ` overflow-y: auto; } -.c3 { - padding: 1rem; -} - -.c5 { - font-weight: 700; -} - -.c8 { +.c7 { font-weight: 700; font-size: 1.7rem; margin-bottom: 1rem; @@ -172,15 +176,7 @@ exports[`DocumentDetailForm component renders as expected 1`] = ` border-bottom: solid 0.1rem; } -.c4 { - text-align: left !important; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; -} - -.c7 { +.c6 { overflow-x: hidden; max-height: 50rem; } @@ -199,92 +195,92 @@ exports[`DocumentDetailForm component renders as expected 1`] = ` class="c0" >
- -
-
- Survey -
-
-
-
- +
+
- File name - : - + Survey +
-
- NewFile.doc -
+ File name + : + +
+
- - +
+ - - - + + + + +
-
-

- Document information -

+ Document information +
-
+
- -
-
+ +
- + + + + + + + + +
-
-

- Details -

-
-
- -
+ Details +
- +
-
-
-
-
-
- +
+
+ Do you want to proceed? +
+
+
- +
+ No +
+ +
+
+ +
diff --git a/source/frontend/src/features/documents/documentDetail/__snapshots__/DocumentDetailView.test.tsx.snap b/source/frontend/src/features/documents/documentDetail/__snapshots__/DocumentDetailView.test.tsx.snap index 1f3e81f64c..4d3f61b8c8 100644 --- a/source/frontend/src/features/documents/documentDetail/__snapshots__/DocumentDetailView.test.tsx.snap +++ b/source/frontend/src/features/documents/documentDetail/__snapshots__/DocumentDetailView.test.tsx.snap @@ -135,6 +135,18 @@ exports[`DocumentDetailView component renders as expected 1`] = ` margin-right: 0; } +.c4 { + font-weight: bold; + border-bottom: 0.2rem solid; + margin-bottom: 2rem; +} + +.c3 { + background-color: white; + text-align: left; + border-radius: 0.5rem; +} + .c2.required::before { content: '*'; position: absolute; @@ -155,14 +167,6 @@ exports[`DocumentDetailView component renders as expected 1`] = ` overflow-y: auto; } -.c3 { - padding: 1rem; -} - -.c5 { - font-weight: 700; -} - .c7 { font-weight: 700; font-size: 1.7rem; @@ -172,14 +176,6 @@ exports[`DocumentDetailView component renders as expected 1`] = ` border-bottom: solid 0.1rem; } -.c4 { - text-align: left !important; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; -} - .c9 { overflow-x: hidden; max-height: 50rem; @@ -189,6 +185,16 @@ exports[`DocumentDetailView component renders as expected 1`] = ` padding: 1rem; } +.c5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row-reverse; + -ms-flex-direction: row-reverse; + flex-direction: row-reverse; +} +
- -
-
- Survey -
-
-
-
- +
+
- File name - : - + Survey +
-
- NewFile.doc -
+ File name + : + +
+
- - +
+ - - - + + + + +
-
-

- Document information -

+ Document information +
+ +
-
-
-
-
- -
-
- Amended -
-
-

- Details -

-
@@ -371,14 +353,42 @@ exports[`DocumentDetailView component renders as expected 1`] = `
- Tag1234 + Amended +
+
+

+ Details +

+
+
+
+ +
+
+ Tag1234 +
diff --git a/source/frontend/src/features/documents/documentUpload/DocumentUploadContainer.tsx b/source/frontend/src/features/documents/documentUpload/DocumentUploadContainer.tsx index aaa0042da8..59f5129e00 100644 --- a/source/frontend/src/features/documents/documentUpload/DocumentUploadContainer.tsx +++ b/source/frontend/src/features/documents/documentUpload/DocumentUploadContainer.tsx @@ -1,5 +1,13 @@ import { FormikProps } from 'formik'; -import { ChangeEvent, useCallback, useEffect, useRef, useState } from 'react'; +import { + ChangeEvent, + forwardRef, + useCallback, + useEffect, + useImperativeHandle, + useRef, + useState, +} from 'react'; import { SelectOption } from '@/components/common/form'; import * as API from '@/constants/API'; @@ -19,15 +27,24 @@ import { useDocumentRelationshipProvider } from '../hooks/useDocumentRelationshi import DocumentUploadForm from './DocumentUploadForm'; export interface IDocumentUploadContainerProps { + ref: React.RefObject< + React.FunctionComponent> + >; parentId: string; relationshipType: DocumentRelationshipType; onUploadSuccess: () => void; onCancel: () => void; + setCanUpload: (canUpload: boolean) => void; } -export const DocumentUploadContainer: React.FunctionComponent< - React.PropsWithChildren -> = props => { +export interface IDocumentUploadContainerRef { + uploadDocument: () => void; +} + +const DocumentUploadContainer = forwardRef< + IDocumentUploadContainerRef, + IDocumentUploadContainerProps +>((props, ref) => { const deleteModalProps = getCancelModalProps(); const { getOptionsByType } = useLookupCodeHelpers(); @@ -131,8 +148,20 @@ export const DocumentUploadContainer: React.FunctionComponent< ]); const onUploadDocument = async (uploadRequest: Api_DocumentUploadRequest) => { - await uploadDocument(props.relationshipType, props.parentId, uploadRequest); - props.onUploadSuccess(); + var result = await uploadDocument(props.relationshipType, props.parentId, uploadRequest); + if (result !== undefined) { + props.onUploadSuccess(); + } + }; + + useImperativeHandle(ref, () => ({ + uploadDocument() { + formikRef.current?.submitForm(); + }, + })); + + const onDocumentSelected = () => { + props.setCanUpload(true); }; return ( @@ -144,8 +173,11 @@ export const DocumentUploadContainer: React.FunctionComponent< documentStatusOptions={documentStatusOptions} mayanMetadataTypes={documentTypeMetadataTypes} onDocumentTypeChange={onDocumentTypeChange} + onDocumentSelected={onDocumentSelected} onUploadDocument={onUploadDocument} onCancel={handleCancelClick} /> ); -}; +}); + +export default DocumentUploadContainer; diff --git a/source/frontend/src/features/documents/documentUpload/DocumentUploadForm.test.tsx b/source/frontend/src/features/documents/documentUpload/DocumentUploadForm.test.tsx index f625949037..1a07ef9de3 100644 --- a/source/frontend/src/features/documents/documentUpload/DocumentUploadForm.test.tsx +++ b/source/frontend/src/features/documents/documentUpload/DocumentUploadForm.test.tsx @@ -5,7 +5,7 @@ import { mockDocumentTypesResponse } from '@/mocks/documents.mock'; import { mockLookups } from '@/mocks/lookups.mock'; import { Api_Storage_DocumentTypeMetadataType } from '@/models/api/DocumentStorage'; import { lookupCodesSlice } from '@/store/slices/lookupCodes'; -import { act, fireEvent, render, RenderOptions, userEvent, waitFor } from '@/utils/test-utils'; +import { fireEvent, render, RenderOptions, userEvent, waitFor } from '@/utils/test-utils'; import { DocumentUploadFormData } from '../ComposedDocument'; import DocumentUploadForm from './DocumentUploadForm'; @@ -18,6 +18,7 @@ const handleSubmit = jest.fn(); const handleCancelClick = jest.fn(); const onUploadDocument = jest.fn(); const onDocumentTypeChange = jest.fn(); +const onDocumentSelected = jest.fn(); const documentStatusOptions: SelectOption[] = [ { label: '', value: 'NONE' }, @@ -61,6 +62,7 @@ describe('DocumentUploadView component', () => { isLoading={false} mayanMetadataTypes={documentTypeMetadataType} onDocumentTypeChange={onDocumentTypeChange} + onDocumentSelected={onDocumentSelected} onUploadDocument={onUploadDocument} onCancel={handleCancelClick} initialDocumentType={'AMMEND'} @@ -118,43 +120,6 @@ describe('DocumentUploadView component', () => { expect(textarea).toBeVisible(); }); - it('save button to be disabled', async () => { - const { getByTestId } = setup({ initialValues }); - - const save = await getByTestId('save'); - - expect(save).toHaveAttribute('disabled'); - }); - - it('should validate required inputs', async () => { - const { findByText, getByTestId } = setup({ initialValues }); - - const save = await getByTestId('save'); - // get the upload button - let uploader = getByTestId('upload-input'); - - // simulate ulpoad event and wait until finish - await waitFor(() => - fireEvent.change(uploader, { - target: { files: [file] }, - }), - ); - userEvent.click(save); - - expect(await findByText(/Mandatory fields are required./i)).toBeVisible(); - }); - - it('should cancel form when Cancel button is clicked', async () => { - const { getByTestId } = setup({ initialValues }); - - const cancel = await getByTestId('cancel'); - act(() => { - userEvent.click(cancel); - }); - expect(handleCancelClick).toBeCalled(); - expect(handleSubmit).not.toBeCalled(); - }); - it.skip('should submit form when Submit button is clicked', async () => { const { getByTestId } = setup({ initialValues }); diff --git a/source/frontend/src/features/documents/documentUpload/DocumentUploadForm.tsx b/source/frontend/src/features/documents/documentUpload/DocumentUploadForm.tsx index 62b51bba7a..4f9faa6495 100644 --- a/source/frontend/src/features/documents/documentUpload/DocumentUploadForm.tsx +++ b/source/frontend/src/features/documents/documentUpload/DocumentUploadForm.tsx @@ -1,21 +1,20 @@ import { Formik, FormikProps } from 'formik'; import { ChangeEvent, useEffect, useState } from 'react'; -import { Col, Row } from 'react-bootstrap'; -import { Button } from '@/components/common/buttons/Button'; import { Select, SelectOption } from '@/components/common/form'; +import FileDragAndDrop from '@/components/common/form/FileDragAndDrop'; import LoadingBackdrop from '@/components/common/LoadingBackdrop'; +import { Section } from '@/components/common/Section/Section'; import { SectionField } from '@/components/common/Section/SectionField'; import TooltipIcon from '@/components/common/TooltipIcon'; import ValidDocumentExtensions from '@/constants/ValidDocumentExtensions'; import { Api_DocumentType, Api_DocumentUploadRequest } from '@/models/api/Document'; import { Api_Storage_DocumentTypeMetadataType } from '@/models/api/DocumentStorage'; -import { StyledGreySection, StyledH2, StyledH3, StyledScrollable } from '../commonStyles'; +import { StyledH3, StyledScrollable } from '../commonStyles'; import { DocumentUploadFormData } from '../ComposedDocument'; import { DocumentMetadataView } from '../DocumentMetadataView'; import { getDocumentMetadataYupSchema } from '../DocumentMetadataYupSchema'; -import { StyledContainer } from '../list/styles'; interface IDocumentUploadFormProps { isLoading: boolean; @@ -24,6 +23,7 @@ interface IDocumentUploadFormProps { documentTypes: Api_DocumentType[]; documentStatusOptions: SelectOption[]; mayanMetadataTypes: Api_Storage_DocumentTypeMetadataType[]; + onDocumentSelected: () => void; onDocumentTypeChange: (changeEvent: ChangeEvent) => void; onUploadDocument: (uploadRequest: Api_DocumentUploadRequest) => void; onCancel: () => void; @@ -41,18 +41,6 @@ const DocumentUploadForm: React.FunctionComponent< return { label: x.documentTypeDescription || '', value: x.id?.toString() || '' }; }); - const handleFileInput = (changeEvent: ChangeEvent) => { - // handle validations - if (changeEvent.target !== null) { - var target = changeEvent.target; - if (target.files !== null) { - setSelectedFile(target.files[0]); - // forces formik to flag the change as dirty - props.formikRef.current?.setFieldValue('fileSet', true); - } - } - }; - const initialFormData = new DocumentUploadFormData( props.documentStatusOptions[0]?.value?.toString(), props.initialDocumentType, @@ -68,10 +56,15 @@ const DocumentUploadForm: React.FunctionComponent< } }, [props.formikRef, props.documentTypes, props.initialDocumentType, props.mayanMetadataTypes]); - const validDocumentExtensions: string = ValidDocumentExtensions.map(x => `.${x}`).join(','); + const onSelectFile = (selectedFile: File | null) => { + setSelectedFile(selectedFile); + // forces formik to flag the change as dirty + props.formikRef.current?.setFieldValue('isSelectedFile', true); + props.onDocumentSelected(); + }; return ( - + <> innerRef={props.formikRef} @@ -97,11 +90,7 @@ const DocumentUploadForm: React.FunctionComponent< > {formikProps => ( <> -
- Choose the document type and select "Browse" to choose the file to upload from your - computer or network to PIMS. -
- + -
+
+ - - - - Document information - - +
+ Document Information{' '} - - + + } + noPadding + > - - - - -
+ Select Document type + + + + + +
-
- -
+ Choose document to upload + : + +
+
+ class="pt-2" + /> +
- +
+ Drag files here to attach or + + +
-
+
+

-

- Document information -

-
-
+ Document Information +
+

+
@@ -319,14 +223,14 @@ exports[`DocumentUploadView component renders as expected 1`] = ` class="pr-0 text-left col-4" >

Details

-
-
-
- -
- + Do you want to proceed?
diff --git a/source/frontend/src/features/mapSideBar/acquisition/common/GenerateForm/modals/__snapshots__/GenerateLetterRecipientsModal.test.tsx.snap b/source/frontend/src/features/mapSideBar/acquisition/common/GenerateForm/modals/__snapshots__/GenerateLetterRecipientsModal.test.tsx.snap index 68286e99c5..4834c8dfeb 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/common/GenerateForm/modals/__snapshots__/GenerateLetterRecipientsModal.test.tsx.snap +++ b/source/frontend/src/features/mapSideBar/acquisition/common/GenerateForm/modals/__snapshots__/GenerateLetterRecipientsModal.test.tsx.snap @@ -376,6 +376,7 @@ exports[`GenerateLetterRecipients modal renders as expected 1`] = ` >