From 52e4f07da0307ba8576287e0ef6ab047fc75c560 Mon Sep 17 00:00:00 2001 From: MellyGray Date: Thu, 26 Oct 2023 10:25:05 +0200 Subject: [PATCH 1/6] feat(DownloadFiles): add button component --- src/files/domain/models/File.ts | 4 + .../download-files/DownloadFilesButton.tsx | 63 ++++++++++++ .../DownloadFilesButton.stories.tsx | 21 ++++ .../files/domain/models/FileMother.ts | 4 +- .../DownloadFilesButton.spec.tsx | 96 +++++++++++++++++++ 5 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx create mode 100644 src/stories/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.stories.tsx create mode 100644 tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx diff --git a/src/files/domain/models/File.ts b/src/files/domain/models/File.ts index 41810f079..624903b07 100644 --- a/src/files/domain/models/File.ts +++ b/src/files/domain/models/File.ts @@ -179,4 +179,8 @@ export class File { } return false } + + get isTabularData(): boolean { + return this.tabularData !== undefined + } } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx new file mode 100644 index 000000000..09374b79c --- /dev/null +++ b/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx @@ -0,0 +1,63 @@ +import { File } from '../../../../../../files/domain/models/File' +import { useDataset } from '../../../../DatasetContext' +import { Button, DropdownButton, DropdownButtonItem } from '@iqss/dataverse-design-system' + +interface DownloadFilesButtonProps { + files: File[] +} + +const MINIMUM_FILES_COUNT_TO_SHOW_DOWNLOAD_FILES_BUTTON = 1 +export function DownloadFilesButton({ files }: DownloadFilesButtonProps) { + const { dataset } = useDataset() + + if ( + files.length < MINIMUM_FILES_COUNT_TO_SHOW_DOWNLOAD_FILES_BUTTON || + !dataset?.permissions.canDownloadFiles + ) { + return <> + } + + if (files.some((file) => file.isTabularData)) { + return ( + + Original Format + Archival Format (.tab) + + ) + } + + return +} + +// Original: +// < div +// jsf:id = "downloadButtonBlockNormal" +// className = "btn-group" +// jsf:rendered = "#{(!(empty DatasetPage.workingVersion.fileMetadatas) +// and +// DatasetPage.workingVersion.fileMetadatas.size() > 1 +// ) +// and +// DatasetPage.downloadButtonAvailable +// and ! +// DatasetPage.isVersionHasTabular() +// } +// "> +// < p +// : +// commandLink +// styleClass = "btn btn-default btn-download" +// disabled = "#{false and DatasetPage.lockedFromDownload}" +// onclick = "if (!testFilesSelected()) return false;" +// action = "#{DatasetPage.startDownloadSelectedOriginal()}" +// update = "@form" +// oncomplete = "showPopup();" > +// < f +// : +// setPropertyActionListener +// target = "#{DatasetPage.fileMetadataForAction}" +// value = "#{null}" / > +// < span +// className = "glyphicon glyphicon-download-alt" / > #{bundle.download} +// < /p:commandLink> +// diff --git a/src/stories/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.stories.tsx b/src/stories/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.stories.tsx new file mode 100644 index 000000000..e7649f520 --- /dev/null +++ b/src/stories/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.stories.tsx @@ -0,0 +1,21 @@ +import { Meta, StoryObj } from '@storybook/react' +import { EditFilesMenu } from '../../../../../../sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu' +import { WithI18next } from '../../../../../WithI18next' +import { WithSettings } from '../../../../../WithSettings' +import { WithLoggedInUser } from '../../../../../WithLoggedInUser' +import { WithDatasetAllPermissionsGranted } from '../../../../WithDatasetAllPermissionsGranted' +import { FileMother } from '../../../../../../../tests/component/files/domain/models/FileMother' +import { DownloadFilesButton } from '../../../../../../sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton' + +const meta: Meta = { + title: 'Sections/Dataset Page/DatasetFiles/FilesTable/DownloadFilesButton', + component: EditFilesMenu, + decorators: [WithI18next, WithSettings, WithLoggedInUser, WithDatasetAllPermissionsGranted] +} + +export default meta +type Story = StoryObj + +export const NonTabularFiles: Story = { + render: () => +} diff --git a/tests/component/files/domain/models/FileMother.ts b/tests/component/files/domain/models/FileMother.ts index 8952971f6..7b238d98d 100644 --- a/tests/component/files/domain/models/FileMother.ts +++ b/tests/component/files/domain/models/FileMother.ts @@ -144,8 +144,8 @@ export class FileMother { ) } - static createMany(quantity: number): File[] { - return Array.from({ length: quantity }).map(() => this.create()) + static createMany(quantity: number, props?: Partial): File[] { + return Array.from({ length: quantity }).map(() => this.create(props)) } static createDefault(props?: Partial): File { diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx new file mode 100644 index 000000000..c3d8ab9fe --- /dev/null +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx @@ -0,0 +1,96 @@ +import { ReactNode } from 'react' +import { Dataset as DatasetModel } from '../../../../../../../../src/dataset/domain/models/Dataset' +import { DatasetProvider } from '../../../../../../../../src/sections/dataset/DatasetProvider' +import { DatasetRepository } from '../../../../../../../../src/dataset/domain/repositories/DatasetRepository' +import { + DatasetMother, + DatasetPermissionsMother +} from '../../../../../../dataset/domain/models/DatasetMother' +import { DownloadFilesButton } from '../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton' +import { FileMother } from '../../../../../../files/domain/models/FileMother' + +const datasetRepository: DatasetRepository = {} as DatasetRepository +describe('DownloadFilesButton', () => { + const withDataset = (component: ReactNode, dataset: DatasetModel | undefined) => { + datasetRepository.getByPersistentId = cy.stub().resolves(dataset) + datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) + + return ( + + {component} + + ) + } + + it('renders the Download Files button if there is more than 1 file in the dataset and the user has download files permission', () => { + const datasetWithDownloadFilesPermission = DatasetMother.create({ + permissions: DatasetPermissionsMother.createWithFilesDownloadAllowed() + }) + const files = FileMother.createMany(2) + cy.mountAuthenticated( + withDataset(, datasetWithDownloadFilesPermission) + ) + + cy.findByRole('button', { name: 'Download' }).should('exist') + }) + + it('does not render the Download Files button if there is only 1 file in the dataset', () => { + const datasetWithDownloadFilesPermission = DatasetMother.create({ + permissions: DatasetPermissionsMother.createWithFilesDownloadAllowed() + }) + const files = FileMother.createMany(1) + cy.mountAuthenticated( + withDataset(, datasetWithDownloadFilesPermission) + ) + + cy.findByRole('button', { name: 'Download' }).should('not.exist') + }) + + it('does not render the Download Files button if the user does not have download files permission', () => { + const datasetWithoutDownloadFilesPermission = DatasetMother.create({ + permissions: DatasetPermissionsMother.createWithFilesDownloadNotAllowed() + }) + const files = FileMother.createMany(2) + cy.mountAuthenticated( + withDataset(, datasetWithoutDownloadFilesPermission) + ) + + cy.findByRole('button', { name: 'Download' }).should('not.exist') + }) + + it('renders the Download Files button as a dropdown if there are tabular files in the dataset', () => { + const datasetWithDownloadFilesPermission = DatasetMother.create({ + permissions: DatasetPermissionsMother.createWithFilesDownloadAllowed() + }) + const files = FileMother.createMany(2, { + tabularData: { + variablesCount: 2, + observationsCount: 3, + unf: 'some-unf' + } + }) + cy.mountAuthenticated( + withDataset(, datasetWithDownloadFilesPermission) + ) + + cy.findByRole('button', { name: 'Download' }).click() + cy.findByRole('button', { name: 'Original Format' }).should('exist') + cy.findByRole('button', { name: 'Archival Format (.tab)' }).should('exist') + }) + + it('does not render the Download Files button as a dropdown if there are no tabular files in the dataset', () => { + const datasetWithDownloadFilesPermission = DatasetMother.create({ + permissions: DatasetPermissionsMother.createWithFilesDownloadAllowed() + }) + const files = FileMother.createMany(2, { tabularData: undefined }) + cy.mountAuthenticated( + withDataset(, datasetWithDownloadFilesPermission) + ) + + cy.findByRole('button', { name: 'Download' }).click() + cy.findByRole('button', { name: 'Original Format' }).should('not.exist') + cy.findByRole('button', { name: 'Archival Format (.tab)' }).should('not.exist') + }) +}) From dc952a6c4234c3144268c0036b317a8f90751f40 Mon Sep 17 00:00:00 2001 From: MellyGray Date: Thu, 26 Oct 2023 10:33:24 +0200 Subject: [PATCH 2/6] feat(DownloadFiles): add icon to the button --- .../DownloadFilesButton.module.scss | 4 ++ .../download-files/DownloadFilesButton.tsx | 47 +++++-------------- .../DownloadFilesButton.stories.tsx | 16 ++++++- 3 files changed, 31 insertions(+), 36 deletions(-) create mode 100644 src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.module.scss diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.module.scss b/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.module.scss new file mode 100644 index 000000000..0c9c49a03 --- /dev/null +++ b/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.module.scss @@ -0,0 +1,4 @@ +.icon { + margin-right: 0.3rem; + margin-bottom: 0.2rem; +} \ No newline at end of file diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx index 09374b79c..2f743dd4c 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx @@ -1,6 +1,8 @@ import { File } from '../../../../../../files/domain/models/File' import { useDataset } from '../../../../DatasetContext' import { Button, DropdownButton, DropdownButtonItem } from '@iqss/dataverse-design-system' +import { Download } from 'react-bootstrap-icons' +import styles from './DownloadFilesButton.module.scss' interface DownloadFilesButtonProps { files: File[] @@ -19,45 +21,20 @@ export function DownloadFilesButton({ files }: DownloadFilesButtonProps) { if (files.some((file) => file.isTabularData)) { return ( - + } + title="Download" + variant="secondary"> Original Format Archival Format (.tab) ) } - return + return ( + + ) } - -// Original: -// < div -// jsf:id = "downloadButtonBlockNormal" -// className = "btn-group" -// jsf:rendered = "#{(!(empty DatasetPage.workingVersion.fileMetadatas) -// and -// DatasetPage.workingVersion.fileMetadatas.size() > 1 -// ) -// and -// DatasetPage.downloadButtonAvailable -// and ! -// DatasetPage.isVersionHasTabular() -// } -// "> -// < p -// : -// commandLink -// styleClass = "btn btn-default btn-download" -// disabled = "#{false and DatasetPage.lockedFromDownload}" -// onclick = "if (!testFilesSelected()) return false;" -// action = "#{DatasetPage.startDownloadSelectedOriginal()}" -// update = "@form" -// oncomplete = "showPopup();" > -// < f -// : -// setPropertyActionListener -// target = "#{DatasetPage.fileMetadataForAction}" -// value = "#{null}" / > -// < span -// className = "glyphicon glyphicon-download-alt" / > #{bundle.download} -// < /p:commandLink> -// diff --git a/src/stories/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.stories.tsx b/src/stories/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.stories.tsx index e7649f520..61e22d129 100644 --- a/src/stories/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.stories.tsx +++ b/src/stories/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.stories.tsx @@ -17,5 +17,19 @@ export default meta type Story = StoryObj export const NonTabularFiles: Story = { - render: () => + render: () => +} + +export const TabularFiles: Story = { + render: () => ( + + ) } From a5b6b1ff1f68f663255bc61af786032fb8210154 Mon Sep 17 00:00:00 2001 From: MellyGray Date: Thu, 26 Oct 2023 10:42:27 +0200 Subject: [PATCH 3/6] feat(DownloadFiles): add to the FileActionsHeader --- .../files-table/file-actions/FileActionsHeader.module.scss | 2 ++ .../files-table/file-actions/FileActionsHeader.tsx | 2 ++ .../file-actions/download-files/DownloadFilesButton.tsx | 5 +++-- .../files-table/file-actions/FileActionsHeader.spec.tsx | 3 ++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.module.scss b/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.module.scss index 743868960..3b4481b8a 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.module.scss +++ b/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.module.scss @@ -1,3 +1,5 @@ .container { text-align: right; + display: flex; + justify-content: end; } \ No newline at end of file diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx index c71e6a119..c762d8196 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx @@ -2,6 +2,7 @@ import { EditFilesMenu } from './edit-files-menu/EditFilesMenu' import { File } from '../../../../../files/domain/models/File' import styles from './FileActionsHeader.module.scss' import { useTranslation } from 'react-i18next' +import { DownloadFilesButton } from './download-files/DownloadFilesButton' interface FileActionsHeaderProps { files: File[] } @@ -10,6 +11,7 @@ export function FileActionsHeader({ files }: FileActionsHeaderProps) { return (
+
) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx index 2f743dd4c..68ae0dccd 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx @@ -25,7 +25,8 @@ export function DownloadFilesButton({ files }: DownloadFilesButtonProps) { id="download-files" icon={} title="Download" - variant="secondary"> + variant="secondary" + withSpacing> Original Format Archival Format (.tab)
@@ -33,7 +34,7 @@ export function DownloadFilesButton({ files }: DownloadFilesButtonProps) { } return ( - ) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx index acb8f6f79..3b8cbc009 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx @@ -11,7 +11,7 @@ describe('FileActionsHeader', () => { it('renders the file actions header', () => { const datasetRepository: DatasetRepository = {} as DatasetRepository const datasetWithUpdatePermissions = DatasetMother.create({ - permissions: DatasetPermissionsMother.createWithUpdateDatasetAllowed(), + permissions: DatasetPermissionsMother.createWithAllAllowed(), hasValidTermsOfAccess: true }) datasetRepository.getByPersistentId = cy.stub().resolves(datasetWithUpdatePermissions) @@ -25,5 +25,6 @@ describe('FileActionsHeader', () => { ) cy.findByRole('button', { name: 'Edit Files' }).should('exist') + cy.findByRole('button', { name: 'Download' }).should('exist') }) }) From ecae5f76f7f7fc895eff28f1363695858c6ff873 Mon Sep 17 00:00:00 2001 From: MellyGray Date: Thu, 26 Oct 2023 12:18:26 +0200 Subject: [PATCH 4/6] feat(DownloadFiles): add translations --- public/locales/en/files.json | 7 +++++++ .../download-files/DownloadFilesButton.tsx | 10 ++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/public/locales/en/files.json b/public/locales/en/files.json index 6c7e8dfa1..662cbbfb6 100644 --- a/public/locales/en/files.json +++ b/public/locales/en/files.json @@ -120,6 +120,13 @@ "provenance": "Provenance", "delete": "Delete" } + }, + "downloadFiles": { + "title": "Download", + "options": { + "original": "Original Format", + "archival": "Archival Format (.tab)" + } } }, "requestAccess": { diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx index 68ae0dccd..e6595a578 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx @@ -3,6 +3,7 @@ import { useDataset } from '../../../../DatasetContext' import { Button, DropdownButton, DropdownButtonItem } from '@iqss/dataverse-design-system' import { Download } from 'react-bootstrap-icons' import styles from './DownloadFilesButton.module.scss' +import { useTranslation } from 'react-i18next' interface DownloadFilesButtonProps { files: File[] @@ -10,6 +11,7 @@ interface DownloadFilesButtonProps { const MINIMUM_FILES_COUNT_TO_SHOW_DOWNLOAD_FILES_BUTTON = 1 export function DownloadFilesButton({ files }: DownloadFilesButtonProps) { + const { t } = useTranslation('files') const { dataset } = useDataset() if ( @@ -24,18 +26,18 @@ export function DownloadFilesButton({ files }: DownloadFilesButtonProps) { } - title="Download" + title={t('actions.downloadFiles.title')} variant="secondary" withSpacing> - Original Format - Archival Format (.tab) + {t('actions.downloadFiles.options.original')} + {t('actions.downloadFiles.options.archival')} ) } return ( ) } From 2fe5ea9901bc7e0fa270e9f80eaa84f6fbc28784 Mon Sep 17 00:00:00 2001 From: MellyGray Date: Thu, 26 Oct 2023 16:38:50 +0200 Subject: [PATCH 5/6] feat(DownloadFiles): add the No Selected Files modal --- public/locales/en/files.json | 15 +++-- .../FilesTableColumnsDefinition.tsx | 11 +++- .../FileActionsHeader.module.scss | 2 +- .../file-actions/FileActionsHeader.tsx | 8 ++- .../download-files/DownloadFilesButton.tsx | 58 ++++++++++++++---- .../edit-files-menu/EditFilesMenu.tsx | 6 +- .../edit-files-menu/EditFilesOptions.tsx | 46 +++++++++++--- .../NoSelectedFilesModal.tsx | 30 ++++++++++ .../files-table/useFilesTable.tsx | 2 +- .../DownloadFilesButton.stories.tsx | 8 ++- .../edit-files-menu/EditFilesMenu.stories.tsx | 2 +- .../file-actions/FileActionsHeader.spec.tsx | 2 +- .../DownloadFilesButton.spec.tsx | 60 +++++++++++++++++-- .../edit-files-menu/EditFilesMenu.spec.tsx | 21 ++++--- .../edit-files-menu/EditFilesOptions.spec.tsx | 57 +++++++++++++++--- 15 files changed, 269 insertions(+), 59 deletions(-) create mode 100644 src/sections/dataset/dataset-files/files-table/file-actions/no-selected-files-modal/NoSelectedFilesModal.tsx diff --git a/public/locales/en/files.json b/public/locales/en/files.json index 662cbbfb6..3e8938625 100644 --- a/public/locales/en/files.json +++ b/public/locales/en/files.json @@ -103,6 +103,11 @@ "message": "This file has already been deleted (or replaced) in the current version. It may not be edited.", "close": "Close" }, + "noSelectedFilesAlert": { + "title": "Select File(s)", + "message": "Please select one or more files.", + "close": "Close" + }, "accessFileMenu": { "title": "Access File", "headers": { @@ -122,11 +127,11 @@ } }, "downloadFiles": { - "title": "Download", - "options": { - "original": "Original Format", - "archival": "Archival Format (.tab)" - } + "title": "Download", + "options": { + "original": "Original Format", + "archival": "Archival Format (.tab)" + } } }, "requestAccess": { diff --git a/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx b/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx index 53f985f51..bdf534f62 100644 --- a/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx +++ b/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx @@ -6,8 +6,12 @@ import { FileInfoHeader } from './file-info/FileInfoHeader' import { FileActionsHeader } from './file-actions/FileActionsHeader' import { FileActionsCell } from './file-actions/file-actions-cell/FileActionsCell' import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' +import { FileSelection } from './row-selection/useFileSelection' -export const createColumnsDefinition = (paginationInfo: FilePaginationInfo): ColumnDef[] => [ +export const createColumnsDefinition = ( + paginationInfo: FilePaginationInfo, + fileSelection: FileSelection +): ColumnDef[] => [ { id: 'select', header: ({ table }) => ( @@ -38,7 +42,10 @@ export const createColumnsDefinition = (paginationInfo: FilePaginationInfo): Col }, { header: ({ table }) => ( - row.original)} /> + row.original)} + fileSelection={fileSelection} + /> ), accessorKey: 'status', cell: (props) => diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.module.scss b/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.module.scss index 3b4481b8a..7154c087e 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.module.scss +++ b/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.module.scss @@ -1,5 +1,5 @@ .container { - text-align: right; display: flex; justify-content: end; + text-align: right; } \ No newline at end of file diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx index c762d8196..204ad474e 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx @@ -3,15 +3,17 @@ import { File } from '../../../../../files/domain/models/File' import styles from './FileActionsHeader.module.scss' import { useTranslation } from 'react-i18next' import { DownloadFilesButton } from './download-files/DownloadFilesButton' +import { FileSelection } from '../row-selection/useFileSelection' interface FileActionsHeaderProps { files: File[] + fileSelection: FileSelection } -export function FileActionsHeader({ files }: FileActionsHeaderProps) { +export function FileActionsHeader({ files, fileSelection }: FileActionsHeaderProps) { const { t } = useTranslation('files') return (
- - + +
) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx index e6595a578..4bf4bbbed 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx @@ -4,15 +4,21 @@ import { Button, DropdownButton, DropdownButtonItem } from '@iqss/dataverse-desi import { Download } from 'react-bootstrap-icons' import styles from './DownloadFilesButton.module.scss' import { useTranslation } from 'react-i18next' +import { FileSelection } from '../../row-selection/useFileSelection' +import { NoSelectedFilesModal } from '../no-selected-files-modal/NoSelectedFilesModal' +import { useState } from 'react' interface DownloadFilesButtonProps { files: File[] + fileSelection: FileSelection } const MINIMUM_FILES_COUNT_TO_SHOW_DOWNLOAD_FILES_BUTTON = 1 -export function DownloadFilesButton({ files }: DownloadFilesButtonProps) { +const SELECTED_FILES_EMPTY = 0 +export function DownloadFilesButton({ files, fileSelection }: DownloadFilesButtonProps) { const { t } = useTranslation('files') const { dataset } = useDataset() + const [showNoFilesSelectedModal, setShowNoFilesSelectedModal] = useState(false) if ( files.length < MINIMUM_FILES_COUNT_TO_SHOW_DOWNLOAD_FILES_BUTTON || @@ -21,23 +27,49 @@ export function DownloadFilesButton({ files }: DownloadFilesButtonProps) { return <> } + const onClick = () => { + if (Object.keys(fileSelection).length === SELECTED_FILES_EMPTY) { + setShowNoFilesSelectedModal(true) + } + } + if (files.some((file) => file.isTabularData)) { return ( - } - title={t('actions.downloadFiles.title')} - variant="secondary" - withSpacing> - {t('actions.downloadFiles.options.original')} - {t('actions.downloadFiles.options.archival')} - + <> + } + title={t('actions.downloadFiles.title')} + variant="secondary" + withSpacing> + + {t('actions.downloadFiles.options.original')} + + + {t('actions.downloadFiles.options.archival')} + + + setShowNoFilesSelectedModal(false)} + /> + ) } return ( - + <> + + setShowNoFilesSelectedModal(false)} + /> + ) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.tsx index c255662df..052d5957e 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.tsx @@ -6,12 +6,14 @@ import { EditFilesOptions } from './EditFilesOptions' import { File } from '../../../../../../files/domain/models/File' import { useTranslation } from 'react-i18next' import { useDataset } from '../../../../DatasetContext' +import { FileSelection } from '../../row-selection/useFileSelection' interface EditFilesMenuProps { files: File[] + fileSelection: FileSelection } const MINIMUM_FILES_COUNT_TO_SHOW_EDIT_FILES_BUTTON = 1 -export function EditFilesMenu({ files }: EditFilesMenuProps) { +export function EditFilesMenu({ files, fileSelection }: EditFilesMenuProps) { const { t } = useTranslation('files') const { user } = useSession() const { dataset } = useDataset() @@ -30,7 +32,7 @@ export function EditFilesMenu({ files }: EditFilesMenuProps) { title={t('actions.editFilesMenu.title')} disabled={dataset.isLockedFromEdits || !dataset.hasValidTermsOfAccess} icon={}> - + ) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.tsx index 8bab46607..1377303f5 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.tsx @@ -1,32 +1,62 @@ import { DropdownButtonItem } from '@iqss/dataverse-design-system' import { File } from '../../../../../../files/domain/models/File' import { useTranslation } from 'react-i18next' +import { useState } from 'react' +import { FileSelection } from '../../row-selection/useFileSelection' +import { NoSelectedFilesModal } from '../no-selected-files-modal/NoSelectedFilesModal' interface EditFileOptionsProps { files: File[] + fileSelection: FileSelection } -export function EditFilesOptions({ files }: EditFileOptionsProps) { +const SELECTED_FILES_EMPTY = 0 +export function EditFilesOptions({ files, fileSelection }: EditFileOptionsProps) { const { t } = useTranslation('files') + const [showNoFilesSelectedModal, setShowNoFilesSelectedModal] = useState(false) const settingsEmbargoAllowed = false // TODO - Ask Guillermo if this is included in the settings endpoint const provenanceEnabledByConfig = false // TODO - Ask Guillermo if this is included in the MVP and from which endpoint is coming from + const onClick = () => { + if (Object.keys(fileSelection).length === SELECTED_FILES_EMPTY) { + setShowNoFilesSelectedModal(true) + } + } + return ( <> - {t('actions.editFilesMenu.options.metadata')} + + {t('actions.editFilesMenu.options.metadata')} + {files.some((file) => file.access.restricted) && ( - {t('actions.editFilesMenu.options.unrestrict')} + + {t('actions.editFilesMenu.options.unrestrict')} + )} {files.some((file) => !file.access.restricted) && ( - {t('actions.editFilesMenu.options.restrict')} + + {t('actions.editFilesMenu.options.restrict')} + )} - {t('actions.editFilesMenu.options.replace')} + + {t('actions.editFilesMenu.options.replace')} + {settingsEmbargoAllowed && ( - {t('actions.editFilesMenu.options.embargo')} + + {t('actions.editFilesMenu.options.embargo')} + )} {provenanceEnabledByConfig && ( - {t('actions.editFilesMenu.options.provenance')} + + {t('actions.editFilesMenu.options.provenance')} + )} - {t('actions.editFilesMenu.options.delete')} + + {t('actions.editFilesMenu.options.delete')} + + setShowNoFilesSelectedModal(false)} + /> ) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/no-selected-files-modal/NoSelectedFilesModal.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/no-selected-files-modal/NoSelectedFilesModal.tsx new file mode 100644 index 000000000..d580b4b8c --- /dev/null +++ b/src/sections/dataset/dataset-files/files-table/file-actions/no-selected-files-modal/NoSelectedFilesModal.tsx @@ -0,0 +1,30 @@ +import { useTranslation } from 'react-i18next' +import { Button, Modal } from '@iqss/dataverse-design-system' +import styles from '../file-actions-cell/file-action-buttons/file-options-menu/FileAlreadyDeletedModal.module.scss' +import { ExclamationCircleFill } from 'react-bootstrap-icons' + +interface NoSelectedFilesModalProps { + show: boolean + handleClose: () => void +} + +export function NoSelectedFilesModal({ show, handleClose }: NoSelectedFilesModalProps) { + const { t } = useTranslation('files') + return ( + + + {t('actions.noSelectedFilesAlert.title')} + + +

