From 22a923f2990056f9cecd6f51c7a37a49411ab333 Mon Sep 17 00:00:00 2001 From: MellyGray Date: Mon, 13 Nov 2023 15:56:28 +0100 Subject: [PATCH 01/11] feat(FileDownloadOptions): add component to the Files Table --- .../access-file-menu/AccessFileMenu.tsx | 2 ++ .../access-file-menu/FileDownloadOptions.tsx | 22 +++++++++++++++++++ .../access-file-menu/AccessFileMenu.spec.tsx | 11 ++++++++++ .../FileDownloadOptions.spec.tsx | 11 ++++++++++ 4 files changed, 46 insertions(+) create mode 100644 src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx create mode 100644 tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.spec.tsx diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/AccessFileMenu.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/AccessFileMenu.tsx index 72a53597a..98f157ef9 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/AccessFileMenu.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/AccessFileMenu.tsx @@ -4,6 +4,7 @@ import { AccessStatus } from './AccessStatus' import { RequestAccessOption } from './RequestAccessOption' import { DropdownButton, DropdownHeader, Tooltip } from '@iqss/dataverse-design-system' import { useTranslation } from 'react-i18next' +import { FileDownloadOptions } from './FileDownloadOptions' interface FileActionButtonAccessFileProps { file: File @@ -23,6 +24,7 @@ export function AccessFileMenu({ file }: FileActionButtonAccessFileProps) { + ) diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx new file mode 100644 index 000000000..5e411ad05 --- /dev/null +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx @@ -0,0 +1,22 @@ +import { DropdownButtonItem, DropdownHeader } from '@iqss/dataverse-design-system' +import { Download } from 'react-bootstrap-icons' +import { File } from '../../../../../../../../files/domain/models/File' + +interface FileDownloadOptionsProps { + file: File +} + +export function FileDownloadOptions({ file }: FileDownloadOptionsProps) { + return ( + <> + + Download Options + + {file.tabularData ? ( + Tabular Data + ) : ( + Original File + )} + + ) +} diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/AccessFileMenu.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/AccessFileMenu.spec.tsx index b0f9224f4..b25c43a4e 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/AccessFileMenu.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/AccessFileMenu.spec.tsx @@ -51,4 +51,15 @@ describe('AccessFileMenu', () => { cy.findByRole('button', { name: 'Access File' }).click() cy.findByRole('button', { name: 'Request Access' }).should('exist') }) + + it('renders the download options header', () => { + cy.customMount( + + + + ) + + cy.findByRole('button', { name: 'Access File' }).click() + cy.findByRole('heading', { name: 'Download Options' }).should('exist') + }) }) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.spec.tsx new file mode 100644 index 000000000..08981927b --- /dev/null +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.spec.tsx @@ -0,0 +1,11 @@ +import { FileDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions' +import { FileMother } from '../../../../../../../../files/domain/models/FileMother' + +const file = FileMother.create() +describe('FileDownloadOptions', () => { + it('renders the download options header', () => { + cy.customMount() + + cy.findByRole('heading', { name: 'Download Options' }).should('exist') + }) +}) From b5568849d57c926e9a72836bc5d50b8b96b5d2b2 Mon Sep 17 00:00:00 2001 From: MellyGray Date: Mon, 6 Nov 2023 10:27:07 +0100 Subject: [PATCH 02/11] feat(FilesTable): add friendly file type mapping --- src/files/domain/models/File.ts | 9 +- .../models/FileTypeToFriendlyTypeMap.ts | 227 ++++++++++++++++++ .../FileCriteriaFilterByType.tsx | 8 +- .../dataset-files/DatasetFiles.spec.tsx | 14 +- .../FileCriteriaFilterByTag.spec.tsx | 3 +- .../FileCriteriaFilterByType.spec.tsx | 41 ++-- .../FileCriteriaForm.spec.tsx | 34 +-- .../file-info-data/FileType.spec.tsx | 4 +- .../e2e/sections/dataset/Dataset.spec.tsx | 4 +- 9 files changed, 285 insertions(+), 59 deletions(-) create mode 100644 src/files/domain/models/FileTypeToFriendlyTypeMap.ts diff --git a/src/files/domain/models/File.ts b/src/files/domain/models/File.ts index d4255cda8..0be9f000c 100644 --- a/src/files/domain/models/File.ts +++ b/src/files/domain/models/File.ts @@ -1,3 +1,5 @@ +import FileTypeToFriendlyTypeMap from './FileTypeToFriendlyTypeMap' + export enum FileSizeUnit { BYTES = 'B', KILOBYTES = 'KB', @@ -122,12 +124,7 @@ export class FileType { constructor(readonly value: string) {} toDisplayFormat(): string { - const words = this.value.split(' ') - return words - .map((word) => { - return word[0].toUpperCase() + word.substring(1) - }) - .join(' ') + return FileTypeToFriendlyTypeMap[this.value] || this.value } } diff --git a/src/files/domain/models/FileTypeToFriendlyTypeMap.ts b/src/files/domain/models/FileTypeToFriendlyTypeMap.ts new file mode 100644 index 000000000..ab4d9b30e --- /dev/null +++ b/src/files/domain/models/FileTypeToFriendlyTypeMap.ts @@ -0,0 +1,227 @@ +const MimeTypeDisplay: Record = { + // Documentation + 'application/pdf': 'Adobe PDF', + 'image/pdf': 'Adobe PDF', + 'text/pdf': 'Adobe PDF', + 'application/x-pdf': 'Adobe PDF', + 'application/cnt': 'Windows Help Contents File', + 'application/msword': 'MS Word', + 'application/vnd.ms-excel': 'MS Excel Spreadsheet', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'MS Excel Spreadsheet', + 'application/vnd.ms-powerpoint': 'MS Powerpoint', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'MS Powerpoint', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'MS Word', + 'application/vnd.oasis.opendocument.spreadsheet': 'OpenOffice Spreadsheet', + 'application/vnd.ms-excel.sheet.macroenabled.12': 'MS Excel Spreadsheet', + // Text + 'text/plain': 'Plain Text', + 'text/x-log': 'Application Log', + 'text/html': 'HTML', + 'application/x-tex': 'LaTeX', + 'text/x-tex': 'LaTeX', + 'text/markdown': 'Markdown Text', + 'text/x-markdown': 'Markdown Text', + 'text/x-r-markdown': 'R Markdown Text', + 'application/rtf': 'Rich Text Format', + 'text/x-rst': 'reStructuredText', + 'text/rtf': 'Rich Text Format', + 'text/richtext': 'Rich Text Format', + 'text/turtle': 'Turtle RDF', + 'application/xml': 'XML', + 'text/xml': 'XML', + // Code + 'text/x-c': 'C++ Source', + 'text/x-c++src': 'C++ Source', + 'text/css': 'Cascading Style Sheet', + 'text/x-fortran': 'Fortran Source Code', + 'application/java-vm': 'Java Class', + 'text/x-java-source': 'Java Source Code', + 'text/javascript': 'Javascript Code', + 'application/javascript': 'Javascript Code', + 'application/x-javascript': 'Javascript Code', + 'text/x-matlab': 'MATLAB Source Code', + 'text/x-mathematica': 'Mathematica Input', + 'text/x-objcsrc': 'Objective-C Source Code', + 'text/x-pascal': 'Pascal Source Code', + 'text/x-perl': 'Perl Script', + 'text/x-perl-script': 'Perl Script', + 'text/php': 'PHP Source Code', + 'application/postscript': 'Postscript', + 'text/x-python': 'Python Source Code', + 'text/x-python-script': 'Python Source Code', + 'text/x-r-source': 'R Source Code', + 'text/x-sh': 'Shell Script', + 'application/x-sh': 'Shell Script', + 'application/x-shellscript': 'Shell Script', + 'application/x-sql': 'SQL Code', + 'text/x-sql': 'SQL Code', + 'application/x-swc': 'Shockwave Flash Component', + 'application/x-msdownload': 'Windows Executable', + 'application/x-ipynb+json': 'Jupyter Notebook', + 'application/x-stata-ado': 'Stata Ado Script', + 'application/x-stata-do': 'Stata Do Script', + 'application/x-stata-dta': 'Stata Data Script', + 'application/x-stata-smcl': 'Stata Markup and Control Language', + 'text/x-stata-syntax': 'Stata Syntax', + 'application/x-stata-syntax': 'Stata Syntax', + 'text/x-spss-syntax': 'SPSS Syntax', + 'application/x-spss-syntax': 'SPSS Syntax', + 'application/x-spss-sps': 'SPSS Script Syntax', + 'text/x-sas-syntax': 'SAS Syntax', + 'application/x-sas-syntax': 'SAS Syntax', + 'type/x-r-syntax': 'R Syntax', + 'application/vnd.wolfram.mathematica.package': 'Wolfram Mathematica Code', + 'application/vnd.wolfram.mathematica': 'Wolfram Mathematica Code', + 'text/x-workflow-description-language': 'Workflow Description Language', + 'text/x-computational-workflow-language': 'Computational Workflow Language', + 'text/x-nextflow': 'Nextflow Script', + 'text/x-r-notebook': 'R Notebook', + 'text/x-ruby-script': 'Ruby Source Code', + 'text/x-dagman': 'DAGMan Workflow', + 'text/x-makefile': 'Makefile Script', + 'text/x-snakemake': 'Snakemake Workflow', + // Ingested Tabular Data + 'text/tab-separated-values': 'Tab-Delimited', + 'text/tsv': 'Tab-Separated Values', + 'text/comma-separated-values': 'Comma Separated Values', + 'text/x-comma-separated-values': 'Comma Separated Values', + 'text/csv': 'Comma Separated Values', + 'text/x-fixed-field': 'Fixed Field Text Data', + 'application/vnd.flographit': 'FloGraphIt Media', + 'application/x-r-data': 'R Data', + 'application/x-rlang-transport': 'R Data', + 'application/x-R-2': 'R Binary', + 'application/x-stata': 'Stata Binary', + 'application/x-stata-6': 'Stata Binary', + 'application/x-stata-13': 'Stata 13 Binary', + 'application/x-stata-14': 'Stata 14 Binary', + 'application/x-stata-15': 'Stata 15 Binary', + 'application/x-spss-por': 'SPSS Portable', + 'application/x-spss-portable': 'SPSS Portable', + 'application/x-spss-sav': 'SPSS Binary', + 'application/x-sas': 'SAS', + 'application/x-sas-transport': 'SAS Transport', + 'application/x-sas-system': 'SAS System', + 'application/x-sas-data': 'SAS Data', + 'application/x-sas-catalog': 'SAS Catalog', + 'application/x-sas-log': 'SAS Log', + 'application/x-sas-output': 'SAS Output', + 'application/softgrid-do': 'Softgrid DTA Script', + 'application/x-dvn-csvspss-zip': 'CSV (w/SPSS card)', + 'application/x-dvn-tabddi-zip': 'TAB (w/DDI)', + 'application/x-emf': 'Extended Metafile', + 'application/x-h5': 'Hierarchical Data Format', + 'application/x-hdf': 'Hierarchical Data Format', + 'application/x-hdf5': 'Hierarchical Data Format', + 'application/geo+json': 'GeoJSON', + 'application/json': 'JSON', + 'application/mathematica': 'Mathematica', + 'application/matlab-mat': 'MATLAB Data', + 'application/x-matlab-data': 'MATLAB Data', + 'application/x-matlab-figure': 'MATLAB Figure', + 'application/x-matlab-workspace': 'MATLAB Workspace', + 'text/x-vcard': 'Virtual Contact File', + 'application/x-xfig': 'MATLAB Figure', + 'application/x-msaccess': 'MS Access', + 'application/netcdf': 'Network Common Data Form', + 'application/x-netcdf': 'Network Common Data Form', + 'application/vnd.lotus-notes': 'Notes Storage Facility', + 'application/x-nsdstat': 'NSDstat', + 'application/vnd.realvnc.bed': 'PLINK Binary', + 'application/vnd.ms-pki.stl': 'STL Format', + 'application/vnd.isac.fcs': 'FCS Data', + 'application/java-serialized-object': 'Java Serialized Object', + 'chemical/x-xyz': 'Co-Ordinate Animation', + // FITS + 'image/fits': 'FITS', + 'application/fits': 'FITS', + // Shape + 'application/dbf': 'dBASE Table for ESRI Shapefile', + 'application/dbase': 'dBASE Table for ESRI Shapefile', + 'application/prj': 'ESRI Shapefile', + 'application/sbn': 'ESRI Spatial Index', + 'application/sbx': 'ESRI Spatial Index', + 'application/shp': 'Shape', + 'application/shx': 'Shape', + 'application/x-esri-shape': 'ESRI Shapefile', + 'application/vnd.google-earth.kml+xml': 'Keyhole Markup Language', + 'application/zipped-shapefile': 'Zipped Shapefiles', + // Archive + 'application/zip': 'ZIP Archive', + 'application/x-zip-compressed': 'ZIP Archive', + 'application/vnd.antix.game-component': 'ATX Archive', + 'application/x-bzip': 'Bzip Archive', + 'application/x-bzip2': 'Bzip Archive', + 'application/vnd.google-earth.kmz': 'Google Earth Archive', + 'application/gzip': 'Gzip Archive', + 'application/x-gzip': 'Gzip Archive', + 'application/x-gzip-compressed': 'Gzip Archive', + 'application/rar': 'RAR Archive', + 'application/x-rar': 'RAR Archive', + 'application/x-rar-compressed': 'RAR Archive', + 'application/tar': 'TAR Archive', + 'application/x-tar': 'TAR Archive', + 'application/x-compressed': 'Compressed Archive', + 'application/x-compressed-tar': 'TAR Archive', + 'application/x-7z-compressed': '7Z Archive', + 'application/x-xz': 'XZ Archive', + 'application/warc': 'Web Archive', + 'application/x-iso9660-image': 'Optical Disc Image', + 'application/vnd.eln+zip': 'ELN Archive', + // Image + 'image/gif': 'GIF Image', + 'image/jpeg': 'JPEG Image', + 'image/jp2': 'JPEG-2000 Image', + 'image/x-portable-bitmap': 'Bitmap Image', + 'image/x-portable-graymap': 'Graymap Image', + 'image/png': 'PNG Image', + 'image/x-portable-anymap': 'Anymap Image', + 'image/x-portable-pixmap': 'Pixmap Image', + 'application/x-msmetafile': 'Enhanced Metafile', + 'application/dicom': 'DICOM Image', + 'image/dicom-rle': 'DICOM Image', + 'image/nii': 'NIfTI Image', + 'image/cmu-raster': 'Raster Image', + 'image/x-rgb': 'RGB Image', + 'image/svg+xml': 'SVG Image', + 'image/tiff': 'TIFF Image', + 'image/bmp': 'Bitmap Image', + 'image/x-xbitmap': 'Bitmap Image', + 'image/RAW': 'Bitmap Image', + 'image/raw': 'Bitmap Image', + 'application/x-tgif': 'TGIF File', + 'image/x-xpixmap': 'Pixmap Image', + 'image/x-xwindowdump': 'X Windows Dump', + 'application/photoshop': 'Photoshop Image', + 'image/vnd.adobe.photoshop': 'Photoshop Image', + 'application/x-photoshop': 'Photoshop Image', + // Audio + 'audio/x-aiff': 'AIFF Audio', + 'audio/mp3': 'MP3 Audio', + 'audio/mpeg': 'MP3 Audio', + 'audio/mp4': 'MPEG-4 Audio', + 'audio/x-m4a': 'MPEG-4 Audio', + 'audio/ogg': 'OGG Audio', + 'audio/wav': 'Waveform Audio', + 'audio/x-wav': 'Waveform Audio', + 'audio/x-wave': 'Waveform Audio', + // Video + 'video/avi': 'AVI Video', + 'video/x-msvideo': 'AVI Video', + 'video/mpeg': 'MPEG Video', + 'video/mp4': 'MPEG-4 Video', + 'video/x-m4v': 'MPEG-4 Video', + 'video/ogg': 'OGG Video', + 'video/quicktime': 'Quicktime Video', + 'video/webm': 'WebM Video', + // Network Data + 'text/xml-graphml': 'GraphML Network Data', + // Other + 'application/octet-stream': 'Unknown', + 'application/x-docker-file': 'Docker Image File', + 'application/x-vagrant-file': 'Vagrant Image File', + // Dataverse-specific + 'application/vnd.dataverse.file-package': 'Dataverse Package' +} + +export default MimeTypeDisplay diff --git a/src/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByType.tsx b/src/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByType.tsx index 53f20b7f0..cf18e51da 100644 --- a/src/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByType.tsx +++ b/src/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByType.tsx @@ -23,13 +23,13 @@ export function FileCriteriaFilterByType({ }: FileCriteriaFilterByTypeProps) { const { t } = useTranslation('files') const [selectedType, setSelectedType] = useState( - criteria.filterByType ?? new FileType('all') + criteria.filterByType ?? new FileType('All') ) const handleTypeChange = (eventKey: string | null) => { if (selectedType.value !== eventKey) { setSelectedType(new FileType(eventKey as string)) onCriteriaChange( - criteria.withFilterByType(eventKey === 'all' ? undefined : (eventKey as string)) + criteria.withFilterByType(eventKey === 'All' ? undefined : (eventKey as string)) ) } } @@ -46,8 +46,8 @@ export function FileCriteriaFilterByType({ withSpacing variant="secondary"> + eventKey="All" + className={selectedType.value === 'All' ? styles['selected-option'] : ''}> All diff --git a/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx b/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx index f6ac50b7c..32cd4f0fe 100644 --- a/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx @@ -25,11 +25,11 @@ const testFilesCountInfo = FilesCountInfoMother.create({ total: 200, perFileType: [ { - type: new FileType('text'), + type: new FileType('text/plain'), count: 5 }, { - type: new FileType('image'), + type: new FileType('image/png'), count: 485 } ], @@ -188,7 +188,7 @@ describe('DatasetFiles', () => { cy.findByText('1 file is currently selected.').should('exist') cy.findByRole('button', { name: 'File Type: All' }).click() - cy.findByText('Image (485)').should('exist').click() + cy.findByText('PNG Image (485)').should('exist').click() cy.findByText('1 file is currently selected.').should('not.exist') }) @@ -357,13 +357,13 @@ describe('DatasetFiles', () => { ) cy.findByRole('button', { name: 'File Type: All' }).click() - cy.findByText('Image (485)').should('exist').click() + cy.findByText('PNG Image (485)').should('exist').click() cy.wrap(fileRepository.getAllByDatasetPersistentId).should( 'be.calledWith', datasetPersistentId, datasetVersion, filePaginationInfo, - new FileCriteria().withFilterByType('image') + new FileCriteria().withFilterByType('image/png') ) }) @@ -456,7 +456,7 @@ describe('DatasetFiles', () => { ) cy.findByRole('button', { name: 'File Type: All' }).click() - cy.findByText('Image (485)').should('exist').click() + cy.findByText('PNG Image (485)').should('exist').click() cy.get('table > thead > tr > th > input[type=checkbox]').click() cy.findByRole('button', { name: 'Select all 200 files in this dataset.' }).click() cy.findByText( @@ -467,7 +467,7 @@ describe('DatasetFiles', () => { 'be.calledWith', datasetPersistentId, datasetVersion, - new FileCriteria().withFilterByType('image') + new FileCriteria().withFilterByType('image/png') ) }) }) diff --git a/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByTag.spec.tsx b/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByTag.spec.tsx index d2acad803..66c28929f 100644 --- a/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByTag.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByTag.spec.tsx @@ -1,6 +1,5 @@ import { FileCriteria, FileTag } from '../../../../../../src/files/domain/models/FileCriteria' import { FilesCountInfoMother } from '../../../../files/domain/models/FilesCountInfoMother' -import { FileType } from '../../../../../../src/files/domain/models/File' import styles from '../../../../../../src/sections/dataset/dataset-files/file-criteria-form/FileCriteriaForm.module.scss' import { FileCriteriaFilterByTag } from '../../../../../../src/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByTag' @@ -12,7 +11,7 @@ const filesCountInfo = FilesCountInfoMother.create({ count: 5 }, { - tag: new FileType('data'), + tag: new FileTag('data'), count: 10 } ] diff --git a/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByType.spec.tsx b/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByType.spec.tsx index e6c72496f..20a5a4435 100644 --- a/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByType.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByType.spec.tsx @@ -8,11 +8,11 @@ const defaultCriteria = new FileCriteria() const filesCountInfo = FilesCountInfoMother.create({ perFileType: [ { - type: new FileType('image'), + type: new FileType('image/png'), count: 5 }, { - type: new FileType('text'), + type: new FileType('text/plain'), count: 10 } ] @@ -33,8 +33,8 @@ describe('FilesCriteriaFilterByType', () => { cy.findByRole('button', { name: 'File Type: All' }).click() cy.findByText('All').should('exist') - cy.findByText('Image (5)').should('exist') - cy.findByText('Text (10)').should('exist') + cy.findByText('PNG Image (5)').should('exist') + cy.findByText('Plain Text (10)').should('exist') }) it('calls onCriteriaChange with the selected filter by type value', () => { @@ -49,17 +49,20 @@ describe('FilesCriteriaFilterByType', () => { ) cy.findByRole('button', { name: 'File Type: All' }).click() - cy.findByText('Image (5)').click() - cy.wrap(onCriteriaChange).should('be.calledWith', defaultCriteria.withFilterByType('image')) - - cy.findByRole('button', { name: 'File Type: Image' }).click() - cy.findByText('Text (10)').click() - cy.wrap(onCriteriaChange).should('be.calledWith', defaultCriteria.withFilterByType('text')) + cy.findByText('PNG Image (5)').click() + cy.wrap(onCriteriaChange).should('be.calledWith', defaultCriteria.withFilterByType('image/png')) + + cy.findByRole('button', { name: 'File Type: PNG Image' }).click() + cy.findByText('Plain Text (10)').click() + cy.wrap(onCriteriaChange).should( + 'be.calledWith', + defaultCriteria.withFilterByType('text/plain') + ) }) it('shows the selected filter in the dropdown title', () => { const onCriteriaChange = cy.stub().as('onCriteriaChange') - const criteria = defaultCriteria.withFilterByType('image') + const criteria = defaultCriteria.withFilterByType('image/png') cy.customMount( { /> ) - cy.findByRole('button', { name: 'File Type: Image' }).click() + cy.findByRole('button', { name: 'File Type: PNG Image' }).click() cy.findByText('All').should('exist').click() cy.wrap(onCriteriaChange).should('be.calledWith', defaultCriteria.withFilterByType(undefined)) }) @@ -88,13 +91,13 @@ describe('FilesCriteriaFilterByType', () => { cy.findByRole('button', { name: 'File Type: All' }).click() cy.findByText('All').should('have.class', styles['selected-option']) - cy.findByRole('button', { name: 'Image (5)' }).click() - cy.findByRole('button', { name: 'File Type: Image' }).click() - cy.findByText('Image (5)').should('have.class', styles['selected-option']) + cy.findByRole('button', { name: 'PNG Image (5)' }).click() + cy.findByRole('button', { name: 'File Type: PNG Image' }).click() + cy.findByText('PNG Image (5)').should('have.class', styles['selected-option']) - cy.findByRole('button', { name: 'Text (10)' }).click() - cy.findByRole('button', { name: 'File Type: Text' }).click() - cy.findByText('Text (10)').should('have.class', styles['selected-option']) + cy.findByRole('button', { name: 'Plain Text (10)' }).click() + cy.findByRole('button', { name: 'File Type: Plain Text' }).click() + cy.findByText('Plain Text (10)').should('have.class', styles['selected-option']) }) it('does not render the filter by type dropdown if there are no filter options', () => { @@ -109,6 +112,6 @@ describe('FilesCriteriaFilterByType', () => { /> ) - cy.findByRole('button', { name: 'File Type: Image' }).should('not.exist') + cy.findByRole('button', { name: 'File Type: PNG Image' }).should('not.exist') }) }) diff --git a/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaForm.spec.tsx b/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaForm.spec.tsx index bd0fb0294..8322b7a71 100644 --- a/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaForm.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/file-criteria-form/FileCriteriaForm.spec.tsx @@ -18,11 +18,11 @@ let onCriteriaChange = () => {} const filesCountInfo = FilesCountInfoMother.create({ perFileType: [ { - type: new FileType('image'), + type: new FileType('image/png'), count: 5 }, { - type: new FileType('text'), + type: new FileType('text/plain'), count: 10 } ], @@ -42,7 +42,7 @@ const filesCountInfo = FilesCountInfoMother.create({ count: 5 }, { - tag: new FileType('data'), + tag: new FileTag('data'), count: 10 } ] @@ -112,7 +112,7 @@ describe('FileCriteriaForm', () => { const criteria = new FileCriteria() .withFilterByTag('document') .withFilterByAccess(FileAccessOption.PUBLIC) - .withFilterByType('image') + .withFilterByType('image/png') .withSearchText('search text') cy.customMount( @@ -128,7 +128,7 @@ describe('FileCriteriaForm', () => { cy.wrap(onCriteriaChange).should('be.calledWith', criteria.withSortBy(FileSortByOption.OLDEST)) - cy.findByRole('button', { name: 'File Type: Image' }).should('exist') + cy.findByRole('button', { name: 'File Type: PNG Image' }).should('exist') cy.findByRole('button', { name: 'Access: Public' }).should('exist') cy.findByRole('button', { name: 'File Tags: Document' }).should('exist') cy.findByLabelText('Search').should('have.value', 'search text') @@ -139,7 +139,7 @@ describe('FileCriteriaForm', () => { const criteria = new FileCriteria() .withFilterByTag('document') .withFilterByAccess(FileAccessOption.PUBLIC) - .withFilterByType('image') + .withFilterByType('image/png') .withSearchText('search text') cy.customMount( @@ -150,12 +150,12 @@ describe('FileCriteriaForm', () => { /> ) - cy.findByRole('button', { name: 'File Type: Image' }).click() - cy.findByText('Text (10)').click() + cy.findByRole('button', { name: 'File Type: PNG Image' }).click() + cy.findByText('Plain Text (10)').click() - cy.wrap(onCriteriaChange).should('be.calledWith', criteria.withFilterByType('text')) + cy.wrap(onCriteriaChange).should('be.calledWith', criteria.withFilterByType('text/plain')) - cy.findByRole('button', { name: 'File Type: Text' }).should('exist') + cy.findByRole('button', { name: 'File Type: Plain Text' }).should('exist') cy.findByRole('button', { name: 'Access: Public' }).should('exist') cy.findByRole('button', { name: 'File Tags: Document' }).should('exist') cy.findByLabelText('Search').should('have.value', 'search text') @@ -166,7 +166,7 @@ describe('FileCriteriaForm', () => { const criteria = new FileCriteria() .withFilterByTag('document') .withFilterByAccess(FileAccessOption.PUBLIC) - .withFilterByType('image') + .withFilterByType('image/png') .withSearchText('search text') cy.customMount( @@ -185,7 +185,7 @@ describe('FileCriteriaForm', () => { criteria.withFilterByAccess(FileAccessOption.RESTRICTED) ) - cy.findByRole('button', { name: 'File Type: Image' }).should('exist') + cy.findByRole('button', { name: 'File Type: PNG Image' }).should('exist') cy.findByRole('button', { name: 'Access: Restricted' }).should('exist') cy.findByRole('button', { name: 'File Tags: Document' }).should('exist') cy.findByLabelText('Search').should('have.value', 'search text') @@ -196,7 +196,7 @@ describe('FileCriteriaForm', () => { const criteria = new FileCriteria() .withFilterByTag('document') .withFilterByAccess(FileAccessOption.PUBLIC) - .withFilterByType('image') + .withFilterByType('image/png') .withSearchText('search text') cy.customMount( @@ -212,7 +212,7 @@ describe('FileCriteriaForm', () => { cy.wrap(onCriteriaChange).should('be.calledWith', criteria.withFilterByTag('data')) - cy.findByRole('button', { name: 'File Type: Image' }).should('exist') + cy.findByRole('button', { name: 'File Type: PNG Image' }).should('exist') cy.findByRole('button', { name: 'Access: Public' }).should('exist') cy.findByRole('button', { name: 'File Tags: Data' }).should('exist') cy.findByLabelText('Search').should('have.value', 'search text') @@ -223,7 +223,7 @@ describe('FileCriteriaForm', () => { const criteria = new FileCriteria() .withFilterByTag('document') .withFilterByAccess(FileAccessOption.PUBLIC) - .withFilterByType('image') + .withFilterByType('image/png') .withSearchText('search text') cy.customMount( @@ -238,7 +238,7 @@ describe('FileCriteriaForm', () => { cy.wrap(onCriteriaChange).should('be.calledWith', criteria.withSearchText('new search')) - cy.findByRole('button', { name: 'File Type: Image' }).should('exist') + cy.findByRole('button', { name: 'File Type: PNG Image' }).should('exist') cy.findByRole('button', { name: 'Access: Public' }).should('exist') cy.findByRole('button', { name: 'File Tags: Document' }).should('exist') cy.findByLabelText('Search').should('have.value', 'new search') @@ -272,7 +272,7 @@ describe('FileCriteriaForm', () => { const criteria = new FileCriteria() .withFilterByTag('document') .withFilterByAccess(FileAccessOption.PUBLIC) - .withFilterByType('image') + .withFilterByType('image/png') cy.customMount( { }) cy.customMount() - cy.findByText(`Text/plain - 123 B`).should('exist') + cy.findByText(`Plain Text - 123 B`).should('exist') }) it('renders the type and size correctly when there are decimals', () => { @@ -24,6 +24,6 @@ describe('FileType', () => { }) cy.customMount() - cy.findByText(`Text/plain - 123.9 MB`).should('exist') + cy.findByText(`Plain Text - 123.9 MB`).should('exist') }) }) diff --git a/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx b/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx index ef743d439..ab66e72d5 100644 --- a/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx +++ b/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx @@ -300,7 +300,7 @@ describe('Dataset', () => { }) }) - it('applies filters to the Files Table in the correct order', () => { + it.only('applies filters to the Files Table in the correct order', () => { const files = [ FileHelper.create('csv', { description: 'Some description', @@ -384,7 +384,7 @@ describe('Dataset', () => { cy.findByText('blob-5').should('exist') cy.findByRole('button', { name: 'File Type: All' }).click({ force: true }) - cy.findByText('Text/csv (2)').should('exist').click({ force: true }) + cy.findByText('Comma Separated Values (2)').should('exist').click({ force: true }) cy.findByText('1 to 2 of 2 Files').should('exist') cy.findByText('blob').should('not.exist') From 9371eaf6346f8573954a9d90a4a4a577b1043325 Mon Sep 17 00:00:00 2001 From: MellyGray Date: Mon, 13 Nov 2023 16:52:49 +0100 Subject: [PATCH 03/11] feat(FileDownloadOptions): add different options for tabular data --- src/files/domain/models/File.ts | 4 +- .../models/FileTypeToFriendlyTypeMap.ts | 3 +- .../access-file-menu/FileDownloadOptions.tsx | 12 +++++- .../files/domain/models/FileMother.ts | 19 ++++++--- .../FileDownloadOptions.spec.tsx | 42 ++++++++++++++++++- 5 files changed, 67 insertions(+), 13 deletions(-) diff --git a/src/files/domain/models/File.ts b/src/files/domain/models/File.ts index 0be9f000c..b6f685c75 100644 --- a/src/files/domain/models/File.ts +++ b/src/files/domain/models/File.ts @@ -121,10 +121,10 @@ export interface FileLabel { } export class FileType { - constructor(readonly value: string) {} + constructor(readonly value: string, readonly original?: string) {} toDisplayFormat(): string { - return FileTypeToFriendlyTypeMap[this.value] || this.value + return FileTypeToFriendlyTypeMap[this.value] || FileTypeToFriendlyTypeMap.unknown } } diff --git a/src/files/domain/models/FileTypeToFriendlyTypeMap.ts b/src/files/domain/models/FileTypeToFriendlyTypeMap.ts index ab4d9b30e..6f40517aa 100644 --- a/src/files/domain/models/FileTypeToFriendlyTypeMap.ts +++ b/src/files/domain/models/FileTypeToFriendlyTypeMap.ts @@ -221,7 +221,8 @@ const MimeTypeDisplay: Record = { 'application/x-docker-file': 'Docker Image File', 'application/x-vagrant-file': 'Vagrant Image File', // Dataverse-specific - 'application/vnd.dataverse.file-package': 'Dataverse Package' + 'application/vnd.dataverse.file-package': 'Dataverse Package', + unknown: 'Unknown' } export default MimeTypeDisplay diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx index 5e411ad05..1db94ac58 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx @@ -1,6 +1,7 @@ import { DropdownButtonItem, DropdownHeader } from '@iqss/dataverse-design-system' import { Download } from 'react-bootstrap-icons' import { File } from '../../../../../../../../files/domain/models/File' +import FileTypeToFriendlyTypeMap from '../../../../../../../../files/domain/models/FileTypeToFriendlyTypeMap' interface FileDownloadOptionsProps { file: File @@ -13,9 +14,16 @@ export function FileDownloadOptions({ file }: FileDownloadOptionsProps) { Download Options {file.tabularData ? ( - Tabular Data + file.type.original && + file.type.original !== 'Unknown' && ( + {`${file.type.original} (Original File Format)`} + ) ) : ( - Original File + + {file.type.toDisplayFormat() === FileTypeToFriendlyTypeMap.unknown + ? 'Original File Format' + : file.type.toDisplayFormat()} + )} ) diff --git a/tests/component/files/domain/models/FileMother.ts b/tests/component/files/domain/models/FileMother.ts index 7b238d98d..08d1899f4 100644 --- a/tests/component/files/domain/models/FileMother.ts +++ b/tests/component/files/domain/models/FileMother.ts @@ -72,7 +72,10 @@ export class FileChecksumMother { export class FileMother { static create(props?: Partial): File { const thumbnail = valueOrUndefined(faker.image.imageUrl()) - const fileType = faker.helpers.arrayElement(['tabular data', faker.system.fileType()]) + const fileType = faker.helpers.arrayElement([ + 'text/tab-separated-values', + faker.system.fileType() + ]) const checksum = valueOrUndefined(faker.datatype.uuid()) const fileMockedData = { id: faker.datatype.number(), @@ -87,7 +90,10 @@ export class FileMother { number: faker.datatype.number(), publishingStatus: faker.helpers.arrayElement(Object.values(FilePublishingStatus)) }, - type: new FileType(thumbnail ? 'image' : fileType), + type: + fileType === 'text/tab-separated-values' + ? new FileType('text/tab-separated-values', 'Comma Separated Values') + : new FileType(thumbnail ? 'image' : fileType), size: { value: faker.datatype.number({ max: 1024, precision: 2 }), unit: faker.helpers.arrayElement(Object.values(FileSizeUnit)) @@ -110,7 +116,7 @@ export class FileMother { directory: valueOrUndefined(faker.system.directoryPath()), embargo: valueOrUndefined(FileEmbargoMother.create()), tabularData: - fileType === 'tabular data' && !checksum + fileType === 'text/tab-separated-values' && !checksum ? { variablesCount: faker.datatype.number(100), observationsCount: faker.datatype.number(100), @@ -208,14 +214,15 @@ export class FileMother { }) } - static createWithTabularData(): File { + static createWithTabularData(props?: Partial): File { return this.createDefault({ - type: new FileType('tabular data'), + type: new FileType('text/tab-separated-values', 'Comma Separated Values'), tabularData: { variablesCount: faker.datatype.number(100), observationsCount: faker.datatype.number(100), unf: `UNF:${faker.datatype.uuid()}==` - } + }, + ...props }) } diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.spec.tsx index 08981927b..4bd94e287 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.spec.tsx @@ -1,11 +1,49 @@ import { FileDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions' import { FileMother } from '../../../../../../../../files/domain/models/FileMother' +import { FileType } from '../../../../../../../../../../src/files/domain/models/File' -const file = FileMother.create() +const fileNonTabular = FileMother.create({ + tabularData: undefined, + type: new FileType('text/plain') +}) +const fileNonTabularUnknown = FileMother.create({ + tabularData: undefined, + type: new FileType('unknown') +}) +const fileTabular = FileMother.createWithTabularData() +const fileTabularUnknown = FileMother.createWithTabularData({ + type: new FileType('text/tab-separated-values', 'Unknown') +}) describe('FileDownloadOptions', () => { it('renders the download options header', () => { - cy.customMount() + cy.customMount() cy.findByRole('heading', { name: 'Download Options' }).should('exist') }) + + it('renders the download options for a non-tabular file of unknown type', () => { + cy.customMount() + + cy.findByRole('button', { name: 'Original File Format' }).should('exist') + }) + + it('renders the download options for a non-tabular file', () => { + cy.customMount() + + cy.findByRole('button', { name: 'Plain Text' }).should('exist') + }) + + it('renders the download options for a tabular file', () => { + cy.customMount() + + cy.findByRole('button', { name: 'Comma Separated Values (Original File Format)' }).should( + 'exist' + ) + }) + + it('renders the download options for a tabular file of unknown original type', () => { + cy.customMount() + + cy.findByRole('button', { name: /(Original File Format)/ }).should('not.exist') + }) }) From 6b2fc6bf73373fd88bcb5d54952e16a86d05497d Mon Sep 17 00:00:00 2001 From: MellyGray Date: Tue, 14 Nov 2023 12:24:44 +0100 Subject: [PATCH 04/11] fix: tabular data display in the files table --- public/locales/en/files.json | 1 + src/files/infrastructure/mappers/JSFileMapper.ts | 6 +++--- .../file-info-cell/file-info-data/FileType.tsx | 7 ++++++- .../file-info-cell/file-info-data/FileType.spec.tsx | 10 ++++++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/public/locales/en/files.json b/public/locales/en/files.json index 3e8938625..7a1d37e24 100644 --- a/public/locales/en/files.json +++ b/public/locales/en/files.json @@ -12,6 +12,7 @@ "pageSize": "Files per page" }, "tabularData": { + "name": "Tabular Data", "variables": "Variables", "observations": "Observations" }, diff --git a/src/files/infrastructure/mappers/JSFileMapper.ts b/src/files/infrastructure/mappers/JSFileMapper.ts index 316c5019c..3956b6d25 100644 --- a/src/files/infrastructure/mappers/JSFileMapper.ts +++ b/src/files/infrastructure/mappers/JSFileMapper.ts @@ -41,7 +41,7 @@ export class JSFileMapper { this.toFileVersion(jsFile.version, datasetVersion, jsFile.publicationDate), this.toFileName(jsFile.name), this.toFileAccess(jsFile.restricted), - this.toFileType(jsFile.contentType), + this.toFileType(jsFile.contentType, jsFile.originalFormatLabel), this.toFileSize(jsFile.sizeBytes), this.toFileDate(jsFile.creationDate, jsFile.publicationDate, jsFile.embargo), this.toFileDownloads(), @@ -107,8 +107,8 @@ export class JSFileMapper { } } - static toFileType(jsFileContentType: string): FileType { - return new FileType(jsFileContentType) + static toFileType(jsFileContentType: string, jsOriginalFormatLabel?: string): FileType { + return new FileType(jsFileContentType, jsOriginalFormatLabel) } static toFileSize(jsFileSize: number): FileSize { diff --git a/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileType.tsx b/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileType.tsx index e34f0dbfb..e7fb9725b 100644 --- a/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileType.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileType.tsx @@ -1,4 +1,5 @@ import { FileSize, FileType as FileTypeModel } from '../../../../../../../files/domain/models/File' +import { useTranslation } from 'react-i18next' interface FileTypeProps { type: FileTypeModel @@ -6,10 +7,14 @@ interface FileTypeProps { } export function FileType({ type, size }: FileTypeProps) { + const { t } = useTranslation('files') return (
- {type.toDisplayFormat()} - {size.toString()} + {type.value === 'text/tab-separated-values' + ? t('table.tabularData.name') + : type.toDisplayFormat()}{' '} + - {size.toString()}
) diff --git a/tests/component/sections/dataset/dataset-files/files-table/files-info/file-info-cell/file-info-data/FileType.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/files-info/file-info-cell/file-info-data/FileType.spec.tsx index 15e8f8902..db176f339 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/files-info/file-info-cell/file-info-data/FileType.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/files-info/file-info-cell/file-info-data/FileType.spec.tsx @@ -26,4 +26,14 @@ describe('FileType', () => { cy.findByText(`Plain Text - 123.9 MB`).should('exist') }) + + it('renders the type correctly when is a tabular file', () => { + const file = FileMother.createWithTabularData({ + size: new FileSize(123.03932894722, FileSizeUnit.BYTES) + }) + + cy.customMount() + + cy.findByText(`Tabular Data - 123 B`).should('exist') + }) }) From f697146d0be166132a01b6b8d62f303c69f1c5dd Mon Sep 17 00:00:00 2001 From: MellyGray Date: Tue, 14 Nov 2023 12:48:05 +0100 Subject: [PATCH 05/11] feat(FileDownloadOptions): split into 2 components --- .../access-file-menu/FileDownloadOptions.tsx | 16 +++----- .../FileNonTabularDownloadOptions.tsx | 22 +++++++++++ .../FileTabularDownloadOptions.tsx | 22 +++++++++++ .../files/domain/models/FileMother.ts | 6 +-- .../FileDownloadOptions.spec.tsx | 19 ---------- .../FileNonTabularDownloadOptions.spec.tsx | 34 +++++++++++++++++ .../FileTabularDownloadOptions.spec.tsx | 37 +++++++++++++++++++ 7 files changed, 122 insertions(+), 34 deletions(-) create mode 100644 src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx create mode 100644 src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx create mode 100644 tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx create mode 100644 tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx index 1db94ac58..7919ba521 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx @@ -1,7 +1,8 @@ -import { DropdownButtonItem, DropdownHeader } from '@iqss/dataverse-design-system' +import { DropdownHeader } from '@iqss/dataverse-design-system' import { Download } from 'react-bootstrap-icons' import { File } from '../../../../../../../../files/domain/models/File' -import FileTypeToFriendlyTypeMap from '../../../../../../../../files/domain/models/FileTypeToFriendlyTypeMap' +import { FileTabularDownloadOptions } from './FileTabularDownloadOptions' +import { FileNonTabularDownloadOptions } from './FileNonTabularDownloadOptions' interface FileDownloadOptionsProps { file: File @@ -14,16 +15,9 @@ export function FileDownloadOptions({ file }: FileDownloadOptionsProps) { Download Options {file.tabularData ? ( - file.type.original && - file.type.original !== 'Unknown' && ( - {`${file.type.original} (Original File Format)`} - ) + ) : ( - - {file.type.toDisplayFormat() === FileTypeToFriendlyTypeMap.unknown - ? 'Original File Format' - : file.type.toDisplayFormat()} - + )} ) diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx new file mode 100644 index 000000000..6dc1621f1 --- /dev/null +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx @@ -0,0 +1,22 @@ +import { File } from '../../../../../../../../files/domain/models/File' +import FileTypeToFriendlyTypeMap from '../../../../../../../../files/domain/models/FileTypeToFriendlyTypeMap' +import { DropdownButtonItem } from '@iqss/dataverse-design-system' + +interface FileNonTabularDownloadOptionsProps { + file: File +} + +export function FileNonTabularDownloadOptions({ file }: FileNonTabularDownloadOptionsProps) { + const originalFileFormatIsKnown = + file.type.toDisplayFormat() !== FileTypeToFriendlyTypeMap.unknown + + if (file.tabularData) { + return <> + } + + return ( + + {originalFileFormatIsKnown ? file.type.toDisplayFormat() : 'Original File Format'} + + ) +} diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx new file mode 100644 index 000000000..1f816850b --- /dev/null +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx @@ -0,0 +1,22 @@ +import { File } from '../../../../../../../../files/domain/models/File' +import { DropdownButtonItem } from '@iqss/dataverse-design-system' + +interface FileTabularDownloadOptionsProps { + file: File +} + +export function FileTabularDownloadOptions({ file }: FileTabularDownloadOptionsProps) { + const originalFileFormatIsKnown = file.type.original && file.type.original !== 'Unknown' + if (!file.tabularData) { + return <> + } + + return ( + <> + {originalFileFormatIsKnown && ( + {`${file.type.original} (Original File Format)`} + )} + {file.type.toDisplayFormat()} + + ) +} diff --git a/tests/component/files/domain/models/FileMother.ts b/tests/component/files/domain/models/FileMother.ts index 08d1899f4..3846fa516 100644 --- a/tests/component/files/domain/models/FileMother.ts +++ b/tests/component/files/domain/models/FileMother.ts @@ -13,6 +13,7 @@ import { FileType, FileChecksum } from '../../../../../src/files/domain/models/File' +import FileTypeToFriendlyTypeMap from '../../../../../src/files/domain/models/FileTypeToFriendlyTypeMap' const valueOrUndefined: (value: T) => T | undefined = (value) => { const shouldShowValue = faker.datatype.boolean() @@ -72,10 +73,7 @@ export class FileChecksumMother { export class FileMother { static create(props?: Partial): File { const thumbnail = valueOrUndefined(faker.image.imageUrl()) - const fileType = faker.helpers.arrayElement([ - 'text/tab-separated-values', - faker.system.fileType() - ]) + const fileType = faker.helpers.arrayElement(Object.keys(FileTypeToFriendlyTypeMap)) const checksum = valueOrUndefined(faker.datatype.uuid()) const fileMockedData = { id: faker.datatype.number(), diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.spec.tsx index 4bd94e287..0c8d299cb 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.spec.tsx @@ -6,14 +6,7 @@ const fileNonTabular = FileMother.create({ tabularData: undefined, type: new FileType('text/plain') }) -const fileNonTabularUnknown = FileMother.create({ - tabularData: undefined, - type: new FileType('unknown') -}) const fileTabular = FileMother.createWithTabularData() -const fileTabularUnknown = FileMother.createWithTabularData({ - type: new FileType('text/tab-separated-values', 'Unknown') -}) describe('FileDownloadOptions', () => { it('renders the download options header', () => { cy.customMount() @@ -21,12 +14,6 @@ describe('FileDownloadOptions', () => { cy.findByRole('heading', { name: 'Download Options' }).should('exist') }) - it('renders the download options for a non-tabular file of unknown type', () => { - cy.customMount() - - cy.findByRole('button', { name: 'Original File Format' }).should('exist') - }) - it('renders the download options for a non-tabular file', () => { cy.customMount() @@ -40,10 +27,4 @@ describe('FileDownloadOptions', () => { 'exist' ) }) - - it('renders the download options for a tabular file of unknown original type', () => { - cy.customMount() - - cy.findByRole('button', { name: /(Original File Format)/ }).should('not.exist') - }) }) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx new file mode 100644 index 000000000..0de7adb88 --- /dev/null +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx @@ -0,0 +1,34 @@ +import { FileDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions' +import { FileMother } from '../../../../../../../../files/domain/models/FileMother' +import { FileType } from '../../../../../../../../../../src/files/domain/models/File' +import { FileNonTabularDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions' + +const fileNonTabular = FileMother.create({ + tabularData: undefined, + type: new FileType('text/plain') +}) +const fileNonTabularUnknown = FileMother.create({ + tabularData: undefined, + type: new FileType('unknown') +}) +const fileTabular = FileMother.createWithTabularData() +describe('FileNonTabularDownloadOptions', () => { + it('renders the download options for a non-tabular file of unknown type', () => { + cy.customMount() + + cy.findByRole('button', { name: 'Original File Format' }).should('exist') + }) + + it('renders the download options for a non-tabular file', () => { + cy.customMount() + + cy.findByRole('button', { name: 'Plain Text' }).should('exist') + }) + + it('does not render the download options for a tabular file', () => { + cy.customMount() + + cy.findByRole('button', { name: 'Original File Format' }).should('not.exist') + cy.findByRole('button', { name: 'Tab-Delimited' }).should('not.exist') + }) +}) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx new file mode 100644 index 000000000..716fd4cf9 --- /dev/null +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx @@ -0,0 +1,37 @@ +import { FileDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions' +import { FileMother } from '../../../../../../../../files/domain/models/FileMother' +import { FileType } from '../../../../../../../../../../src/files/domain/models/File' +import { FileTabularDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions' + +const fileNonTabular = FileMother.create({ + tabularData: undefined, + type: new FileType('text/plain') +}) +const fileTabular = FileMother.createWithTabularData() +const fileTabularUnknown = FileMother.createWithTabularData({ + type: new FileType('text/tab-separated-values', 'Unknown') +}) +describe('FileTabularDownloadOptions', () => { + it('renders the download options for a tabular file', () => { + cy.customMount() + + cy.findByRole('button', { name: 'Comma Separated Values (Original File Format)' }).should( + 'exist' + ) + cy.findByRole('button', { name: 'Tab-Delimited' }).should('exist') + }) + + it('renders the download options for a tabular file of unknown original type', () => { + cy.customMount() + + cy.findByRole('button', { name: /(Original File Format)/ }).should('not.exist') + cy.findByRole('button', { name: 'Tab-Delimited' }).should('exist') + }) + + it('does not render the download options for a non-tabular file', () => { + cy.customMount() + + cy.findByRole('button', { name: /(Original File Format)/ }).should('not.exist') + cy.findByRole('button', { name: 'Tab-Delimited' }).should('not.exist') + }) +}) From 82b5fd03256a40e9ad489941e86fcc0fc09ef559 Mon Sep 17 00:00:00 2001 From: MellyGray Date: Tue, 14 Nov 2023 12:55:00 +0100 Subject: [PATCH 06/11] feat(FileDownloadOptions): add some TODOs --- .../access-file-menu/FileDownloadOptions.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx index 7919ba521..e401c2573 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx @@ -22,3 +22,6 @@ export function FileDownloadOptions({ file }: FileDownloadOptionsProps) { ) } + +// TODO: Add guestbook support +// TODO: Add file package support From 68757f816802fe79a2388f1438386e2313df0f6d Mon Sep 17 00:00:00 2001 From: MellyGray Date: Tue, 14 Nov 2023 13:24:25 +0100 Subject: [PATCH 07/11] feat(FileDownloadOptions): add disabled conditions --- src/dataset/domain/models/Dataset.ts | 30 +++++++++ .../FileNonTabularDownloadOptions.tsx | 10 ++- .../FileTabularDownloadOptions.tsx | 19 +++++- .../dataset/domain/models/DatasetMother.ts | 4 ++ .../FileNonTabularDownloadOptions.spec.tsx | 62 ++++++++++++++++--- .../FileTabularDownloadOptions.spec.tsx | 59 +++++++++++++++++- 6 files changed, 167 insertions(+), 17 deletions(-) diff --git a/src/dataset/domain/models/Dataset.ts b/src/dataset/domain/models/Dataset.ts index 409a7ab5e..26fe11ed2 100644 --- a/src/dataset/domain/models/Dataset.ts +++ b/src/dataset/domain/models/Dataset.ts @@ -325,6 +325,36 @@ export class Dataset { return this.isLocked && !(lockedReasonIsInReview && this.permissions.canPublishDataset) } + public get isLockedFromFileDownload(): boolean { + if (!this.isLocked) { + return false + } + + if ( + this.locks.some((lock) => + [ + DatasetLockReason.FINALIZE_PUBLICATION, + DatasetLockReason.DCM_UPLOAD, + DatasetLockReason.INGEST + ].includes(lock.reason) + ) + ) { + return true + } + + if ( + this.locks.some((lock) => lock.reason === DatasetLockReason.IN_REVIEW) && + !this.permissions.canUpdateDataset + ) { + return true + } + + // If the lock reason is workflow and the workflow userId is different than the current user, then is locked + // TODO - Ask how we want to manage pending workflows + + return false + } + static Builder = class { public readonly labels: DatasetLabel[] = [] public readonly alerts: DatasetAlert[] = [] diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx index 6dc1621f1..12f651272 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx @@ -1,12 +1,14 @@ -import { File } from '../../../../../../../../files/domain/models/File' +import { File, FileIngestStatus } from '../../../../../../../../files/domain/models/File' import FileTypeToFriendlyTypeMap from '../../../../../../../../files/domain/models/FileTypeToFriendlyTypeMap' import { DropdownButtonItem } from '@iqss/dataverse-design-system' +import { useDataset } from '../../../../../../DatasetContext' interface FileNonTabularDownloadOptionsProps { file: File } export function FileNonTabularDownloadOptions({ file }: FileNonTabularDownloadOptionsProps) { + const { dataset } = useDataset() const originalFileFormatIsKnown = file.type.toDisplayFormat() !== FileTypeToFriendlyTypeMap.unknown @@ -15,7 +17,11 @@ export function FileNonTabularDownloadOptions({ file }: FileNonTabularDownloadOp } return ( - + {originalFileFormatIsKnown ? file.type.toDisplayFormat() : 'Original File Format'} ) diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx index 1f816850b..56e54dbaf 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx @@ -1,12 +1,15 @@ -import { File } from '../../../../../../../../files/domain/models/File' +import { File, FileIngestStatus } from '../../../../../../../../files/domain/models/File' import { DropdownButtonItem } from '@iqss/dataverse-design-system' +import { useDataset } from '../../../../../../DatasetContext' interface FileTabularDownloadOptionsProps { file: File } export function FileTabularDownloadOptions({ file }: FileTabularDownloadOptionsProps) { + const { dataset } = useDataset() const originalFileFormatIsKnown = file.type.original && file.type.original !== 'Unknown' + if (!file.tabularData) { return <> } @@ -14,9 +17,19 @@ export function FileTabularDownloadOptions({ file }: FileTabularDownloadOptionsP return ( <> {originalFileFormatIsKnown && ( - {`${file.type.original} (Original File Format)`} + {`${file.type.original} (Original File Format)`} )} - {file.type.toDisplayFormat()} + + {file.type.toDisplayFormat()} + ) } diff --git a/tests/component/dataset/domain/models/DatasetMother.ts b/tests/component/dataset/domain/models/DatasetMother.ts index bca8773c5..8a255d4be 100644 --- a/tests/component/dataset/domain/models/DatasetMother.ts +++ b/tests/component/dataset/domain/models/DatasetMother.ts @@ -183,6 +183,10 @@ export class DatasetLockMother { static createLockedInEditInProgress(): DatasetLock { return this.create({ reason: DatasetLockReason.EDIT_IN_PROGRESS }) } + + static createLockedFromFileDownload(): DatasetLock { + return this.create({ reason: DatasetLockReason.INGEST }) + } } export class DatasetMother { diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx index 0de7adb88..3ad279917 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx @@ -1,34 +1,78 @@ -import { FileDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions' import { FileMother } from '../../../../../../../../files/domain/models/FileMother' -import { FileType } from '../../../../../../../../../../src/files/domain/models/File' +import { + FileIngestStatus, + FileType +} from '../../../../../../../../../../src/files/domain/models/File' import { FileNonTabularDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions' +import { DatasetProvider } from '../../../../../../../../../../src/sections/dataset/DatasetProvider' +import { DatasetRepository } from '../../../../../../../../../../src/dataset/domain/repositories/DatasetRepository' +import { + DatasetLockMother, + DatasetMother, + DatasetPermissionsMother +} from '../../../../../../../../dataset/domain/models/DatasetMother' const fileNonTabular = FileMother.create({ tabularData: undefined, type: new FileType('text/plain') }) -const fileNonTabularUnknown = FileMother.create({ - tabularData: undefined, - type: new FileType('unknown') -}) -const fileTabular = FileMother.createWithTabularData() describe('FileNonTabularDownloadOptions', () => { it('renders the download options for a non-tabular file of unknown type', () => { + const fileNonTabularUnknown = FileMother.create({ + tabularData: undefined, + type: new FileType('unknown') + }) cy.customMount() - cy.findByRole('button', { name: 'Original File Format' }).should('exist') + cy.findByRole('button', { name: 'Original File Format' }) + .should('exist') + .should('not.have.class', 'disabled') }) it('renders the download options for a non-tabular file', () => { cy.customMount() - cy.findByRole('button', { name: 'Plain Text' }).should('exist') + cy.findByRole('button', { name: 'Plain Text' }) + .should('exist') + .should('not.have.class', 'disabled') }) it('does not render the download options for a tabular file', () => { + const fileTabular = FileMother.createWithTabularData() cy.customMount() cy.findByRole('button', { name: 'Original File Format' }).should('not.exist') cy.findByRole('button', { name: 'Tab-Delimited' }).should('not.exist') }) + + it('renders the options as disabled when the file ingest is in progress', () => { + const fileNonTabularInProgress = FileMother.create({ + tabularData: undefined, + type: new FileType('text/plain'), + ingest: { + status: FileIngestStatus.IN_PROGRESS + } + }) + cy.customMount() + + cy.findByRole('button', { name: 'Plain Text' }).should('have.class', 'disabled') + }) + + it('renders the options as disabled when the dataset is locked from file download', () => { + const datasetRepository: DatasetRepository = {} as DatasetRepository + const datasetLockedFromFileDownload = DatasetMother.create({ + locks: [DatasetLockMother.createLockedFromFileDownload()] + }) + datasetRepository.getByPersistentId = cy.stub().resolves(datasetLockedFromFileDownload) + + cy.customMount( + + + + ) + + cy.findByRole('button', { name: 'Plain Text' }).should('have.class', 'disabled') + }) }) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx index 716fd4cf9..2cb18b1e9 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx @@ -1,7 +1,17 @@ import { FileDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions' import { FileMother } from '../../../../../../../../files/domain/models/FileMother' -import { FileType } from '../../../../../../../../../../src/files/domain/models/File' +import { + FileIngestStatus, + FileType +} from '../../../../../../../../../../src/files/domain/models/File' import { FileTabularDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions' +import { FileNonTabularDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions' +import { DatasetRepository } from '../../../../../../../../../../src/dataset/domain/repositories/DatasetRepository' +import { + DatasetLockMother, + DatasetMother +} from '../../../../../../../../dataset/domain/models/DatasetMother' +import { DatasetProvider } from '../../../../../../../../../../src/sections/dataset/DatasetProvider' const fileNonTabular = FileMother.create({ tabularData: undefined, @@ -18,14 +28,18 @@ describe('FileTabularDownloadOptions', () => { cy.findByRole('button', { name: 'Comma Separated Values (Original File Format)' }).should( 'exist' ) - cy.findByRole('button', { name: 'Tab-Delimited' }).should('exist') + cy.findByRole('button', { name: 'Tab-Delimited' }) + .should('exist') + .should('not.have.class', 'disabled') }) it('renders the download options for a tabular file of unknown original type', () => { cy.customMount() cy.findByRole('button', { name: /(Original File Format)/ }).should('not.exist') - cy.findByRole('button', { name: 'Tab-Delimited' }).should('exist') + cy.findByRole('button', { name: 'Tab-Delimited' }) + .should('exist') + .should('not.have.class', 'disabled') }) it('does not render the download options for a non-tabular file', () => { @@ -34,4 +48,43 @@ describe('FileTabularDownloadOptions', () => { cy.findByRole('button', { name: /(Original File Format)/ }).should('not.exist') cy.findByRole('button', { name: 'Tab-Delimited' }).should('not.exist') }) + + it('renders the options as disabled when the file ingest is in progress', () => { + const fileTabularInProgress = FileMother.createWithTabularData({ + ingest: { + status: FileIngestStatus.IN_PROGRESS + } + }) + cy.customMount() + + cy.findByRole('button', { name: 'Comma Separated Values (Original File Format)' }) + .should('exist') + .should('have.class', 'disabled') + cy.findByRole('button', { name: 'Tab-Delimited' }) + .should('exist') + .should('have.class', 'disabled') + }) + + it('renders the options as disabled when the dataset is locked from file download', () => { + const datasetRepository: DatasetRepository = {} as DatasetRepository + const datasetLockedFromFileDownload = DatasetMother.create({ + locks: [DatasetLockMother.createLockedFromFileDownload()] + }) + datasetRepository.getByPersistentId = cy.stub().resolves(datasetLockedFromFileDownload) + + cy.customMount( + + + + ) + + cy.findByRole('button', { name: 'Comma Separated Values (Original File Format)' }) + .should('exist') + .should('have.class', 'disabled') + cy.findByRole('button', { name: 'Tab-Delimited' }) + .should('exist') + .should('have.class', 'disabled') + }) }) From a3e872ba87816f1b9a6a8f69a1394dc8feb723a2 Mon Sep 17 00:00:00 2001 From: MellyGray Date: Tue, 14 Nov 2023 15:07:53 +0100 Subject: [PATCH 08/11] feat(FileDownloadOptions): add RData option --- .../FileCriteriaFilterByType.tsx | 4 +++- .../FileTabularDownloadOptions.tsx | 9 ++++++++ .../domain/models/FilesCountInfoMother.tsx | 5 +++-- .../FileNonTabularDownloadOptions.spec.tsx | 3 +-- .../FileTabularDownloadOptions.spec.tsx | 21 ++++++++++++++++++- 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByType.tsx b/src/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByType.tsx index cf18e51da..ff1bd0d61 100644 --- a/src/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByType.tsx +++ b/src/sections/dataset/dataset-files/file-criteria-form/FileCriteriaFilterByType.tsx @@ -41,7 +41,9 @@ export function FileCriteriaFilterByType({ return ( diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx index 56e54dbaf..dad8cdae2 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx @@ -30,6 +30,15 @@ export function FileTabularDownloadOptions({ file }: FileTabularDownloadOptionsP }> {file.type.toDisplayFormat()} + {file.type.original !== 'R Data' && ( + + R Data + + )} ) } diff --git a/tests/component/files/domain/models/FilesCountInfoMother.tsx b/tests/component/files/domain/models/FilesCountInfoMother.tsx index 9be971256..43122d743 100644 --- a/tests/component/files/domain/models/FilesCountInfoMother.tsx +++ b/tests/component/files/domain/models/FilesCountInfoMother.tsx @@ -2,6 +2,7 @@ import { FileType } from '../../../../../src/files/domain/models/File' import { faker } from '@faker-js/faker' import { FilesCountInfo } from '../../../../../src/files/domain/models/FilesCountInfo' import { FileAccessOption, FileTag } from '../../../../../src/files/domain/models/FileCriteria' +import FileTypeToFriendlyTypeMap from '../../../../../src/files/domain/models/FileTypeToFriendlyTypeMap' export class FilesCountInfoMother { static create(props?: Partial): FilesCountInfo { @@ -10,11 +11,11 @@ export class FilesCountInfoMother { total: faker.datatype.number(), perFileType: [ { - type: new FileType(faker.system.fileType()), + type: new FileType(faker.helpers.arrayElement(Object.keys(FileTypeToFriendlyTypeMap))), count: faker.datatype.number({ max: total }) }, { - type: new FileType(faker.system.fileType()), + type: new FileType(faker.helpers.arrayElement(Object.keys(FileTypeToFriendlyTypeMap))), count: faker.datatype.number({ max: total }) } ], diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx index 3ad279917..da32a5321 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.spec.tsx @@ -8,8 +8,7 @@ import { DatasetProvider } from '../../../../../../../../../../src/sections/data import { DatasetRepository } from '../../../../../../../../../../src/dataset/domain/repositories/DatasetRepository' import { DatasetLockMother, - DatasetMother, - DatasetPermissionsMother + DatasetMother } from '../../../../../../../../dataset/domain/models/DatasetMother' const fileNonTabular = FileMother.create({ diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx index 2cb18b1e9..d54605ab2 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.spec.tsx @@ -5,7 +5,6 @@ import { FileType } from '../../../../../../../../../../src/files/domain/models/File' import { FileTabularDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions' -import { FileNonTabularDownloadOptions } from '../../../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions' import { DatasetRepository } from '../../../../../../../../../../src/dataset/domain/repositories/DatasetRepository' import { DatasetLockMother, @@ -31,6 +30,7 @@ describe('FileTabularDownloadOptions', () => { cy.findByRole('button', { name: 'Tab-Delimited' }) .should('exist') .should('not.have.class', 'disabled') + cy.findByRole('button', { name: 'R Data' }).should('exist').should('not.have.class', 'disabled') }) it('renders the download options for a tabular file of unknown original type', () => { @@ -40,6 +40,7 @@ describe('FileTabularDownloadOptions', () => { cy.findByRole('button', { name: 'Tab-Delimited' }) .should('exist') .should('not.have.class', 'disabled') + cy.findByRole('button', { name: 'R Data' }).should('exist').should('not.have.class', 'disabled') }) it('does not render the download options for a non-tabular file', () => { @@ -47,6 +48,7 @@ describe('FileTabularDownloadOptions', () => { cy.findByRole('button', { name: /(Original File Format)/ }).should('not.exist') cy.findByRole('button', { name: 'Tab-Delimited' }).should('not.exist') + cy.findByRole('button', { name: 'R Data' }).should('not.exist') }) it('renders the options as disabled when the file ingest is in progress', () => { @@ -63,6 +65,7 @@ describe('FileTabularDownloadOptions', () => { cy.findByRole('button', { name: 'Tab-Delimited' }) .should('exist') .should('have.class', 'disabled') + cy.findByRole('button', { name: 'R Data' }).should('exist').should('have.class', 'disabled') }) it('renders the options as disabled when the dataset is locked from file download', () => { @@ -86,5 +89,21 @@ describe('FileTabularDownloadOptions', () => { cy.findByRole('button', { name: 'Tab-Delimited' }) .should('exist') .should('have.class', 'disabled') + cy.findByRole('button', { name: 'R Data' }).should('exist').should('have.class', 'disabled') + }) + + it('does not render the RData option if the file type is already R Data', () => { + const fileTabularRData = FileMother.createWithTabularData({ + type: new FileType('text/tab-separated-values', 'R Data') + }) + cy.customMount() + + cy.findByRole('button', { name: 'R Data (Original File Format)' }) + .should('exist') + .should('not.have.class', 'disabled') + cy.findByRole('button', { name: 'Tab-Delimited' }) + .should('exist') + .should('not.have.class', 'disabled') + cy.findByRole('button', { name: 'R Data' }).should('not.exist') }) }) From de4919085782dbe4c70095b5fa1328c0deaa8be8 Mon Sep 17 00:00:00 2001 From: MellyGray Date: Tue, 14 Nov 2023 15:36:19 +0100 Subject: [PATCH 09/11] feat(FileDownloadOptions): add translations --- public/locales/en/files.json | 8 +++++ .../access-file-menu/FileDownloadOptions.tsx | 5 +++- .../FileNonTabularDownloadOptions.tsx | 6 +++- .../FileTabularDownloadOptions.tsx | 29 ++++++++----------- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/public/locales/en/files.json b/public/locales/en/files.json index 7a1d37e24..98fc2b0bb 100644 --- a/public/locales/en/files.json +++ b/public/locales/en/files.json @@ -113,6 +113,14 @@ "title": "Access File", "headers": { "fileAccess": "File Access" + }, + "downloadOptions": { + "title": "Download Options", + "options": { + "original": "Original File Format", + "RData": "R Data", + "tabular": "Tab-Delimited" + } } }, "editFilesMenu": { diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx index e401c2573..61f0e8400 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileDownloadOptions.tsx @@ -3,16 +3,19 @@ import { Download } from 'react-bootstrap-icons' import { File } from '../../../../../../../../files/domain/models/File' import { FileTabularDownloadOptions } from './FileTabularDownloadOptions' import { FileNonTabularDownloadOptions } from './FileNonTabularDownloadOptions' +import { useTranslation } from 'react-i18next' interface FileDownloadOptionsProps { file: File } export function FileDownloadOptions({ file }: FileDownloadOptionsProps) { + const { t } = useTranslation('files') return ( <> - Download Options + {t('actions.accessFileMenu.downloadOptions.title')} + {file.tabularData ? ( diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx index 12f651272..e7306abb0 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileNonTabularDownloadOptions.tsx @@ -2,12 +2,14 @@ import { File, FileIngestStatus } from '../../../../../../../../files/domain/mod import FileTypeToFriendlyTypeMap from '../../../../../../../../files/domain/models/FileTypeToFriendlyTypeMap' import { DropdownButtonItem } from '@iqss/dataverse-design-system' import { useDataset } from '../../../../../../DatasetContext' +import { useTranslation } from 'react-i18next' interface FileNonTabularDownloadOptionsProps { file: File } export function FileNonTabularDownloadOptions({ file }: FileNonTabularDownloadOptionsProps) { + const { t } = useTranslation('files') const { dataset } = useDataset() const originalFileFormatIsKnown = file.type.toDisplayFormat() !== FileTypeToFriendlyTypeMap.unknown @@ -22,7 +24,9 @@ export function FileNonTabularDownloadOptions({ file }: FileNonTabularDownloadOp file.ingest.status === FileIngestStatus.IN_PROGRESS || (dataset && dataset.isLockedFromFileDownload) }> - {originalFileFormatIsKnown ? file.type.toDisplayFormat() : 'Original File Format'} + {originalFileFormatIsKnown + ? file.type.toDisplayFormat() + : t('actions.accessFileMenu.downloadOptions.options.original')} ) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx index dad8cdae2..c4e76fbd1 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/access-file-menu/FileTabularDownloadOptions.tsx @@ -1,14 +1,19 @@ import { File, FileIngestStatus } from '../../../../../../../../files/domain/models/File' import { DropdownButtonItem } from '@iqss/dataverse-design-system' import { useDataset } from '../../../../../../DatasetContext' +import { useTranslation } from 'react-i18next' interface FileTabularDownloadOptionsProps { file: File } export function FileTabularDownloadOptions({ file }: FileTabularDownloadOptionsProps) { + const { t } = useTranslation('files') const { dataset } = useDataset() const originalFileFormatIsKnown = file.type.original && file.type.original !== 'Unknown' + const downloadDisabled = + file.ingest.status === FileIngestStatus.IN_PROGRESS || + (dataset && dataset.isLockedFromFileDownload) if (!file.tabularData) { return <> @@ -17,26 +22,16 @@ export function FileTabularDownloadOptions({ file }: FileTabularDownloadOptionsP return ( <> {originalFileFormatIsKnown && ( - {`${file.type.original} (Original File Format)`} + {`${file.type.original} (${t( + 'actions.accessFileMenu.downloadOptions.options.original' + )})`} )} - - {file.type.toDisplayFormat()} + + {t('actions.accessFileMenu.downloadOptions.options.tabular')} {file.type.original !== 'R Data' && ( - - R Data + + {t('actions.accessFileMenu.downloadOptions.options.RData')} )} From 3a31ac5b2f3c1c5238d247c7aea3c54b7e06ec9a Mon Sep 17 00:00:00 2001 From: MellyGray Date: Tue, 14 Nov 2023 15:52:31 +0100 Subject: [PATCH 10/11] feat(FileDownloadOptions): add stories --- .../access-file-menu/AccessFileMenu.stories.tsx | 8 ++++++++ tests/component/files/domain/models/FileMother.ts | 2 +- .../file-info-data/file-thumbnail/FileThumbnail.spec.tsx | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/stories/dataset/dataset-files/files-table/file-actions/file-action-buttons/access-file-menu/AccessFileMenu.stories.tsx b/src/stories/dataset/dataset-files/files-table/file-actions/file-action-buttons/access-file-menu/AccessFileMenu.stories.tsx index 030cb0609..a3a80a32d 100644 --- a/src/stories/dataset/dataset-files/files-table/file-actions/file-action-buttons/access-file-menu/AccessFileMenu.stories.tsx +++ b/src/stories/dataset/dataset-files/files-table/file-actions/file-action-buttons/access-file-menu/AccessFileMenu.stories.tsx @@ -20,6 +20,14 @@ export const Default: Story = { render: () => } +export const NonTabularFiles: Story = { + render: () => +} + +export const TabularFiles: Story = { + render: () => +} + export const Restricted: Story = { render: () => } diff --git a/tests/component/files/domain/models/FileMother.ts b/tests/component/files/domain/models/FileMother.ts index 3846fa516..99dc36332 100644 --- a/tests/component/files/domain/models/FileMother.ts +++ b/tests/component/files/domain/models/FileMother.ts @@ -154,7 +154,7 @@ export class FileMother { static createDefault(props?: Partial): File { const defaultFile = { - type: new FileType('file'), + type: new FileType('text/plain'), version: { number: 1, publishingStatus: FilePublishingStatus.RELEASED diff --git a/tests/component/sections/dataset/dataset-files/files-table/files-info/file-info-cell/file-info-data/file-thumbnail/FileThumbnail.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/files-info/file-info-cell/file-info-data/file-thumbnail/FileThumbnail.spec.tsx index 9689efb80..48eadf5d1 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/files-info/file-info-cell/file-info-data/file-thumbnail/FileThumbnail.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/files-info/file-info-cell/file-info-data/file-thumbnail/FileThumbnail.spec.tsx @@ -92,7 +92,7 @@ describe('FileThumbnail', () => { cy.customMount() - cy.findByText('icon-other').should('exist') + cy.findByText('icon-document').should('exist') cy.findByText('Restricted File Icon').should('exist') cy.findByText('Restricted File Icon').should('exist').parent().trigger('mouseover') @@ -115,7 +115,7 @@ describe('FileThumbnail', () => { ) - cy.findByText('icon-other').should('exist') + cy.findByText('icon-document').should('exist') cy.findByText('Restricted File Icon').should('not.exist') cy.findByText('Restricted with access Icon').should('exist').parent().trigger('mouseover') From 00e1f10e736638b7e15186667b5abb1012b94928 Mon Sep 17 00:00:00 2001 From: MellyGray Date: Mon, 20 Nov 2023 10:11:16 +0100 Subject: [PATCH 11/11] fix: remove .only from e2e test --- tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx b/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx index ab66e72d5..3bc6a7c50 100644 --- a/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx +++ b/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx @@ -300,7 +300,7 @@ describe('Dataset', () => { }) }) - it.only('applies filters to the Files Table in the correct order', () => { + it('applies filters to the Files Table in the correct order', () => { const files = [ FileHelper.create('csv', { description: 'Some description',