Browse Source

[ui-storagebrowser] refactor storage browser and folder structure (#3914)

[ui-storagebrowser] refactor storage browser and folder structure
Ram Prasad Agarwal 11 months ago
parent
commit
23315717d9
29 changed files with 237 additions and 237 deletions
  1. 1 1
      apps/filebrowser/src/filebrowser/api.py
  2. 12 16
      desktop/core/src/desktop/js/apps/storageBrowser/FileChooserModal/FileChooserModal.tsx
  3. 4 4
      desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage.scss
  4. 6 6
      desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage.tsx
  5. 1 2
      desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserTab/StorageBrowserTab.scss
  6. 13 16
      desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserTab/StorageBrowserTab.tsx
  7. 1 1
      desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageBrowserActions/StorageBrowserActions.scss
  8. 0 0
      desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageBrowserActions/StorageBrowserActions.test.tsx
  9. 3 3
      desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageBrowserActions/StorageBrowserActions.tsx
  10. 0 0
      desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageDirectoryPage.scss
  11. 33 33
      desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageDirectoryPage.tsx
  12. 20 4
      desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/SummaryModal/SummaryModal.scss
  13. 18 20
      desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/SummaryModal/SummaryModal.test.tsx
  14. 103 0
      desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/SummaryModal/SummaryModal.tsx
  15. 0 100
      desktop/core/src/desktop/js/apps/storageBrowser/SummaryModal/SummaryModal.tsx
  16. 1 1
      desktop/core/src/desktop/js/reactComponents/FileChooser/FileChooserModal/FileChooserModal.tsx
  17. 0 1
      desktop/core/src/desktop/js/reactComponents/FileChooser/api.ts
  18. 11 22
      desktop/core/src/desktop/js/reactComponents/FileChooser/types.ts
  19. 1 0
      desktop/core/src/desktop/js/reactComponents/PathBrowser/Breadcrumb/Breadcrumb.scss
  20. 0 0
      desktop/core/src/desktop/js/reactComponents/PathBrowser/Breadcrumb/Breadcrumb.tsx
  21. 0 0
      desktop/core/src/desktop/js/reactComponents/PathBrowser/OverflowTooltip.tsx
  22. 0 0
      desktop/core/src/desktop/js/reactComponents/PathBrowser/OverflowingItem.scss
  23. 0 0
      desktop/core/src/desktop/js/reactComponents/PathBrowser/OverflowingItem.tsx
  24. 0 0
      desktop/core/src/desktop/js/reactComponents/PathBrowser/PathBrowser.scss
  25. 0 0
      desktop/core/src/desktop/js/reactComponents/PathBrowser/PathBrowser.test.tsx
  26. 4 5
      desktop/core/src/desktop/js/reactComponents/PathBrowser/PathBrowser.tsx
  27. 0 0
      desktop/core/src/desktop/js/reactComponents/PathBrowser/PathBrowser.util.test.ts
  28. 4 1
      desktop/core/src/desktop/js/reactComponents/PathBrowser/PathBrowser.util.ts
  29. 1 1
      desktop/core/src/desktop/js/reactComponents/imports.js

+ 1 - 1
apps/filebrowser/src/filebrowser/api.py

@@ -656,7 +656,7 @@ def content_summary(request):
     replication_factor = request.fs.stats(path)['replication']
 
     content_summary.summary.update({'replication': replication_factor})
-    response['summary'] = content_summary.summary
+    response = content_summary.summary
   except Exception:
     return HttpResponse(f'Failed to fetch content summary for path: {path}', status=500)
 

+ 12 - 16
desktop/core/src/desktop/js/apps/storageBrowser/FileChooserModal/FileChooserModal.tsx

@@ -28,8 +28,8 @@ import useDebounce from '../../../utils/useDebounce';
 import useLoadData from '../../../utils/hooks/useLoadData';
 
 import { ListDirectory } from '../../../reactComponents/FileChooser/types';
-import { VIEWFILES_API_URl } from '../../../reactComponents/FileChooser/api';
-import PathBrowser from '../../../reactComponents/FileChooser/PathBrowser/PathBrowser';
+import { LIST_DIRECTORY_API_URL } from '../../../reactComponents/FileChooser/api';
+import PathBrowser from '../../../reactComponents/PathBrowser/PathBrowser';
 
 import './FileChooserModal.scss';
 
@@ -66,16 +66,14 @@ const FileChooserModal = ({
     setDestPath(sourcePath);
   }, [sourcePath]);
 
-  const { data: filesData, loading } = useLoadData<ListDirectory>(
-    `${VIEWFILES_API_URl}${destPath}`,
-    {
-      params: {
-        pagesize: '1000',
-        filter: searchTerm
-      },
-      skip: destPath === '' || destPath === undefined
-    }
-  );
+  const { data: filesData, loading } = useLoadData<ListDirectory>(LIST_DIRECTORY_API_URL, {
+    params: {
+      path: destPath,
+      pagesize: '1000',
+      filter: searchTerm
+    },
+    skip: destPath === '' || destPath === undefined || !showModal
+  });
 
   const tableData: FileChooserTableData[] = useMemo(() => {
     if (!filesData?.files) {
@@ -139,9 +137,7 @@ const FileChooserModal = ({
       okText={submitText}
       title={title}
       open={showModal}
-      onCancel={() => {
-        onClose();
-      }}
+      onCancel={onClose}
       onOk={() => {
         onSubmit(destPath);
         onClose();
@@ -169,7 +165,7 @@ const FileChooserModal = ({
         <Spin spinning={loading}>
           <Table
             className="hue-filechooser-modal__table"
-            dataSource={tableData?.slice(2)}
+            dataSource={tableData}
             pagination={false}
             columns={getColumns(tableData[0] ?? {})}
             rowKey={(record, index) => record.path + index}

+ 4 - 4
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserPage.scss → desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage.scss

@@ -15,11 +15,11 @@
 // limitations under the License.
 
 @use 'variables' as vars;
-@import '../../../components/styles/mixins';
+@use 'mixins';
 
 .hue-storage-browser.antd.cuix {
-  @include fillAbsolute;
-  @include flexRowLayout;
+  @include mixins.fillAbsolute;
+  @include mixins.flexRowLayout;
 
   height: 100%;
 
@@ -50,4 +50,4 @@
       }
     }
   }
-}
+}

+ 6 - 6
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserPage.tsx → desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage.tsx

@@ -19,13 +19,13 @@ import { Tabs, Spin } from 'antd';
 
 import DataBrowserIcon from '@cloudera/cuix-core/icons/react/DataBrowserIcon';
 
-import { i18nReact } from '../../../utils/i18nReact';
-import CommonHeader from '../../../reactComponents/CommonHeader/CommonHeader';
-import StorageBrowserTabContent from './StorageBrowserTabContents/StorageBrowserTabContent';
-import { ApiFileSystem, FILESYSTEMS_API_URL } from '../../../reactComponents/FileChooser/api';
+import { i18nReact } from '../../utils/i18nReact';
+import CommonHeader from '../../reactComponents/CommonHeader/CommonHeader';
+import StorageBrowserTab from './StorageBrowserTab/StorageBrowserTab';
+import { ApiFileSystem, FILESYSTEMS_API_URL } from '../../reactComponents/FileChooser/api';
 
 import './StorageBrowserPage.scss';
-import useLoadData from '../../../utils/hooks/useLoadData';
+import useLoadData from '../../utils/hooks/useLoadData';
 
 const StorageBrowserPage = (): JSX.Element => {
   const { t } = i18nReact.useTranslation();
@@ -42,7 +42,7 @@ const StorageBrowserPage = (): JSX.Element => {
           items={fileSystems?.map(system => ({
             label: system.file_system.toUpperCase(),
             key: system.file_system + '_tab',
-            children: <StorageBrowserTabContent homeDir={system.user_home_directory} />
+            children: <StorageBrowserTab homeDir={system.user_home_directory} />
           }))}
         />
       </Spin>

+ 1 - 2
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserTabContents/StorageBrowserTabContent.scss → desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserTab/StorageBrowserTab.scss

@@ -14,7 +14,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 @use 'variables' as vars;
-@use 'mixins';
 
 .antd.cuix {
   .hue-storage-browser-tab-content {
@@ -51,4 +50,4 @@
     flex: 0 0 auto;
     font-weight: 600;
   }
-}
+}

+ 13 - 16
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserTabContents/StorageBrowserTabContent.tsx → desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserTab/StorageBrowserTab.tsx

@@ -17,19 +17,19 @@
 import React, { useState } from 'react';
 import { Spin } from 'antd';
 
-import { i18nReact } from '../../../../utils/i18nReact';
+import { i18nReact } from '../../../utils/i18nReact';
 import BucketIcon from '@cloudera/cuix-core/icons/react/BucketIcon';
 
-import PathBrowser from '../../../../reactComponents/FileChooser/PathBrowser/PathBrowser';
-import StorageBrowserTable from '../StorageBrowserTable/StorageBrowserTable';
-import { FILE_STATS_API_URL } from '../../../../reactComponents/FileChooser/api';
-import { BrowserViewType, FileStats } from '../../../../reactComponents/FileChooser/types';
-import useLoadData from '../../../../utils/hooks/useLoadData';
+import PathBrowser from '../../../reactComponents/PathBrowser/PathBrowser';
+import StorageDirectoryPage from '../StorageDirectoryPage/StorageDirectoryPage';
+import { FILE_STATS_API_URL } from '../../../reactComponents/FileChooser/api';
+import { BrowserViewType, FileStats } from '../../../reactComponents/FileChooser/types';
+import useLoadData from '../../../utils/hooks/useLoadData';
 
-import './StorageBrowserTabContent.scss';
-import StorageFilePage from '../../StorageFilePage/StorageFilePage';
+import './StorageBrowserTab.scss';
+import StorageFilePage from '../StorageFilePage/StorageFilePage';
 
-interface StorageBrowserTabContentProps {
+interface StorageBrowserTabProps {
   homeDir: string;
   testId?: string;
 }
@@ -38,10 +38,7 @@ const defaultProps = {
   testId: 'hue-storage-browser-tab-content'
 };
 
-const StorageBrowserTabContent = ({
-  homeDir,
-  testId
-}: StorageBrowserTabContentProps): JSX.Element => {
+const StorageBrowserTab = ({ homeDir, testId }: StorageBrowserTabProps): JSX.Element => {
   const [filePath, setFilePath] = useState<string>(homeDir);
   const fileName = filePath?.split('/')?.pop() ?? '';
 
@@ -76,7 +73,7 @@ const StorageBrowserTabContent = ({
           />
         </div>
         {fileStats?.type === BrowserViewType.dir && (
-          <StorageBrowserTable fileStats={fileStats} onFilePathChange={setFilePath} />
+          <StorageDirectoryPage fileStats={fileStats} onFilePathChange={setFilePath} />
         )}
         {fileStats?.type === BrowserViewType.file && (
           <StorageFilePage fileName={fileName} fileStats={fileStats} />
@@ -86,6 +83,6 @@ const StorageBrowserTabContent = ({
   );
 };
 
-StorageBrowserTabContent.defaultProps = defaultProps;
+StorageBrowserTab.defaultProps = defaultProps;
 
-export default StorageBrowserTabContent;
+export default StorageBrowserTab;

+ 1 - 1
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserActions/StorageBrowserActions.scss → desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageBrowserActions/StorageBrowserActions.scss

@@ -14,7 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 @use 'mixins';
-@use 'variables' as vars;
+
 $action-dropdown-width: 214px;
 
 .hue-storage-browser__table-actions-dropdown {

+ 0 - 0
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserActions/StorageBrowserActions.test.tsx → desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageBrowserActions/StorageBrowserActions.test.tsx


+ 3 - 3
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserActions/StorageBrowserActions.tsx → desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageBrowserActions/StorageBrowserActions.tsx

@@ -25,7 +25,6 @@ import DuplicateIcon from '@cloudera/cuix-core/icons/react/DuplicateIcon';
 import CopyClipboardIcon from '@cloudera/cuix-core/icons/react/CopyClipboardIcon';
 
 import { i18nReact } from '../../../../utils/i18nReact';
-import { StorageBrowserTableData } from '../../../../reactComponents/FileChooser/types';
 import {
   isHDFS,
   isOFS,
@@ -49,15 +48,16 @@ import {
 import huePubSub from '../../../../utils/huePubSub';
 import useSaveData from '../../../../utils/hooks/useSaveData';
 
-import SummaryModal from '../../SummaryModal/SummaryModal';
+import SummaryModal from '../SummaryModal/SummaryModal';
 import InputModal from '../../InputModal/InputModal';
 import FileChooserModal from '../../FileChooserModal/FileChooserModal';
 
 import './StorageBrowserActions.scss';
+import { StorageDirectoryTableData } from '../../../../reactComponents/FileChooser/types';
 
 interface StorageBrowserRowActionsProps {
   currentPath: string;
-  selectedFiles: StorageBrowserTableData[];
+  selectedFiles: StorageDirectoryTableData[];
   onSuccessfulAction: () => void;
   setLoadingFiles: (value: boolean) => void;
 }

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


+ 33 - 33
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserTable/StorageBrowserTable.tsx → desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageDirectoryPage.tsx

@@ -30,34 +30,34 @@ 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 useDebounce from '../../../../utils/useDebounce';
+import { i18nReact } from '../../../utils/i18nReact';
+import huePubSub from '../../../utils/huePubSub';
+import useDebounce from '../../../utils/useDebounce';
 
 import {
   LIST_DIRECTORY_API_URL,
   CREATE_DIRECTORY_API_URL,
   CREATE_FILE_API_URL
-} from '../../../../reactComponents/FileChooser/api';
+} from '../../../reactComponents/FileChooser/api';
 import {
-  StorageBrowserTableData,
   SortOrder,
   ListDirectory,
   FileStats,
-  BrowserViewType
-} from '../../../../reactComponents/FileChooser/types';
-import Pagination from '../../../../reactComponents/Pagination/Pagination';
-import StorageBrowserActions from '../StorageBrowserActions/StorageBrowserActions';
-import InputModal from '../../InputModal/InputModal';
-import formatBytes from '../../../../utils/formatBytes';
-import useSaveData from '../../../../utils/hooks/useSaveData';
-
-import './StorageBrowserTable.scss';
-import { formatTimestamp } from '../../../../utils/dateTimeUtils';
-import useLoadData from '../../../../utils/hooks/useLoadData';
-import { DEFAULT_PAGE_SIZE } from '../../../../utils/constants/storageBrowser';
-
-interface StorageBrowserTableProps {
+  BrowserViewType,
+  StorageDirectoryTableData
+} from '../../../reactComponents/FileChooser/types';
+import Pagination from '../../../reactComponents/Pagination/Pagination';
+import StorageBrowserActions from './StorageBrowserActions/StorageBrowserActions';
+import InputModal from '../InputModal/InputModal';
+import formatBytes from '../../../utils/formatBytes';
+import useSaveData from '../../../utils/hooks/useSaveData';
+
+import './StorageDirectoryPage.scss';
+import { formatTimestamp } from '../../../utils/dateTimeUtils';
+import useLoadData from '../../../utils/hooks/useLoadData';
+import { DEFAULT_PAGE_SIZE } from '../../../utils/constants/storageBrowser';
+
+interface StorageDirectoryPageProps {
   fileStats: FileStats;
   onFilePathChange: (path: string) => void;
   className?: string;
@@ -71,17 +71,17 @@ const defaultProps = {
   testId: 'hue-storage-browser__table'
 };
 
-const StorageBrowserTable = ({
+const StorageDirectoryPage = ({
   fileStats,
   onFilePathChange,
   className,
   rowClassName,
   testId,
   ...restProps
-}: StorageBrowserTableProps): JSX.Element => {
+}: StorageDirectoryPageProps): JSX.Element => {
   const [loadingFiles, setLoadingFiles] = useState<boolean>(false);
   const [tableHeight, setTableHeight] = useState<number>(100);
-  const [selectedFiles, setSelectedFiles] = useState<StorageBrowserTableData[]>([]);
+  const [selectedFiles, setSelectedFiles] = useState<StorageDirectoryTableData[]>([]);
   const [showNewFolderModal, setShowNewFolderModal] = useState<boolean>(false);
   const [showNewFileModal, setShowNewFileModal] = useState<boolean>(false);
 
@@ -112,14 +112,14 @@ const StorageBrowserTable = ({
       fileStats.type !== BrowserViewType.dir
   });
 
-  const tableData: StorageBrowserTableData[] = useMemo(() => {
+  const tableData: StorageDirectoryTableData[] = useMemo(() => {
     if (!filesData?.files) {
       return [];
     }
 
     return filesData?.files?.map(file => ({
       name: file.path.split('/').pop() ?? '',
-      size: formatBytes(file.size),
+      size: file.type === BrowserViewType.file ? formatBytes(file.size) : '',
       user: file.user,
       group: file.group,
       permission: file.rwx,
@@ -183,10 +183,10 @@ const StorageBrowserTable = ({
     }
   };
 
-  const getColumns = (file: StorageBrowserTableData) => {
-    const columns: ColumnProps<StorageBrowserTableData>[] = [];
+  const getColumns = (file: StorageDirectoryTableData) => {
+    const columns: ColumnProps<StorageDirectoryTableData>[] = [];
     for (const key of Object.keys(file)) {
-      const column: ColumnProps<StorageBrowserTableData> = {
+      const column: ColumnProps<StorageDirectoryTableData> = {
         dataIndex: key,
         title: (
           <div
@@ -209,7 +209,7 @@ const StorageBrowserTable = ({
       };
       if (key === 'name') {
         column.width = '40%';
-        column.render = (_, record: StorageBrowserTableData) => (
+        column.render = (_, record: StorageDirectoryTableData) => (
           <Tooltip title={record.name} mouseEnterDelay={1.5}>
             <span className="hue-storage-browser__table-cell-icon">
               {record.type === 'dir' ? <FolderIcon /> : <FileOutlined />}
@@ -229,7 +229,7 @@ const StorageBrowserTable = ({
     );
   };
 
-  const onRowClicked = (record: StorageBrowserTableData) => {
+  const onRowClicked = (record: StorageDirectoryTableData) => {
     return {
       onClick: () => {
         onFilePathChange(record.path);
@@ -241,7 +241,7 @@ const StorageBrowserTable = ({
   };
 
   const rowSelection = {
-    onChange: (_: React.Key[], selectedRows: StorageBrowserTableData[]) => {
+    onChange: (_: React.Key[], selectedRows: StorageDirectoryTableData[]) => {
       setSelectedFiles(selectedRows);
     }
   };
@@ -357,7 +357,7 @@ const StorageBrowserTable = ({
         <Table
           className={className}
           columns={getColumns(tableData[0] ?? {})}
-          dataSource={tableData?.slice(2)}
+          dataSource={tableData}
           onRow={onRowClicked}
           pagination={false}
           rowClassName={rowClassName}
@@ -404,5 +404,5 @@ const StorageBrowserTable = ({
   );
 };
 
-StorageBrowserTable.defaultProps = defaultProps;
-export default StorageBrowserTable;
+StorageDirectoryPage.defaultProps = defaultProps;
+export default StorageDirectoryPage;

+ 20 - 4
desktop/core/src/desktop/js/apps/storageBrowser/SummaryModal/SummaryModal.scss → desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/SummaryModal/SummaryModal.scss

@@ -16,11 +16,27 @@
 @use 'variables' as vars;
 
 .antd.cuix {
-  .hue-summary-modal__row {
-    margin-bottom: vars.$fluidx-spacing-s;
+  .hue-summary-modal__grid {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    gap: 16px;
 
-    &:last-child {
-      margin-bottom: 0;
+    &__summary-item {
+      display: flex;
+      flex-direction: column;
+      font-size: 12px;
+
+      &__label {
+        text-transform: uppercase;
+        letter-spacing: 0.3px;
+        color: vars.$fluidx-gray-700;
+      }
+
+      &__value {
+        height: 12px;
+        align-self: stretch;
+        color: vars.$fluidx-gray-900;
+      }
     }
   }
 }

+ 18 - 20
desktop/core/src/desktop/js/apps/storageBrowser/SummaryModal/SummaryModal.test.tsx → desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/SummaryModal/SummaryModal.test.tsx

@@ -17,12 +17,12 @@ import React from 'react';
 import { waitFor, screen, fireEvent, render } from '@testing-library/react';
 import '@testing-library/jest-dom';
 
-import { get } from '../../../api/utils';
-import formatBytes from '../../../utils/formatBytes';
+import { get } from '../../../../api/utils';
+import formatBytes from '../../../../utils/formatBytes';
 import SummaryModal from './SummaryModal';
 
 // Mock the `get` function
-jest.mock('../../../api/utils', () => ({
+jest.mock('../../../../api/utils', () => ({
   get: jest.fn()
 }));
 
@@ -42,19 +42,17 @@ describe('SummaryModal', () => {
   });
 
   const mockSummary = {
-    summary: {
-      directoryCount: 0,
-      ecPolicy: 'Replicated',
-      fileCount: 1,
-      length: 0,
-      quota: -1,
-      spaceConsumed: 0,
-      spaceQuota: -1,
-      typeQuota: -1,
-      replication: 3
-    }
+    directoryCount: 0,
+    ecPolicy: 'Replicated',
+    fileCount: 1,
+    length: 0,
+    quota: -1,
+    spaceConsumed: 0,
+    spaceQuota: -1,
+    typeQuota: -1,
+    replication: 3
   };
-  test('renders path of file in title', async () => {
+  it('should render path of file in title', async () => {
     const { getByText } = render(
       <SummaryModal showModal={true} onClose={() => {}} path="some/path" />
     );
@@ -63,17 +61,17 @@ describe('SummaryModal', () => {
     });
   });
 
-  test('renders summary content after successful data fetching', async () => {
+  it('should render summary content after successful data fetching', async () => {
     const { getByText, getAllByText } = render(
       <SummaryModal showModal={true} onClose={() => {}} path="some/path" />
     );
     await waitFor(async () => {
-      expect(getByText('DISKSPACE CONSUMED')).toBeInTheDocument();
-      expect(getAllByText(formatBytes(mockSummary.summary.spaceConsumed))[0]).toBeInTheDocument();
+      expect(getByText('Diskspace Consumed')).toBeInTheDocument();
+      expect(getAllByText(formatBytes(mockSummary.spaceConsumed))[0]).toBeInTheDocument();
     });
   });
 
-  test('renders space consumed in Bytes after the values are formatted', async () => {
+  it('should render space consumed in Bytes after the values are formatted', async () => {
     render(<SummaryModal path={'/user/demo'} showModal={true} onClose={() => {}} />);
     const spaceConsumed = await screen.findAllByText('0 Byte');
     await waitFor(() => {
@@ -81,7 +79,7 @@ describe('SummaryModal', () => {
     });
   });
 
-  test('should call onClose function when close button is clicked', async () => {
+  it('should call onClose function when close button is clicked', async () => {
     const mockOnClose = jest.fn();
     const { getByText } = render(
       <SummaryModal showModal={true} onClose={mockOnClose} path="some/path" />

+ 103 - 0
desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/SummaryModal/SummaryModal.tsx

@@ -0,0 +1,103 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import React from 'react';
+import Modal from 'cuix/dist/components/Modal';
+import { Spin } from 'antd';
+
+import huePubSub from '../../../../utils/huePubSub';
+import { i18nReact } from '../../../../utils/i18nReact';
+import formatBytes from '../../../../utils/formatBytes';
+import useLoadData from '../../../../utils/hooks/useLoadData';
+import { CONTENT_SUMMARY_API_URL } from '../../../../reactComponents/FileChooser/api';
+import { ContentSummary } from '../../../../reactComponents/FileChooser/types';
+
+import './SummaryModal.scss';
+
+interface SummaryModalProps {
+  path: string;
+  showModal: boolean;
+  onClose: () => void;
+}
+
+const SummaryModal = ({ showModal, onClose, path }: SummaryModalProps): JSX.Element => {
+  const { t } = i18nReact.useTranslation();
+
+  const { data: responseSummary, loading } = useLoadData<ContentSummary>(CONTENT_SUMMARY_API_URL, {
+    params: { path },
+    onError: error => {
+      huePubSub.publish('hue.error', error);
+    },
+    skip: path === '' || path === undefined || !showModal
+  });
+
+  const summary = [
+    {
+      key: 'diskspaceConsumed',
+      label: t('Diskspace Consumed'),
+      value: formatBytes(responseSummary?.spaceConsumed)
+    },
+    { key: 'bytesUsed', label: t('Bytes Used'), value: formatBytes(responseSummary?.length) },
+    {
+      key: 'namespaceQuota',
+      label: t('Namespace Quota'),
+      value: formatBytes(responseSummary?.quota)
+    },
+    {
+      key: 'diskspaceQuota',
+      label: t('Diskspace Quota'),
+      value: formatBytes(responseSummary?.spaceQuota)
+    },
+    {
+      key: 'replicationFactor',
+      label: t('Replication Factor'),
+      value: responseSummary?.replication
+    },
+    { key: 'blank', label: '', value: '' },
+    {
+      key: 'numberOfDirectories',
+      label: t('Number of Directories'),
+      value: responseSummary?.directoryCount
+    },
+    { key: 'numberOfFiles', label: t('Number of Files'), value: responseSummary?.fileCount }
+  ];
+
+  //TODO:Handle long modal title
+  return (
+    <Modal
+      className="hue-summary-modal cuix antd"
+      okText={t('Close')}
+      onOk={onClose}
+      open={showModal}
+      title={t('Summary for ') + path}
+      cancellable={false}
+      onCancel={onClose}
+    >
+      <Spin spinning={loading}>
+        <div className="hue-summary-modal__grid">
+          {summary?.map(item => (
+            <div key={item.key} className="hue-summary-modal__grid__summary-item">
+              <div className="hue-summary-modal__grid__summary-item__label">{item.label}</div>
+              <div className="hue-summary-modal__grid__summary-item__value">{item.value}</div>
+            </div>
+          ))}
+        </div>
+      </Spin>
+    </Modal>
+  );
+};
+
+export default SummaryModal;

+ 0 - 100
desktop/core/src/desktop/js/apps/storageBrowser/SummaryModal/SummaryModal.tsx

@@ -1,100 +0,0 @@
-// Licensed to Cloudera, Inc. under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  Cloudera, Inc. licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-import React, { useMemo } from 'react';
-import Modal from 'cuix/dist/components/Modal';
-import { Row, Col, Spin } from 'antd';
-
-import huePubSub from '../../../utils/huePubSub';
-import { i18nReact } from '../../../utils/i18nReact';
-import formatBytes from '../../../utils/formatBytes';
-import useLoadData from '../../../utils/hooks/useLoadData';
-import { CONTENT_SUMMARY_API_URL } from '../../../reactComponents/FileChooser/api';
-import { ContentSummary } from '../../../reactComponents/FileChooser/types';
-
-import './SummaryModal.scss';
-interface SummaryModalProps {
-  path: string;
-  showModal: boolean;
-  onClose: () => void;
-}
-
-const SummaryModal = ({ showModal, onClose, path }: SummaryModalProps): JSX.Element => {
-  const { t } = i18nReact.useTranslation();
-
-  const getSummary = () => {
-    const cols = summary.map((item, index) => (
-      <Col key={'summaryItem' + index} span={12}>
-        <Row key={'summaryItem' + index + 'key'}>{item[0]}</Row>
-        <Row key={'summaryItem' + index + 'value'}>{item[1]}</Row>
-      </Col>
-    ));
-
-    const rows: JSX.Element[] = [];
-    for (let i = 0; i < cols.length - 1; i = i + 2) {
-      rows.push(
-        <Row key={'summaryRow' + i} className="hue-summary-modal__row">
-          {cols[i]}
-          {cols[i + 1]}
-        </Row>
-      );
-    }
-    return rows;
-  };
-
-  const {
-    data: responseSummary,
-    loading: loadingSummary,
-    error
-  } = useLoadData<ContentSummary>(CONTENT_SUMMARY_API_URL, {
-    params: {
-      path: path
-    },
-    onError: () => {
-      huePubSub.publish('hue.error', error);
-    },
-    skip: path === '' || path === undefined || !showModal
-  });
-
-  const summary = useMemo(() => {
-    return [
-      ['DISKSPACE CONSUMED', formatBytes(responseSummary?.summary.spaceConsumed)],
-      ['BYTES USED', formatBytes(responseSummary?.summary.length)],
-      ['NAMESPACE QUOTA', formatBytes(responseSummary?.summary.quota)],
-      ['DISKSPACE QUOTA', formatBytes(responseSummary?.summary.spaceQuota)],
-      ['REPLICATION FACTOR', responseSummary?.summary.replication],
-      [,],
-      ['NUMBER OF DIRECTORIES', responseSummary?.summary.directoryCount],
-      ['NUMBER OF FILES', responseSummary?.summary.fileCount]
-    ];
-  }, [responseSummary]);
-
-  //TODO:Handle long modal title
-  return (
-    <Modal
-      className="hue-summary-modal cuix antd"
-      okText={t('Close')}
-      onOk={onClose}
-      open={showModal}
-      title={t('Summary for ') + path}
-      cancellable={false}
-      onCancel={onClose}
-    >
-      <Spin spinning={loadingSummary}>{summary && getSummary()}</Spin>
-    </Modal>
-  );
-};
-
-export default SummaryModal;

+ 1 - 1
desktop/core/src/desktop/js/reactComponents/FileChooser/FileChooserModal/FileChooserModal.tsx

@@ -25,7 +25,7 @@ import AdlsIcon from '../../../components/icons/AdlsIcon';
 import { ApiFileSystem, FILESYSTEMS_API_URL } from '../api';
 import { FileSystem } from '../types';
 import './FileChooserModal.scss';
-import PathBrowser from '../PathBrowser/PathBrowser';
+import PathBrowser from '../../PathBrowser/PathBrowser';
 import useLoadData from '../../../utils/hooks/useLoadData';
 
 interface FileProps {

+ 0 - 1
desktop/core/src/desktop/js/reactComponents/FileChooser/api.ts

@@ -15,7 +15,6 @@
 // limitations under the License.
 
 export const FILESYSTEMS_API_URL = '/api/v1/storage/filesystems';
-export const VIEWFILES_API_URl = '/api/v1/storage/view=';
 export const FILE_STATS_API_URL = '/api/v1/storage/stat';
 export const LIST_DIRECTORY_API_URL = '/api/v1/storage/list';
 export const FILE_PREVIEW_API_URL = '/api/v1/storage/display';

+ 11 - 22
desktop/core/src/desktop/js/reactComponents/FileChooser/types.ts

@@ -36,16 +36,12 @@ export interface FileStats {
   user: string;
 }
 
-export interface StorageBrowserTableData {
+export interface StorageDirectoryTableData
+  extends Pick<FileStats, 'path' | 'user' | 'group' | 'type' | 'replication'> {
   name: string;
   size: string;
-  user: string;
-  group: string;
   permission: string;
   mtime: string;
-  type: string;
-  path: string;
-  replication: number;
 }
 
 export interface PageStats {
@@ -58,11 +54,6 @@ export interface PageStats {
   total_count: number;
 }
 
-export interface BreadcrumbData {
-  label: string;
-  url: string;
-}
-
 export interface FilePreview {
   contents: string;
   compression?: string;
@@ -84,17 +75,15 @@ export interface ListDirectory {
 }
 
 export interface ContentSummary {
-  summary: {
-    directoryCount: number;
-    ecPolicy: string;
-    fileCount: number;
-    length: number;
-    quota: number;
-    spaceConsumed: number;
-    spaceQuota: number;
-    typeQuota: number;
-    replication: number;
-  };
+  directoryCount: number;
+  ecPolicy: string;
+  fileCount: number;
+  length: number;
+  quota: number;
+  spaceConsumed: number;
+  spaceQuota: number;
+  typeQuota: number;
+  replication: number;
 }
 
 export enum SortOrder {

+ 1 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/Breadcrumb/Breadcrumb.scss → desktop/core/src/desktop/js/reactComponents/PathBrowser/Breadcrumb/Breadcrumb.scss

@@ -13,6 +13,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+
 .antd.cuix {
   .hue-path-browser__breadcrumb {
     display: contents;

+ 0 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/Breadcrumb/Breadcrumb.tsx → desktop/core/src/desktop/js/reactComponents/PathBrowser/Breadcrumb/Breadcrumb.tsx


+ 0 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/OverflowTooltip.tsx → desktop/core/src/desktop/js/reactComponents/PathBrowser/OverflowTooltip.tsx


+ 0 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/OverflowingItem.scss → desktop/core/src/desktop/js/reactComponents/PathBrowser/OverflowingItem.scss


+ 0 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/OverflowingItem.tsx → desktop/core/src/desktop/js/reactComponents/PathBrowser/OverflowingItem.tsx


+ 0 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/PathBrowser.scss → desktop/core/src/desktop/js/reactComponents/PathBrowser/PathBrowser.scss


+ 0 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/PathBrowser.test.tsx → desktop/core/src/desktop/js/reactComponents/PathBrowser/PathBrowser.test.tsx


+ 4 - 5
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/PathBrowser.tsx → desktop/core/src/desktop/js/reactComponents/PathBrowser/PathBrowser.tsx

@@ -19,14 +19,13 @@ import { Input, Dropdown } from 'antd';
 import { BorderlessButton } from 'cuix/dist/components/Button';
 import { MenuItemType } from 'antd/lib/menu/hooks/useItems';
 
-import HdfsIcon from '../../../components/icons/HdfsIcon';
-import S3Icon from '../../../components/icons/S3Icon';
-import AdlsIcon from '../../../components/icons/AdlsIcon';
+import HdfsIcon from '../../components/icons/HdfsIcon';
+import S3Icon from '../../components/icons/S3Icon';
+import AdlsIcon from '../../components/icons/AdlsIcon';
 
-import { BreadcrumbData } from '../types';
 import Breadcrumb from './Breadcrumb/Breadcrumb';
 import './PathBrowser.scss';
-import { getBreadcrumbs, getFileSystemAndPath } from './PathBrowser.util';
+import { getBreadcrumbs, getFileSystemAndPath, BreadcrumbData } from './PathBrowser.util';
 
 interface PathBrowserProps {
   filePath: string;

+ 0 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/PathBrowser.util.test.ts → desktop/core/src/desktop/js/reactComponents/PathBrowser/PathBrowser.util.test.ts


+ 4 - 1
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/PathBrowser.util.ts → desktop/core/src/desktop/js/reactComponents/PathBrowser/PathBrowser.util.ts

@@ -14,7 +14,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import { BreadcrumbData } from '../types';
+export interface BreadcrumbData {
+  label: string;
+  url: string;
+}
 
 export const getFileSystemAndPath = (
   filePath: string

+ 1 - 1
desktop/core/src/desktop/js/reactComponents/imports.js

@@ -8,7 +8,7 @@ export async function loadComponent(name) {
       return (await import('../apps/editor/components/result/reactExample/ReactExample')).default;
 
     case 'StorageBrowserPage':
-      return (await import('../apps/storageBrowser/StorageBrowserPage/StorageBrowserPage')).default;
+      return (await import('../apps/storageBrowser/StorageBrowserPage')).default;
 
     case 'Metrics':
       return (await import('../apps/admin/Metrics/MetricsTab')).default;