Explorar el Código

[ui-core] add dynamic height to Paginated table component (#4091)

Ram Prasad Agarwal hace 7 meses
padre
commit
db8f7b2c43

+ 1 - 1
desktop/core/src/desktop/js/apps/admin/ServerLogs/ServerLogsTab.tsx

@@ -55,7 +55,7 @@ const ServerLogs: React.FC = (): JSX.Element => {
 
   return (
     <div className="hue-server-logs-component">
-      <LoadingErrorWrapper loading={loading} errors={[]}>
+      <LoadingErrorWrapper loading={loading}>
         <ServerLogsHeader
           onFilterChange={setFilter}
           onWrapLogsChange={setWrapLogs}

+ 9 - 17
desktop/core/src/desktop/js/apps/newimporter/ImporterFilePreview/ImporterFilePreview.tsx

@@ -25,8 +25,6 @@ import {
 import { convertToAntdColumns, convertToDataSource } from '../utils/utils';
 import { i18nReact } from '../../../utils/i18nReact';
 import { BorderlessButton, PrimaryButton } from 'cuix/dist/components/Button';
-import LoadingErrorWrapper from '../../../reactComponents/LoadingErrorWrapper/LoadingErrorWrapper';
-import useResizeObserver from '../../../utils/hooks/useResizeObserver/useResizeObserver';
 import PaginatedTable from '../../../reactComponents/PaginatedTable/PaginatedTable';
 import { GUESS_FORMAT_URL, GUESS_FIELD_TYPES_URL } from '../api';
 
@@ -84,8 +82,6 @@ const ImporterFilePreview = ({ fileMetaData }: ImporterFilePreviewProps): JSX.El
   const columns = convertToAntdColumns(previewData?.columns ?? []);
   const tableData = convertToDataSource(columns, previewData?.sample);
 
-  const [ref, rect] = useResizeObserver();
-
   return (
     <div className="hue-importer-preview-page">
       <div className="hue-importer-preview-page__header">
@@ -100,19 +96,15 @@ const ImporterFilePreview = ({ fileMetaData }: ImporterFilePreviewProps): JSX.El
         </div>
       </div>
       <div className="hue-importer-preview-page__metadata">{t('DESTINATION')}</div>
-      <div className="hue-importer-preview-page__main-section" ref={ref}>
-        <LoadingErrorWrapper loading={guessingFormat || guessingFields} errors={[]} hideChildren>
-          <PaginatedTable<ImporterTableData>
-            data={tableData}
-            columns={columns}
-            rowKey="importerDataKey"
-            scroll={{
-              y: Math.max(rect.height - 60, 100),
-              x: true
-            }}
-            locale={{ emptyText: t('No data available') }}
-          />
-        </LoadingErrorWrapper>
+      <div className="hue-importer-preview-page__main-section">
+        <PaginatedTable<ImporterTableData>
+          loading={guessingFormat || guessingFields}
+          data={tableData}
+          columns={columns}
+          rowKey="importerDataKey"
+          isDynamicHeight
+          locale={{ emptyText: t('No data found in the file!') }}
+        />
       </div>
     </div>
   );

+ 7 - 13
desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageDirectoryPage.scss

@@ -48,23 +48,17 @@ $icon-margin: 5px;
       }
     }
 
-    &__table-container {
-      flex: 1;
+    &__name-column {
       display: flex;
-      flex-direction: column;
+      gap: 6px;
+      color: vars.$fluidx-blue-700;
 
-      &__name-column {
+      &__icon {
         display: flex;
-        gap: 6px;
-        color: vars.$fluidx-blue-700;
-
-        &__icon {
-          display: flex;
-        }
+      }
 
-        &__label {
-          @include mixins.nowrap-ellipsis;
-        }
+      &__label {
+        @include mixins.nowrap-ellipsis;
       }
     }
   }

+ 30 - 38
desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageDirectoryPage.tsx

@@ -41,7 +41,6 @@ import DragAndDrop from '../../../reactComponents/DragAndDrop/DragAndDrop';
 import UUID from '../../../utils/string/UUID';
 import { RegularFile, FileStatus } from '../../../utils/hooks/useFileUpload/types';
 import FileUploadQueue from '../../../reactComponents/FileUploadQueue/FileUploadQueue';
-import useResizeObserver from '../../../utils/hooks/useResizeObserver/useResizeObserver';
 import LoadingErrorWrapper from '../../../reactComponents/LoadingErrorWrapper/LoadingErrorWrapper';
 import StorageDirectoryActions from './StorageDirectoryActions/StorageDirectoryActions';
 import PaginatedTable, {
@@ -156,10 +155,6 @@ const StorageDirectoryPage = ({
     setFilesToUpload(prevFiles => [...prevFiles, ...newUploadItems]);
   };
 
-  const [tableRef, rect] = useResizeObserver();
-  // 40px for table header, 50px for pagination
-  const tableBodyHeight = Math.max(rect.height - 90, 100);
-
   const errorConfig = [
     {
       enabled: !!listDirectoryError,
@@ -178,17 +173,15 @@ const StorageDirectoryPage = ({
       width: '40%',
       render: (_, record) => (
         <Tooltip title={record.name} mouseEnterDelay={1.5}>
-          <div className="hue-storage-browser-directory__table-container__name-column">
-            <div className="hue-storage-browser-directory__table-container__name-column__icon">
+          <div className="hue-storage-browser-directory__name-column">
+            <div className="hue-storage-browser-directory__name-column__icon">
               {record.type === BrowserViewType.dir ? (
                 <FolderIcon height={18} width={18} />
               ) : (
                 <FileIcon height={18} width={18} />
               )}
             </div>
-            <div className="hue-storage-browser-directory__table-container__name-column__label">
-              {record.name}
-            </div>
+            <div className="hue-storage-browser-directory__name-column__label">{record.name}</div>
           </div>
         </Tooltip>
       )
@@ -253,35 +246,34 @@ const StorageDirectoryPage = ({
         </div>
       </div>
 
-      <div ref={tableRef} className="hue-storage-browser-directory__table-container">
-        <DragAndDrop onDrop={onFilesDrop}>
-          <LoadingErrorWrapper
+      <DragAndDrop onDrop={onFilesDrop}>
+        <LoadingErrorWrapper errors={errorConfig}>
+          <PaginatedTable<StorageDirectoryTableData>
             loading={(loadingFiles || listDirectoryLoading) && !polling}
-            errors={errorConfig}
-          >
-            <PaginatedTable<StorageDirectoryTableData>
-              data={tableData}
-              columns={columnsConfig}
-              rowKey={r => `${r.path}_${r.type}_${r.mtime}`}
-              onRowClick={onRowClicked}
-              onRowSelect={setSelectedFiles}
-              sortByColumn={sortByColumn}
-              setSortByColumn={setSortByColumn}
-              sortOrder={sortOrder}
-              setSortOrder={setSortOrder}
-              locale={{ emptyText: t('Folder is empty') }}
-              scroll={{ y: tableBodyHeight }}
-              testId={testId}
-              pagination={{
-                pageSize,
-                setPageSize,
-                setPageNumber,
-                pageStats: filesData?.page
-              }}
-            />
-          </LoadingErrorWrapper>
-        </DragAndDrop>
-      </div>
+            data={tableData}
+            columns={columnsConfig}
+            rowKey={r => `${r.path}_${r.type}_${r.mtime}`}
+            onRowClick={onRowClicked}
+            onRowSelect={setSelectedFiles}
+            sortByColumn={sortByColumn}
+            setSortByColumn={setSortByColumn}
+            sortOrder={sortOrder}
+            setSortOrder={setSortOrder}
+            locale={{
+              emptyText: searchTerm.length ? t('No results found!') : t('Folder is empty')
+            }}
+            isDynamicHeight
+            testId={testId}
+            pagination={{
+              pageSize,
+              setPageSize,
+              setPageNumber,
+              pageStats: filesData?.page
+            }}
+          />
+        </LoadingErrorWrapper>
+      </DragAndDrop>
+
       {filesToUpload.length > 0 && (
         <FileUploadQueue
           filesQueue={filesToUpload}

+ 4 - 4
desktop/core/src/desktop/js/reactComponents/LoadingErrorWrapper/LoadingErrorWrapper.tsx

@@ -29,15 +29,15 @@ interface WrapperError {
 }
 
 interface LoadingErrorWrapperProps {
-  loading: boolean;
-  errors: WrapperError[];
+  loading?: boolean;
+  errors?: WrapperError[];
   children: React.ReactNode;
   hideChildren?: boolean;
 }
 
 const LoadingErrorWrapper = ({
-  loading,
-  errors,
+  loading = false,
+  errors = [],
   children,
   hideChildren = false
 }: LoadingErrorWrapperProps): JSX.Element => {

+ 25 - 9
desktop/core/src/desktop/js/reactComponents/PaginatedTable/PaginatedTable.scss

@@ -17,18 +17,34 @@
 @use 'variables' as vars;
 @use 'mixins';
 
-.hue-table {
-  .ant-table-cell {
-    @include mixins.nowrap-ellipsis;
+.antd.cuix {
+  .hue-table-container {
+    display: flex;
+    flex-direction: column;
+    flex: 1;
   }
 
-  .ant-table-selection-column {
-    // This prevents the eplipses from being applied to the selection column
-    text-overflow: initial !important;
+  .hue-table-container__placeholder--hidden {
+
+    // In CUIX implementation, label.loading is rendering along with spinner
+    // this ensures the placeholder is hidden when loading is true
+    .ant-table-placeholder {
+      visibility: hidden;
+    }
   }
 
-  .ant-table-placeholder {
-    height: 100px;
-    text-align: center;
+  .hue-table {
+    .ant-table-selection-column {
+      // This prevents the eplipses from being applied to the selection column
+      text-overflow: initial;
+    }
+
+    .ant-table-placeholder {
+      height: 100px;
+
+      .ant-table-cell {
+        text-align: center;
+      }
+    }
   }
 }

+ 36 - 17
desktop/core/src/desktop/js/reactComponents/PaginatedTable/PaginatedTable.tsx

@@ -16,6 +16,7 @@
 
 import React, { HTMLAttributes } from 'react';
 import Table, { type ColumnProps } from 'cuix/dist/components/Table';
+import { TablePaginationConfig } from 'antd/es/table';
 import {
   TableLocale,
   RowSelectionType,
@@ -24,11 +25,10 @@ import {
   SortOrder
 } from 'antd/lib/table/interface';
 import { PanelRender } from 'rc-table/lib/interface';
-import type { TableProps } from 'rc-table/lib/Table';
 import Pagination, { PaginationProps } from '../Pagination/Pagination';
+import useResizeObserver from '../../utils/hooks/useResizeObserver/useResizeObserver';
 
 import './PaginatedTable.scss';
-import { TablePaginationConfig } from 'antd/es/table';
 
 export interface PaginatedTableProps<T> {
   title?: PanelRender<T>;
@@ -37,7 +37,7 @@ export interface PaginatedTableProps<T> {
   onRowClick?: (record: T) => HTMLAttributes<HTMLElement>;
   locale?: TableLocale;
   onRowSelect?: (selectedRows: T[]) => void;
-  scroll?: TableProps<T>['scroll'];
+  isDynamicHeight?: boolean;
   sortByColumn?: ColumnProps<T>['dataIndex'];
   sortOrder?: SortOrder;
   setSortByColumn?: (column: ColumnProps<T>['dataIndex']) => void;
@@ -46,15 +46,20 @@ export interface PaginatedTableProps<T> {
   rowKey: ((record: T) => string) | string;
   pagination?: Partial<PaginationProps>;
   rowClassName?: ((record: T) => string) | string;
+  loading?: boolean;
 }
 
+const TABLE_HEADER_HEIGHT = 47;
+const PAGINATION_HEIGHT = 49;
+
 const PaginatedTable = <T extends object>({
+  loading = false,
   title,
   data,
   columns,
   onRowClick,
   onRowSelect,
-  scroll,
+  isDynamicHeight = false,
   sortByColumn,
   sortOrder,
   setSortByColumn,
@@ -100,8 +105,23 @@ const PaginatedTable = <T extends object>({
     }));
   };
 
+  const isPaginationEnabled =
+    pagination?.pageStats && pagination?.pageStats?.totalPages > 0 && pagination.setPageNumber;
+
+  const [tableRef, rect] = useResizeObserver();
+
+  const tableOffset = isPaginationEnabled
+    ? PAGINATION_HEIGHT + TABLE_HEADER_HEIGHT
+    : TABLE_HEADER_HEIGHT;
+  const tableBodyHeight = Math.max(rect.height - tableOffset, 100);
+
+  const tableScroll = isDynamicHeight ? { y: tableBodyHeight } : undefined;
+
   return (
-    <>
+    <div
+      ref={tableRef}
+      className={`hue-table-container ${loading ? 'hue-table-container__placeholder--hidden' : ''}`}
+    >
       <Table
         title={title}
         className="hue-table"
@@ -113,22 +133,21 @@ const PaginatedTable = <T extends object>({
         rowClassName={rowClassName}
         rowKey={rowKey}
         rowSelection={rowSelection}
-        scroll={scroll}
+        scroll={tableScroll}
         data-testid={testId}
         locale={locale}
+        loading={loading}
         sticky
       />
-      {pagination?.pageStats &&
-        pagination?.pageStats?.totalPages > 0 &&
-        pagination.setPageNumber && (
-          <Pagination
-            setPageSize={pagination.setPageSize}
-            pageSize={pagination.pageSize}
-            setPageNumber={pagination.setPageNumber}
-            pageStats={pagination?.pageStats}
-          />
-        )}
-    </>
+      {isPaginationEnabled && (
+        <Pagination
+          setPageSize={pagination.setPageSize}
+          pageSize={pagination.pageSize}
+          setPageNumber={pagination.setPageNumber!}
+          pageStats={pagination.pageStats!}
+        />
+      )}
+    </div>
   );
 };
 

+ 12 - 18
desktop/core/src/desktop/js/reactComponents/TaskServer/TaskServer.scss

@@ -16,7 +16,7 @@
 
 @use 'variables' as vars;
 
-.cuix.antd {
+.antd.cuix {
   .hue-task-server {
     display: flex;
     height: 100%;
@@ -30,25 +30,19 @@
       text-align: left;
     }
 
-    &__table {
-      display: flex;
-      flex: 1;
-      height: 100%;
-
-      .ant-tag-success {
-        background-color: vars.$fluidx-green-500;
-        color: vars.$fluidx-gray-900;
-      }
+    .ant-tag-success {
+      background-color: vars.$fluidx-green-500;
+      color: vars.$fluidx-gray-900;
+    }
 
-      .ant-tag-error {
-        background-color: vars.$fluidx-red-500;
-        color: vars.$fluidx-gray-900;
-      }
+    .ant-tag-error {
+      background-color: vars.$fluidx-red-500;
+      color: vars.$fluidx-gray-900;
+    }
 
-      .ant-tag-warning {
-        background-color: vars.$fluidx-amber-500;
-        color: vars.$fluidx-gray-900;
-      }
+    .ant-tag-warning {
+      background-color: vars.$fluidx-amber-500;
+      color: vars.$fluidx-gray-900;
     }
 
     &__actions {

+ 21 - 21
desktop/core/src/desktop/js/reactComponents/TaskServer/TaskServer.tsx

@@ -32,7 +32,6 @@ import { GET_TASKS_URL, KILL_TASK_URL } from './constants.ts';
 import ScheduleTaskModal from './ScheduleTaskModal/ScheduleTaskModal.tsx';
 import TaskLogsModal from './TaskLogsModal/TaskLogsModal.tsx';
 import LoadingErrorWrapper from '../LoadingErrorWrapper/LoadingErrorWrapper.tsx';
-import useResizeObserver from '../../utils/hooks/useResizeObserver/useResizeObserver';
 
 import './TaskServer.scss';
 
@@ -69,22 +68,26 @@ export const TaskServer: React.FC = () => {
     {
       title: t('User'),
       dataIndex: ['result', 'username'],
-      key: 'user'
+      key: 'user',
+      width: '10%'
     },
     {
       title: t('Progress'),
       dataIndex: ['result', 'progress'],
-      key: 'progress'
+      key: 'progress',
+      width: '10%'
     },
     {
       title: t('Task Name'),
       dataIndex: ['result', 'taskName'],
-      key: 'taskName'
+      key: 'taskName',
+      width: '10%'
     },
     {
       title: t('Parameters'),
       dataIndex: 'parameters',
       key: 'parameters',
+      width: '10%',
       render: (_, record: TaskServerResponse) => {
         if (record.result?.taskName) {
           const paramterText = {
@@ -103,6 +106,7 @@ export const TaskServer: React.FC = () => {
       title: t('Status'),
       dataIndex: 'status',
       key: 'status',
+      width: '10%',
       render: (status: TaskServerResponse['status']) => (
         <Tag color={statusTagColor(status)}>{status.toUpperCase()}</Tag>
       )
@@ -111,11 +115,13 @@ export const TaskServer: React.FC = () => {
       title: t('Started'),
       dataIndex: ['result', 'taskStart'],
       key: 'started',
+      width: '10%',
       render: (text: TaskServerResult['taskStart']) => formatTimestamp(text)
     },
     {
       title: t('Duration'),
       key: 'duration',
+      width: '10%',
       render: (_, record: TaskServerResponse) => {
         if (record.result?.taskStart && record.result?.taskEnd) {
           return calculateDuration(record.result?.taskStart, record.result?.taskEnd);
@@ -195,9 +201,6 @@ export const TaskServer: React.FC = () => {
     setSelectedTasks([]);
   };
 
-  const [ref, rect] = useResizeObserver();
-  const tableBodyHeight = Math.max(rect.height - 40, 100);
-
   return (
     <div className="hue-task-server">
       <div className="hue-task-server__actions">
@@ -232,20 +235,17 @@ export const TaskServer: React.FC = () => {
           <span>{t('All')}</span>
         </Checkbox>
       </div>
-      <div className="hue-task-server__table" ref={ref}>
-        <LoadingErrorWrapper loading={!tasks && loading} errors={errors}>
-          <PaginatedTable<TaskServerResponse>
-            onRowSelect={setSelectedTasks}
-            columns={columns}
-            data={filteredTasks}
-            rowKey="taskId"
-            scroll={{ y: tableBodyHeight }}
-            locale={{
-              emptyText: t('No tasks found')
-            }}
-          />
-        </LoadingErrorWrapper>
-      </div>
+      <LoadingErrorWrapper loading={false} errors={errors}>
+        <PaginatedTable<TaskServerResponse>
+          loading={!tasks && loading}
+          onRowSelect={setSelectedTasks}
+          columns={columns}
+          data={filteredTasks}
+          rowKey="taskId"
+          isDynamicHeight
+          locale={{ emptyText: t('No tasks found') }}
+        />
+      </LoadingErrorWrapper>
       {showSchedulePopup && <ScheduleTaskModal onClose={() => showScheduleTaskPopup(false)} />}
       {!!selectedTaskId && (
         <TaskLogsModal taskId={selectedTaskId} onClose={() => setSelectedTaskId(null)} />