Kaynağa Gözat

Added typing for react table and implement search

jnfrati 5 yıl önce
ebeveyn
işleme
d57a6f6824

+ 120 - 0
dashboard/react-table.d.ts

@@ -0,0 +1,120 @@
+import {
+  UseColumnOrderInstanceProps,
+  UseColumnOrderState,
+  UseExpandedHooks,
+  UseExpandedInstanceProps,
+  UseExpandedOptions,
+  UseExpandedRowProps,
+  UseExpandedState,
+  UseFiltersColumnOptions,
+  UseFiltersColumnProps,
+  UseFiltersInstanceProps,
+  UseFiltersOptions,
+  UseFiltersState,
+  UseGlobalFiltersColumnOptions,
+  UseGlobalFiltersInstanceProps,
+  UseGlobalFiltersOptions,
+  UseGlobalFiltersState,
+  UseGroupByCellProps,
+  UseGroupByColumnOptions,
+  UseGroupByColumnProps,
+  UseGroupByHooks,
+  UseGroupByInstanceProps,
+  UseGroupByOptions,
+  UseGroupByRowProps,
+  UseGroupByState,
+  UsePaginationInstanceProps,
+  UsePaginationOptions,
+  UsePaginationState,
+  UseResizeColumnsColumnOptions,
+  UseResizeColumnsColumnProps,
+  UseResizeColumnsOptions,
+  UseResizeColumnsState,
+  UseRowSelectHooks,
+  UseRowSelectInstanceProps,
+  UseRowSelectOptions,
+  UseRowSelectRowProps,
+  UseRowSelectState,
+  UseRowStateCellProps,
+  UseRowStateInstanceProps,
+  UseRowStateOptions,
+  UseRowStateRowProps,
+  UseRowStateState,
+  UseSortByColumnOptions,
+  UseSortByColumnProps,
+  UseSortByHooks,
+  UseSortByInstanceProps,
+  UseSortByOptions,
+  UseSortByState
+} from 'react-table'
+
+declare module 'react-table' {
+  // take this file as-is, or comment out the sections that don't apply to your plugin configuration
+  
+  export interface TableOptions<D extends object = {}>
+    extends UseExpandedOptions<D>,
+      UseFiltersOptions<D>,
+      UseGlobalFiltersOptions<D>,
+      UseGroupByOptions<D>,
+      UsePaginationOptions<D>,
+      UseResizeColumnsOptions<D>,
+      UseRowSelectOptions<D>,
+      UseRowStateOptions<D>,
+      UseSortByOptions<D>,
+      // note that having Record here allows you to add anything to the options, this matches the spirit of the
+      // underlying js library, but might be cleaner if it's replaced by a more specific type that matches your
+      // feature set, this is a safe default.
+      Record<string, any> {}
+
+  export interface Hooks<D extends object = {}>
+    extends UseExpandedHooks<D>,
+      UseGroupByHooks<D>,
+      UseRowSelectHooks<D>,
+      UseSortByHooks<D> {}
+
+  export interface TableInstance<D extends object = {}>
+    extends UseColumnOrderInstanceProps<D>,
+      UseExpandedInstanceProps<D>,
+      UseFiltersInstanceProps<D>,
+      UseGlobalFiltersInstanceProps<D>,
+      UseGroupByInstanceProps<D>,
+      UsePaginationInstanceProps<D>,
+      UseRowSelectInstanceProps<D>,
+      UseRowStateInstanceProps<D>,
+      UseSortByInstanceProps<D> {}
+
+  export interface TableState<D extends object = {}>
+    extends UseColumnOrderState<D>,
+      UseExpandedState<D>,
+      UseFiltersState<D>,
+      UseGlobalFiltersState<D>,
+      UseGroupByState<D>,
+      UsePaginationState<D>,
+      UseResizeColumnsState<D>,
+      UseRowSelectState<D>,
+      UseRowStateState<D>,
+      UseSortByState<D> {}
+
+  export interface ColumnInterface<D extends object = {}>
+    extends UseFiltersColumnOptions<D>,
+      UseGlobalFiltersColumnOptions<D>,
+      UseGroupByColumnOptions<D>,
+      UseResizeColumnsColumnOptions<D>,
+      UseSortByColumnOptions<D> {}
+
+  export interface ColumnInstance<D extends object = {}>
+    extends UseFiltersColumnProps<D>,
+      UseGroupByColumnProps<D>,
+      UseResizeColumnsColumnProps<D>,
+      UseSortByColumnProps<D> {}
+
+  export interface Cell<D extends object = {}, V = any>
+    extends UseGroupByCellProps<D>,
+      UseRowStateCellProps<D> {}
+
+  export interface Row<D extends object = {}>
+    extends UseExpandedRowProps<D>,
+      UseGroupByRowProps<D>,
+      UseRowSelectRowProps<D>,
+      UseRowStateRowProps<D> {}
+}

+ 99 - 25
dashboard/src/components/Table.tsx

@@ -1,37 +1,113 @@
 import React from "react";
 import styled from "styled-components";
