diff --git a/src/sections/dataset/dataset-files/DatasetFilesScrollable.module.scss b/src/sections/dataset/dataset-files/DatasetFilesScrollable.module.scss index ead54d613..d86142df1 100644 --- a/src/sections/dataset/dataset-files/DatasetFilesScrollable.module.scss +++ b/src/sections/dataset/dataset-files/DatasetFilesScrollable.module.scss @@ -1,6 +1,7 @@ .files-scrollable-container { min-height: 400px; max-height: 650px; + overflow-x: hidden; overflow-y: auto; &--empty { @@ -11,7 +12,11 @@ position: sticky; top: 0; z-index: 2; - padding: 0.25rem 0.25rem 1rem; + padding: 0.25rem 0.5rem 1rem 0.25rem; background: var(--bs-white); } + + table { + margin-bottom: 0; + } } diff --git a/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx b/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx index 475c33fbe..1e53cb074 100644 --- a/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx +++ b/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx @@ -19,7 +19,7 @@ export const createColumnsDefinition = ( { - const { table, fileSelection, selectAllFiles, clearFileSelection } = useFilesTableScrollable( - files, - paginationInfo, - accumulatedCount - ) + const { table, fileSelection, selectAllPossibleRows, clearRowsSelection } = + useFilesTableScrollable(files, paginationInfo, accumulatedCount) const [previousCriteria, setPreviousCriteria] = useState(criteria) @@ -50,10 +47,10 @@ export const FilesTableScrollable = ({ useEffect(() => { if (previousCriteria != criteria) { - clearFileSelection(false) + clearRowsSelection() } setPreviousCriteria(criteria) - }, [criteria, previousCriteria, clearFileSelection]) + }, [criteria, previousCriteria, clearRowsSelection]) return ( <> @@ -64,9 +61,9 @@ export const FilesTableScrollable = ({ data-testid="table-top-messages"> >, - setRowSelection: (rowSelection: RowSelection) => void, - paginationInfo: FilePaginationInfo -) => { - const [fileSelection, setFileSelection] = useState({}) - const justClearedAll = useRef(false) - const justSelectedAll = useRef(false) - - const updateFileSelection = useDeepCompareCallback( - (currentFileSelection: FileSelection) => { - const newFileSelection: FileSelection = {} - - Object.entries(selectedRowsModels).forEach(([string, Row]) => { - const rowIndex = parseInt(string) - newFileSelection[rowIndex] = Row.original - }) - - const currentSelectionCount = Object.keys(currentFileSelection).length - const newSelectionCount = Object.keys(newFileSelection).length - - // WHY: This condition means that the user has deselected some rows and we shouldn't merge the selections - if (newSelectionCount < currentSelectionCount) { - return newFileSelection - } - - return { ...currentFileSelection, ...newFileSelection } - }, - [selectedRowsModels] - ) - - const selectAllFiles = () => { - setRowSelection(createRowSelection(paginationInfo.totalItems)) - - const totalFilesFileSelection = createFileSelection(paginationInfo.totalItems) - - const newFileSelection = { ...totalFilesFileSelection, ...fileSelection } - setFileSelection(newFileSelection) - justSelectedAll.current = true - } - const clearFileSelection = (withFlag = true) => { - setRowSelection({}) - setFileSelection({}) - - if (withFlag) { - justClearedAll.current = true - } - } - - useEffect(() => { - if (justClearedAll.current) { - justClearedAll.current = false - return - } - - if (justSelectedAll.current) { - justSelectedAll.current = false - return - } - - setFileSelection((current) => updateFileSelection(current)) - }, [updateFileSelection]) - - return { - fileSelection, - selectAllFiles, - clearFileSelection - } -} - -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): FileSelection { - 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/useFilesTableScrollable.tsx b/src/sections/dataset/dataset-files/files-table/useFilesTableScrollable.tsx index fa73c035b..3210028f1 100644 --- a/src/sections/dataset/dataset-files/files-table/useFilesTableScrollable.tsx +++ b/src/sections/dataset/dataset-files/files-table/useFilesTableScrollable.tsx @@ -1,8 +1,8 @@ import { useEffect, useState } from 'react' +import { useDeepCompareMemo } from 'use-deep-compare' import { FilePreview } from '../../../../files/domain/models/FilePreview' import { getCoreRowModel, Row, useReactTable } from '@tanstack/react-table' import { createColumnsDefinition } from './FilesTableColumnsDefinition' -import { useFileSelectionScrollable } from './row-selection/useFileSelectionScrollable' import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' export type RowSelection = { @@ -21,11 +21,20 @@ export function useFilesTableScrollable( const [rowSelection, setRowSelection] = useState({}) const [selectedRowsModels, setSelectedRowsModels] = useState>>({}) - const { fileSelection, selectAllFiles, clearFileSelection } = useFileSelectionScrollable( - selectedRowsModels, - setRowSelection, - paginationInfo - ) + const fileSelection: FileSelection = useDeepCompareMemo(() => { + const result: FileSelection = Object.entries(selectedRowsModels).reduce((acc, [key, row]) => { + acc[key] = row.original + return acc + }, {} as FileSelection) + + Object.keys(rowSelection).forEach((key) => { + if (!(key in result)) { + result[key] = undefined + } + }) + + return result + }, [selectedRowsModels, rowSelection]) const table = useReactTable({ data: files, @@ -42,6 +51,14 @@ export function useFilesTableScrollable( const selectedRowsById = table.getSelectedRowModel().rowsById + // This is only intended to be used by the select all button, regardless if all files are loaded or not will create a selection for all possible rows + const selectAllPossibleRows = () => { + const allPossiblesRowsSelected = createRowSelection(paginationInfo.totalItems) + setRowSelection(allPossiblesRowsSelected) + } + + const clearRowsSelection = () => setRowSelection({}) + useEffect(() => { table.setPageSize(paginationInfo.pageSize) table.setPageIndex(paginationInfo.page - 1) @@ -54,7 +71,17 @@ export function useFilesTableScrollable( return { table, fileSelection, - selectAllFiles, - clearFileSelection + selectAllPossibleRows, + clearRowsSelection } } + +function createRowSelection(numberOfRows: number) { + const rowSelection: Record = {} + + for (let i = 0; i < numberOfRows; i++) { + rowSelection[String(i)] = true + } + + return rowSelection +} diff --git a/tests/component/sections/dataset/dataset-files/DatasetFilesScrollable.spec.tsx b/tests/component/sections/dataset/dataset-files/DatasetFilesScrollable.spec.tsx index 0ce8ab03b..99b6b2c8c 100644 --- a/tests/component/sections/dataset/dataset-files/DatasetFilesScrollable.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/DatasetFilesScrollable.spec.tsx @@ -65,8 +65,6 @@ const testFilesCountInfo = FilesCountInfoMother.create({ }) const settingsRepository = {} as SettingRepository -// TODO:ME Test everything here, to difficult to test passing ref of sentry - describe('DatasetFilesScrollable', () => { beforeEach(() => { fileRepository.getAllByDatasetPersistentIdWithCount = cy.stub().resolves(testFiles) @@ -369,6 +367,40 @@ describe('DatasetFilesScrollable', () => { cy.findByText('200 files are currently selected.').should('exist') }) + it('selects all files when clicking the select all button and mantains selection when loading more on scroll to bottom', () => { + cy.customMount( + + ) + cy.findByRole('columnheader', { name: '10 of 200 Files displayed' }).should('exist') + cy.get('table > thead > tr > th > input[type=checkbox]').click() + cy.findByText('10 files are currently selected.').should('exist') + cy.findByRole('button', { name: 'Select all 200 files in this dataset.' }).click() + cy.findByText('200 files are currently selected.').should('exist') + + // Scroll to bottom to load more files + cy.findByTestId('scrollable-files-container').as('scrollableFilesContainer') + cy.get('@scrollableFilesContainer').scrollTo('bottom') + + cy.findByText('200 files are currently selected.').should('exist') + cy.findByRole('columnheader', { name: '20 of 200 Files displayed' }).should('exist') + + // Scroll to bottom to load more files + cy.get('@scrollableFilesContainer').scrollTo('bottom') + + cy.findByText('200 files are currently selected.').should('exist') + cy.findByRole('columnheader', { name: '30 of 200 Files displayed' }).should('exist') + + // Scroll to bottom to load more files + cy.get('@scrollableFilesContainer').scrollTo('bottom') + + cy.findByText('200 files are currently selected.').should('exist') + cy.findByRole('columnheader', { name: '40 of 200 Files displayed' }).should('exist') + }) + it('maintains the selection when scrolling to bottom and loading more files', () => { cy.customMount( { cy.get('table > tbody > tr:nth-child(2)').should('have.class', styles['selected-row']) cy.findByText('1 file is currently selected.').should('exist') + // Scroll to bottom to load more files cy.findByTestId('scrollable-files-container').as('scrollableFilesContainer') cy.get('@scrollableFilesContainer').scrollTo('bottom') @@ -389,9 +422,27 @@ describe('DatasetFilesScrollable', () => { cy.get('table > tbody > tr:nth-child(2)').should('have.class', styles['selected-row']) cy.findByText('1 file is currently selected.').should('exist') - // cy.get('table > tbody > tr:nth-child(3) > td:nth-child(1) > input[type=checkbox]').click() - // cy.get('table > tbody > tr:nth-child(3)').should('have.class', styles['selected-row']) - // cy.findByText('2 files are currently selected.').should('exist') + + // Scroll to bottom to load more files + cy.get('@scrollableFilesContainer').scrollTo('bottom') + + cy.findByRole('columnheader', { name: '30 of 200 Files displayed' }).should('exist') + cy.get('table > tbody > tr:nth-child(2)').should('have.class', styles['selected-row']) + cy.findByText('1 file is currently selected.').should('exist') + + // Scroll to bottom to load more files + cy.get('@scrollableFilesContainer').scrollTo('bottom') + + cy.findByRole('columnheader', { name: '40 of 200 Files displayed' }).should('exist') + cy.get('table > tbody > tr:nth-child(2)').should('have.class', styles['selected-row']) + cy.findByText('1 file is currently selected.').should('exist') + + // Scroll to bottom to load more files + cy.get('@scrollableFilesContainer').scrollTo('bottom') + + cy.findByRole('columnheader', { name: '50 of 200 Files displayed' }).should('exist') + cy.get('table > tbody > tr:nth-child(2)').should('have.class', styles['selected-row']) + cy.findByText('1 file is currently selected.').should('exist') }) it('removes the selection when the header checkbox is clicked again', () => {