From c91caf3fa30ec98853b4f5cda6ef35bbba064d3e Mon Sep 17 00:00:00 2001 From: Scott Willrich <swillrich@gmail.com> Date: Wed, 29 Sep 2021 10:34:08 -0500 Subject: [PATCH] task/FP-1102 - Add ability to compress folder to folder download message modal (#499) * Updated DataFilesDownloadMessageModal.js to compress folders for download * added fixture and updated DataFilesDownloadMessageModal.test.js * fixed linting errors * fixed one more linting error * Added styleName to match recent changes to DataFileCompressModal.js Co-authored-by: Sal Tijerina <r.sal.tijerina@gmail.com> --- .../DataFilesDownloadMessageModal.js | 195 +++++++++++++++--- .../DataFilesDownloadMessageModal.fixture.js | 37 ++++ .../DataFilesDownloadMessageModal.test.js | 7 +- 3 files changed, 209 insertions(+), 30 deletions(-) create mode 100644 client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.fixture.js diff --git a/client/src/components/DataFiles/DataFilesModals/DataFilesDownloadMessageModal.js b/client/src/components/DataFiles/DataFilesModals/DataFilesDownloadMessageModal.js index c22777245..82cbccb1d 100644 --- a/client/src/components/DataFiles/DataFilesModals/DataFilesDownloadMessageModal.js +++ b/client/src/components/DataFiles/DataFilesModals/DataFilesDownloadMessageModal.js @@ -1,13 +1,102 @@ -import React from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; -import { Icon } from '_common'; -import './DataFilesDownloadMessageModal.module.scss'; +import React, { useMemo } from 'react'; +import { useSelector, useDispatch, shallowEqual } from 'react-redux'; +import { + Button, + Modal, + ModalHeader, + ModalBody, + ModalFooter, + Input, + InputGroupAddon +} from 'reactstrap'; +import { + LoadingSpinner, + FormField, + Icon, + InlineMessage, + SectionMessage +} from '_common'; +import { useHistory, useLocation } from 'react-router-dom'; +import { Formik, Form } from 'formik'; +import * as yup from 'yup'; +import './DataFilesCompressModal.module.scss'; const DataFilesDownloadMessageModal = () => { + const history = useHistory(); + const location = useLocation(); + const dispatch = useDispatch(); + const status = useSelector( + state => state.files.operationStatus.compress, + shallowEqual + ); + const isOpen = useSelector(state => state.files.modals.downloadMessage); - const dispatch = useDispatch(); + const params = useSelector( + state => state.files.params.FilesListing, + shallowEqual + ); + + const selectedFiles = useSelector( + ({ files: { selected, listing } }) => + selected.FilesListing.map(i => ({ + ...listing.FilesListing[i] + })), + shallowEqual + ); + const selected = useMemo(() => selectedFiles, [isOpen]); + const formRef = React.useRef(); + + const onOpened = () => { + dispatch({ + type: 'FETCH_FILES_MODAL', + payload: { ...params, section: 'modal' } + }); + }; + + const onClosed = () => { + dispatch({ type: 'DATA_FILES_MODAL_CLOSE' }); + if (status) { + dispatch({ + type: 'DATA_FILES_SET_OPERATION_STATUS', + payload: { status: {}, operation: 'compress' } + }); + history.push(location.pathname); + } + }; + + const compressCallback = () => { + const { filenameDisplay, filetype } = formRef.current.values; + const filename = `${filenameDisplay}${filetype}`; + dispatch({ + type: 'DATA_FILES_COMPRESS', + payload: { filename, files: selected } + }); + }; + + let buttonIcon; + if (status === 'RUNNING') { + buttonIcon = <LoadingSpinner placement="inline" />; + } else if (status === 'ERROR') { + buttonIcon = <Icon name="alert" />; + } else { + buttonIcon = null; + } + const initialValues = { + filenameDisplay: + selectedFiles[0] && selectedFiles.length === 1 + ? selectedFiles[0].name + : '', + filetype: '.zip' + }; + const validationSchema = yup.object().shape({ + filenameDisplay: yup + .string() + .trim('The filename cannot include leading and trailing spaces') + .strict(true) + .required('The filename is required') + }); + const toggle = () => { dispatch({ type: 'DATA_FILES_TOGGLE_MODAL', @@ -16,27 +105,83 @@ const DataFilesDownloadMessageModal = () => { }; return ( - <Modal isOpen={isOpen} toggle={toggle} size="md" className="dataFilesModal"> + <Modal + isOpen={isOpen} + onOpened={onOpened} + onClosed={onClosed} + toggle={toggle} + className="dataFilesModal" + > <ModalHeader toggle={toggle} charCode=""> - Download + Download Folder </ModalHeader> - <ModalBody> - <p styleName="title">Folders must be compressed before download.</p> - <ol> - <li>Select the folder(s).</li> - <li> - Press the " - <Icon name="compress">↩</Icon> Compress" icon above the table. - </li> - <li>Complete and submit the form.</li> - <li>After the job finishes, you may download the compressed file.</li> - </ol> - </ModalBody> - <ModalFooter> - <Button type="button" className="data-files-btn" onClick={toggle}> - Close - </Button> - </ModalFooter> + <Formik + innerRef={formRef} + initialValues={initialValues} + validationSchema={validationSchema} + onSubmit={compressCallback} + > + {({ setFieldValue, values, isValid }) => { + const handleSelectChange = e => { + setFieldValue('filetype', e.target.value); + }; + const formDisabled = status === 'RUNNING' || status === 'SUCCESS'; + const buttonDisabled = + formDisabled || !isValid || values.filenameDisplay === ''; + return ( + <Form> + <ModalBody> + <SectionMessage type="warning"> + Folders must be compressed before downloading + </SectionMessage> + <FormField + label="Compressed File Name" + name="filenameDisplay" + disabled={formDisabled} + addonType="append" + addon={ + <InputGroupAddon addonType="append" styleName="input-field"> + <Input + type="select" + name="filetype" + bsSize="sm" + onChange={handleSelectChange} + disabled={formDisabled} + > + <option value=".zip">.zip</option> + <option value=".tar.gz">.tar.gz</option> + </Input> + </InputGroupAddon> + } + /> + <p> + <em> + A job to compress this folder will be submitted. The + compressed file will appear in this directory for you to + download. + </em> + </p> + </ModalBody> + <ModalFooter> + <InlineMessage isVisible={status === 'SUCCESS'} type="success"> + Successfully started compress job + </InlineMessage> + <Button + className="data-files-btn" + disabled={buttonDisabled} + styleName="submit-button" + type="submit" + > + {buttonIcon} + <span styleName={buttonIcon ? 'with-icon' : ''}> + Compress + </span> + </Button> + </ModalFooter> + </Form> + ); + }} + </Formik> </Modal> ); }; diff --git a/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.fixture.js b/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.fixture.js new file mode 100644 index 000000000..a93e12873 --- /dev/null +++ b/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.fixture.js @@ -0,0 +1,37 @@ +const DataFilesDownloadMessageModalFixture = { + operationStatus: { + copy: {}, + compress: {} + }, + listing: { + FilesListing: { + name: 'testfile', + path: '/testfile', + lastModified: '2020-07-01T10:12:36-05:00', + length: 4096, + permissions: 'ALL', + format: 'folder', + system: 'test.system', + mimeType: 'text/directory', + type: 'dir' + } + }, + params: { + FilesListing: { + api: 'tapis', + scheme: 'private', + system: 'test.system', + path: '' + } + }, + selected: { + FilesListing: [0], + modal: [] + }, + modals: { + copy: true, + downloadMessage: true + } +}; + +export default DataFilesDownloadMessageModalFixture; diff --git a/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.test.js b/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.test.js index 3afb9154c..164cb3d3f 100644 --- a/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.test.js +++ b/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.test.js @@ -1,15 +1,12 @@ import React from 'react'; import configureStore from 'redux-mock-store'; import renderComponent from 'utils/testing'; +import DataFilesDownloadMessageModalFixture from './DataFilesDownloadMessageModal.fixture'; import DataFilesDownloadMessageModal from '../DataFilesDownloadMessageModal'; const mockStore = configureStore(); const initialMockState = { - files: { - modals: { - downloadMessage: true - } - } + files: DataFilesDownloadMessageModalFixture }; describe('DataFilesDownloadMessageModal', () => {