Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

custom-table-search

noobster101-ui59MIT1.2.8TypeScript support: included

A customizable, dynamic table component with advanced search, sorting, and pagination for React.

custom-table, table-search, react-table, data-table, pagination, sorting, filtering, grid-view, react-component, advanced-search, dynamic-table, frontend, UI-component

readme

custom-table-search is a React component that provides a dynamic, customizable table with features like advanced search, sorting, pagination, and a grid view toggle. It’s ideal for applications needing flexible data exploration and presentation. Features

Customizable Columns: Define columns with options like labels, searchability, and sortability. Advanced Search and Filtering: Apply multiple conditions with SQL-like connectors (AND, OR). Sorting: Sort data by clicking column headers. Pagination: Control page size and navigate through records. Grid and Table Views: Toggle between grid and table views for versatile data display.

Features

  • Customizable Columns: Define table columns with labels, sortability, and search options.
  • Use customRenderer to display custom content like buttons or styled text in specific columns.
  • Advanced Search and Filtering: Apply multiple conditions with SQL-like connectors (AND, OR) for complex queries.
  • Sorting: Sort data by clicking on column headers.
  • Pagination: Control page size and navigate through data records.
  • Grid and Table Views: Toggle between grid and table views.
  • Action Buttons: Add custom action buttons (e.g., Edit, Delete, View) in rows to perform specific actions.

Installation

To install custom-table-search, run the following command:

npm install custom-table-search

Example 1: Static Data

This example uses local static data and showcases pagination, sorting, and action buttons.

import React, { useState, useEffect } from "react";
import TableCustom from "custom-table-search";
import "bootstrap/dist/css/bootstrap.min.css";

const sampleData = [
{ id: 1, name: "John Doe", email: "john@example.com", status: "Active" },
{ id: 2, name: "Jane Smith", email: "jane@example.com", status: "Inactive" },
{ id: 3, name: "Alice Johnson", email: "alice@example.com", status: "Active" },
// Add more data as needed
];

    const StaticDataPage = () => {
    const [data, setData] = useState([]);
    const [currentPage, setCurrentPage] = useState(1);
    const [pageSize, setPageSize] = useState(5); // Define setPageSize here
    const [totalRecords, setTotalRecords] = useState(sampleData.length);
    const customEntriesOptions = [5, 10, 20, 50];
    const customDefaultPageSize = 10;

    const columns = [
        { key: "id", label: "ID", sortable: true },
        { key: "name", label: "Name", sortable: true, searchable: true },
        { key: "email", label: "Email", sortable: true, searchable: true },
        {
          key: "status",
          label: "Status",
          textAlign: "center",
          customRenderer: (row) => (
            <span
                style={{
                    color: row.status === "Active" ? "green" : "red",
                }}
            >
                {row.status}
            </span>
        ),
        },
        {
          key: "actions",
          label: "Actions",
          textAlign: "center",
          customRenderer: (row) => (
            <button
                className="btn btn-primary"
                onClick={() => alert(`Action for ${row.name}`)}
            >
                Action
            </button>
          ),
        },
    ];


    // Pagination function
    const fetchPage = (page, size) => {
        setCurrentPage(page);
        setPageSize(size);
        const startIdx = (page - 1) * size;
        setData(sampleData.slice(startIdx, startIdx + size));
    };

    useEffect(() => {
        fetchPage(currentPage, pageSize);
    }, [currentPage, pageSize]);

    return (
            <div className="container mt-5">
                <h2>Static Data Table</h2>
                <TableCustom
                    data={data}
                    columns={columns}
                    gridViewEnabled={true}
                    entriesEnabled={true}
                    paginationEnabled={true}
                    searchEnabled={true}
                    currentPage={currentPage}
                    setCurrentPage={setCurrentPage}
                    fetchPage={fetchPage}
                    pageSize={pageSize}
                    setPageSize={setPageSize} // Pass setPageSize here
                    totalRecords={totalRecords}
                    entriesOptions={customEntriesOptions}
                    defaultPageSize={customDefaultPageSize}
                    bgColor="#111"
                    txtColor="#fff"
                    borderColor="#ddd"
                />
            </div>
        );
    };

    export default StaticDataPage;

Example 2: API Data

This example uses local static data and showcases pagination, sorting, and action buttons.

import React, { useRef, useState, useEffect } from "react";
import TableCustom from "custom-table-search";

import "bootstrap/dist/css/bootstrap.min.css";