-import { Column, Row, useTable } from "react-table";
+import { Column, Row, useGlobalFilter, useTable } from "react-table";
+import InputRow from "./values-form/InputRow";
+import Loading from "components/Loading";
+
+const GlobalFilter: React.FunctionComponent<any> = ({ setGlobalFilter }) => {
+  const [value, setValue] = React.useState("");
+  const onChange = (value: string) => {
+    setGlobalFilter(value || undefined);
+  };
+
+  return (
+    <SearchRow>
+      <i className="material-icons">search</i>
+      <SearchInput>
+        <StyledInputRow
+          placeholder="Search"
+          type="input"
+          value={value || ""}
+          setValue={(value) => {
+            setValue(value as string);
+            onChange(value as string);
+          }}
+        />
+      </SearchInput>
+    </SearchRow>
+  );
+};
 
 export type TableProps = {
   columns: Column<any>[];
   data: any[];
   onRowClick?: (row: Row) => void;
+  isLoading: boolean;
 };
 
 const Table: React.FC<TableProps> = ({
   columns: columnsData,
   data,
   onRowClick,
+  isLoading,
 }) => {
   const {
     getTableProps,
     getTableBodyProps,
-    headers,
-    columns,
     rows,
+    setGlobalFilter,
     prepareRow,
     headerGroups,
-  } = useTable({
-    columns: columnsData,
-    data,
-  });
+    visibleColumns,
+  } = useTable(
+    {
+      columns: columnsData,
+      data,
+    },
+    useGlobalFilter
+  );
+
+  const renderRows = () => {
+    if (isLoading) {
+      return (
+        <StyledTr disableHover={true} selected={false}>
+          <StyledTd colSpan={visibleColumns.length}>
+            <Loading />
+          </StyledTd>
+        </StyledTr>
+      );
+    }
+
+    if (!rows.length) {
+      return (
+        <StyledTr disableHover={true} selected={false}>
+          <StyledTd colSpan={visibleColumns.length}>No data available</StyledTd>
+        </StyledTr>
+      );
+    }
+    return (
+      <>
+        {rows.map((row) => {
+          prepareRow(row);
+
+          return (
+            <StyledTr
+              {...row.getRowProps()}
+              onClick={() => onRowClick && onRowClick(row)}
+              selected={false}
+            >
+              {row.cells.map((cell) => (
+                <StyledTd {...cell.getCellProps()}>
+                  {cell.render("Cell")}
+                </StyledTd>
+              ))}
+            </StyledTr>
+          );
+        })}
+      </>
+    );
+  };
 
   return (
     <TableWrapper>
+      <GlobalFilter setGlobalFilter={setGlobalFilter} />
       <StyledTable {...getTableProps()}>
         <StyledTHead>
           {headerGroups.map((headerGroup) => (
-            <StyledTr {...headerGroup.getHeaderGroupProps()} disableHover={true}>
+            <StyledTr
+              {...headerGroup.getHeaderGroupProps()}
+              disableHover={true}
+            >
               {headerGroup.headers.map((column) => (
                 <StyledTh {...column.getHeaderProps()}>
                   {column.render("Header")}
@@ -40,23 +116,7 @@ const Table: React.FC<TableProps> = ({
             </StyledTr>
           ))}
         </StyledTHead>
-        <tbody {...getTableBodyProps()}>
-          {rows.map((row) => {
-            prepareRow(row);
-
-            return (
-              <StyledTr
-                {...row.getRowProps()}
-                onClick={() => onRowClick && onRowClick(row)}
-                selected={false}
-              >
-                {row.cells.map( cell => (
-                  <StyledTd {...cell.getCellProps()}>{cell.render("Cell")}</StyledTd>
-                ))}
-              </StyledTr>
-            );
-          })}
-        </tbody>
+        <tbody {...getTableBodyProps()}>{renderRows()}</tbody>
       </StyledTable>
     </TableWrapper>
   );
@@ -101,3 +161,17 @@ export const StyledTable = styled.table`
   min-width: 500px;
   border-collapse: collapse;
 `;
+
+const SearchRow = styled.div`
+  display: flex;
+  align-items: center;
+  margin: 14px 0;
+`;
+
+const StyledInputRow = styled(InputRow)``;
+
+const SearchInput = styled.div`
+  ${StyledInputRow} {
+    margin: 0 0 0 10px;
+  }
+`;

+ 2 - 1
dashboard/src/components/values-form/InputRow.tsx

@@ -11,6 +11,7 @@ type PropsType = {
   width?: string;
   disabled?: boolean;
   isRequired?: boolean;
+  className?: string;
 };
 
 type StateType = {
@@ -33,7 +34,7 @@ export default class InputRow extends Component<PropsType, StateType> {
   render() {
     let { label, value, type, unit, placeholder, width } = this.props;
     return (
-      <StyledInputRow>
+      <StyledInputRow className={this.props.className}>
         {label && (
           <Label>
             {label} <Required>{this.props.isRequired ? " *" : null}</Required>

+ 6 - 3
dashboard/src/main/home/cluster-dashboard/dashboard/NodeList.tsx

@@ -9,6 +9,7 @@ import { Context } from "shared/Context";
 const NodeList: React.FC = () => {
   const context = useContext(Context);
   const [nodeList, setNodeList] = useState([]);
+  const [loading, setLoading] = useState<boolean>(false);
 
   const columns = useMemo<Column<any>[]>(
     () => [
@@ -42,6 +43,7 @@ const NodeList: React.FC = () => {
 
   useEffect(() => {
     const { currentCluster, currentProject } = context;
+    setLoading(true)
     api
       .getClusterNodes(
         "<token>",
@@ -58,13 +60,14 @@ const NodeList: React.FC = () => {
       })
       .catch(() => {
         console.log({ error: true });
-      });
+      })
+      .finally(() => setLoading(false));
   }, [context, setNodeList]);
 
   return (
     <NodeListWrapper>
       <StyledChart>
-        <Table columns={columns} data={data} />
+        <Table columns={columns} data={data} isLoading={loading}/>
       </StyledChart>
     </NodeListWrapper>
   );
@@ -78,7 +81,7 @@ const NodeListWrapper = styled.div`
 
 const StyledChart = styled.div`
   background: #26282f;
-  padding: 10px;
+  padding: 14px;
   border-radius: 5px;
   box-shadow: 0 5px 8px 0px #00000033;
   position: relative;