GGotchaA
GGotchaA

Reputation: 295

Search bar, <input/>

Hello everyone :D I need your advise/tip. Right now I have a APIDataTable component. It has its rows, columns and etc. This component is responsible to show/build data table on frontend with search bar in it above the table. I have an search bar, which is not functional right now. I want it to search data from data table. What should I start from? How can i make it perform search in Table. Thank you for any advise and tip <3

import React, { useEffect, useState } from "react";
import { plainToClassFromExist } from "class-transformer";
import { Pagination } from "../../models/Pagination";
import {
  DataTable,
  DataTableHead,
  DataTableHeadCell,
  DataTableBody,
  DataTableRow,
  DataTableCell,
} from "../DataTable";
import { request } from "../../api";
import "./index.css";
import { MenuSurface } from "../MenuSurface";
import { IconButton } from "../IconButton";
import { Checkbox } from "../Checkbox";
import { Dialog } from "../Dialog";
import { GridCell, GridRow } from "../Grid";
import { Button } from "../Button";

export class Column<T> {
  label: string;
  width?: number;
  filter?: JSX.Element;
  render: (row: T) => JSX.Element | string;

  constructor(column: Partial<Column<T>>) {
    Object.assign(this, column);
  }
}

type APIDataTableProps<T> = {
  apiPath?: string;
  params?: string;
  columns?: Column<T>[];
  type: Function;
  onRowClick?: (row: T) => void;
};

export const APIDataTable = <T extends object>({
  apiPath,
  params,
  columns,
  type,
  onRowClick,
}: APIDataTableProps<T>) => {
  const [data, setData] = useState<Pagination<T>>(null);
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState(15);
  const [isLoading, setIsLoading] = useState(false);
  const [isDialogOpen, setDialogOpen] = useState(false);

  const [isMenuSurFaceOpen, setMenuSurfaceOpen] = useState(false);
  const [hiddenColumns, setHiddenColumns] = useState<number[]>(
    JSON.parse(localStorage.getItem(`hiddenColumns-${apiPath + params}`)) || []
  );

  const fetchData = async () => {
    const urlSearchParams = new URLSearchParams(params);

    urlSearchParams.set("page", page.toString());
    urlSearchParams.set("page_size", pageSize.toString());

    const url = `${apiPath}?${urlSearchParams}`;

    const response = await request(url);

    const data = plainToClassFromExist(new Pagination<T>(type), response, {
      excludeExtraneousValues: true,
    });

    setData(data);

    setIsLoading(false);
  };

  useEffect(() => {
    if (!!apiPath) {
      setIsLoading(true);
      fetchData();
    }
  }, [page, pageSize]);

  const headCells = columns
    .filter((e, i) => !hiddenColumns?.includes(i))
    .map((column) => (
      <DataTableHeadCell key={column.label} width={column.width}>
        {column.label}
      </DataTableHeadCell>
    ));

  const rows = data?.results?.map((row, index) => (
    <DataTableRow
      key={"row-" + index}
      onClick={() => !!onRowClick && onRowClick(row)}
    >
      {columns
        .filter((e, i) => !hiddenColumns?.includes(i))
        .map((column) => {
          return (
            <DataTableCell key={column.label} width={column.width}>
              <div className="data-table-cell-text">{column.render(row)}</div>
            </DataTableCell>
          );
        })}
    </DataTableRow>
  ));

  let uncheckedCheckboxes = hiddenColumns;

  const onCheckboxChange = (index: number, value: boolean) => {
    if (!value) {
      uncheckedCheckboxes.push(index);
      //setHiddenColumns(uncheckedCheckboxes);
    } else {
      const array = [...uncheckedCheckboxes];
      array.splice(array.indexOf(index), 1);

      uncheckedCheckboxes = array;
    }
  };
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div className="data-table-container">
      <div className="search-test">
        <div className="mdc-menu-surface--anchor">
          <label
            className="mdc-text-field mdc-text-field--filled mdc-text-field--no-label mdc-text-field--with-leading-icon mdc-text-field--with-trailing-icon"
            htmlFor="input"
            id="search-menu-surface"
          >
            <IconButton density={-1} icon="search" />
            <input
              className="mdc-text-field__input "
              type="text"
              placeholder="Поиск"
              id="searchinput"
              
            />

            <IconButton
              density={-1}
              icon="arrow_drop_down"
              onClick={() => {
                setMenuSurfaceOpen(true);
              }}
            />
          </label>
          <MenuSurface
            isOpen={isMenuSurFaceOpen}
            onClose={() => setMenuSurfaceOpen(false)}
            fullwidth
          >
            <div className="data-table-filters-container">
              {columns.map(
                (column) =>
                  !!column.filter && (
                    <div className="data-table-filter">
                      <div className="data-table-filter-label mdc-typography--subtitle1">
                        {column.label}
                      </div>
                      <div className="data-table-column-filter">
                        {column.filter}
                      </div>
                    </div>
                    // <GridRow>
                    //   <GridCell span={3}>{column.label}</GridCell>

                    //   <GridCell span={3}>{column.filter}</GridCell>
                    // </GridRow>
                  )
              )}
              {/* <GridCell span={2}> */}
              {/* <Button label="Поиск" raised /> */}
              {/* <Button
                  label="Отмена"
                  raised
                  onClick={() => {
                    setIsOpen(false);
                  }}
                /> */}
              {/* </GridCell> */}
            </div>
          </MenuSurface>
        </div>
        <IconButton
          onClick={() => {
            setDialogOpen(true);
          }}
          density={-1}
          icon="settings"
        />
        <Dialog
          isOpen={isDialogOpen}
          onOkClick={() => {
            localStorage.setItem(
              `hiddenColumns-${apiPath + params}`,
              JSON.stringify(uncheckedCheckboxes)
            );
            setDialogOpen(false);
            setHiddenColumns(uncheckedCheckboxes);
          }}
          onCloseClick={() => setDialogOpen(false)}
        >
          <div
            style={{
              display: "flex",
              flexDirection: "column",
            }}
          >
            {columns.map((column, index) => (
              <Checkbox
                label={column.label}
                onChange={(value) => onCheckboxChange(index, value)}
                defaultChecked={!uncheckedCheckboxes.includes(index)}
              />
            ))}
          </div>
        </Dialog>
      </div>

      <DataTable
        pagination={true}
        count={data?.count}
        rowsNumber={data?.results.length}
        page={page}
        next={data?.next}
        previous={data?.previous}
        isLoading={isLoading}
        onNextClick={() => setPage(page + 1)}
        onPreviosClick={() => setPage(page - 1)}
      >
        <DataTableHead>{headCells}</DataTableHead>
        <DataTableBody>{rows}</DataTableBody>
      </DataTable>
    </div>
  );
};

Upvotes: 0

Views: 276

Answers (1)

Michael Bauer
Michael Bauer

Reputation: 465

I'm guessing that you want to search bar to effectively filter out rows that don't match. in this case what you want to do is add a filter to the search text (naturally you'll add a state for the search value, but it looks like you'll have that handled.

You'll add your filter here const rows = data?.results?.filter(...).map

You filter function will look something like this

const rows = data?.results.filter((row) => {
    // In my own code if I have other filters I just make them return false
    // if they don't match
    if (
      searchText &&
      !(
       // exact match example
       row.field === searchText ||
       // case-insensitive example
        row.otherField?.toLowerCase().includes(searchText)
       // can continue with '||' and matching any other field you want to search by
      )
    )
      return false;

    return true;
}).map(...)

Upvotes: 1

Related Questions