skip to Main Content

When I hover over any row in the Datagrid, I want the "Analyze" button to change from variant outlined to contained. I cannot get any event to trigger when the row is hovered over, nor can I find any information on how to update/re-render a cell within that row when the mouse is within that row.

"@mui/x-data-grid": "^5.17.25",
"@mui/x-data-grid-generator": "^6.0.0",
"@mui/x-data-grid-pro": "^6.0.0",

import React, { useRef, useState, useEffect } from "react";
import { DataGrid, GridRowsProp, GridColDef } from "@mui/x-data-grid";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import { useTheme } from "@mui/system";
import Link from "next/link";
import TextField from "@mui/material/TextField";
import InputAdornment from "@mui/material/InputAdornment";
import SearchIcon from "@mui/icons-material/Search";
import AddIcon from "@mui/icons-material/Add";
import CircularProgress from "@mui/material/CircularProgress";
import { alpha, styled, lighten } from "@mui/material/styles";

export default function PropertiesList({ newProperties }) {
  const theme = useTheme();
  const boxRef = useRef(null);
  const [searchText, setSearchText] = useState("");
  const columns = getColumns(theme);

  function getColumns(theme) {
    // commented because irrelevant
    return [
      {
        field: "id",
        headerName: "Actions",
        width: 150,
        renderCell: (params) => {
          return (
            <Box
              sx={{
                display: "flex",
                justifyContent: "space-between",
                width: "100%"
              }}
            >
              <Link
                href="/properties/[id]"
                as={`/properties/${params.row.original_doc || params.row.id}`}
              >
                <Button
                  size="small"
                  variant="outlined"
                  startIcon={<CalculateIcon />}
                  sx={{
                    backgroundColor:
                      hoveredRowId === params.id
                        ? theme.palette.success.main
                        : ""
                  }}
                >
                  Analyze
                </Button>
              </Link>
            </Box>
          );
        }
      }
    ];
  }

  useEffect(() => {
    if (!boxRef.current) return;
    const screenHeight = window.innerHeight;
    boxRef.current.style.height = `${screenHeight - 120}px`;
  }, []);

  const handleRowOver = (params) => {
    // change the analyze button from "outlined" to "contained" when hovered.
    // The below console.log does not trigger.
    console.log(`Row ${params.id} is being hovered over`);
  };

  return (
    <Box ref={boxRef}>
      {!newProperties && (
        <Box
          sx={{
            height: "calc(100vh - 160px)",
            display: "flex",
            justifyContent: "center",
            alignItems: "center"
          }}
        >
          <CircularProgress size={32} />
        </Box>
      )}
      {newProperties && (
        <>
          <Box
            sx={{
              display: "flex",
              justifyContent: "space-between",
              alignItems: "center",
              background: theme.palette.background.background2,
              marginTop: 3,
              // marginBottom: 1,
              padding: 2,
              border: "1px solid " + theme.palette.contrast.contrast1,
              borderTopLeftRadius: 8,
              borderTopRightRadius: 8
            }}
          >
            <TextField
              label="Search for property"
              placeholder=""
              sx={{ marginTop: 1, marginBottom: 1 }}
              onChange={(event) => setSearchText(event.target.value)}
              InputProps={{
                startAdornment: (
                  <InputAdornment position="start">
                    <SearchIcon />
                  </InputAdornment>
                )
              }}
            />

            <Link href="/properties/add">
              <Button
                size="medium"
                variant="contained"
                sx={{ height: 50 }}
                startIcon={<AddIcon />}
              >
                Add Property
              </Button>
            </Link>
          </Box>

          <DataGrid
            rowMouseEnter={handleRowOver}
            sx={{
              border: "1px solid " + theme.palette.contrast.contrast1,
              height: "calc(100vh - 280px)",
              background: theme.palette.background.background1,
              "& .MuiDataGrid-virtualScroller::-webkit-scrollbar": {
                height: "0.4em",
                width: "0.4em"
              },
              "& .MuiDataGrid-virtualScroller::-webkit-scrollbar-track": {
                background: theme.palette.contrast.contrast1
              },
              "& .MuiDataGrid-virtualScroller::-webkit-scrollbar-thumb": {
                backgroundColor: theme.palette.contrast.contrast2
              },
              "& .MuiDataGrid-virtualScroller::-webkit-scrollbar-thumb:hover": {
                background: theme.palette.contrast.default
              },
              borderTopLeftRadius: 0,
              borderTopRightRadius: 0,
              borderBottomLeftRadius: 8,
              borderBottomRightRadius: 8
            }}
            rows={newProperties.filter(
              (row) =>
                (row.address &&
                  row.address
                    .toLowerCase()
                    .includes(searchText.toLowerCase())) ||
                (row.city &&
                  row.city.toLowerCase().includes(searchText.toLowerCase())) ||
                (row.state &&
                  row.state.toLowerCase().includes(searchText.toLowerCase())) ||
                (row.zip &&
                  row.zip
                    .toString()
                    .toLowerCase()
                    .includes(searchText.toLowerCase()))
            )}
            columns={columns}
            pageSize={13}
            disableColumnFilter
            disableSelectionOnClick
            disableColumnSelector
          />
        </>
      )}
    </Box>
  );
}

2