+ {t('actions.noSelectedFilesAlert.message')} +

+
+ + + +
+ ) +} diff --git a/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx b/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx index a3e0d6d46..b403e9782 100644 --- a/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx +++ b/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx @@ -21,7 +21,7 @@ export function useFilesTable(files: File[], paginationInfo: FilePaginationInfo) ) const table = useReactTable({ data: files, - columns: createColumnsDefinition(paginationInfo), + columns: createColumnsDefinition(paginationInfo, fileSelection), state: { rowSelection: currentPageRowSelection }, diff --git a/src/stories/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.stories.tsx b/src/stories/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.stories.tsx index 61e22d129..87836ad96 100644 --- a/src/stories/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.stories.tsx +++ b/src/stories/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.stories.tsx @@ -17,7 +17,12 @@ export default meta type Story = StoryObj export const NonTabularFiles: Story = { - render: () => + render: () => ( + + ) } export const TabularFiles: Story = { @@ -30,6 +35,7 @@ export const TabularFiles: Story = { unf: 'some-unf' } })} + fileSelection={{}} /> ) } diff --git a/src/stories/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.stories.tsx b/src/stories/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.stories.tsx index a50a9cff0..692bc1980 100644 --- a/src/stories/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.stories.tsx +++ b/src/stories/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.stories.tsx @@ -16,5 +16,5 @@ export default meta type Story = StoryObj export const Default: Story = { - render: () => + render: () => } diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx index 3b8cbc009..1baf2e865 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx @@ -20,7 +20,7 @@ describe('FileActionsHeader', () => { - + ) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx index c3d8ab9fe..e380f0786 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx @@ -30,7 +30,10 @@ describe('DownloadFilesButton', () => { }) const files = FileMother.createMany(2) cy.mountAuthenticated( - withDataset(, datasetWithDownloadFilesPermission) + withDataset( + , + datasetWithDownloadFilesPermission + ) ) cy.findByRole('button', { name: 'Download' }).should('exist') @@ -42,7 +45,10 @@ describe('DownloadFilesButton', () => { }) const files = FileMother.createMany(1) cy.mountAuthenticated( - withDataset(, datasetWithDownloadFilesPermission) + withDataset( + , + datasetWithDownloadFilesPermission + ) ) cy.findByRole('button', { name: 'Download' }).should('not.exist') @@ -54,7 +60,10 @@ describe('DownloadFilesButton', () => { }) const files = FileMother.createMany(2) cy.mountAuthenticated( - withDataset(, datasetWithoutDownloadFilesPermission) + withDataset( + , + datasetWithoutDownloadFilesPermission + ) ) cy.findByRole('button', { name: 'Download' }).should('not.exist') @@ -72,7 +81,10 @@ describe('DownloadFilesButton', () => { } }) cy.mountAuthenticated( - withDataset(, datasetWithDownloadFilesPermission) + withDataset( + , + datasetWithDownloadFilesPermission + ) ) cy.findByRole('button', { name: 'Download' }).click() @@ -86,11 +98,49 @@ describe('DownloadFilesButton', () => { }) const files = FileMother.createMany(2, { tabularData: undefined }) cy.mountAuthenticated( - withDataset(, datasetWithDownloadFilesPermission) + withDataset( + , + datasetWithDownloadFilesPermission + ) ) cy.findByRole('button', { name: 'Download' }).click() cy.findByRole('button', { name: 'Original Format' }).should('not.exist') cy.findByRole('button', { name: 'Archival Format (.tab)' }).should('not.exist') }) + + it('shows the No Selected Files modal if no files are selected', () => { + const datasetWithDownloadFilesPermission = DatasetMother.create({ + permissions: DatasetPermissionsMother.createWithFilesDownloadAllowed() + }) + const files = FileMother.createMany(2) + cy.mountAuthenticated( + withDataset( + , + datasetWithDownloadFilesPermission + ) + ) + + cy.findByRole('button', { name: 'Download' }).click() + cy.findByText('Select File(s)').should('exist') + }) + + it('does not show the No Selected Files modal if files are selected', () => { + const datasetWithDownloadFilesPermission = DatasetMother.create({ + permissions: DatasetPermissionsMother.createWithFilesDownloadAllowed() + }) + const files = FileMother.createMany(2) + cy.mountAuthenticated( + withDataset( + , + datasetWithDownloadFilesPermission + ) + ) + + cy.findByRole('button', { name: 'Download' }).click() + cy.findByText('Select File(s)').should('not.exist') + }) }) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.spec.tsx index eaa400bd2..0cc9ac5b9 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.spec.tsx @@ -32,27 +32,31 @@ describe('EditFilesMenu', () => { it('renders the Edit Files menu', () => { cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset(, datasetWithUpdatePermissions) ) cy.findByRole('button', { name: 'Edit Files' }).should('exist') }) it('does not render the Edit Files menu when the user is not authenticated', () => { - cy.customMount(withDataset(, datasetWithUpdatePermissions)) + cy.customMount( + withDataset(, datasetWithUpdatePermissions) + ) cy.findByRole('button', { name: 'Edit Files' }).should('not.exist') }) it('does not render the Edit Files menu when there are no files in the dataset', () => { - cy.mountAuthenticated(withDataset(, datasetWithUpdatePermissions)) + cy.mountAuthenticated( + withDataset(, datasetWithUpdatePermissions) + ) cy.findByRole('button', { name: 'Edit Files' }).should('not.exist') }) it('renders the Edit Files options', () => { cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset(, datasetWithUpdatePermissions) ) cy.findByRole('button', { name: 'Edit Files' }).click() @@ -65,7 +69,10 @@ describe('EditFilesMenu', () => { }) cy.mountAuthenticated( - withDataset(, datasetWithNoUpdatePermissions) + withDataset( + , + datasetWithNoUpdatePermissions + ) ) cy.findByRole('button', { name: 'Edit Files' }).should('not.exist') @@ -78,7 +85,7 @@ describe('EditFilesMenu', () => { }) cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset(, datasetWithUpdatePermissions) ) cy.findByRole('button', { name: 'Edit Files' }).should('be.disabled') @@ -91,7 +98,7 @@ describe('EditFilesMenu', () => { }) cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset(, datasetWithUpdatePermissions) ) cy.findByRole('button', { name: 'Edit Files' }).should('be.disabled') diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.spec.tsx index 8006fe0aa..2571bfcf9 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.spec.tsx @@ -4,7 +4,7 @@ import { FileMother } from '../../../../../../files/domain/models/FileMother' const files = FileMother.createMany(2) describe('EditFilesOptions', () => { it('renders the EditFilesOptions', () => { - cy.customMount() + cy.customMount() cy.findByRole('button', { name: 'Metadata' }).should('exist') cy.findByRole('button', { name: 'Replace' }).should('exist') @@ -15,27 +15,66 @@ describe('EditFilesOptions', () => { it('renders the restrict option if some file is unrestricted', () => { const fileUnrestricted = FileMother.createDefault() - cy.customMount() + cy.customMount() - cy.findByRole('button', { name: 'Restrict' }).should('exist') + cy.findByRole('button', { name: 'Restrict' }).should('exist').click() + cy.findByText('Select File(s)').should('exist') + cy.findByText('Close').click() }) it('renders the unrestrict option if some file is restricted', () => { const fileRestricted = FileMother.createWithRestrictedAccess() - cy.customMount() + cy.customMount() - cy.findByRole('button', { name: 'Unrestrict' }).should('exist') + cy.findByRole('button', { name: 'Unrestrict' }).should('exist').click() + cy.findByText('Select File(s)').should('exist') + cy.findByText('Close').click() }) it.skip('renders the embargo option if the embargo is allowed by settings', () => { - cy.customMount() + cy.customMount() - cy.findByRole('button', { name: 'Embargo' }).should('exist') + cy.findByRole('button', { name: 'Embargo' }).should('exist').click() + cy.findByText('Select File(s)').should('exist') + cy.findByText('Close').click() }) it.skip('renders provenance option if provenance is enabled in config', () => { - cy.customMount() + cy.customMount() - cy.findByRole('button', { name: 'Provenance' }).should('exist') + cy.findByRole('button', { name: 'Provenance' }).should('exist').click() + cy.findByText('Select File(s)').should('exist') + cy.findByText('Close').click() + }) + + it('shows the No Selected Files message when no files are selected and one option is clicked', () => { + cy.customMount() + + cy.findByRole('button', { name: 'Metadata' }).click() + cy.findByText('Select File(s)').should('exist') + cy.findByText('Close').click() + + cy.findByRole('button', { name: 'Replace' }).click() + cy.findByText('Select File(s)').should('exist') + cy.findByText('Close').click() + + cy.findByRole('button', { name: 'Delete' }).click() + cy.findByText('Select File(s)').should('exist') + cy.findByText('Close').click() + }) + + it('does not show the No Selected Files message when files are selected and one option is clicked', () => { + cy.customMount( + + ) + + cy.findByRole('button', { name: 'Metadata' }).click() + cy.findByText('Select File(s)').should('not.exist') + + cy.findByRole('button', { name: 'Replace' }).click() + cy.findByText('Select File(s)').should('not.exist') + + cy.findByRole('button', { name: 'Delete' }).click() + cy.findByText('Select File(s)').should('not.exist') }) }) From 9675c50c155c92b532594201874db2332223b87c Mon Sep 17 00:00:00 2001 From: MellyGray Date: Tue, 31 Oct 2023 17:17:37 +0100 Subject: [PATCH 6/6] fix: enable can download files in dataset realistic mock --- tests/component/dataset/domain/models/DatasetMother.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/component/dataset/domain/models/DatasetMother.ts b/tests/component/dataset/domain/models/DatasetMother.ts index 2c6cf0e48..d5df54ddd 100644 --- a/tests/component/dataset/domain/models/DatasetMother.ts +++ b/tests/component/dataset/domain/models/DatasetMother.ts @@ -423,7 +423,7 @@ export class DatasetMother { } ] as DatasetMetadataBlocks, permissions: { - canDownloadFiles: false, + canDownloadFiles: true, canUpdateDataset: false, canPublishDataset: false, canManageDatasetPermissions: false,