Selaa lähdekoodia

[ui-storageBrowser]Implement multiselect for Storage browser table data (#3755)

* [ui-storageBrowser]Implement multiselect for Storage browser table data
Nidhi Bhat G 1 vuosi sitten
vanhempi
commit
c740708303

+ 0 - 10
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserRowActions/StorageBrowserRowActions.scss → desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserActions/StorageBrowserActions.scss

@@ -17,17 +17,7 @@
 @use 'variables' as vars;
 $action-dropdown-width: 214px;
 
-//TODO: Remove styling for cuix button
-.antd.cuix {
-  .hue-storage-browser__table-actions-btn {
-    box-shadow: none;
-    background-color: transparent;
-    border: none;
-  }
-}
-
 .hue-storage-browser__table-actions-dropdown {
-  align-items: center;
   width: $action-dropdown-width;
   @include mixins.hue-svg-icon__d3-conflict;
 }

+ 37 - 23
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserRowActions/StorageBrowserRowActions.test.tsx → desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserActions/StorageBrowserActions.test.tsx

@@ -18,7 +18,7 @@ import { render } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import '@testing-library/jest-dom';
 
-import StorageBrowserRowActions from './StorageBrowserRowActions';
+import StorageBrowserActions from './StorageBrowserActions';
 import { StorageBrowserTableData } from '../../../../reactComponents/FileChooser/types';
 
 describe('StorageBrowserRowActions', () => {
@@ -33,65 +33,79 @@ describe('StorageBrowserRowActions', () => {
     type: '',
     path: ''
   };
+  const mockRecord2: StorageBrowserTableData[] = [
+    {
+      name: 'test',
+      size: '0\u00a0bytes',
+      user: 'demo',
+      group: 'demo',
+      permission: 'drwxr-xr-x',
+      mtime: 'May 12, 2024 10:37 PM',
+      type: 'file',
+      path: ''
+    },
+    {
+      name: 'testFolder',
+      size: '0\u00a0bytes',
+      user: 'demo',
+      group: 'demo',
+      permission: 'drwxr-xr-x',
+      mtime: 'May 12, 2024 10:37 PM',
+      type: 'dir',
+      path: ''
+    }
+  ];
+
+  test('does not render view summary option when there are multiple records', async () => {
+    const user = userEvent.setup();
+    const { getByRole, queryByRole } = render(
+      <StorageBrowserActions selectedFiles={mockRecord2} />
+    );
+    await user.click(getByRole('button'));
+    expect(queryByRole('menuitem', { name: 'View Summary' })).toBeNull();
+  });
+
   test('renders view summary option when record is a hdfs file', async () => {
-    const onViewSummary = jest.fn();
     const user = userEvent.setup();
     mockRecord.path = '/user/demo/test';
     mockRecord.type = 'file';
     const { getByRole, queryByRole } = render(
-      <StorageBrowserRowActions rowData={mockRecord} onViewSummary={onViewSummary} />
+      <StorageBrowserActions selectedFiles={[mockRecord]} />
     );
     await user.click(getByRole('button'));
     expect(queryByRole('menuitem', { name: 'View Summary' })).not.toBeNull();
   });
 
   test('renders view summary option when record is a ofs file', async () => {
-    const onViewSummary = jest.fn();
     const user = userEvent.setup();
     mockRecord.path = 'ofs://demo/test';
     mockRecord.type = 'file';
     const { getByRole, queryByRole } = render(
-      <StorageBrowserRowActions rowData={mockRecord} onViewSummary={onViewSummary} />
+      <StorageBrowserActions selectedFiles={[mockRecord]} />
     );
     await user.click(getByRole('button'));
     expect(queryByRole('menuitem', { name: 'View Summary' })).not.toBeNull();
   });
 
   test('does not render view summary option when record is a hdfs folder', async () => {
-    const onViewSummary = jest.fn();
     const user = userEvent.setup();
     mockRecord.path = '/user/demo/test';
     mockRecord.type = 'dir';
     const { getByRole, queryByRole } = render(
-      <StorageBrowserRowActions rowData={mockRecord} onViewSummary={onViewSummary} />
+      <StorageBrowserActions selectedFiles={[mockRecord]} />
     );
     await user.click(getByRole('button'));
     expect(queryByRole('menuitem', { name: 'View Summary' })).toBeNull();
   });
 
   test('does not render view summary option when record is a an abfs file', async () => {
-    const onViewSummary = jest.fn();
     const user = userEvent.setup();
     mockRecord.path = 'abfs://demo/test';
     mockRecord.type = 'file';
     const { getByRole, queryByRole } = render(
-      <StorageBrowserRowActions rowData={mockRecord} onViewSummary={onViewSummary} />
+      <StorageBrowserActions selectedFiles={[mockRecord]} />
     );
     await user.click(getByRole('button'));
     expect(queryByRole('menuitem', { name: 'View Summary' })).toBeNull();
   });
-
-  test('calls onViewSummary after View summary menu option is clicked', async () => {
-    const onViewSummary = jest.fn();
-    const user = userEvent.setup();
-    mockRecord.path = '/user/demo/test';
-    mockRecord.type = 'file';
-    const { getByRole } = render(
-      <StorageBrowserRowActions rowData={mockRecord} onViewSummary={onViewSummary} />
-    );
-    await user.click(getByRole('button'));
-    expect(onViewSummary).not.toBeCalled();
-    await user.click(getByRole('menuitem', { name: 'View Summary' }));
-    expect(onViewSummary).toBeCalled();
-  });
 });

+ 42 - 30
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserRowActions/StorageBrowserRowActions.tsx → desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserActions/StorageBrowserActions.tsx

@@ -14,34 +14,40 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import React from 'react';
+import React, { useState } from 'react';
 import { Dropdown } from 'antd';
 import { MenuItemType } from 'antd/lib/menu/hooks/useItems';
 
-import { BorderlessButton } from 'cuix/dist/components/Button';
-import MoreVerticalIcon from '@cloudera/cuix-core/icons/react/MoreVerticalIcon';
+import Button from 'cuix/dist/components/Button';
+import DropDownIcon from '@cloudera/cuix-core/icons/react/DropdownIcon';
 import InfoIcon from '@cloudera/cuix-core/icons/react/InfoIcon';
 
 import { i18nReact } from '../../../../utils/i18nReact';
 import { StorageBrowserTableData } from '../../../../reactComponents/FileChooser/types';
-import { isHDFS, isOFS } from '../../../../../js/utils/storageBrowserUtils';
+import { isHDFS, isOFS } from '../../../../utils/storageBrowserUtils';
 
-import './StorageBrowserRowActions.scss';
+import SummaryModal from '../../SummaryModal/SummaryModal';
+
+import './StorageBrowserActions.scss';
 
 interface StorageBrowserRowActionsProps {
-  rowData: StorageBrowserTableData;
-  onViewSummary: (selectedFilePath: string) => void;
+  selectedFiles: StorageBrowserTableData[];
 }
 
-const StorageBrowserRowActions = ({
-  rowData,
-  onViewSummary
-}: StorageBrowserRowActionsProps): JSX.Element => {
+const StorageBrowserActions = ({ selectedFiles }: StorageBrowserRowActionsProps): JSX.Element => {
+  const [showSummaryModal, setShowSummaryModal] = useState<boolean>(false);
+  const [selectedFile, setSelectedFile] = useState<string>('');
+
   const { t } = i18nReact.useTranslation();
 
-  //TODO: handle multiple file selection scenarios
-  const isSummaryEnabled = () =>
-    (isHDFS(rowData.path) || isOFS(rowData.path)) && rowData.type === 'file';
+  const isSummaryEnabled = () => {
+    const selectedFile = selectedFiles[0];
+    return (
+      selectedFiles.length == 1 &&
+      (isHDFS(selectedFile.path) || isOFS(selectedFile.path)) &&
+      selectedFile.type === 'file'
+    );
+  };
 
   const getActions = () => {
     const actions: MenuItemType[] = [];
@@ -51,7 +57,8 @@ const StorageBrowserRowActions = ({
         icon: <InfoIcon />,
         label: t('View Summary'),
         onClick: () => {
-          onViewSummary(rowData.path);
+          setSelectedFile(selectedFiles[0].path);
+          setShowSummaryModal(true);
         }
       });
     }
@@ -59,22 +66,27 @@ const StorageBrowserRowActions = ({
   };
 
   return (
-    <Dropdown
-      overlayClassName="hue-storage-browser__table-actions-dropdown"
-      menu={{
-        items: getActions(),
-        className: 'hue-storage-browser__table-actions-menu'
-      }}
-      trigger={['click']}
-    >
-      <BorderlessButton
-        onClick={e => e.stopPropagation()}
-        className="hue-storage-browser__table-actions-btn"
-        data-event=""
-        icon={<MoreVerticalIcon />}
+    <>
+      <Dropdown
+        overlayClassName="hue-storage-browser__table-actions-dropdown"
+        menu={{
+          items: getActions(),
+          className: 'hue-storage-browser__table-actions-menu'
+        }}
+        trigger={['click', 'hover']}
+      >
+        <Button data-event="">
+          {t('Actions')}
+          <DropDownIcon />
+        </Button>
+      </Dropdown>
+      <SummaryModal
+        showModal={showSummaryModal}
+        path={selectedFile}
+        onClose={() => setShowSummaryModal(false)}
       />
-    </Dropdown>
+    </>
   );
 };
 
-export default StorageBrowserRowActions;
+export default StorageBrowserActions;

+ 0 - 27
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserTabContents/StorageBrowserTabContent.scss

@@ -16,9 +16,6 @@
 @use 'variables' as vars;
 @use 'mixins';
 
-$icon-margin: 5px;
-$action-dropdown-width: 214px;
-
 .antd.cuix {
   .hue-storage-browser-tabContent {
     margin: vars.$fluidx-spacing-s 0;
@@ -53,28 +50,4 @@ $action-dropdown-width: 214px;
     font-weight: 600;
     margin: 0 vars.$fluidx-spacing-xs 0 vars.$fluidx-spacing-s;
   }
-
-  .hue-storage-browser__actions-bar {
-    display: flex;
-    margin: vars.$fluidx-spacing-s;
-    justify-content: space-between;
-  }
-
-  .hue-storage-browser__search {
-    width: 30%;
-  }
-
-  .hue-storage-browser__actions-bar-right {
-    display: flex;
-    gap: vars.$fluidx-spacing-xs;
-
-    .cdp-icon-dropdown {
-      margin-left: $icon-margin;
-    }
-  }
-}
-
-.hue-storage-browser__actions-dropdown {
-  width: $action-dropdown-width;
-  @include mixins.hue-svg-icon__d3-conflict;
 }

+ 6 - 155
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserTabContents/StorageBrowserTabContent.tsx

@@ -15,27 +15,14 @@
 // limitations under the License.
 
 import React, { useState, useEffect } from 'react';
-import { Spin, Dropdown, Input } from 'antd';
-import type { MenuProps } from 'antd';
-import Button from 'cuix/dist/components/Button';
-import { PrimaryButton } from 'cuix/dist/components/Button';
+import { Spin } from 'antd';
 
 import { i18nReact } from '../../../../utils/i18nReact';
 import BucketIcon from '@cloudera/cuix-core/icons/react/BucketIcon';
-import CopyClipboardIcon from '@cloudera/cuix-core/icons/react/CopyClipboardIcon';
-import DataMovementIcon from '@cloudera/cuix-core/icons/react/DataMovementIcon';
-import DeleteIcon from '@cloudera/cuix-core/icons/react/DeleteIcon';
-import DownloadIcon from '@cloudera/cuix-core/icons/react/DownloadIcon';
-import DropDownIcon from '@cloudera/cuix-core/icons/react/DropdownIcon';
-import FolderIcon from '@cloudera/cuix-core/icons/react/ProjectIcon';
-import ImportIcon from '@cloudera/cuix-core/icons/react/ImportIcon';
-//TODO: Use cuix icon (Currently fileIcon does not exist in cuix)
-import { FileOutlined } from '@ant-design/icons';
 
 import PathBrowser from '../../../../reactComponents/FileChooser/PathBrowser/PathBrowser';
-import InputModal from '../../InputModal/InputModal';
 import StorageBrowserTable from '../StorageBrowserTable/StorageBrowserTable';
-import { fetchFiles, mkdir, touch } from '../../../../reactComponents/FileChooser/api';
+import { fetchFiles } from '../../../../reactComponents/FileChooser/api';
 import {
   PathAndFileData,
   StorageBrowserTableData,
@@ -69,103 +56,11 @@ const StorageBrowserTabContent = ({
   const [sortOrder, setSortOrder] = useState<SortOrder>(SortOrder.NONE);
   //TODO: Add filter functionality
   const [filterData] = useState<string>('');
-  const [showNewFolderModal, setShowNewFolderModal] = useState<boolean>(false);
-  const [showNewFileModal, setShowNewFileModal] = useState<boolean>(false);
+
   const [refreshKey, setRefreshKey] = useState<number>(0);
 
   const { t } = i18nReact.useTranslation();
 
-  const newActionsMenuItems: MenuProps['items'] = [
-    {
-      key: 'create',
-      type: 'group',
-      label: t('CREATE'),
-      children: [
-        {
-          icon: <FileOutlined />,
-          key: 'new_file',
-          label: t('New File'),
-          onClick: () => {
-            setShowNewFileModal(true);
-          }
-        },
-        {
-          icon: <FolderIcon />,
-          key: 'new_folder',
-          label: t('New Folder'),
-          onClick: () => {
-            setShowNewFolderModal(true);
-          }
-        }
-      ]
-    },
-    {
-      key: 'upload',
-      type: 'group',
-      label: t('UPLOAD'),
-      children: [
-        {
-          icon: <ImportIcon />,
-          key: 'upload',
-          label: t('New Upload')
-        }
-      ]
-    }
-  ];
-
-  const bulkActionsMenuItems: MenuProps['items'] = [
-    {
-      icon: <CopyClipboardIcon />,
-      key: 'copy',
-      label: t('Copy')
-    },
-    {
-      icon: <DataMovementIcon />,
-      key: 'move',
-      label: t('Move')
-    },
-    {
-      icon: <DownloadIcon />,
-      key: 'download',
-      label: t('Download')
-    },
-    {
-      icon: <DeleteIcon />,
-      key: 'delete',
-      label: t('Delete')
-    }
-  ];
-
-  const handleCreateNewFolder = (folderName: string) => {
-    setLoadingFiles(true);
-    mkdir(folderName, filePath)
-      .then(() => {
-        setRefreshKey(oldKey => oldKey + 1);
-      })
-      .catch(error => {
-        // eslint-disable-next-line no-restricted-syntax
-        console.log(error);
-      })
-      .finally(() => {
-        setLoadingFiles(false);
-      });
-  };
-
-  const handleCreateNewFile = (fileName: string) => {
-    setLoadingFiles(true);
-    touch(fileName, filePath)
-      .then(() => {
-        setRefreshKey(oldKey => oldKey + 1);
-      })
-      .catch(error => {
-        // eslint-disable-next-line no-restricted-syntax
-        console.log(error);
-      })
-      .finally(() => {
-        setLoadingFiles(false);
-      });
-  };
-
   useEffect(() => {
     setLoadingFiles(true);
     fetchFiles(filePath, pageSize, pageNumber, filterData, sortByColumn, sortOrder)
@@ -215,37 +110,6 @@ const StorageBrowserTabContent = ({
             showIcon={false}
           />
         </div>
-        <div className="hue-storage-browser__actions-bar">
-          <Input className="hue-storage-browser__search" placeholder={t('Search')} />
-          <div className="hue-storage-browser__actions-bar-right">
-            <Dropdown
-              overlayClassName="hue-storage-browser__actions-dropdown"
-              menu={{
-                items: bulkActionsMenuItems,
-                className: 'hue-storage-browser__action-menu'
-              }}
-              trigger={['hover', 'click']}
-            >
-              <Button data-event={''}>
-                {t('Bulk Actions')}
-                <DropDownIcon />
-              </Button>
-            </Dropdown>
-            <Dropdown
-              overlayClassName="hue-storage-browser__actions-dropdown"
-              menu={{
-                items: newActionsMenuItems,
-                className: 'hue-storage-browser__action-menu'
-              }}
-              trigger={['hover', 'click']}
-            >
-              <PrimaryButton data-event={''}>
-                {t('New')}
-                <DropDownIcon />
-              </PrimaryButton>
-            </Dropdown>
-          </div>
-        </div>
         <StorageBrowserTable
           dataSource={files}
           pageStats={pageStats}
@@ -257,22 +121,9 @@ const StorageBrowserTabContent = ({
           onSortOrderChange={setSortOrder}
           sortByColumn={sortByColumn}
           sortOrder={sortOrder}
-        />
-        <InputModal
-          title={t('Create New Folder')}
-          inputLabel={t('Enter Folder name here')}
-          submitText={t('Create')}
-          showModal={showNewFolderModal}
-          onSubmit={handleCreateNewFolder}
-          onClose={() => setShowNewFolderModal(false)}
-        />
-        <InputModal
-          title={t('Create New File')}
-          inputLabel={t('Enter File name here')}
-          submitText={t('Create')}
-          showModal={showNewFileModal}
-          onSubmit={handleCreateNewFile}
-          onClose={() => setShowNewFileModal(false)}
+          setRefreshKey={setRefreshKey}
+          setLoadingFiles={setLoadingFiles}
+          filePath={filePath}
         />
       </div>
     </Spin>

+ 26 - 0
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserTable/StorageBrowserTable.scss

@@ -18,8 +18,29 @@
 
 $cell-height: 40px;
 $table-placeholder-height: 100px;
+$action-dropdown-width: 214px;
+$icon-margin: 5px;
 
 .antd.cuix {
+  .hue-storage-browser__actions-bar {
+    display: flex;
+    margin: vars.$fluidx-spacing-s;
+    justify-content: space-between;
+  }
+
+  .hue-storage-browser__search {
+    width: 30%;
+  }
+
+  .hue-storage-browser__actions-bar-right {
+    display: flex;
+    gap: vars.$fluidx-spacing-xs;
+
+    .cdp-icon-dropdown {
+      margin-left: $icon-margin;
+    }
+  }
+
   .hue-storage-browser__table {
     margin: 0 vars.$fluidx-spacing-s;
 
@@ -58,3 +79,8 @@ $table-placeholder-height: 100px;
     text-transform: capitalize;
   }
 }
+
+.hue-storage-browser__actions-dropdown {
+  width: $action-dropdown-width;
+  @include mixins.hue-svg-icon__d3-conflict;
+}

+ 143 - 31
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserTable/StorageBrowserTable.tsx

@@ -15,29 +15,39 @@
 // limitations under the License.
 
 import React, { useEffect, useState } from 'react';
-import { i18nReact } from '../../../../utils/i18nReact';
-import Table from 'cuix/dist/components/Table';
 import { ColumnProps } from 'antd/lib/table';
+import { Dropdown, Input } from 'antd';
+import { MenuItemGroupType } from 'antd/lib/menu/hooks/useItems';
+import Tooltip from 'antd/es/tooltip';
+
 import FolderIcon from '@cloudera/cuix-core/icons/react/ProjectIcon';
 import SortAscending from '@cloudera/cuix-core/icons/react/SortAscendingIcon';
 import SortDescending from '@cloudera/cuix-core/icons/react/SortDescendingIcon';
+import DropDownIcon from '@cloudera/cuix-core/icons/react/DropdownIcon';
+import ImportIcon from '@cloudera/cuix-core/icons/react/ImportIcon';
 //TODO: Use cuix icon (Currently fileIcon does not exist in cuix)
 import { FileOutlined } from '@ant-design/icons';
 
+import { PrimaryButton } from 'cuix/dist/components/Button';
+import Table from 'cuix/dist/components/Table';
+
+import { i18nReact } from '../../../../utils/i18nReact';
+import huePubSub from '../../../../utils/huePubSub';
+import { mkdir, touch } from '../../../../reactComponents/FileChooser/api';
 import {
   PageStats,
   StorageBrowserTableData,
   SortOrder
 } from '../../../../reactComponents/FileChooser/types';
 import Pagination from '../../../../reactComponents/Pagination/Pagination';
-import StorageBrowserRowActions from '../StorageBrowserRowActions/StorageBrowserRowActions';
-import './StorageBrowserTable.scss';
-import Tooltip from 'antd/es/tooltip';
-import SummaryModal from '../../SummaryModal/SummaryModal';
+import StorageBrowserActions from '../StorageBrowserActions/StorageBrowserActions';
+import InputModal from '../../InputModal/InputModal';
 
+import './StorageBrowserTable.scss';
 interface StorageBrowserTableProps {
   className?: string;
   dataSource?: StorageBrowserTableData[];
+  filePath: string;
   onFilepathChange: (path: string) => void;
   onPageNumberChange: (pageNumber: number) => void;
   onPageSizeChange: (pageSize: number) => void;
@@ -48,6 +58,8 @@ interface StorageBrowserTableProps {
   sortByColumn: string;
   sortOrder: SortOrder;
   rowClassName?: string;
+  setRefreshKey: (value: number) => void;
+  setLoadingFiles: (value: boolean) => void;
   testId?: string;
 }
 
@@ -60,6 +72,7 @@ const defaultProps = {
 const StorageBrowserTable = ({
   className,
   dataSource,
+  filePath,
   onFilepathChange,
   onPageNumberChange,
   onPageSizeChange,
@@ -70,16 +83,56 @@ const StorageBrowserTable = ({
   pageSize,
   pageStats,
   rowClassName,
+  setRefreshKey,
+  setLoadingFiles,
   testId,
   ...restProps
 }: StorageBrowserTableProps): JSX.Element => {
   const [tableHeight, setTableHeight] = useState<number>();
-  const [showSummaryModal, setShowSummaryModal] = useState<boolean>(false);
-  //TODO: accept multiple files and folder select
-  const [selectedFile, setSelectedFile] = useState<string>('');
+  const [selectedFiles, setSelectedFiles] = useState<StorageBrowserTableData[]>([]);
+  const [showNewFolderModal, setShowNewFolderModal] = useState<boolean>(false);
+  const [showNewFileModal, setShowNewFileModal] = useState<boolean>(false);
 
   const { t } = i18nReact.useTranslation();
 
+  const newActionsMenuItems: MenuItemGroupType[] = [
+    {
+      key: 'create',
+      type: 'group',
+      label: t('CREATE'),
+      children: [
+        {
+          icon: <FileOutlined />,
+          key: 'new_file',
+          label: t('New File'),
+          onClick: () => {
+            setShowNewFileModal(true);
+          }
+        },
+        {
+          icon: <FolderIcon />,
+          key: 'new_folder',
+          label: t('New Folder'),
+          onClick: () => {
+            setShowNewFolderModal(true);
+          }
+        }
+      ]
+    },
+    {
+      key: 'upload',
+      type: 'group',
+      label: t('UPLOAD'),
+      children: [
+        {
+          icon: <ImportIcon />,
+          key: 'upload',
+          label: t('New Upload')
+        }
+      ]
+    }
+  ];
+
   const onColumnTitleClicked = (columnClicked: string) => {
     if (columnClicked === sortByColumn) {
       if (sortOrder === SortOrder.NONE) {
@@ -95,11 +148,6 @@ const StorageBrowserTable = ({
     }
   };
 
-  const onViewSummary = (filePath: string) => {
-    setSelectedFile(filePath);
-    setShowSummaryModal(true);
-  };
-
   const getColumns = (file: StorageBrowserTableData) => {
     const columns: ColumnProps<StorageBrowserTableData>[] = [];
     for (const [key] of Object.entries(file)) {
@@ -135,20 +183,11 @@ const StorageBrowserTable = ({
             <span className="hue-storage-browser__table-cell-name">{record.name}</span>
           </Tooltip>
         );
-      } else {
-        column.width = key === 'mtime' ? '15%' : '9%';
+      } else if (key === 'mtime') {
+        column.width = '15%';
       }
       columns.push(column);
     }
-    columns.push({
-      dataIndex: 'actions',
-      title: '',
-      key: 'actions',
-      render: (_, record: StorageBrowserTableData) => (
-        <StorageBrowserRowActions onViewSummary={onViewSummary} rowData={record} />
-      ),
-      width: '4%'
-    });
     return columns.filter(col => col.dataIndex !== 'type' && col.dataIndex !== 'path');
   };
 
@@ -164,6 +203,12 @@ const StorageBrowserTable = ({
     };
   };
 
+  const rowSelection = {
+    onChange: (selectedRowKeys: React.Key[], selectedRows: StorageBrowserTableData[]) => {
+      setSelectedFiles(selectedRows);
+    }
+  };
+
   //pagination related functions handled by parent
   const onPreviousPageButtonClicked = (previousPageNumber: number) => {
     //If previous page does not exists api returns 0
@@ -175,6 +220,36 @@ const StorageBrowserTable = ({
     onPageNumberChange(nextPageNumber === 0 ? numPages : nextPageNumber);
   };
 
+  const handleCreateNewFolder = (folderName: string) => {
+    setLoadingFiles(true);
+    mkdir(folderName, filePath)
+      .then(() => {
+        setRefreshKey(oldKey => oldKey + 1);
+      })
+      .catch(error => {
+        huePubSub.publish('hue.error', error);
+        setShowNewFolderModal(false);
+      })
+      .finally(() => {
+        setLoadingFiles(false);
+      });
+  };
+
+  const handleCreateNewFile = (fileName: string) => {
+    setLoadingFiles(true);
+    touch(fileName, filePath)
+      .then(() => {
+        setRefreshKey(oldKey => oldKey + 1);
+      })
+      .catch(error => {
+        huePubSub.publish('hue.error', error);
+        setShowNewFileModal(false);
+      })
+      .finally(() => {
+        setLoadingFiles(false);
+      });
+  };
+
   useEffect(() => {
     //TODO: handle table resize
     const calculateTableHeight = () => {
@@ -210,6 +285,26 @@ const StorageBrowserTable = ({
   if (dataSource && pageStats) {
     return (
       <>
+        <div className="hue-storage-browser__actions-bar">
+          <Input className="hue-storage-browser__search" placeholder={t('Search')} />
+          <div className="hue-storage-browser__actions-bar-right">
+            <StorageBrowserActions selectedFiles={selectedFiles} />
+            <Dropdown
+              overlayClassName="hue-storage-browser__actions-dropdown"
+              menu={{
+                items: newActionsMenuItems,
+                className: 'hue-storage-browser__action-menu'
+              }}
+              trigger={['hover', 'click']}
+            >
+              <PrimaryButton data-event={''}>
+                {t('New')}
+                <DropDownIcon />
+              </PrimaryButton>
+            </Dropdown>
+          </div>
+        </div>
+
         <Table
           className={className}
           columns={getColumns(dataSource[0])}
@@ -218,11 +313,16 @@ const StorageBrowserTable = ({
           pagination={false}
           rowClassName={rowClassName}
           rowKey={(record, index) => record.path + '' + index}
+          rowSelection={{
+            type: 'checkbox',
+            ...rowSelection
+          }}
           scroll={{ y: tableHeight }}
           data-testid={`${testId}`}
           locale={locale}
           {...restProps}
-        ></Table>
+        />
+
         <Pagination
           onNextPageButtonClicked={onNextPageButtonClicked}
           onPageNumberChange={onPageNumberChange}
@@ -231,11 +331,23 @@ const StorageBrowserTable = ({
           pageSize={pageSize}
           pageStats={pageStats}
         />
-        <SummaryModal
-          showModal={showSummaryModal}
-          path={selectedFile}
-          onClose={() => setShowSummaryModal(false)}
-        ></SummaryModal>
+
+        <InputModal
+          title={t('Create New Folder')}
+          inputLabel={t('Enter Folder name here')}
+          submitText={t('Create')}
+          showModal={showNewFolderModal}
+          onSubmit={handleCreateNewFolder}
+          onClose={() => setShowNewFolderModal(false)}
+        />
+        <InputModal
+          title={t('Create New File')}
+          inputLabel={t('Enter File name here')}
+          submitText={t('Create')}
+          showModal={showNewFileModal}
+          onSubmit={handleCreateNewFile}
+          onClose={() => setShowNewFileModal(false)}
+        />
       </>
     );
   } else {