Answers


  1. The MUI DataGrid does not include any event function regarding hovering.
    (You only have functions like onCellClick(), onCellEditStart(), onCellKeyDown(), …)

    I thought about event Listener like onMouseEnter and onMouseLeave, but, as mentioned here, that it will cause so many renders.

    Maybe using the sx prop would help to style those blocks with :hover:

    Something like:

    function getColumns(theme) {
      return [
        {
          field: "id",
          headerName: "Actions",
          width: 150,
          renderCell: (params) => {
            return (
              <Box
                sx={{
                  display: "flex",
                  justifyContent: "space-between",
                  width: "100%",
                  '&:hover button': {
                    backgroundColor: theme.palette.success.main,
                    border: '1px solid transparent',
                    color: theme.palette.common.white,
                  },
                }}
              >
                <Link
                  href="/properties/[id]"
                  as={`/properties/${params.row.original_doc || params.row.id}`}
                >
                  <Button
                    size="small"
                    variant="outlined"
                    startIcon={<CalculateIcon />}
                    sx={{
                      borderColor: theme.palette.success.main,
                      '&:hover': {
                        backgroundColor: theme.palette.success.main,
                        borderColor: 'transparent',
                        color: theme.palette.common.white,
                      },
                    }}
                  >
                    Analyze
                  </Button>
                </Link>
              </Box>
            );
          },
        },
      ];
    }
    

    Associated with

    <DataGrid
      onRowMouseEnter={handleRowOver}
      // Other props
    />
    

    (not rowMouseEnter)

    Using a similar idea as the one show in DataGrid row:

    <DataGrid
      // Other props
      slotProps={{
        row: {
          onMouseEnter: (event) => handleRowOver(Number(event.currentTarget.getAttribute('data-id'))),
          onMouseLeave: () => console.log('Mouse left the row'),
        },
      }}
    />
    

    with:

    const handleRowOver = (rowId) => {
      console.log(`Row ${rowId} is being hovered over`);
    };
    

    When you hover over a row in the DataGrid, the handleRowOver function will be called, and you can see the log in the browser console.
    When the mouse leaves the row, the anonymous function will print a message.
    The hover effect for the "Analyze" button will still be handled by the :hover pseudo-class in the sx prop.

    Login or Signup to reply.
  2. As @VonC mentioned in his answer, you can use slotProps to pass props to a row element, in particular onMouseEnter and onMouseLeave. Using the technique described here, I was able to reproduce the behavior you are trying to achieve in a fairly concise manner.

    The main idea is to fire an event inside onMouseEnter and onMouseLeave that we will subscribe to in our custom button component.

    In order to achieve isolation of events between different rows, we will include the row id in the event name.

    It was difficult to run your component without a context, so to demonstrate the principle, I built a minimal DataGrid myself.

    You can see a live example here:

    Edit MUI Datagrid change cell props on row hover

    Code:

    import React, { FC, useState, useEffect } from "react";
    import Button from "@mui/material/Button";
    import { DataGrid, GridColDef } from "@mui/x-data-grid";
    import CalculateIcon from "@mui/icons-material/Calculate";
    
    const CustomButtonElement: FC<{ rowId: number | string }> = ({ rowId }) => {
      const [rowHovered, setRowHovered] = useState(false);
      useEffect(() => {
        const handleCustomEvent = (e) => setRowHovered(e.detail.hovered);
        document.addEventListener(`row${rowId}HoverChange`, handleCustomEvent);
        // cleanup listener
        return () =>
          document.removeEventListener(`row${rowId}HoverChange`, handleCustomEvent);
      }, [rowId]);
    
      return (
        <Button variant={rowHovered ? "outlined" : "contained"}>
          <CalculateIcon />
        </Button>
      );
    };
    
    export default function DataGridDemo() {
      const rows = [
        { id: 1, lastName: "Snow", firstName: "Jon", age: 35 },
        { id: 2, lastName: "Lannister", firstName: "Cersei", age: 42 },
        { id: 3, lastName: "Lannister", firstName: "Jaime", age: 45 },
        { id: 4, lastName: "Stark", firstName: "Arya", age: 16 },
        { id: 5, lastName: "Targaryen", firstName: "Daenerys", age: null }
      ];
    
      const columns: GridColDef[] = [
        { field: "id", headerName: "ID", width: 90 },
        {
          field: "",
          headerName: "Action",
          renderCell: (params) => <CustomButtonElement rowId={params.id} />
        },
        { field: "firstName", headerName: "First Name", width: 90 },
        { field: "lastName", headerName: "Last Name", width: 90 }
      ];
    
      const handleRowHovered = (event: React.MouseEvent<HTMLElement>) => {
        const rowId = event.currentTarget?.dataset?.id;
        document.dispatchEvent(
          new CustomEvent(`row${rowId}HoverChange`, { detail: { hovered: true } })
        );
      };
    
      const handleRowLeaved = (event: React.MouseEvent<HTMLElement>) => {
        const rowId = event.currentTarget?.dataset?.id;
        document.dispatchEvent(
          new CustomEvent(`row${rowId}HoverChange`, { detail: { hovered: false } })
        );
      };
    
      return (
        <DataGrid
          rows={rows}
          columns={columns}
          slotProps={{
            row: {
              onMouseEnter: handleRowHovered,
              onMouseLeave: handleRowLeaved
            }
          }}
        />
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search