function Table()

in frontend/src/components/Table.tsx [57:667]


function Table(props: Props) {
  const { t } = useTranslation();
  const windowSize = useWindowSize();
  const isMobile = props.mobileNavigation || windowSize.width <= 600;
  const borderlessClassName = !props.disableBorderless
    ? " usa-table--borderless"
    : "";
  const className = props.className ? ` ${props.className}` : "";
  const [currentPage, setCurrentPage] = useState<string>("1");

  const {
    initialSortByField,
    initialSortAscending,
    sortByColumn,
    sortByDesc,
    setSortByColumn,
    setSortByDesc,
    reset,
  } = props;

  const initialSortBy = useMemo(() => {
    return initialSortByField
      ? [
          {
            id: initialSortByField,
            desc: !initialSortAscending,
          },
        ]
      : [];
  }, [initialSortByField, initialSortAscending]);

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    toggleSortBy,
    prepareRow,
    rows,
    page,
    canPreviousPage,
    canNextPage,
    pageOptions,
    pageCount,
    gotoPage,
    nextPage,
    previousPage,
    setPageSize,
    state: { pageIndex, pageSize },
    selectedFlatRows,
    setGlobalFilter,
    toggleAllRowsSelected,
  } = useTable(
    {
      columns: props.columns,
      data: props.rows,
      disableSortRemove: true,
      initialState: props.disablePagination
        ? {
            selectedRowIds: {},
            sortBy: initialSortBy,
          }
        : {
            selectedRowIds: {},
            sortBy: initialSortBy,
            pageIndex: 0,
            pageSize: props.pageSize || 25,
          },
    },
    useGlobalFilter,
    useSortBy,
    usePagination,
    useRowSelect,
    (hooks) => {
      if (props.selection === "single") {
        hooks.visibleColumns.push((columns) => [
          {
            id: "selection",
            Header: ({}) => <div></div>,
            Cell: ({ row }) => (
              <div>
                <IndeterminateRadio
                  onClick={() => {
                    toggleAllRowsSelected(false);
                    row.toggleRowSelected();
                  }}
                  {...row.getToggleRowSelectedProps()}
                />
              </div>
            ),
          },
          ...columns,
        ]);
      } else if (props.selection === "multiple") {
        hooks.visibleColumns.push((columns) => [
          {
            id: "selection",
            Header: ({ getToggleAllRowsSelectedProps }) => (
              <IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
            ),
            Cell: ({ row }) => (
              <IndeterminateCheckbox
                {...row.getToggleRowSelectedProps()}
                title={
                  props.screenReaderField
                    ? row.values[props.screenReaderField]
                    : null
                }
              />
            ),
          },
          ...columns,
        ]);
      } else if (props.addNumbersColumn) {
        hooks.visibleColumns.push((columns) => [
          {
            id: "numbersListing",
            Header: () => {
              return <div>1</div>;
            },
            Cell: ({ row }) => {
              return <div>{row.index + 2}</div>;
            },
          },
          ...columns,
        ]);
      }
    }
  );

  useEffect(() => {
    if (setSortByColumn && setSortByDesc && reset) {
      for (const headerGroup of headerGroups) {
        for (const header of headerGroup.headers) {
          if (
            header.isSorted &&
            (sortByColumn !== header.id || sortByDesc !== header.isSortedDesc)
          ) {
            reset({
              sortData: header.id
                ? `${header.id}###${header.isSortedDesc ? "desc" : "asc"}`
                : "",
            });
            setSortByColumn(header.id);
            setSortByDesc(header.isSortedDesc);
            break;
          }
        }
      }
    }
  }, [rows]);

  useEffect(() => {
    if (sortByColumn) {
      toggleSortBy(sortByColumn, sortByDesc);
    }
  }, [sortByColumn, sortByDesc]);

  const { onSelection, filterQuery } = props;
  useEffect(() => {
    setGlobalFilter(filterQuery);
  }, [filterQuery, setGlobalFilter]);

  useEffect(() => {
    if (onSelection) {
      const values = selectedFlatRows.map((flatRow) => flatRow.original);
      onSelection(values);
    }
  }, [selectedFlatRows, onSelection]);

  const currentRows = props.disablePagination ? rows : page;

  const getCellBackground = useCallback(
    (id: string, defaultColor: string) => {
      if (id.startsWith("checkbox")) {
        for (const selectedHeader of Array.from(props.selectedHeaders ?? [])) {
          if (id.includes(selectedHeader)) {
            return "#97d4ea";
          }
        }
        return defaultColor;
      } else {
        return props.selectedHeaders?.has(id) ? "#97d4ea" : defaultColor;
      }
    },
    [props.selectedHeaders]
  );

  return (
    <div className="overflow-x-hidden overflow-y-hidden">
      <table
        className={`usa-table${borderlessClassName}${className}`}
        width={props.width}
        {...getTableProps()}
      >
        <thead>
          {headerGroups.map((headerGroup) => (
            <tr {...headerGroup.getHeaderGroupProps()}>
              {headerGroup.headers.map((column, i) => (
                <th
                  scope="col"
                  {...column.getHeaderProps()}
                  aria-sort={
                    column.isSorted
                      ? column.isSortedDesc
                        ? "desc"
                        : "asc"
                      : ""
                  }
                  style={
                    props.selection !== "none"
                      ? {
                          padding: "0.5rem 1rem",
                        }
                      : {
                          minWidth: column.minWidth,
                          backgroundColor: `${getCellBackground(
                            column.id,
                            ""
                          )}`,
                        }
                  }
                >
                  <span>
                    {
                      /**
                       * The split is to remove the quotes from the
                       * string, the filter to remove the resulted
                       * empty ones, and the join to form it again.
                       */
                      typeof column.render("Header") === "string"
                        ? (column.render("Header") as string)
                            .split('"')
                            .filter(Boolean)
                            .join()
                        : column.render("Header")
                    }
                  </span>
                  {(props.selection !== "none" && i === 0) ||
                  (column.id && column.id.startsWith("checkbox")) ||
                  (column.id &&
                    column.id.startsWith("numbersListing")) ? null : (
                    <button
                      className="margin-left-1 usa-button usa-button--unstyled"
                      {...column.getSortByToggleProps()}
                      title={`${t("ToggleSortBy")} ${column.Header}`}
                      type="button"
                    >
                      <FontAwesomeIcon
                        className={`hover:text-base ${
                          column.isSorted ? "text-base-darker" : "text-base"
                        }`}
                        icon={
                          column.isSorted && column.isSortedDesc
                            ? faChevronDown
                            : faChevronUp
                        }
                      />
                    </button>
                  )}
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody {...getTableBodyProps()}>
          {currentRows.map((row) => {
            prepareRow(row);
            return (
              <tr {...row.getRowProps()}>
                {row.cells.map((cell, j) => {
                  return j === 0 && props.selection === "none" ? (
                    <th
                      style={{
                        backgroundColor: `${getCellBackground(
                          cell.column.id,
                          props.addNumbersColumn ? "#f0f0f0" : ""
                        )}`,
                      }}
                      scope="row"
                      {...cell.getCellProps()}
                    >
                      {cell.render("Cell")}
                    </th>
                  ) : (
                    <td
                      style={{
                        backgroundColor: `${getCellBackground(
                          cell.column.id,
                          props.hiddenColumns?.has(cell.column.id)
                            ? "#adadad"
                            : ""
                        )}`,
                      }}
                      {...cell.getCellProps()}
                    >
                      {cell.render("Cell")}
                    </td>
                  );
                })}
              </tr>
            );
          })}
          {!props.disablePagination && rows.length ? (
            <tr role="row">
              <td
                role="cell"
                colSpan={
                  props.columns.length -
                  (props.hiddenColumns ? props.hiddenColumns.size : 0) +
                  (props.title ? 0 : 1)
                }
                className={`button-cell-padding${
                  props.keepBorderBottom ? "" : " button-cell-border"
                }`}
              >
                <div
                  className={`${
                    isMobile ? "grid-col margin-top-2" : "grid-row margin-y-1"
                  } font-sans-sm`}
                >
                  <div className={`${isMobile ? "text-center" : "grid-col-4"}`}>
                    <label
                      htmlFor="Page"
                      className="margin-left-1 margin-right-2px"
                    >{`${t("Page")} `}</label>
                    <span className="margin-right-1">
                      <input
                        id="Page"
                        type="text"
                        value={`${currentPage}`}
                        className="margin-right-2px"
                        onChange={(e) => {
                          setCurrentPage(e.target.value);
                        }}
                        style={{ width: "33px" }}
                        pattern="\d*"
                      />
                      {` of ${pageOptions.length} `}
                    </span>
                    <button
                      type="button"
                      className={`${
                        isMobile ? "" : "usa-button "
                      }usa-button--unstyled margin-right-2 text-base-darker hover:text-base-darkest active:text-base-darkest`}
                      onClick={() => {
                        if (currentPage) {
                          const currentPageNumber = Number(currentPage);
                          if (
                            !isNaN(currentPageNumber) &&
                            currentPageNumber >= 1 &&
                            currentPageNumber <= pageOptions.length
                          ) {
                            gotoPage(currentPageNumber - 1);
                          }
                        }
                      }}
                    >
                      {t("Go")}
                    </button>
                  </div>
                  <div
                    className={`${
                      isMobile ? "margin-left-05" : "grid-col-4"
                    } text-center`}
                  >
                    {!isMobile && (
                      <>
                        <button
                          type="button"
                          className="margin-right-1"
                          onClick={() => {
                            setCurrentPage("1");
                            gotoPage(0);
                          }}
                          disabled={!canPreviousPage}
                          aria-label={t("GoToFirstPage")}
                        >
                          <FontAwesomeIcon
                            icon={faAngleDoubleLeft}
                            className="margin-top-2px"
                            aria-label={t("GoToFirstPage")}
                          />
                        </button>
                        <button
                          type="button"
                          className="margin-right-2"
                          onClick={() => {
                            setCurrentPage(`${pageIndex}`);
                            previousPage();
                          }}
                          disabled={!canPreviousPage}
                          aria-label={t("GoToPrevPage")}
                        >
                          <FontAwesomeIcon
                            icon={faAngleLeft}
                            className="margin-top-2px"
                            aria-label={t("GoToPrevPage")}
                          />
                        </button>
                      </>
                    )}
                    {isMobile ? <div className="margin-top-2" /> : ""}
                    {isMobile && (
                      <>
                        <button
                          type="button"
                          className="margin-right-1"
                          onClick={() => {
                            setCurrentPage("1");
                            gotoPage(0);
                          }}
                          disabled={!canPreviousPage}
                          aria-label={t("GoToFirstPage")}
                        >
                          <FontAwesomeIcon
                            icon={faAngleDoubleLeft}
                            className="margin-top-2px"
                            aria-label={t("GoToFirstPage")}
                          />
                        </button>
                        <button
                          type="button"
                          className="margin-right-2"
                          onClick={() => {
                            setCurrentPage(`${pageIndex}`);
                            previousPage();
                          }}
                          disabled={!canPreviousPage}
                          aria-label={t("GoToPrevPage")}
                        >
                          <FontAwesomeIcon
                            icon={faAngleLeft}
                            className="margin-top-2px"
                            aria-label={t("GoToPrevPage")}
                          />
                        </button>
                      </>
                    )}
                    <button
                      type="button"
                      className="margin-right-1"
                      onClick={() => {
                        setCurrentPage(`${pageIndex + 2}`);
                        nextPage();
                      }}
                      disabled={!canNextPage}
                      aria-label={t("GoToNextPage")}
                    >
                      <FontAwesomeIcon
                        icon={faAngleRight}
                        className="margin-top-2px"
                        aria-label={t("GoToNextPage")}
                      />
                    </button>
                    <button
                      type="button"
                      onClick={() => {
                        setCurrentPage(`${pageCount}`);
                        gotoPage(pageCount - 1);
                      }}
                      disabled={!canNextPage}
                      aria-label={t("GoToLastPage")}
                    >
                      <FontAwesomeIcon
                        icon={faAngleDoubleRight}
                        className="margin-top-2px"
                        aria-label={t("GoToLastPage")}
                      />
                    </button>
                  </div>
                  <div
                    className={`${
                      isMobile
                        ? "margin-top-2 margin-left-05 margin-y-2 text-center"
                        : "grid-col-4 text-right"
                    }`}
                  >
                    <select
                      value={pageSize}
                      onChange={(e) => {
                        setPageSize(Number(e.target.value));
                      }}
                      className="margin-right-05"
                      aria-label={t("SelectPageSize")}
                    >
                      {[5, 10, 20, 25, 50, 100].map((pageSize) => (
                        <option key={pageSize} value={pageSize}>
                          {t("Show")} {pageSize}
                        </option>
                      ))}
                    </select>
                  </div>
                </div>
                <hr className="border-top margin-0" />
                <div
                  className={`${
                    isMobile ? "padding-left-05" : "grid-row"
                  } text-base-darker text-italic padding-y-05 padding-right-1`}
                >
                  {isMobile && (
                    <div className="text-center">
                      {t("ShowingPages", {
                        startItem: pageIndex * pageSize + 1,
                        endItem: Math.min(
                          pageIndex * pageSize + pageSize,
                          rows.length
                        ),
                        totalItems: rows.length,
                      })}
                    </div>
                  )}
                  {props.title && (
                    <div
                      className={`${
                        isMobile
                          ? "text-center margin-top-05"
                          : "grid-col-6 text-left"
                      }`}
                    >
                      <div style={{ display: "inline-flex" }}>
                        <FontAwesomeIcon
                          icon={faDownload}
                          className="margin-right-1"
                          size="xs"
                        />
                      </div>
                      <div style={{ display: "inline-flex" }}>
                        <div className="margin-right-05">
                          <CSVLink
                            className="text-base-darker"
                            data={props.rows}
                            filename={props.title}
                          >
                            {t("DownloadCSV")}
                          </CSVLink>
                        </div>
                      </div>
                    </div>
                  )}
                  {!isMobile && (
                    <div
                      className={`grid-col-${
                        props.title ? "6" : "12"
                      } text-right`}
                    >
                      {t("ShowingPages", {
                        startItem: pageIndex * pageSize + 1,
                        endItem: Math.min(
                          pageIndex * pageSize + pageSize,
                          rows.length
                        ),
                        totalItems: rows.length,
                      })}
                    </div>
                  )}
                </div>
              </td>
            </tr>
          ) : (
            <>
              {props.title && (
                <tr role="row">
                  <td
                    role="cell"
                    colSpan={
                      props.columns.length -
                      (props.hiddenColumns ? props.hiddenColumns.size : 0) +
                      (props.title ? 0 : 1)
                    }
                    className={`button-cell-padding${
                      props.keepBorderBottom ? "" : " button-cell-border"
                    }`}
                  >
                    <div className="grid-row text-base-darker text-italic padding-y-05 padding-right-1">
                      <div
                        className={`${
                          isMobile
                            ? "text-center margin-top-05"
                            : "grid-col-6 text-left"
                        }`}
                      >
                        <div style={{ display: "inline-flex" }}>
                          <FontAwesomeIcon
                            icon={faDownload}
                            className="margin-right-1"
                            size="xs"
                          />
                        </div>
                        <div style={{ display: "inline-flex" }}>
                          <div className="margin-right-05">
                            <CSVLink
                              className="text-base-darker"
                              data={props.rows}
                              filename={props.title}
                            >
                              {t("DownloadCSV")}
                            </CSVLink>
                          </div>
                        </div>
                      </div>
                    </div>
                  </td>
                </tr>
              )}
            </>
          )}
        </tbody>
      </table>
    </div>
  );
}