Browse Source

improve table sort

Gitea 3 years ago
parent
commit
656efdd698

+ 0 - 19
client/src/components/__test__/grid/Grid.spec.tsx

@@ -91,25 +91,6 @@ describe('Test Grid index', () => {
     expect(titleNodes[1].textContent).toEqual(title[1]);
   });
 
-  it('Test SearchForm', () => {
-    const SearchForm = () => <div id="search-form"></div>;
-    act(() => {
-      render(
-        <MilvusGrid
-          primaryKey="id"
-          rows={[]}
-          colDefinitions={[]}
-          rowCount={0}
-          toolbarConfigs={[]}
-          searchForm={<SearchForm />}
-        />,
-        container
-      );
-    });
-
-    expect(container.querySelectorAll('#search-form').length).toEqual(1);
-  });
-
   it('Test Toolbar ', () => {
     const ToolbarConfig: ToolBarConfig[] = [
       {

+ 34 - 27
client/src/components/grid/Grid.tsx

@@ -20,12 +20,7 @@ const userStyle = makeStyles(theme => ({
     padding: theme.spacing(20),
     width: '100%',
   },
-  titleIcon: {
-    verticalAlign: '-3px',
-    '& svg': {
-      fill: '#32363c',
-    },
-  },
+
   tableTitle: {
     '& .last': {
       color: 'rgba(0, 0, 0, 0.54)',
@@ -71,6 +66,31 @@ const userStyle = makeStyles(theme => ({
   },
 }));
 
+/**
+ *
+ * @param rowCount required. totoal data count for pagination
+ * @param rowsPerPage per page for pagination, default is 10
+ * @param primaryKey required. The unique column for your data. use for checkbox and render key.
+ * @param onChangePage handle page change
+ * @param labelDisplayedRows Custom pagination label function, return string;
+ * @param page current page for pagination
+ * @param showToolbar control toolbar display. default is false
+ * @param rows table data you want to render
+ * @param colDefinitions Define how to render table heder.
+ * @param isLoading table loading status
+ * @param title  Render breadcrumbs
+ * @param openCheckBox control checkbox display. default is true
+ * @param disableSelect disable table row select. default false
+ * @param noData when table is empty, what tip we need to show.
+ * @param showHoverStyle control table row hover style display
+ * @param headEditable if true, user can edit header.
+ * @param editHeads Only headEditable is true will render editHeads
+ * @param tableCellMaxWidth Define table cell max width, default is 300
+ * @param handlesort how to sort table, if it's undefined, then you can not sort table
+ * @param order 'desc' | 'asc'. sort direction
+ * @param order order by which table field
+ * @returns
+ */
 const MilvusGrid: FC<MilvusGridType> = props => {
   const classes = userStyle();
 
@@ -79,8 +99,8 @@ const MilvusGrid: FC<MilvusGridType> = props => {
   const gridTrans = commonTrans('grid');
 
   const {
-    rowCount = 10,
-    rowsPerPage = 5,
+    rowCount = 20,
+    rowsPerPage = 10,
     primaryKey = 'id',
     showToolbar = false,
     toolbarConfigs = [],
@@ -91,14 +111,11 @@ const MilvusGrid: FC<MilvusGridType> = props => {
       console.log('nextPageNum', nextPageNum);
     },
     labelDisplayedRows,
-    // pageUnit = 'item',
     page = 0,
     rows = [],
     colDefinitions = [],
     isLoading = false,
     title,
-    // titleIcon = <CollectionIcon />,
-    searchForm,
     openCheckBox = true,
     disableSelect = false,
     noData = gridTrans.noData,
@@ -109,6 +126,9 @@ const MilvusGrid: FC<MilvusGridType> = props => {
     setSelected = () => {},
     setRowsPerPage = () => {},
     tableCellMaxWidth,
+    handleSort,
+    order,
+    orderBy,
   } = props;
 
   const _isSelected = (row: { [x: string]: any }) => {
@@ -136,16 +156,6 @@ const MilvusGrid: FC<MilvusGridType> = props => {
     setSelected([]);
   };
 
-  // const defaultLabelRows = ({ from = 0, to = 0, count = 0 }) => {
-  //   const plural = pageUnit.charAt(pageUnit.length - 1) === 'y' ? 'ies' : 's';
-  //   const formatUnit =
-  //     pageUnit.charAt(pageUnit.length - 1) === 'y'
-  //       ? pageUnit.slice(0, pageUnit.length - 1)
-  //       : pageUnit;
-  //   const unit = count > 1 ? `${formatUnit}${plural}` : pageUnit;
-  //   return `${count} ${unit}`;
-  // };
-
   const defaultLabelRows = ({ from = 0, to = 0, count = 0 }) => {
     return (
       <>
@@ -184,12 +194,6 @@ const MilvusGrid: FC<MilvusGridType> = props => {
         </Grid>
       )}
 
-      {searchForm && (
-        <Grid item xs={12}>
-          {searchForm}
-        </Grid>
-      )}
-
       {(showToolbar || toolbarConfigs.length > 0) && (
         <Grid item>
           <CustomToolbar
@@ -217,6 +221,9 @@ const MilvusGrid: FC<MilvusGridType> = props => {
           headEditable={headEditable}
           editHeads={editHeads}
           tableCellMaxWidth={tableCellMaxWidth}
+          handleSort={handleSort}
+          order={order}
+          orderBy={orderBy}
         ></Table>
         {rowCount ? (
           <TablePagination

+ 125 - 137
client/src/components/grid/Table.tsx

@@ -11,7 +11,6 @@ import { TableType } from './Types';
 import { Box, Button, Typography } from '@material-ui/core';
 import EnhancedTableHead from './TableHead';
 import EditableTableHead from './TableEditableHead';
-import { stableSort, getComparator } from './Utils';
 import ActionBar from './ActionBar';
 import LoadingTable from './LoadingTable';
 import CopyButton from '../advancedSearch/CopyButton';
@@ -129,10 +128,11 @@ const EnhancedTable: FC<TableType> = props => {
     editHeads = [],
     // if table cell max width not be passed, table row will use 300px as default
     tableCellMaxWidth = '300px',
+    handleSort,
+    order,
+    orderBy,
   } = props;
   const classes = useStyles({ tableCellMaxWidth });
-  const [order, setOrder] = React.useState('asc');
-  const [orderBy, setOrderBy] = React.useState<string>('');
   const [tableMouseStatus, setTableMouseStatus] = React.useState<boolean[]>([]);
   const [loadingRowCount, setLoadingRowCount] = useState<number>(0);
 
@@ -141,12 +141,6 @@ const EnhancedTable: FC<TableType> = props => {
   const { t: commonTrans } = useTranslation();
   const copyTrans = commonTrans('copy');
 
-  const handleRequestSort = (event: any, property: string) => {
-    const isAsc = orderBy === property && order === 'asc';
-    setOrder(isAsc ? 'desc' : 'asc');
-    setOrderBy(property);
-  };
-
   useEffect(() => {
     const height: number = (containerRef.current as any)!.offsetHeight;
     // table header 57px, loading row 40px
@@ -190,7 +184,7 @@ const EnhancedTable: FC<TableType> = props => {
               order={order}
               orderBy={orderBy}
               onSelectAllClick={onSelectedAll}
-              onRequestSort={handleRequestSort}
+              handleSort={handleSort}
               rowCount={rows.length}
               openCheckBox={openCheckBox}
             />
@@ -200,139 +194,133 @@ const EnhancedTable: FC<TableType> = props => {
           {!isLoading && (
             <TableBody>
               {rows && rows.length ? (
-                stableSort(rows, getComparator(order, orderBy)).map(
-                  (row, index) => {
-                    const isItemSelected = isSelected(row);
-                    const labelId = `enhanced-table-checkbox-${index}`;
+                rows.map((row, index) => {
+                  const isItemSelected = isSelected(row);
+                  const labelId = `enhanced-table-checkbox-${index}`;
+
+                  const handleMouseEnter = () => {
+                    setTableMouseStatus(v => {
+                      const copy = [...v];
+                      copy[index] = true;
+                      return copy;
+                    });
+                  };
+                  const handleMouseLeave = () =>
+                    setTableMouseStatus(v => {
+                      const copy = [...v];
+                      copy[index] = false;
+                      return copy;
+                    });
 
-                    const handleMouseEnter = () => {
-                      setTableMouseStatus(v => {
-                        const copy = [...v];
-                        copy[index] = true;
-                        return copy;
-                      });
-                    };
-                    const handleMouseLeave = () =>
-                      setTableMouseStatus(v => {
-                        const copy = [...v];
-                        copy[index] = false;
-                        return copy;
-                      });
+                  return (
+                    <TableRow
+                      hover={showHoverStyle}
+                      key={'row' + row[primaryKey] + index}
+                      onClick={event => onSelected(event, row)}
+                      role="checkbox"
+                      aria-checked={isItemSelected}
+                      tabIndex={-1}
+                      selected={isItemSelected && !disableSelect}
+                      classes={{
+                        hover: classes.rowHover,
+                      }}
+                      onMouseEnter={handleMouseEnter}
+                      onMouseLeave={handleMouseLeave}
+                    >
+                      {openCheckBox && (
+                        <TableCell
+                          padding="checkbox"
+                          className={classes.checkbox}
+                        >
+                          <Checkbox
+                            checked={isItemSelected}
+                            color="primary"
+                            inputProps={{ 'aria-labelledby': labelId }}
+                          />
+                        </TableCell>
+                      )}
 
-                    return (
-                      <TableRow
-                        hover={showHoverStyle}
-                        key={'row' + row[primaryKey] + index}
-                        onClick={event => onSelected(event, row)}
-                        role="checkbox"
-                        aria-checked={isItemSelected}
-                        tabIndex={-1}
-                        selected={isItemSelected && !disableSelect}
-                        classes={{
-                          hover: classes.rowHover,
-                        }}
-                        onMouseEnter={handleMouseEnter}
-                        onMouseLeave={handleMouseLeave}
-                      >
-                        {openCheckBox && (
+                      {colDefinitions.map((colDef, i) => {
+                        const { actionBarConfigs = [], needCopy = false } =
+                          colDef;
+                        const cellStyle = colDef.getStyle
+                          ? colDef.getStyle(row[colDef.id])
+                          : {};
+                        return colDef.showActionCell ? (
                           <TableCell
-                            padding="checkbox"
-                            className={classes.checkbox}
+                            className={`${classes.cell} ${classes.tableCell} ${
+                              colDef.isHoverAction
+                                ? classes.hoverActionCell
+                                : ''
+                            }`}
+                            key="manage"
+                            style={cellStyle}
                           >
-                            <Checkbox
-                              checked={isItemSelected}
-                              color="primary"
-                              inputProps={{ 'aria-labelledby': labelId }}
-                            />
+                            <ActionBar
+                              showLabel={tableMouseStatus[index]}
+                              configs={actionBarConfigs}
+                              isHoverType={colDef.isHoverAction}
+                              row={row}
+                            ></ActionBar>
                           </TableCell>
-                        )}
-
-                        {colDefinitions.map((colDef, i) => {
-                          const { actionBarConfigs = [], needCopy = false } =
-                            colDef;
-                          const cellStyle = colDef.getStyle
-                            ? colDef.getStyle(row[colDef.id])
-                            : {};
-                          return colDef.showActionCell ? (
-                            <TableCell
-                              className={`${classes.cell} ${
-                                classes.tableCell
-                              } ${
-                                colDef.isHoverAction
-                                  ? classes.hoverActionCell
-                                  : ''
-                              }`}
-                              key="manage"
-                              style={cellStyle}
-                            >
-                              <ActionBar
-                                showLabel={tableMouseStatus[index]}
-                                configs={actionBarConfigs}
-                                isHoverType={colDef.isHoverAction}
-                                row={row}
-                              ></ActionBar>
-                            </TableCell>
-                          ) : (
-                            <TableCell
-                              key={'cell' + row[primaryKey] + i}
-                              // padding={i === 0 ? 'none' : 'default'}
-                              align={colDef.align || 'left'}
-                              className={`${classes.cell} ${classes.tableCell}`}
-                              style={cellStyle}
-                            >
-                              {row[colDef.id] &&
-                              typeof row[colDef.id] === 'string' ? (
-                                <Typography title={row[colDef.id]}>
-                                  {colDef.onClick ? (
-                                    <Button
-                                      color="primary"
-                                      data-data={row[colDef.id]}
-                                      data-index={index}
-                                      onClick={e => {
-                                        colDef.onClick &&
-                                          colDef.onClick(e, row);
-                                      }}
-                                    >
-                                      {row[colDef.id]}
-                                    </Button>
-                                  ) : (
-                                    row[colDef.id]
-                                  )}
-                                </Typography>
-                              ) : (
-                                <>
-                                  {colDef.onClick ? (
-                                    <Button
-                                      color="primary"
-                                      data-data={row[colDef.id]}
-                                      data-index={index}
-                                      onClick={e => {
-                                        colDef.onClick &&
-                                          colDef.onClick(e, row);
-                                      }}
-                                    >
-                                      {row[colDef.id]}
-                                    </Button>
-                                  ) : (
-                                    row[colDef.id]
-                                  )}
-                                </>
-                              )}
+                        ) : (
+                          <TableCell
+                            key={'cell' + row[primaryKey] + i}
+                            // padding={i === 0 ? 'none' : 'default'}
+                            align={colDef.align || 'left'}
+                            className={`${classes.cell} ${classes.tableCell}`}
+                            style={cellStyle}
+                          >
+                            {row[colDef.id] &&
+                            typeof row[colDef.id] === 'string' ? (
+                              <Typography title={row[colDef.id]}>
+                                {colDef.onClick ? (
+                                  <Button
+                                    color="primary"
+                                    data-data={row[colDef.id]}
+                                    data-index={index}
+                                    onClick={e => {
+                                      colDef.onClick && colDef.onClick(e, row);
+                                    }}
+                                  >
+                                    {row[colDef.id]}
+                                  </Button>
+                                ) : (
+                                  row[colDef.id]
+                                )}
+                              </Typography>
+                            ) : (
+                              <>
+                                {colDef.onClick ? (
+                                  <Button
+                                    color="primary"
+                                    data-data={row[colDef.id]}
+                                    data-index={index}
+                                    onClick={e => {
+                                      colDef.onClick && colDef.onClick(e, row);
+                                    }}
+                                  >
+                                    {row[colDef.id]}
+                                  </Button>
+                                ) : (
+                                  row[colDef.id]
+                                )}
+                              </>
+                            )}
 
-                              {needCopy && row[colDef.id] && (
-                                <CopyButton
-                                  label={copyTrans.label}
-                                  value={row[colDef.id]}
-                                  className={classes.copyBtn}
-                                />
-                              )}
-                            </TableCell>
-                          );
-                        })}
-                      </TableRow>
-                    );
-                  }
-                )
+                            {needCopy && row[colDef.id] && (
+                              <CopyButton
+                                label={copyTrans.label}
+                                value={row[colDef.id]}
+                                className={classes.copyBtn}
+                              />
+                            )}
+                          </TableCell>
+                        );
+                      })}
+                    </TableRow>
+                  );
+                })
               ) : (
                 <tr>
                   <td

+ 3 - 3
client/src/components/grid/TableHead.tsx

@@ -46,12 +46,12 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
     numSelected,
     rowCount,
     colDefinitions = [],
-    onRequestSort,
+    handleSort,
     openCheckBox,
   } = props;
   const classes = useStyles();
   const createSortHandler = (property: string) => (event: React.MouseEvent) => {
-    onRequestSort(event, property);
+    handleSort && handleSort(event, property);
   };
 
   return (
@@ -79,7 +79,7 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
             }
             className={classes.tableCell}
           >
-            {headCell.label && !headCell.notSort ? (
+            {headCell.label && handleSort && !headCell.notSort ? (
               <TableSortLabel
                 active={orderBy === (headCell.sortBy || headCell.id)}
                 direction={

+ 12 - 7
client/src/components/grid/Types.ts

@@ -1,6 +1,6 @@
 import { IconsType } from '../icons/Types';
 import { SearchType } from '../customInput/Types';
-import { ReactElement } from 'react';
+import React, { ReactElement } from 'react';
 
 export type IconConfigType = {
   [x: string]: JSX.Element;
@@ -8,6 +8,8 @@ export type IconConfigType = {
 
 export type ColorType = 'default' | 'inherit' | 'primary' | 'secondary';
 
+export type SortDirection = 'asc' | 'desc';
+
 /**
  * selected: selected data in table checkbox
  */
@@ -47,12 +49,12 @@ export type ToolBarConfig = Partial<TableSwitchType> &
 
 export type TableHeadType = {
   onSelectAllClick: (e: React.ChangeEvent) => void;
-  order: any;
-  orderBy: string;
+  order?: SortDirection;
+  orderBy?: string;
   numSelected: number;
   rowCount: number;
   colDefinitions: ColDefinitionsType[];
-  onRequestSort: (e: any, p: string) => void;
+  handleSort?: (e: any, p: string) => void;
   openCheckBox?: boolean;
 };
 
@@ -83,6 +85,9 @@ export type TableType = {
   editHeads: EditableHeads[];
   // with unit like '20px'
   tableCellMaxWidth?: string;
+  handleSort?: (e: any, orderBy: string) => void;
+  order?: SortDirection;
+  orderBy?: string;
 };
 
 export type ColDefinitionsType = {
@@ -123,10 +128,7 @@ export type MilvusGridType = ToolBarType & {
   colDefinitions: ColDefinitionsType[];
   isLoading?: boolean;
   title?: string[];
-  searchForm?: React.ReactNode;
   openCheckBox?: boolean;
-  titleIcon?: React.ReactNode;
-  pageUnit?: string;
   disableSelect?: boolean;
   noData?: string;
   showHoverStyle?: boolean;
@@ -134,6 +136,9 @@ export type MilvusGridType = ToolBarType & {
   editHeads?: EditableHeads[];
   // with unit like '20px'
   tableCellMaxWidth?: string;
+  handleSort?: (e: any, orderBy: string) => void;
+  order?: SortDirection;
+  orderBy?: string;
 };
 
 export type ActionBarType = {

+ 12 - 3
client/src/hooks/Pagination.ts

@@ -1,18 +1,27 @@
 import { useMemo, useState } from 'react';
+import { stableSort, getComparator } from '../utils/Sort';
 
-export const usePaginationHook = (list: any[]) => {
+export const usePaginationHook = (
+  list: any[],
+  orderBy?: string,
+  order?: 'asc' | 'desc'
+) => {
   const [currentPage, setCurrentPage] = useState(0);
   const [pageSize, setPageSize] = useState(10);
 
   const total = list.length;
   const { data, offset } = useMemo(() => {
     const offset = pageSize * currentPage;
-    const data = list.slice(offset, offset + pageSize);
+    // only when user click sort, orderBy will have value
+    const sortList = orderBy
+      ? stableSort(list, getComparator(order || 'asc', orderBy))
+      : list;
+    const data = sortList.slice(offset, offset + pageSize);
     return {
       offset,
       data,
     };
-  }, [list, currentPage, pageSize]);
+  }, [pageSize, currentPage, orderBy, list, order]);
 
   const handleCurrentPage = (page: number) => {
     setCurrentPage(page);

+ 13 - 1
client/src/pages/collections/Collections.tsx

@@ -66,6 +66,9 @@ const Collections = () => {
   const { handleAction } = useLoadAndReleaseDialogHook({ type: 'collection' });
   const { handleInsertDialog } = useInsertDialogHook();
   const [collections, setCollections] = useState<CollectionView[]>([]);
+  const [orderBy, setOrderBy] = useState('');
+  const [order, setOrder] = useState<'asc' | 'desc'>('asc');
+
   const [searchedCollections, setSearchedCollections] = useState<
     CollectionView[]
   >([]);
@@ -76,7 +79,7 @@ const Collections = () => {
     handleCurrentPage,
     total,
     data: collectionList,
-  } = usePaginationHook(searchedCollections);
+  } = usePaginationHook(searchedCollections, orderBy, order);
   const [loading, setLoading] = useState<boolean>(true);
   const [selectedCollections, setSelectedCollections] = useState<
     CollectionView[]
@@ -415,6 +418,12 @@ const Collections = () => {
     setSelectedCollections([]);
   };
 
+  const handleGridSort = (e: any, property: string) => {
+    const isAsc = orderBy === property && order === 'asc';
+    setOrder(isAsc ? 'desc' : 'asc');
+    setOrderBy(property);
+  };
+
   const CollectionIcon = icons.navCollection;
 
   return (
@@ -433,6 +442,9 @@ const Collections = () => {
           rowsPerPage={pageSize}
           setRowsPerPage={handlePageSize}
           isLoading={loading}
+          handleSort={handleGridSort}
+          order={order}
+          orderBy={orderBy}
         />
       ) : (
         <>

+ 0 - 0
client/src/components/grid/Utils.ts → client/src/utils/Sort.ts


+ 3 - 1
server/src/collections/collections.service.ts

@@ -13,7 +13,7 @@ import {
 } from '@zilliz/milvus2-sdk-node/dist/milvus/types';
 import { throwErrorFromSDK } from '../utils/Error';
 import { findKeyValue } from '../utils/Helper';
-import { LOADING_STATE, ROW_COUNT } from '../utils/Const';
+import { ROW_COUNT } from '../utils/Const';
 import {
   ShowCollectionsReq,
   ShowCollectionsType,
@@ -141,6 +141,8 @@ export class CollectionsService {
         });
       }
     }
+    // add default sort - Descending order
+    data.sort((a, b) => b.createdTime - a.createdTime);
     return data;
   }