const sampleData = [
{ id: 1, name: "John Doe", email: "john@example.com", status: "Active" },
{ id: 2, name: "Jane Smith", email: "jane@example.com", status: "Inactive" },
{ id: 3, name: "Alice Johnson", email: "alice@example.com", status: "Active" },
// Add more data as needed
];

const ApiDataExample = () => {
const tableRef = useRef();
const [data, setData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(12);
const [totalRecords, setTotalRecords] = useState(sampleData.length);
// const customEntriesOptions = [5, 10, 20, 50];
// const customDefaultPageSize = 10;

const columns = [
    { key: "id", label: "ID", sortable: true },
    { key: "name", label: "Name", sortable: true, searchable: true },
    { key: "email", label: "Email", sortable: true, searchable: true },
    { key: "status", label: "Status", sortable: true, searchable: true },
];

// Function to fetch page data (simulating an API call)
const fetchPage = async (page, size) => {
    setCurrentPage(page);
    setPageSize(size);
    const startIdx = (page - 1) * size;
    const pagedData = sampleData.slice(startIdx, startIdx + size); // Simulate API data slice
    setData(pagedData);
    setTotalRecords(sampleData.length); // Set the total record count based on data length
};

useEffect(() => {
    const criteria = tableRef.current?.getSearchCriteria?.();
    const sort = tableRef.current?.getSortConfig?.();
    fetchPage(currentPage, pageSize, criteria, sort);
}, []);

return (
        <div className="container mt-5">
            <h2>API Data Table</h2>
            <TableCustom
                ref={tableRef}
                data={data}
                columns={columns}
                paginationEnabled={true}
                currentPage={currentPage}
                setCurrentPage={setCurrentPage}
                fetchPage={fetchPage} // Pass fetchPage to handle pagination
                pageSize={pageSize} // Current page size
                setPageSize={setPageSize} // Function to change page size
                totalRecords={totalRecords}
                // entriesOptions={customEntriesOptions}
                // defaultPageSize={customDefaultPageSize}
            />
        </div>
    );
};

export default ApiDataPage;

Example Explanation

- StaticDataTable:
    - Uses a local array (sampleData) and paginates by slicing the array based on currentPage and pageSize.
    - Action buttons for each row to demonstrate how custom buttons work within the table.
- ApiDataTable:
    - Fetches data from a mock API with pagination and page size passed as parameters.
    - Action buttons are also included here, demonstrating row-specific interactions with API-based data.

Props

Core Props

Prop Type Default Description
data Array [] Array of data objects for each row.
columns Array [] Column configuration with options like label, searchability, and sortability.
gridViewEnabled Boolean true Enables toggle between grid and table view.
entriesEnabled Boolean true Enables page size dropdown to control entries per page.
paginationEnabled Boolean true Enables pagination controls for navigating pages.
searchEnabled Boolean true Enables column-specific search functionality.
currentPage Number 1 Current page number for pagination.
setCurrentPage Function Required Function to update the current page.
fetchPage Function Required Function to fetch data for the specified page and size, taking search/sort parameters.
pageSize Number 12 Number of records per page.
setPageSize Function Required Function to set the number of records per page.
totalRecords Number 0 Total number of records for accurate pagination.
bgColor String #f8f9fa Background color for the table.
txtColor String #333333 Text color for the table.

Column Configuration

Each column object in the columns array can have the following properties:

Column Property Type Default Description
key String Required Unique identifier for the column.
label String "" Display name for the column header.
sortable Boolean false Allows sorting when true.
searchable Boolean false Enables search/filtering for this column.
textAlign String "left" Aligns text within the column (e.g., "center").
type String "text" Input type for search fields (e.g., "text", "email").

Object Creted on Search and Filter

Example : For a search where the user is filtering by name and email columns with multiple conditions, the searchCriteria payload might look like this:

[
    {
        "column": "name",
        "conditions": [
        {
            "value": "John Doe",
            "operator": "LIKE",
            "localConnector": "OR"
        },
        {
            "value": "Alice Johnson",
            "operator": "LIKE"
        }
        ],
        "columnConnector": "AND"
    },
    {
        "column": "email",
        "conditions": [
        {
            "value": "john@example.com",
            "operator": "LIKE",
            "localConnector": "OR"
        },
        {
            "value": "alice@example.com",
            "operator": "LIKE"
        }
        ],
        "columnConnector": null
    }
]

This object can be included in the body of an API request for server-side filtering. The API can interpret this payload to construct a query like:

WHERE (name LIKE 'John Doe' OR name LIKE 'Alice Johnson')
AND (email LIKE 'john@example.com' OR email LIKE 'alice@example.com')

Parsing searchCriteria in Your Backend

On the server side, you’ll need to parse searchCriteria to generate a SQL or database query. For example:

-Loop through each column in the searchCriteria. -Apply each condition in the column, using localConnector to connect multiple conditions within a column. -Combine columns using columnConnector to achieve the full logical structure.

Example (in pseudo-code):

// Example pseudo-code for building a query string from searchCriteria
let query = "SELECT * FROM table WHERE ";
searchCriteria.forEach((columnObj, colIndex) => {
    const columnQuery = columnObj.conditions.map((cond, condIndex) => {
        return `${columnObj.column} ${cond.operator} '${cond.value}'` +
        (cond.localConnector ? ` ${cond.localConnector} ` : "");
    }).join("");

    query += columnQuery + (columnObj.columnConnector ? ` ${columnObj.columnConnector} ` : "");
});

Tips for Using the searchCriteria Object

  • SQL Injection Safety: If using SQL, ensure values are sanitized or parameterized to avoid SQL injection risks.
  • Flexible Conditions: With operator, localConnector, and columnConnector, you can build highly customizable queries, ideal for advanced filtering.
  • Testing: Test searchCriteria generation and API handling with various combinations to ensure robust handling of complex queries.

This searchCriteria object structure enables powerful and flexible search capabilities for your data, allowing precise filtering directly from the frontend component.

Example : Fetching data Search Inputs

{
    "page": 1,
    "size": 10,
    "searchCriteria": [
        {
        "column": "name",
        "conditions": [
            {
            "value": "John Doe",
            "operator": "LIKE",
            "localConnector": "OR"
            },
            {
            "value": "Alice Johnson",
            "operator": "LIKE"
            }
        ],
        "columnConnector": "AND"
        },
        {
        "column": "email",
        "conditions": [
            {
            "value": "john@example.com",
            "operator": "LIKE",
            "localConnector": "OR"
            },
            {
            "value": "alice@example.com",
            "operator": "LIKE"
            }
        ],
        "columnConnector": null
        }
    ],
    "sortConfig": []
}

Example Usage in API Calls

Here’s how you might use the searchCriteria object as a payload for an API call to retrieve filtered data.

const fetchData = async (page, size, searchCriteria, sortConfig) => {
    try {
        const response = await fetch('/api/your-endpoint', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            page,
            size,
            searchCriteria,
            sortConfig
        })
        });
        const result = await response.json();
        // Handle the data from the response, e.g., update state
        setData(result.data);
        setTotalRecords(result.totalRecords);
    } catch (error) {
        console.error("Error fetching data:", error);
    }
};

