mvd
mvd

Reputation: 2720

React Material-UI override popover completely

I currently using a Select component in my app.

select component

I built a custom modal component that I want to launch instead of the list items when the select is clicked. Is there a way to override the handler for clicks on all portions of the component, such as icon, text field, and dropdown arrow to launch my modal? I want to take just the styling of this component essentially and override the onChange and MenuItem stuff.

<Select
  value={props.selectedValue}
  onChange={props.onTimeChange}
  displayEmpty
  startAdornment={
    <InputAdornment position="start">
      <DateRangeIcon />
    </InputAdornment>
  }
>
  {/* DONT USE THESE MENU ITEMS AND USE CUSTOM MODAL INSTEAD */}
  {/*<MenuItem value={-1} disabled>*/}
  {/*  Start Date*/}
  {/*</MenuItem>*/}
  {/*<MenuItem value={1}>Last Hour</MenuItem>*/}
  {/*<MenuItem value={24}>Last Day</MenuItem>*/}
  {/*<MenuItem value={24 * 7}>Last Week</MenuItem>*/}
  {/*<MenuItem value={24 * 31}>Last Month</MenuItem>*/}
  {/*<MenuItem value={''}>All</MenuItem>*/}
</Select>

Upvotes: 1

Views: 3085

Answers (1)

Ryan Cogswell
Ryan Cogswell

Reputation: 80966

In order for it to make sense to leverage Select while using an alternative display for the options, it is important that you provide it with menu items for all the allowed values, because the display of the selected item is based on finding a matching MenuItem for the current value (though it would also be possible to provide the Select with a single MenuItem with a dynamic value and text matching whatever the current selected value is).

You can use a "controlled" approach for managing the open state of the Select using the open and onOpen props (you can leave out onClose since the close should always be triggered by your custom display of the options). This way, rather than trying to override the different events that cause the Select to open, you just let it tell you when it should open (via the onOpen prop), but instead of opening the Select, leave its open prop as always false and only open your custom popup.

Here's a working example:

import React from "react";
import InputAdornment from "@material-ui/core/InputAdornment";
import Button from "@material-ui/core/Button";
import DateRangeIcon from "@material-ui/icons/DateRange";
import Popover from "@material-ui/core/Popover";
import Box from "@material-ui/core/Box";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";

export default function SimplePopover() {
  const [value, setValue] = React.useState(1);
  const [open, setOpen] = React.useState(false);
  const selectRef = React.useRef();

  const handleSelection = newValue => {
    setValue(newValue);
    setOpen(false);
  };

  return (
    <Box m={2}>
      <Select
        ref={selectRef}
        value={value}
        onChange={e => setValue(e.target.value)}
        displayEmpty
        open={false}
        onOpen={() => setOpen(true)}
        startAdornment={
          <InputAdornment position="start">
            <DateRangeIcon />
          </InputAdornment>
        }
      >
        <MenuItem value={1}>Last Hour</MenuItem>
        <MenuItem value={24}>Last Day</MenuItem>
        <MenuItem value={24 * 7}>Last Week</MenuItem>
        <MenuItem value={24 * 31}>Last Month</MenuItem>
        <MenuItem value={""}>All</MenuItem>
      </Select>
      <Popover
        id="simple-popover"
        open={open}
        anchorEl={selectRef.current}
        onClose={() => handleSelection(value)}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "left"
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "left"
        }}
      >
        <Button onClick={() => handleSelection(1)}>Last Hour</Button>
        <Button onClick={() => handleSelection(24)}>Last Day</Button>
      </Popover>
    </Box>
  );
}

Edit alternate view of options for Select

Here's a second example using a single, dynamic MenuItem for the selected value instead of a comprehensive set of menu items:

import React from "react";
import InputAdornment from "@material-ui/core/InputAdornment";
import Button from "@material-ui/core/Button";
import DateRangeIcon from "@material-ui/icons/DateRange";
import Popover from "@material-ui/core/Popover";
import Box from "@material-ui/core/Box";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";

export default function SimplePopover() {
  const [value, setValue] = React.useState(1);
  const [text, setText] = React.useState("Last Hour");
  const [open, setOpen] = React.useState(false);
  const selectRef = React.useRef();

  const handleSelection = (newValue, newText) => {
    setValue(newValue);
    setText(newText);
    setOpen(false);
  };

  return (
    <Box m={2}>
      <Select
        ref={selectRef}
        value={value}
        onChange={e => setValue(e.target.value)}
        displayEmpty
        open={false}
        onOpen={() => setOpen(true)}
        startAdornment={
          <InputAdornment position="start">
            <DateRangeIcon />
          </InputAdornment>
        }
      >
        <MenuItem value={value}>{text}</MenuItem>
      </Select>
      <Popover
        id="simple-popover"
        open={open}
        anchorEl={selectRef.current}
        onClose={() => handleSelection(value)}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "left"
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "left"
        }}
      >
        <Button onClick={() => handleSelection(1, "Last Hour")}>
          Last Hour
        </Button>
        <Button onClick={() => handleSelection(24, "Last Day")}>
          Last Day
        </Button>
      </Popover>
    </Box>
  );
}

Edit alternate view of options for Select

Upvotes: 2

Related Questions