/* eslint-disable react/prop-types */ // disable this rule as it was throwing an error in Header and Cell component // definitions for the selection row for some reason when we dont really need it. import React, { useMemo, useEffect, useCallback, useContext } from "react"; import { TableContext } from "context/table"; import classnames from "classnames"; import { useTable, useSortBy, useRowSelect, Row, usePagination, useFilters, HeaderGroup, Column, } from "react-table"; import { isString, kebabCase, noop } from "lodash"; import { useDebouncedCallback } from "use-debounce/lib"; import { useDeepEffect } from "utilities/hooks"; import sort from "utilities/sort"; import { AppContext } from "context/app"; import Button from "components/buttons/Button"; // @ts-ignore import FleetIcon from "components/icons/FleetIcon"; import Spinner from "components/Spinner"; import { ButtonVariant } from "components/buttons/Button/Button"; // @ts-ignore import ActionButton, { IActionButtonProps } from "./ActionButton"; const baseClass = "data-table-container"; interface IDataTableProps { columns: Column[]; data: any; isLoading: boolean; manualSortBy?: boolean; sortHeader: any; sortDirection: any; onSort: any; // TODO: an event type disableMultiRowSelect: boolean; showMarkAllPages: boolean; isAllPagesSelected: boolean; // TODO: make dependent on showMarkAllPages toggleAllPagesSelected?: any; // TODO: an event type and make it dependent on showMarkAllPages resultsTitle: string; defaultPageSize: number; primarySelectActionButtonVariant?: ButtonVariant; primarySelectActionButtonIcon?: string; primarySelectActionButtonText?: string | ((targetIds: number[]) => string); onPrimarySelectActionClick: any; // figure out type secondarySelectActions?: IActionButtonProps[]; isClientSidePagination?: boolean; isClientSideFilter?: boolean; highlightOnHover?: boolean; searchQuery?: string; searchQueryColumn?: string; selectedDropdownFilter?: string; clearSelectionCount?: number; onSelectSingleRow?: (value: Row) => void; onResultsCountChange?: (value: number) => void; } const CLIENT_SIDE_DEFAULT_PAGE_SIZE = 20; // This data table uses react-table for implementation. The relevant documentation of the library // can be found here https://react-table.tanstack.com/docs/api/useTable const DataTable = ({ columns: tableColumns, data: tableData, isLoading, manualSortBy = false, sortHeader, sortDirection, onSort, disableMultiRowSelect, showMarkAllPages, isAllPagesSelected, toggleAllPagesSelected, resultsTitle, defaultPageSize, primarySelectActionButtonIcon, primarySelectActionButtonVariant, onPrimarySelectActionClick, primarySelectActionButtonText, secondarySelectActions, isClientSidePagination, isClientSideFilter, highlightOnHover, searchQuery, searchQueryColumn, selectedDropdownFilter, clearSelectionCount, onSelectSingleRow, onResultsCountChange, }: IDataTableProps): JSX.Element => { const { resetSelectedRows } = useContext(TableContext); const { isOnlyObserver } = useContext(AppContext); const columns = useMemo(() => { return tableColumns; }, [tableColumns]); // The table data needs to be ordered by the order we received from the API. const data = useMemo(() => { return tableData; }, [tableData]); const { headerGroups, rows, prepareRow, selectedFlatRows, toggleAllRowsSelected, isAllRowsSelected, state: tableState, page, // Instead of using 'rows', we'll use page, // which has only the rows for the active page // The rest of these things are super handy, too ;) canPreviousPage, canNextPage, // pageOptions, // pageCount, // gotoPage, nextPage, previousPage, setPageSize, setFilter, } = useTable( { columns, data, initialState: { sortBy: useMemo(() => { return [{ id: sortHeader, desc: sortDirection === "desc" }]; }, [sortHeader, sortDirection]), }, disableMultiSort: true, disableSortRemove: true, manualSortBy, // Initializes as false, but changes briefly to true on successful notification autoResetSelectedRows: resetSelectedRows, sortTypes: React.useMemo( () => ({ caseInsensitive: (a: any, b: any, id: any) => { let valueA = a.values[id]; let valueB = b.values[id]; valueA = isString(valueA) ? valueA.toLowerCase() : valueA; valueB = isString(valueB) ? valueB.toLowerCase() : valueB; if (valueB > valueA) { return -1; } if (valueB < valueA) { return 1; } return 0; }, dateStrings: (a: any, b: any, id: any) => sort.dateStringsAsc(a.values[id], b.values[id]), }), [] ), }, useFilters, useSortBy, usePagination, useRowSelect ); const { sortBy, selectedRowIds } = tableState; // Listen for changes to filters if clientSideFilter is enabled const setDebouncedClientFilter = useDebouncedCallback( (column: string, query: string) => { setFilter(column, query); }, 300 ); useEffect(() => { if (isClientSideFilter && onResultsCountChange) { onResultsCountChange(rows.length); } }, [isClientSideFilter, onResultsCountChange, rows.length]); useEffect(() => { if (isClientSideFilter && searchQueryColumn) { setDebouncedClientFilter(searchQueryColumn, searchQuery || ""); } }, [searchQuery, searchQueryColumn]); useEffect(() => { if (isClientSideFilter && selectedDropdownFilter) { selectedDropdownFilter === "all" ? setDebouncedClientFilter("platforms", "") : setDebouncedClientFilter("platforms", selectedDropdownFilter); } }, [selectedDropdownFilter]); useEffect(() => { toggleAllRowsSelected(false); }, [clearSelectionCount]); // This is used to listen for changes to sort. If there is a change // Then the sortHandler change is fired. useEffect(() => { const column = sortBy[0]; if (column !== undefined) { if ( column.id !== sortHeader || column.desc !== (sortDirection === "desc") ) { onSort(column.id, column.desc); } } else { onSort(undefined); } }, [sortBy, sortHeader, onSort, sortDirection]); useEffect(() => { if (isAllPagesSelected) { toggleAllRowsSelected(true); } }, [isAllPagesSelected, toggleAllRowsSelected]); useEffect(() => { setPageSize(CLIENT_SIDE_DEFAULT_PAGE_SIZE); }, [setPageSize]); useDeepEffect(() => { if ( Object.keys(selectedRowIds).length < rows.length && toggleAllPagesSelected ) { toggleAllPagesSelected(false); } }, [tableState.selectedRowIds, toggleAllPagesSelected]); const onToggleAllPagesClick = useCallback(() => { toggleAllPagesSelected(); }, [toggleAllPagesSelected]); const onClearSelectionClick = useCallback(() => { toggleAllRowsSelected(false); toggleAllPagesSelected(false); }, [toggleAllPagesSelected, toggleAllRowsSelected]); const onSingleRowClick = useCallback( (row) => { if (disableMultiRowSelect) { row.toggleRowSelected(); onSelectSingleRow && onSelectSingleRow(row); toggleAllRowsSelected(false); } }, [disableMultiRowSelect, onSelectSingleRow, toggleAllRowsSelected] ); const renderColumnHeader = (column: HeaderGroup) => { return (
{selectedFlatRows.length} {isAllPagesSelected && "+"} {" "} selected
); }; const renderAreAllSelected = (): JSX.Element | null => { if (isAllPagesSelected) { returnAll matching {resultsTitle} are selected
; } if (isAllRowsSelected) { returnAll {resultsTitle} on this page are selected
; } return null; }; const renderActionButton = ( actionButtonProps: IActionButtonProps ): JSX.Element => { const { name, onActionButtonClick, buttonText, targetIds, variant, hideButton, icon, iconPosition, } = actionButtonProps; return (| {headerGroups[0].headers[0].render("Header")} |
{renderSelectedCount()}
{secondarySelectActions && renderSecondarySelectActions()}
{primarySelectActionButtonText &&
renderPrimarySelectAction()}
{toggleAllPagesSelected && renderAreAllSelected()}
{shouldRenderToggleAllPages && (
)}
|
|---|---|
| {renderColumnHeader(column)} | ))}|
| {cell.render("Cell")} | ); })}