Action Buttons

Action buttons (Edit, Delete, View) are added to each row by configuring the data prop with custom actions. Each button is attached to a function to handle its respective action:

const dataWithActions = data.map((row) => ({
...row,
actions: (
    <div className="d-flex justify-content-center">
        <button className="btn btn-primary btn-sm me-2" onClick={() => handleEdit(row)}>Edit</button>
        <button className="btn btn-danger btn-sm me-2" onClick={() => handleDelete(row)}>Delete</button>
        <button className="btn btn-info btn-sm" onClick={() => handleView(row)}>View</button>
    </div>
),
}));

These buttons perform specific functions when clicked, allowing the user to interact with each row in the table. Customize the icons and actions as needed.## Advanced Usage

Search Conditions and SQL-like Filters

  • Multiple Conditions per Column: Apply multiple conditions per column (e.g., LIKE, EQUAL, CONTAINS).
  • Logical Operators: Use AND or OR to connect conditions for complex queries.

Custom Actions

Add action buttons (e.g., Edit, Delete) by including an actions key in your data. Customize these buttons to handle events such as editing, deleting, or viewing details of a row.

Styling

The component uses standard Bootstrap styles. To customize further, apply your own CSS or Bootstrap overrides. Troubleshooting

Dependency Conflicts: Ensure you have compatible versions of react, react-dom, and react-bootstrap. Styling Issues: Ensure Bootstrap is included in your project or adjust styles as needed. Pagination Limits: Ensure totalRecords accurately reflects the number of entries for pagination to work correctly.

Troubleshooting

  • Dependency Conflicts: Ensure you have compatible versions of react, react-dom, and react-bootstrap.
  • Styling Issues: Ensure Bootstrap is included in your project or adjust styles as needed.
  • Pagination Limits: Ensure totalRecords accurately reflects the number of entries for pagination to work correctly.

Contributing

Feel free to submit issues or pull requests for bug fixes or enhancements. Contributions are welcome!

License

This project is licensed under the MIT License.