16243: Added tests
[arvados-workbench2.git] / src / components / collection-panel-files / collection-panel-files.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from 'react';
6 import { TreeItem, TreeItemStatus } from '~/components/tree/tree';
7 import { FileTreeData } from '~/components/file-tree/file-tree-data';
8 import { FileTree } from '~/components/file-tree/file-tree';
9 import { CollectionFileType } from "~/models/collection-file";
10 import { IconButton, Grid, Typography, StyleRulesCallback, withStyles, WithStyles, CardHeader, Card, Button, Tooltip, CircularProgress } from '@material-ui/core';
11 import { CustomizeTableIcon } from '~/components/icon/icon';
12 import { DownloadIcon } from '~/components/icon/icon';
13 import { SearchInput } from '../search-input/search-input';
14
15 export interface CollectionPanelFilesProps {
16     items: Array<TreeItem<FileTreeData>>;
17     isWritable: boolean;
18     isLoading: boolean;
19     tooManyFiles: boolean;
20     onUploadDataClick: () => void;
21     onSearchChange: (searchValue: string) => void;
22     onItemMenuOpen: (event: React.MouseEvent<HTMLElement>, item: TreeItem<FileTreeData>, isWritable: boolean) => void;
23     onOptionsMenuOpen: (event: React.MouseEvent<HTMLElement>, isWritable: boolean) => void;
24     onSelectionToggle: (event: React.MouseEvent<HTMLElement>, item: TreeItem<FileTreeData>) => void;
25     onCollapseToggle: (id: string, status: TreeItemStatus) => void;
26     onFileClick: (id: string) => void;
27     loadFilesFunc: () => void;
28     currentItemUuid?: string;
29 }
30
31 export type CssRules = 'root' | 'cardSubheader' | 'nameHeader' | 'fileSizeHeader' | 'uploadIcon' | 'button' | 'centeredLabel' | 'cardHeaderContent' | 'cardHeaderContentTitle';
32
33 const styles: StyleRulesCallback<CssRules> = theme => ({
34     root: {
35         paddingBottom: theme.spacing.unit,
36         height: '100%'
37     },
38     cardSubheader: {
39         paddingTop: 0,
40         paddingBottom: 0,
41         minHeight: 8 * theme.spacing.unit,
42     },
43     cardHeaderContent: {
44         display: 'flex',
45         paddingRight: 2 * theme.spacing.unit,
46         justifyContent: 'space-between',
47     },
48     cardHeaderContentTitle: {
49         paddingLeft: theme.spacing.unit,
50         paddingTop: 2 * theme.spacing.unit,
51         paddingRight: 2 * theme.spacing.unit,
52     },
53     nameHeader: {
54         marginLeft: '75px'
55     },
56     fileSizeHeader: {
57         marginRight: '65px'
58     },
59     uploadIcon: {
60         transform: 'rotate(180deg)'
61     },
62     button: {
63         marginRight: -theme.spacing.unit,
64         marginTop: '8px'
65     },
66     centeredLabel: {
67         fontSize: '0.875rem',
68         textAlign: 'center'
69     },
70 });
71
72 export const CollectionPanelFilesComponent = ({ onItemMenuOpen, onSearchChange, onOptionsMenuOpen, onUploadDataClick, classes,
73     isWritable, isLoading, tooManyFiles, loadFilesFunc, ...treeProps }: CollectionPanelFilesProps & WithStyles<CssRules>) => {
74     const { useState, useEffect } = React;
75     const [searchValue, setSearchValue] = useState('');
76
77     useEffect(() => {
78         onSearchChange(searchValue);
79     }, [searchValue]);
80
81     return (<Card data-cy='collection-files-panel' className={classes.root}>
82         <CardHeader
83             title={
84                 <div className={classes.cardHeaderContent}>
85                     <span className={classes.cardHeaderContentTitle}>Files</span>
86                     <SearchInput
87                         value={searchValue}
88                         onSearch={setSearchValue} />
89                 </div>
90             }
91             className={classes.cardSubheader}
92             classes={{ action: classes.button }}
93             action={<>
94                 {isWritable &&
95                     <Button
96                         data-cy='upload-button'
97                         onClick={onUploadDataClick}
98                         variant='contained'
99                         color='primary'
100                         size='small'>
101                         <DownloadIcon className={classes.uploadIcon} />
102                     Upload data
103                 </Button>}
104                 {!tooManyFiles &&
105                     <Tooltip title="More options" disableFocusListener>
106                         <IconButton
107                             data-cy='collection-files-panel-options-btn'
108                             onClick={(ev) => onOptionsMenuOpen(ev, isWritable)}>
109                             <CustomizeTableIcon />
110                         </IconButton>
111                     </Tooltip>}
112             </>
113             } />
114         {tooManyFiles
115             ? <div className={classes.centeredLabel}>
116                 File listing may take some time, please click to browse: <Button onClick={loadFilesFunc}><DownloadIcon />Show files</Button>
117             </div>
118             : <>
119                 <Grid container justify="space-between">
120                     <Typography variant="caption" className={classes.nameHeader}>
121                         Name
122                 </Typography>
123                     <Typography variant="caption" className={classes.fileSizeHeader}>
124                         File size
125                 </Typography>
126                 </Grid>
127                 {isLoading
128                     ? <div className={classes.centeredLabel}><CircularProgress /></div>
129                     : <div style={{ height: 'calc(100% - 60px)' }}>
130                         <FileTree
131                             onMenuOpen={(ev, item) => onItemMenuOpen(ev, item, isWritable)}
132                             {...treeProps}
133                             items={treeProps.items.filter((item) => {
134                                 if (item.data.type === CollectionFileType.FILE) {
135                                     return item.data.name.toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
136                                 }
137
138                                 return true;
139                             })} /></div>}
140             </>
141         }
142     </Card>);
143 };
144
145 export const CollectionPanelFiles = withStyles(styles)(CollectionPanelFilesComponent);