diff --git a/packages/datagateway-common/src/views/downloadButton.component.test.tsx b/packages/datagateway-common/src/views/downloadButton.component.test.tsx index 659fc08bb..1bf646eef 100644 --- a/packages/datagateway-common/src/views/downloadButton.component.test.tsx +++ b/packages/datagateway-common/src/views/downloadButton.component.test.tsx @@ -60,28 +60,30 @@ describe('Generic download button', () => { }); it('renders correctly', () => { - const textButtonWrapper = createWrapper({ + const props: DownloadButtonProps = { entityType: 'datafile', entityName: 'test', entityId: 1, - }); + entitySize: 1, + }; + const textButtonWrapper = createWrapper(props); expect(textButtonWrapper.find('button').text()).toBe('buttons.download'); const iconButtonWrapper = createWrapper({ - entityType: 'datafile', - entityName: 'test', - entityId: 1, + ...props, variant: 'icon', }); expect(iconButtonWrapper.find('button').text()).toBe(''); }); it('calls download investigation on button press for both text and icon buttons', () => { - let wrapper = createWrapper({ + const props: DownloadButtonProps = { entityType: 'investigation', entityName: 'test', entityId: 1, - }); + entitySize: 1, + }; + let wrapper = createWrapper(props); wrapper.find('#download-btn-1').first().simulate('click'); expect(downloadInvestigation).toHaveBeenCalledWith( @@ -93,9 +95,7 @@ describe('Generic download button', () => { jest.clearAllMocks(); wrapper = createWrapper({ - entityType: 'investigation', - entityName: 'test', - entityId: 1, + ...props, variant: 'icon', }); @@ -108,11 +108,13 @@ describe('Generic download button', () => { }); it('calls download dataset on button press for both text and icon buttons', () => { - let wrapper = createWrapper({ + const props: DownloadButtonProps = { entityType: 'dataset', entityName: 'test', entityId: 1, - }); + entitySize: 1, + }; + let wrapper = createWrapper(props); wrapper.find('#download-btn-1').first().simulate('click'); expect(downloadDataset).toHaveBeenCalledWith( @@ -124,9 +126,7 @@ describe('Generic download button', () => { jest.clearAllMocks(); wrapper = createWrapper({ - entityType: 'dataset', - entityName: 'test', - entityId: 1, + ...props, variant: 'icon', }); @@ -139,11 +139,13 @@ describe('Generic download button', () => { }); it('calls download datafile on button press for both text and icon buttons', () => { - let wrapper = createWrapper({ + const props: DownloadButtonProps = { entityType: 'datafile', entityName: 'test', entityId: 1, - }); + entitySize: 1, + }; + let wrapper = createWrapper(props); wrapper.find('#download-btn-1').first().simulate('click'); expect(downloadDatafile).toHaveBeenCalledWith( @@ -155,14 +157,12 @@ describe('Generic download button', () => { jest.clearAllMocks(); wrapper = createWrapper({ - entityType: 'dataset', - entityName: 'test', - entityId: 1, + ...props, variant: 'icon', }); wrapper.find('#download-btn-1').first().simulate('click'); - expect(downloadDataset).toHaveBeenCalledWith( + expect(downloadDatafile).toHaveBeenCalledWith( 'https://www.example.com/ids', 1, 'test' @@ -174,8 +174,34 @@ describe('Generic download button', () => { entityType: 'datafile', entityName: undefined, entityId: 1, + entitySize: 1, }); expect(wrapper.find(DownloadButton).children().length).toBe(0); }); + + it('renders a tooltip and disabled button if entity size is zero', () => { + const props: DownloadButtonProps = { + entityType: 'datafile', + entityName: 'test', + entityId: 1, + entitySize: 0, + }; + let wrapper = createWrapper(props); + + expect(wrapper.exists('#tooltip-1')); + wrapper.find('#download-btn-1').first().simulate('click'); + expect(downloadDatafile).not.toHaveBeenCalled(); + + jest.clearAllMocks(); + + wrapper = createWrapper({ + ...props, + variant: 'icon', + }); + + expect(wrapper.exists('#tooltip-1')); + wrapper.find('#download-btn-1').first().simulate('click'); + expect(downloadDatafile).not.toHaveBeenCalled(); + }); }); diff --git a/packages/datagateway-common/src/views/downloadButton.component.tsx b/packages/datagateway-common/src/views/downloadButton.component.tsx index 1d45f320f..8b708f7dd 100644 --- a/packages/datagateway-common/src/views/downloadButton.component.tsx +++ b/packages/datagateway-common/src/views/downloadButton.component.tsx @@ -1,4 +1,5 @@ -import { Button, IconButton } from '@material-ui/core'; +import { Button, IconButton, Tooltip, Typography } from '@material-ui/core'; +import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; import { GetApp } from '@material-ui/icons'; import { downloadDatafile } from '../api/datafiles'; import { downloadDataset } from '../api/datasets'; @@ -8,17 +9,32 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; +const useStylesTooltip = makeStyles((theme: Theme) => + createStyles({ + tooltip: { + backgroundColor: theme.palette.common.black, + fontSize: '0.875rem', + }, + arrow: { + color: theme.palette.common.black, + }, + }) +); + export interface DownloadButtonProps { entityType: 'investigation' | 'dataset' | 'datafile'; entityId: number; entityName: string | undefined; + entitySize: number; variant?: 'text' | 'outlined' | 'contained' | 'icon'; } const DownloadButton: React.FC = ( props: DownloadButtonProps ) => { - const { entityType, entityId, entityName, variant } = props; + const { entityType, entityId, entityName, variant, entitySize } = props; + const { ...classes } = useStylesTooltip(); + const [t] = useTranslation(); const idsUrl = useSelector((state: StateType) => state.dgcommon.urls.idsUrl); @@ -31,7 +47,7 @@ const DownloadButton: React.FC = ( downloadInvestigation(idsUrl, entityId, entityName); } else if (entityType === 'dataset') { downloadDataset(idsUrl, entityId, entityName); - } else if (entityType === 'datafile') { + } else { downloadDatafile(idsUrl, entityId, entityName); } }; @@ -39,32 +55,87 @@ const DownloadButton: React.FC = ( if (!entityName) return null; if (variant === 'icon') { return ( - { - downloadData(entityType, entityId, entityName); - }} - className="tour-dataview-download" - > - - +
+ {entitySize <= 0 ? ( + {t('buttons.unable_to_download_tooltip')} + } + id={`tooltip-${entityId}`} + placement="left" + arrow + classes={classes} + > + + + + + + + ) : ( + { + downloadData(entityType, entityId, entityName); + }} + className="tour-dataview-download" + > + + + )} +
); } else { return ( - +
+ {entitySize <= 0 ? ( + {t('buttons.unable_to_download_tooltip')} + } + id={`tooltip-${entityId}`} + placement="bottom" + arrow + classes={classes} + > + + + + + ) : ( + + )} +
); } }; diff --git a/packages/datagateway-dataview/public/res/default.json b/packages/datagateway-dataview/public/res/default.json index 3d1fb58fa..a60da05b0 100644 --- a/packages/datagateway-dataview/public/res/default.json +++ b/packages/datagateway-dataview/public/res/default.json @@ -211,7 +211,8 @@ "buttons": { "add_to_cart": "Add to selection", "remove_from_cart": "Remove from selection", - "download": "Download" + "download": "Download", + "unable_to_download_tooltip": "Unable to download - this item is empty" }, "advanced_filters": { "show": "Show Advanced Filters", diff --git a/packages/datagateway-dataview/src/views/card/isis/isisDatasetsCardView.component.tsx b/packages/datagateway-dataview/src/views/card/isis/isisDatasetsCardView.component.tsx index c33c3ff5d..111321113 100644 --- a/packages/datagateway-dataview/src/views/card/isis/isisDatasetsCardView.component.tsx +++ b/packages/datagateway-dataview/src/views/card/isis/isisDatasetsCardView.component.tsx @@ -159,11 +159,14 @@ const ISISDatasetsCardView = ( entityType="dataset" entityId={dataset.id} entityName={dataset.name} + entitySize={ + data ? sizeQueries[data.indexOf(dataset)]?.data ?? -1 : -1 + } /> ), ], - [classes.actionButtons, data] + [classes.actionButtons, data, sizeQueries] ); const moreInformation = React.useCallback( diff --git a/packages/datagateway-dataview/src/views/card/isis/isisInvestigationsCardView.component.tsx b/packages/datagateway-dataview/src/views/card/isis/isisInvestigationsCardView.component.tsx index 366a8f579..4ecf497a7 100644 --- a/packages/datagateway-dataview/src/views/card/isis/isisInvestigationsCardView.component.tsx +++ b/packages/datagateway-dataview/src/views/card/isis/isisInvestigationsCardView.component.tsx @@ -205,11 +205,14 @@ const ISISInvestigationsCardView = ( entityType="investigation" entityId={investigation.id} entityName={investigation.name} + entitySize={ + data ? sizeQueries[data.indexOf(investigation)]?.data ?? -1 : -1 + } /> ), ], - [classes.actionButtons, data] + [classes.actionButtons, data, sizeQueries] ); const moreInformation = React.useCallback( diff --git a/packages/datagateway-dataview/src/views/landing/isis/isisDatasetLanding.component.tsx b/packages/datagateway-dataview/src/views/landing/isis/isisDatasetLanding.component.tsx index 650ad1de0..416693d76 100644 --- a/packages/datagateway-dataview/src/views/landing/isis/isisDatasetLanding.component.tsx +++ b/packages/datagateway-dataview/src/views/landing/isis/isisDatasetLanding.component.tsx @@ -247,11 +247,14 @@ const LandingPage = (props: LandingPageProps): React.ReactElement => { allIds={[parseInt(datasetId)]} entityId={parseInt(datasetId)} /> - +
+ +
diff --git a/packages/datagateway-dataview/src/views/landing/isis/isisInvestigationLanding.component.tsx b/packages/datagateway-dataview/src/views/landing/isis/isisInvestigationLanding.component.tsx index b6ff71fd2..7661bf05d 100644 --- a/packages/datagateway-dataview/src/views/landing/isis/isisInvestigationLanding.component.tsx +++ b/packages/datagateway-dataview/src/views/landing/isis/isisInvestigationLanding.component.tsx @@ -520,11 +520,14 @@ const LandingPage = (props: LandingPageProps): React.ReactElement => { allIds={[dataset.id]} entityId={dataset.id} /> - +
+ +
))} diff --git a/packages/datagateway-dataview/src/views/table/datafileTable.component.tsx b/packages/datagateway-dataview/src/views/table/datafileTable.component.tsx index c56959554..39b65adf2 100644 --- a/packages/datagateway-dataview/src/views/table/datafileTable.component.tsx +++ b/packages/datagateway-dataview/src/views/table/datafileTable.component.tsx @@ -180,6 +180,7 @@ const DatafileTable = (props: DatafileTableProps): React.ReactElement => { entityId={rowData.id} entityName={(rowData as Datafile).location} variant="icon" + entitySize={(rowData as Datafile).fileSize ?? -1} /> ), ]} diff --git a/packages/datagateway-dataview/src/views/table/isis/isisDatafilesTable.component.tsx b/packages/datagateway-dataview/src/views/table/isis/isisDatafilesTable.component.tsx index 318b469bb..c14efee58 100644 --- a/packages/datagateway-dataview/src/views/table/isis/isisDatafilesTable.component.tsx +++ b/packages/datagateway-dataview/src/views/table/isis/isisDatafilesTable.component.tsx @@ -184,6 +184,7 @@ const ISISDatafilesTable = ( entityId={rowData.id} entityName={(rowData as Datafile).location} variant="icon" + entitySize={(rowData as Datafile).fileSize ?? -1} /> ), ]} diff --git a/packages/datagateway-dataview/src/views/table/isis/isisDatasetsTable.component.tsx b/packages/datagateway-dataview/src/views/table/isis/isisDatasetsTable.component.tsx index bfc0c563e..e7697e6af 100644 --- a/packages/datagateway-dataview/src/views/table/isis/isisDatasetsTable.component.tsx +++ b/packages/datagateway-dataview/src/views/table/isis/isisDatasetsTable.component.tsx @@ -205,6 +205,10 @@ const ISISDatasetsTable = ( entityId={rowData.id} entityName={rowData.name} variant="icon" + entitySize={ + sizeQueries[aggregatedData.indexOf(rowData as Dataset)]?.data ?? + -1 + } /> ), ]} diff --git a/packages/datagateway-dataview/src/views/table/isis/isisInvestigationsTable.component.tsx b/packages/datagateway-dataview/src/views/table/isis/isisInvestigationsTable.component.tsx index 332c37079..61537fbca 100644 --- a/packages/datagateway-dataview/src/views/table/isis/isisInvestigationsTable.component.tsx +++ b/packages/datagateway-dataview/src/views/table/isis/isisInvestigationsTable.component.tsx @@ -242,6 +242,10 @@ const ISISInvestigationsTable = ( entityId={rowData.id} entityName={rowData.name} variant="icon" + entitySize={ + sizeQueries[aggregatedData.indexOf(rowData as Investigation)] + ?.data ?? -1 + } /> ), ]} diff --git a/packages/datagateway-search/cypress/integration/searchPageContainer.spec.ts b/packages/datagateway-search/cypress/integration/searchPageContainer.spec.ts index 38b7de97d..a061ea37d 100644 --- a/packages/datagateway-search/cypress/integration/searchPageContainer.spec.ts +++ b/packages/datagateway-search/cypress/integration/searchPageContainer.spec.ts @@ -214,7 +214,11 @@ describe('SearchPageContainer Component', () => { cy.get('select[id="select-max-results"]', { timeout: 10000, - }).select('20'); + }) + .select('20') + .wait(['@investigations', '@investigations', '@investigationsCount'], { + timeout: 10000, + }); cy.get('[aria-label="card-buttons"]', { timeout: 10000 }).should( 'have.length', 20 @@ -222,7 +226,11 @@ describe('SearchPageContainer Component', () => { cy.get('select[id="select-max-results"]', { timeout: 10000, - }).select('30'); + }) + .select('30') + .wait(['@investigations', '@investigations', '@investigationsCount'], { + timeout: 10000, + }); cy.get('[aria-label="card-buttons"]', { timeout: 10000 }).should( 'have.length', 30 diff --git a/packages/datagateway-search/src/card/datasetSearchCardView.component.tsx b/packages/datagateway-search/src/card/datasetSearchCardView.component.tsx index 6c39da6b9..0248f353e 100644 --- a/packages/datagateway-search/src/card/datasetSearchCardView.component.tsx +++ b/packages/datagateway-search/src/card/datasetSearchCardView.component.tsx @@ -364,6 +364,9 @@ const DatasetCardView = (props: DatasetCardViewProps): React.ReactElement => { entityType="dataset" entityId={dataset.id} entityName={dataset.name} + entitySize={ + data ? sizeQueries[data.indexOf(dataset)]?.data ?? -1 : -1 + } /> ), @@ -378,7 +381,7 @@ const DatasetCardView = (props: DatasetCardViewProps): React.ReactElement => { ), ], - [classes.actionButtons, data, hierarchy] + [classes.actionButtons, data, hierarchy, sizeQueries] ); return ( diff --git a/packages/datagateway-search/src/card/investigationSearchCardView.component.tsx b/packages/datagateway-search/src/card/investigationSearchCardView.component.tsx index b2b2845c1..affb10241 100644 --- a/packages/datagateway-search/src/card/investigationSearchCardView.component.tsx +++ b/packages/datagateway-search/src/card/investigationSearchCardView.component.tsx @@ -365,12 +365,17 @@ const InvestigationCardView = ( entityType="investigation" entityId={investigation.id} entityName={investigation.name} + entitySize={ + data + ? sizeQueries[data.indexOf(investigation)]?.data ?? -1 + : -1 + } /> ), ] : [], - [classes.actionButtons, data, hierarchy] + [classes.actionButtons, data, hierarchy, sizeQueries] ); return (