Home > Net >  How to stop modal from closing when clicking on a select option?
How to stop modal from closing when clicking on a select option?

Time:11-06

I've made a custom filter for MUI's datagrid, the filter has two select's which allow you to filter by the column and filter type. The selects are quite big and endup outside the modal, when clicking on an option the whole modal closes, how can I prevent this from happening?

I've used this tutorial - Detect click outside React component to detect clicks outside the filter.

The code below shows the filter and I've also made an codesandbox example here - https://codesandbox.io/s/awesome-panka-g92vhn?file=/src/DataGridCustomFilter.js:0-6708

any help would be appreciated

import React, { useState, useEffect, useRef } from "react";
import {
  Button,
  Stack,
  FormControl,
  InputLabel,
  Select,
  MenuItem,
  Paper,
  Grid,
  IconButton,
  TextField,
  ClickAwayListener
} from "@material-ui/core";
import FilterListIcon from "@mui/icons-material/FilterList";
import AddIcon from "@mui/icons-material/Add";
import CloseIcon from "@mui/icons-material/Close";
import { useForm, useFieldArray, Controller } from "react-hook-form";
import { columns } from "./columns";

const filterTypes = {
  string: ["contains", "equals", "starts with", "ends with", "is any of"],
  int: ["contains", "equals", "less than", "greater than"]
};

function FilterRow({
  len,
  setOpen,
  field,
  control,
  columns,
  index,
  handleRemoveFilter
}) {

  return (
    <Grid container spacing={0}>
      <Grid
        item
        md={1}
        style={{
          display: "flex",
          alignSelf: "flex-end",
          alignItems: "center"
        }}
      >
        <IconButton
          size="small"
          onClick={() => {
            if (len === 1) {
              setOpen(false);
            } else {
              console.log(index, "---");
              handleRemoveFilter(index);
            }
          }}
        >
          <CloseIcon style={{ fontSize: "20px" }} />
        </IconButton>
      </Grid>

      <Grid item md={4}>
        <Controller
          name={`filterForm.${index}.column`}
          control={control}
          render={({ field: { onChange, value }, fieldState: { error } }) => (
            <FormControl variant="standard" sx={{ width: "100%" }}>
              <InputLabel>Column</InputLabel>
              <Select
                value={value}
                onChange={onChange}
                label="Column"
                defaultValue=""
              >
                {columns.map((a) => {
                  return a.exclude_filter === true ? null : (
                    <MenuItem value={a.headerName}>{a.headerName}</MenuItem>
                  );
                })}
              </Select>
            </FormControl>
          )}
        />
      </Grid>
      <Grid item md={3}>
        <Controller
          name={`filterForm.${index}.filter`}
          control={control}
          render={({ field: { onChange, value }, fieldState: { error } }) => (
            <FormControl variant="standard" sx={{ width: "100%" }}>
              <InputLabel>Filter</InputLabel>

              <Select
                value={value}
                onChange={onChange}
                label="Filter"
                defaultValue=""
              >
                {filterTypes.string.map((a) => {
                  return <MenuItem value={a}>{a}</MenuItem>;
                })}
              </Select>
            </FormControl>
          )}
        />
      </Grid>
      <Grid item md={4}>
        <Controller
          name={`filterForm.${index}.value`}
          control={control}
          render={({ field: { onChange, value }, fieldState: { error } }) => (
            <FormControl>
              <TextField
                onChange={onChange}
                value={value}
                label="Value"
                variant="standard"
              />
            </FormControl>
          )}
        />
        {/* )} */}
      </Grid>
    </Grid>
  );
}

function DataGridCustomFilter() {
  const { control, handleSubmit } = useForm();
  const { fields, append, remove } = useFieldArray({
    control,
    name: "filterForm"
  });

  const [open, setOpen] = useState(false);

  const onSubmit = (data) => {};

  useEffect(() => {
    if (fields.length === 0) {
      append({
        column: "ID",
        filter: filterTypes.string[0],
        value: ""
      });
    }
  }, [fields]);

  const [clickedOutside, setClickedOutside] = useState(false);

  const myRef = useRef();

  const handleClickOutside = (e) => {
    if (myRef.current && !myRef.current.contains(e.target)) {
      setClickedOutside(true);
      setOpen(!open);
    }
  };

  useEffect(() => {
    document.addEventListener("mousedown", handleClickOutside);
    return () => document.removeEventListener("mousedown", handleClickOutside);
  });

  return (
    <>
      <Button
        startIcon={<FilterListIcon />}
        size="small"
        onClick={() => {
          setOpen(!open);
        }}
        // disabled={isDisabled}
      >
        FILTERS
      </Button>
      {open ? (
        <div ref={myRef}>
          <Paper
            style={{
              width: 550,
              padding: 10,
              zIndex: 1300,
              position: "absolute",
              inset: "0px auto auto 0px",
              margin: 0,
              display: "block"
              // transform: "translate3d(160.556px, 252.222px, 0px)",
            }}
            variant="elevation"
            elevation={5}
          >
            <form onSubmit={handleSubmit(onSubmit)}>
              <Stack spacing={0.5}>
                <div style={{ maxHeight: 210, overflow: "scroll" }}>
                  {fields.map((field, index) => {
                    return (
                      <div style={{ paddingBottom: 5 }}>
                        <FilterRow
                          len={fields.length}
                          control={control}
                          setOpen={setOpen}
                          field={field}
                          columns={columns}
                          handleRemoveFilter={() => remove(index)}
                          {...{ control, index, field }}
                          // handleClickAway={handleClickAway}
                        />
                      </div>
                    );
                  })}
                </div>
                <div style={{ marginTop: 10, paddingLeft: 40 }}>
                  <Stack direction="row" spacing={1}>
                    <Button size="small" startIcon={<AddIcon />}>
                      ADD FILTER
                    </Button>
                    <Button size="small" type="submit">
                      {fields.length > 1 ? "APPLY FILTERS" : "APPLY FILTER"}
                    </Button>
                  </Stack>
                </div>
              </Stack>
            </form>
          </Paper>
        </div>
      ) : null}
    </>
  );
}

export default DataGridCustomFilter;

So far I've tried MUI's ClickAwayListener and the example above, both seem to give the same result

CodePudding user response:

DataGrid component uses NativeSelect. I have checked your codesandbox and tried replacing Select to NativeSelect and MenuItem to Option. filter is working properly. below is sample code for update.

...
<NativeSelect
  value={value}
  onChange={onChange}
  label="Column"
  defaultValue=""
>
  {columns.map((a) => {
    return a.exclude_filter === true ? null : (
      <option  value={a.headerName}>{a.headerName}</option >
    );
  })}
</NativeSelect>
...
  • Related