diff --git a/src/sections/dataset/dataset-files/files-table/FilesTable.tsx b/src/sections/dataset/dataset-files/files-table/FilesTable.tsx
index 129792f8f..908d983ec 100644
--- a/src/sections/dataset/dataset-files/files-table/FilesTable.tsx
+++ b/src/sections/dataset/dataset-files/files-table/FilesTable.tsx
@@ -15,7 +15,7 @@ interface FilesTableProps {
}
export function FilesTable({ files, isLoading, paginationInfo }: FilesTableProps) {
- const { table, rowSelection, selectAllRows, clearRowSelection } = useFilesTable(
+ const { table, fileSelection, selectAllFiles, clearFileSelection } = useFilesTable(
files,
paginationInfo
)
@@ -26,14 +26,12 @@ export function FilesTable({ files, isLoading, paginationInfo }: FilesTableProps
return (
<>
- row.original)}
+ clearRowSelection={clearFileSelection}
/>
+
diff --git a/src/sections/dataset/dataset-files/files-table/row-selection/RowSelectionMessage.tsx b/src/sections/dataset/dataset-files/files-table/row-selection/RowSelectionMessage.tsx
index 26d028ec4..82d0c8791 100644
--- a/src/sections/dataset/dataset-files/files-table/row-selection/RowSelectionMessage.tsx
+++ b/src/sections/dataset/dataset-files/files-table/row-selection/RowSelectionMessage.tsx
@@ -1,10 +1,10 @@
-import { RowSelection } from './useRowSelection'
+import { FileSelection } from './useFileSelection'
import { Button } from '@iqss/dataverse-design-system'
import { useTranslation } from 'react-i18next'
import styles from './RowSelectionMessage.module.scss'
interface RowSelectionMessageProps {
- rowSelection: RowSelection
+ fileSelection: FileSelection
totalFilesCount: number
selectAllRows: () => void
clearRowSelection: () => void
@@ -14,13 +14,13 @@ const MINIMUM_SELECTED_FILES_TO_SHOW_MESSAGE = 0
const MINIMUM_FILES_TO_SHOW_MESSAGE = 10
export function RowSelectionMessage({
- rowSelection,
+ fileSelection,
totalFilesCount,
selectAllRows,
clearRowSelection
}: RowSelectionMessageProps) {
const { t } = useTranslation('files')
- const selectedFilesCount = Object.keys(rowSelection).length
+ const selectedFilesCount = Object.keys(fileSelection).length
const showMessage =
totalFilesCount > MINIMUM_FILES_TO_SHOW_MESSAGE &&
selectedFilesCount > MINIMUM_SELECTED_FILES_TO_SHOW_MESSAGE
diff --git a/src/sections/dataset/dataset-files/files-table/row-selection/useFileSelection.ts b/src/sections/dataset/dataset-files/files-table/row-selection/useFileSelection.ts
new file mode 100644
index 000000000..e8e9fec2c
--- /dev/null
+++ b/src/sections/dataset/dataset-files/files-table/row-selection/useFileSelection.ts
@@ -0,0 +1,114 @@
+import { useEffect, useState } from 'react'
+import { FilePaginationInfo } from '../../../../../files/domain/models/FilePaginationInfo'
+import { File } from '../../../../../files/domain/models/File'
+import { Row } from '@tanstack/react-table'
+import { RowSelection } from '../useFilesTable'
+
+export type FileSelection = {
+ [key: string]: File | undefined
+}
+
+export function useFileSelection(
+ currentPageSelectedRowModel: Record>,
+ setCurrentPageRowSelection: (rowSelection: RowSelection) => void,
+ paginationInfo: FilePaginationInfo
+) {
+ const [fileSelection, setFileSelection] = useState({})
+ const updateFileSelection = () => {
+ const currentPageFileSelection = getCurrentPageFileSelection()
+ const currentPageIndexes = getCurrentPageIndexes()
+
+ Object.keys(fileSelection).forEach((key) => {
+ const rowIndex = parseInt(key)
+ if (currentPageIndexes.includes(rowIndex)) {
+ if (!currentPageFileSelection[key]) {
+ delete fileSelection[key]
+ }
+ }
+ })
+
+ return { ...fileSelection, ...currentPageFileSelection }
+ }
+ const getCurrentPageIndexes = () => {
+ return Array.from(
+ { length: paginationInfo.pageSize },
+ (_, i) => i + (paginationInfo.page - 1) * paginationInfo.pageSize
+ )
+ }
+ const getCurrentPageFileSelection = () => {
+ const rowSelectionFixed: FileSelection = {}
+ const currentPageIndexes = getCurrentPageIndexes()
+
+ Object.entries(currentPageSelectedRowModel).forEach(([string, Row]) => {
+ const rowIndex = parseInt(string)
+ rowSelectionFixed[currentPageIndexes[rowIndex]] = Row.original
+ })
+ return rowSelectionFixed
+ }
+ const computeCurrentPageRowSelection = () => {
+ const rowSelectionOfCurrentPage: RowSelection = {}
+ const currentPageIndexes = getCurrentPageIndexes()
+
+ Object.keys(fileSelection).forEach((key) => {
+ const rowIndex = parseInt(key)
+ if (currentPageIndexes.includes(rowIndex)) {
+ rowSelectionOfCurrentPage[currentPageIndexes.indexOf(rowIndex)] = true
+ }
+ })
+
+ return rowSelectionOfCurrentPage
+ }
+ const selectAllFiles = () => {
+ setCurrentPageRowSelection(createRowSelection(paginationInfo.pageSize))
+ setFileSelection(createFileSelection(paginationInfo.totalFiles))
+ }
+ const clearFileSelection = () => {
+ setCurrentPageRowSelection({})
+ setFileSelection({})
+ }
+ const toggleAllFilesSelected = () => {
+ if (areAllFilesSelected()) {
+ clearFileSelection()
+ } else {
+ selectAllFiles()
+ }
+ }
+ const areAllFilesSelected = () => {
+ return Object.keys(fileSelection).length === paginationInfo.totalFiles
+ }
+
+ useEffect(() => {
+ setFileSelection(updateFileSelection())
+ }, [currentPageSelectedRowModel])
+
+ useEffect(() => {
+ setCurrentPageRowSelection(computeCurrentPageRowSelection())
+ }, [paginationInfo])
+
+ return {
+ fileSelection,
+ selectAllFiles,
+ clearFileSelection,
+ toggleAllFilesSelected
+ }
+}
+
+export function createRowSelection(numberOfRows: number) {
+ const rowSelection: Record = {}
+
+ for (let i = 0; i < numberOfRows; i++) {
+ rowSelection[String(i)] = true
+ }
+
+ return rowSelection
+}
+
+export function createFileSelection(numberOfRows: number) {
+ const fileSelection: FileSelection = {}
+
+ for (let i = 0; i < numberOfRows; i++) {
+ fileSelection[String(i)] = undefined
+ }
+
+ return fileSelection
+}
diff --git a/src/sections/dataset/dataset-files/files-table/row-selection/useRowSelection.ts b/src/sections/dataset/dataset-files/files-table/row-selection/useRowSelection.ts
deleted file mode 100644
index d7aa68b96..000000000
--- a/src/sections/dataset/dataset-files/files-table/row-selection/useRowSelection.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-import { useEffect, useState } from 'react'
-import { FilePaginationInfo } from '../../../../../files/domain/models/FilePaginationInfo'
-
-export type RowSelection = {
- [key: string]: boolean
-}
-
-export function useRowSelection(
- currentPageRowSelection: RowSelection,
- setCurrentPageRowSelection: (rowSelection: RowSelection) => void,
- paginationInfo: FilePaginationInfo
-) {
- const [rowSelection, setRowSelection] = useState({})
- const updatedRowSelection = () => {
- const currentPageRowSelectionFixed = getCurrentPageRowSelectionFixed()
- const currentPageIndexes = getCurrentPageIndexes()
-
- Object.keys(rowSelection).forEach((key) => {
- const rowIndex = parseInt(key)
- if (currentPageIndexes.includes(rowIndex)) {
- if (!currentPageRowSelectionFixed[key]) {
- delete rowSelection[key]
- }
- }
- })
-
- return { ...rowSelection, ...currentPageRowSelectionFixed }
- }
- const getCurrentPageIndexes = () => {
- return Array.from(
- { length: paginationInfo.pageSize },
- (_, i) => i + (paginationInfo.page - 1) * paginationInfo.pageSize
- )
- }
- const getCurrentPageRowSelectionFixed = () => {
- const rowSelectionFixed: RowSelection = {}
- const currentPageIndexes = getCurrentPageIndexes()
-
- Object.keys(currentPageRowSelection).forEach((key) => {
- const rowIndex = parseInt(key)
- rowSelectionFixed[currentPageIndexes[rowIndex]] = currentPageRowSelection[key]
- })
- return rowSelectionFixed
- }
- const getRowSelectionOfCurrentPage = () => {
- const rowSelectionOfCurrentPage: RowSelection = {}
- const currentPageIndexes = getCurrentPageIndexes()
-
- Object.keys(rowSelection).forEach((key) => {
- const rowIndex = parseInt(key)
- if (currentPageIndexes.includes(rowIndex)) {
- rowSelectionOfCurrentPage[currentPageIndexes.indexOf(rowIndex)] = rowSelection[key]
- }
- })
-
- return rowSelectionOfCurrentPage
- }
- const selectAllRows = () => {
- setCurrentPageRowSelection(createRowSelection(paginationInfo.pageSize))
- setRowSelection(createRowSelection(paginationInfo.totalFiles))
- }
- const clearRowSelection = () => {
- setCurrentPageRowSelection({})
- setRowSelection({})
- }
- const toggleAllRowsSelected = () => {
- if (isAllRowsSelected()) {
- clearRowSelection()
- } else {
- selectAllRows()
- }
- }
- const isAllRowsSelected = () => {
- return Object.keys(rowSelection).length === paginationInfo.totalFiles
- }
-
- useEffect(() => {
- setRowSelection(updatedRowSelection())
- }, [currentPageRowSelection])
-
- useEffect(() => {
- setCurrentPageRowSelection(getRowSelectionOfCurrentPage())
- }, [paginationInfo])
-
- return {
- rowSelection,
- setRowSelection,
- selectAllRows,
- clearRowSelection,
- toggleAllRowsSelected
- }
-}
-
-export function createRowSelection(numberOfRows: number) {
- const rowSelection: Record = {}
-
- for (let i = 0; i < numberOfRows; i++) {
- rowSelection[String(i)] = true
- }
-
- return rowSelection
-}
diff --git a/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx b/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx
index bb524d4b4..467f76e1b 100644
--- a/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx
+++ b/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx
@@ -1,20 +1,24 @@
import { useEffect, useState } from 'react'
import { File } from '../../../../files/domain/models/File'
-import { getCoreRowModel, useReactTable } from '@tanstack/react-table'
+import { getCoreRowModel, Row, useReactTable } from '@tanstack/react-table'
import { createColumnsDefinition } from './FilesTableColumnsDefinition'
import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo'
-import { RowSelection, useRowSelection } from './row-selection/useRowSelection'
+import { useFileSelection } from './row-selection/useFileSelection'
+
+export type RowSelection = {
+ [key: string]: boolean
+}
export function useFilesTable(files: File[], paginationInfo: FilePaginationInfo) {
const [currentPageRowSelection, setCurrentPageRowSelection] = useState({})
- const { rowSelection, selectAllRows, clearRowSelection, toggleAllRowsSelected } = useRowSelection(
- currentPageRowSelection,
- setCurrentPageRowSelection,
- paginationInfo
- )
+ const [currentPageSelectedRowModel, setCurrentPageSelectedRowModel] = useState<
+ Record>
+ >({})
+ const { fileSelection, selectAllFiles, clearFileSelection, toggleAllFilesSelected } =
+ useFileSelection(currentPageSelectedRowModel, setCurrentPageRowSelection, paginationInfo)
const table = useReactTable({
data: files,
- columns: createColumnsDefinition(toggleAllRowsSelected),
+ columns: createColumnsDefinition(toggleAllFilesSelected),
state: {
rowSelection: currentPageRowSelection
},
@@ -30,10 +34,14 @@ export function useFilesTable(files: File[], paginationInfo: FilePaginationInfo)
table.setPageIndex(paginationInfo.page - 1)
}, [paginationInfo])
+ useEffect(() => {
+ setCurrentPageSelectedRowModel(table.getSelectedRowModel().rowsById)
+ }, [table.getSelectedRowModel().rowsById])
+
return {
table,
- rowSelection,
- selectAllRows,
- clearRowSelection
+ fileSelection,
+ selectAllFiles,
+ clearFileSelection
}
}
diff --git a/src/sections/dataset/dataset-files/files-table/zip-download-limit-message/ZipDownloadLimitMessage.tsx b/src/sections/dataset/dataset-files/files-table/zip-download-limit-message/ZipDownloadLimitMessage.tsx
index a066dba7a..7e1c474ee 100644
--- a/src/sections/dataset/dataset-files/files-table/zip-download-limit-message/ZipDownloadLimitMessage.tsx
+++ b/src/sections/dataset/dataset-files/files-table/zip-download-limit-message/ZipDownloadLimitMessage.tsx
@@ -5,19 +5,18 @@ import { useSettings } from '../../../../settings/SettingsContext'
import { SettingName } from '../../../../../settings/domain/models/Setting'
import { ZipDownloadLimit } from '../../../../../settings/domain/models/ZipDownloadLimit'
import { useEffect, useState } from 'react'
+import { FileSelection } from '../row-selection/useFileSelection'
interface ZipDownloadLimitMessageProps {
- selectedFiles: File[]
+ fileSelection: FileSelection
}
const MINIMUM_FILES_TO_SHOW_MESSAGE = 1
-export function ZipDownloadLimitMessage({ selectedFiles }: ZipDownloadLimitMessageProps) {
+export function ZipDownloadLimitMessage({ fileSelection }: ZipDownloadLimitMessageProps) {
const { t } = useTranslation('files')
const { getSettingByName } = useSettings()
const [zipDownloadLimitInBytes, setZipDownloadLimitInBytes] = useState()
- const selectionTotalSizeInBytes = getFilesTotalSizeInBytes(selectedFiles)
-
useEffect(() => {
getSettingByName(SettingName.ZIP_DOWNLOAD_LIMIT)
.then((zipDownloadLimit) => {
@@ -28,9 +27,11 @@ export function ZipDownloadLimitMessage({ selectedFiles }: ZipDownloadLimitMessa
})
}, [getSettingByName])
+ // TODO - When selecting all files, the size should come from a call to a use case that returns the total size of the dataset files. Check issue https://github.com/IQSS/dataverse-frontend/issues/170
+ const selectionTotalSizeInBytes = getFilesTotalSizeInBytes(Object.values(fileSelection))
const showMessage =
zipDownloadLimitInBytes &&
- selectedFiles.length > MINIMUM_FILES_TO_SHOW_MESSAGE &&
+ Object.values(fileSelection).length > MINIMUM_FILES_TO_SHOW_MESSAGE &&
selectionTotalSizeInBytes > zipDownloadLimitInBytes
if (!showMessage) {
@@ -48,8 +49,10 @@ export function ZipDownloadLimitMessage({ selectedFiles }: ZipDownloadLimitMessa
)
}
-function getFilesTotalSizeInBytes(files: File[]) {
- return files.map((file) => file.size).reduce((bytes, size) => bytes + size.toBytes(), 0)
+function getFilesTotalSizeInBytes(files: (File | undefined)[]) {
+ return files
+ .map((file) => file?.size)
+ .reduce((bytes, size) => bytes + (size ? size.toBytes() : 0), 0)
}
function bytesToHumanReadable(bytes: number) {
diff --git a/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx b/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx
index 25088e757..2049c0c7d 100644
--- a/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx
+++ b/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx
@@ -9,8 +9,11 @@ import {
} from '../../../../../src/files/domain/models/FileCriteria'
import { FilesCountInfoMother } from '../../../files/domain/models/FilesCountInfoMother'
import { FilePaginationInfo } from '../../../../../src/files/domain/models/FilePaginationInfo'
-import { FileType } from '../../../../../src/files/domain/models/File'
+import { FileSizeUnit, FileType } from '../../../../../src/files/domain/models/File'
import styles from '../../../../../src/sections/dataset/dataset-files/files-table/FilesTable.module.scss'
+import { SettingMother } from '../../../settings/domain/models/SettingMother'
+import { ZipDownloadLimit } from '../../../../../src/settings/domain/models/ZipDownloadLimit'
+import { SettingsContext } from '../../../../../src/sections/settings/SettingsContext'
const testFiles = FileMother.createMany(10)
const datasetPersistentId = 'test-dataset-persistent-id'
@@ -161,6 +164,36 @@ describe('DatasetFiles', () => {
cy.findByText('1 file is currently selected.').should('exist')
})
+
+ it('renders the zip download limit message when selecting rows from different pages', () => {
+ const getSettingByName = cy
+ .stub()
+ .resolves(SettingMother.createZipDownloadLimit(new ZipDownloadLimit(1, FileSizeUnit.BYTES)))
+
+ cy.customMount(
+
+
+
+ )
+
+ cy.get('table > tbody > tr:nth-child(2) > td:nth-child(1) > input[type=checkbox]').click()
+ cy.findByRole('button', { name: 'Next' }).click()
+ cy.get('table > tbody > tr:nth-child(3) > td:nth-child(1) > input[type=checkbox]').click()
+
+ cy.findByText(
+ /exceeds the zip limit of 1.0 B. Please unselect some files to continue./
+ ).should('exist')
+
+ cy.get('table > tbody > tr:nth-child(3) > td:nth-child(1) > input[type=checkbox]').click()
+
+ cy.findByText(
+ /exceeds the zip limit of 1.0 B. Please unselect some files to continue./
+ ).should('not.exist')
+ })
})
describe('Calling use cases', () => {
diff --git a/tests/component/sections/dataset/dataset-files/files-table/row-selection/RowSelectionMessage.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/row-selection/RowSelectionMessage.spec.tsx
index e5eb4b997..5584bdb76 100644
--- a/tests/component/sections/dataset/dataset-files/files-table/row-selection/RowSelectionMessage.spec.tsx
+++ b/tests/component/sections/dataset/dataset-files/files-table/row-selection/RowSelectionMessage.spec.tsx
@@ -1,5 +1,5 @@
import { RowSelectionMessage } from '../../../../../../../src/sections/dataset/dataset-files/files-table/row-selection/RowSelectionMessage'
-import { createRowSelection } from '../../../../../../../src/sections/dataset/dataset-files/files-table/row-selection/useRowSelection'
+import { createRowSelection } from '../../../../../../../src/sections/dataset/dataset-files/files-table/row-selection/useFileSelection'
let selectAllRows = () => {}
let clearRowSelection = () => {}
@@ -12,7 +12,7 @@ describe('RowSelectionMessage', () => {
it('renders the message when there are more than 10 files and some row is selected', () => {
cy.customMount(
{
it('does not render the message when there are less than 10 files', () => {
cy.customMount(
{
it('does not render the message when there are more than 10 files but no row is selected', () => {
cy.customMount(
{
it('renders the plural form of the message when there is more than 1 row selected', () => {
cy.customMount(
{
it("calls selectAllRows when the 'Select all' button is clicked", () => {
cy.customMount(
{
it("calls clearRowSelection when the 'Clear selection.' button is clicked", () => {
cy.customMount(
{
- it('should render the component with no message if conditions are not met', () => {
+ it('should not render if there is less than 1 file selected', () => {
const getSettingByName = cy
.stub()
.resolves(SettingMother.createZipDownloadLimit(zipDownloadLimit))
@@ -19,7 +19,9 @@ describe('ZipDownloadLimitMessage', () => {
cy.customMount(
)
@@ -27,13 +29,32 @@ describe('ZipDownloadLimitMessage', () => {
cy.findByText(/The overall size of the files selected/).should('not.exist')
})
- it('should render the component with the appropriate message if conditions are met', () => {
+ it('should not render if the zipDownloadLimit is not exceeded', () => {
+ const getSettingByName = cy
+ .stub()
+ .resolves(SettingMother.createZipDownloadLimit(zipDownloadLimit))
+
+ cy.customMount(
+
+
+
+ )
+
+ cy.findByText(/The overall size of the files selected/).should('not.exist')
+ })
+
+ it('should render if there is more than 1 file and they exceed the zipDownloadLimit', () => {
const getSettingByName = cy
.stub()
.resolves(SettingMother.createZipDownloadLimit(zipDownloadLimit))
cy.customMount(
-
+
)
@@ -46,13 +67,13 @@ describe('ZipDownloadLimitMessage', () => {
const getSettingByName = cy
.stub()
.resolves(SettingMother.createZipDownloadLimit(zipDownloadLimit))
- const files = [
- FileMother.create({ size: new FileSize(1000000, FileSizeUnit.PETABYTES) }),
- FileMother.create({ size: new FileSize(1000000, FileSizeUnit.PETABYTES) })
- ]
+ const fileSelection = {
+ '1': FileMother.create({ size: new FileSize(1000000, FileSizeUnit.PETABYTES) }),
+ '2': FileMother.create({ size: new FileSize(1000000, FileSizeUnit.PETABYTES) })
+ }
cy.customMount(
-
+
)