import React, { ReactNode, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { IAction, IActions, IFormatter, Table, TableBody, TableHeader, TableVariant, } from "@patternfly/react-table"; import { PaginatingTableToolbar } from "./PaginatingTableToolbar"; import { Spinner } from "@patternfly/react-core"; import { TableToolbar } from "./TableToolbar"; import _ from "lodash"; type Row = { data: T; cells: (keyof T)[]; }; type DataTableProps = { ariaLabelKey: string; columns: Field[]; rows: Row[]; actions?: IActions; }; function DataTable({ columns, rows, actions, ariaLabelKey, }: DataTableProps) { const { t } = useTranslation(); return ( { return { ...column, title: t(column.displayKey || column.name) }; })} rows={rows} actions={actions} aria-label={t(ariaLabelKey)} >
); } export type Field = { name: string; displayKey?: string; cellFormatters?: IFormatter[]; cellRenderer?: (row: T) => ReactNode; }; export type Action = IAction & { onRowClick?: (row: T) => Promise | void; }; export type DataListProps = { loader: (first?: number, max?: number, search?: string) => Promise; isPaginated?: boolean; ariaLabelKey: string; searchPlaceholderKey: string; columns: Field[]; actions?: Action[]; toolbarItem?: ReactNode; emptyState?: ReactNode; }; export function DataList({ ariaLabelKey, searchPlaceholderKey, isPaginated = false, loader, columns, actions, toolbarItem, emptyState, }: DataListProps) { const { t } = useTranslation(); const [rows, setRows] = useState[]>(); const [filteredData, setFilteredData] = useState[]>(); const [loading, setLoading] = useState(false); const [max, setMax] = useState(10); const [first, setFirst] = useState(0); const [search, setSearch] = useState(""); const load = async () => { setLoading(true); const data = await loader(first, max, search); setRows( data!.map((value) => { return { data: value, cells: columns.map((col) => { if (col.cellRenderer) { return col.cellRenderer(value); } return (value as any)[col.name]; }), }; }) ); setLoading(false); }; useEffect(() => { load(); }, [first, max]); const filter = (search: string) => { setFilteredData( rows!.filter((row) => row.cells.some( (cell) => cell && cell.toString().toLowerCase().includes(search.toLowerCase()) ) ) ); }; const convertAction = () => actions && _.cloneDeep(actions).map((action: Action, index: number) => { delete action.onRowClick; action.onClick = async (_, rowIndex) => { const result = await actions[index].onRowClick!(rows![rowIndex].data); if (result) { load(); } }; return action; }); const searchOnChange = (value: string) => { if (isPaginated) { setSearch(value); } else { filter(value); } }; const Loading = () => (
); return ( <> {!rows && } {rows && isPaginated && ( { setFirst(first); setMax(max); }} inputGroupName={`${ariaLabelKey}input`} inputGroupOnChange={searchOnChange} inputGroupOnClick={load} inputGroupPlaceholder={t(searchPlaceholderKey)} toolbarItem={toolbarItem} > {!loading && (emptyState === undefined || rows.length !== 0) && ( )} {!loading && rows.length === 0 && emptyState} {loading && } )} {rows && !isPaginated && ( {}} inputGroupPlaceholder={t(searchPlaceholderKey)} toolbarItem={toolbarItem} > {(emptyState === undefined || rows.length !== 0) && ( )} {rows.length === 0 && emptyState} )} ); }