mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
4.12k stars 1.28k forks source link

[DataGrid] Quick filter causes error in combination of row grouping and custom aggregation #9677

Closed evgeniiarts closed 1 year ago

evgeniiarts commented 1 year ago

Duplicates

Latest version

Steps to reproduce ๐Ÿ•น

Link to live example: https://codesandbox.io/s/intelligent-monad-rp9tdm?file=/src/App.js

Steps:

  1. Try the quick filter initially and verify that it works.
  2. Apply a second grouping criteria, such as "Year" column in addition to initial state.
  3. Type in a value into the quick filter and see the error "No row with id #undefined found"

Current behavior ๐Ÿ˜ฏ

The quick filter does not work when applying a custom aggregation function and grouping rows at the same time.

Expected behavior ๐Ÿค”

The quick filter should deliver results regardless of grouping and aggregation because it is an important way of narrowing down information within a large dataset.

Context ๐Ÿ”ฆ

I am working on large datagrids for financial applications where my users need to narrow down information in various ways, one of which is the quick filter. At the same time, I use custom aggregation functions to display total statuses of certain datasets. The features all together would be a great and useful combination for efficient daily workflows.

Your environment ๐ŸŒŽ

npx @mui/envinfo ``` System: OS: Windows 10 10.0.22621 Binaries: Node: 18.12.0 - C:\Program Files\nodejs\node.EXE Yarn: 1.22.19 - ~\AppData\Roaming\npm\yarn.CMD npm: 9.8.0 - C:\Program Files\nodejs\npm.CMD Browsers: Chrome: Version 114.0.5735.199 (Official Build) (64-bit) Edge: Spartan (44.22621.1992.0), Chromium (114.0.1823.79) npmPackages: @emotion/react: ^11.11.1 => 11.11.1 @emotion/styled: ^11.11.0 => 11.11.0 @mui/icons-material: ^5.13.7 => 5.14.0 @mui/material: ^5.13.7 => 5.14.0 @mui/x-data-grid-generator ^6.10.0 => 6.10.0 @mui/x-data-grid-premium: ^6.10.0 => 6.10.0 react: ^18.2.0 => 18.2.0 react-dom: ^18.2.0 => 18.2.0 ```

Order ID or Support key ๐Ÿ’ณ (optional)

No response

m4theushw commented 1 year ago

Is it not related to https://github.com/mui/mui-x/issues/9666?

cherniavskii commented 1 year ago

@m4theushw Might be, it used to work fine with 6.9.0 - https://codesandbox.io/s/reverent-aj-q4h9ry

boldurean commented 1 year ago

This issue also exists when you re-generate rows based on "filter" or "categories" outside the grid. Imagine the case:

const { rows, columns } = useMemo(
    () => generateData(data, statusFilter, handleClickReview),
    [data, handleClickReview, statusFilter]
  );

If status filter changes it generateData supposed to filter rows inside based on new value. Grid brakes with same Error msg. "No row with id #{your id} found"

evgeniiarts commented 1 year ago

In case this helps someone, my current workaround is to create a custom quick filter within the GridToolbar:

import React, { useState, useEffect } from "react";
import {
    GridToolbarContainer,
    GridToolbarColumnsButton,
    GridToolbarFilterButton,
    GridToolbarDensitySelector,
    GridToolbarExport,
    gridFilteredSortedRowIdsSelector,
} from "@mui/x-data-grid-premium";
import Input from "@mui/material/Input";
import IconButton from "@mui/material/IconButton";
import InputAdornment from "@mui/material/InputAdornment";
import SearchIcon from "@mui/icons-material/Search";
import ClearIcon from '@mui/icons-material/Clear';

// pass apiRef and initial rows to custom toolbar
function CustomToolbar({ apiRef, rows }) {
    const [quickFilter, setQuickFilter] = useState("");

    const handleQuickFilterChange = (event) => {
        setQuickFilter(event.target.value);
    };
    const handleClearClick = () => {
        setQuickFilter("");
    };

    useEffect(() => {
        const visibleRowIds = gridFilteredSortedRowIdsSelector(apiRef.current.state);
        const visibleRows = visibleRowIds.map((id) => apiRef.current.getRow(id));

        const lowerCaseQuickFilter = quickFilter.toLowerCase();

        // Rows to delete (not matching the quickFilter)
        const rowsToDelete = visibleRows.filter((row) => {
            if (row?.id) {
                return !Object.values(row).some((cellValue) => {
                    return String(cellValue).toLowerCase().includes(lowerCaseQuickFilter);
                });
            }
            return false;
        });

        // Rows to add (missing from visibleRows, but matching the quickFilter in initial rows)
        const rowsToAdd = rows.filter((row) => {
            if (row?.id && !visibleRows.some((visibleRow) => visibleRow.id === row.id)) {
                return Object.values(row).some((cellValue) => {
                    return String(cellValue).toLowerCase().includes(lowerCaseQuickFilter);
                });
            }
            return false;
        });

        // Delete rows not matching the quickFilter
        apiRef.current.updateRows(rowsToDelete.map((row) => ({ id: row.id, _action: "delete" })));

        // Add rows from initial rows that match the quickFilter
        apiRef.current.updateRows(rowsToAdd);
    }, [apiRef, quickFilter]);

    return (
        <GridToolbarContainer>
            <div style={{ display: "flex", width: "100%", padding: "8px" }}>
                <GridToolbarColumnsButton />
                <GridToolbarFilterButton />
                <GridToolbarDensitySelector />
                <GridToolbarExport printOptions={{ allColumns: true }} csvOptions={{ allColumns: true }} excelOptions={{ allColumns: true }} />
                <div style={{ marginLeft: "auto" }}>
                    <Input
                        id="quicksearch"
                        variant="standard"
                        placeholder="Quicksearch..."
                        value={quickFilter}
                        onChange={handleQuickFilterChange}
                        startAdornment={
                            <InputAdornment position="start">
                                <SearchIcon />
                            </InputAdornment>
                        }
                        endAdornment={
                            <IconButton sx={{ visibility: quickFilter ? "visible" : "hidden" }} onClick={handleClearClick}>
                                <ClearIcon />
                            </IconButton>
                        }
                    />
                </div>
            </div>
        </GridToolbarContainer>
    );
}

export default CustomToolbar;

Keep in mind that it filters only the rowNodes of lowest level, meaning the original rows which were used in the data grid. Not the auto-generated group rows.

romgrk commented 1 year ago

Can you clarify the reproduction steps?

evgeniiarts commented 1 year ago

Yes, of course.

  1. Navigate to this sample: https://codesandbox.io/s/intelligent-monad-rp9tdm?file=/src/App.js
  2. Scroll to the right of the data grid and select "Group by year" from the "Year" column's menu
  3. Type in anything into the quick filter and see the error appearing.
cherniavskii commented 1 year ago

Thanks, @evgeniiarts! It looks like this fix from https://github.com/mui/mui-x/pull/9708 is not enough to fix the issue in this case: https://codesandbox.io/s/reverent-feather-3qd5qf cc @romgrk