Shamoon
Shamoon

Reputation: 43491

How do I use an SVG as an IconComponent in a Material UI Select?

I have

import CaretDownIcon from 'src/../public/images/svg/caret-down.svg';


      <Select
        className={selectClassName}
        data-testid={testId}
        // IconComponent={<SvgIcon>{CaretDownIcon}</SvgIcon>}
        // IconComponent={CaretDownIcon}
        inputProps={{
          name,
          id: labelId,
        }}
        {...rest}
      >

I tried both of those commented lines, but no dice. What's the right way?

Upvotes: 4

Views: 5655

Answers (1)

Ryan Cogswell
Ryan Cogswell

Reputation: 80986

You need to create a component for your custom svg icon. Copy the path from the svg file to make a component as shown below:

function CustomSvgIcon(props) {
  return (
    <SvgIcon {...props}>
      <path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z" />
    </SvgIcon>
  );
}

then you can use that with IconComponent={CustomSvgIcon}.

Here's a full working example:

import React from "react";
import PropTypes from "prop-types";
import { withStyles } from "@material-ui/core/styles";
import InputLabel from "@material-ui/core/InputLabel";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import Select from "@material-ui/core/Select";
import SvgIcon from "@material-ui/core/SvgIcon";

const styles = (theme) => ({
  root: {
    display: "flex",
    flexWrap: "wrap"
  },
  formControl: {
    margin: theme.spacing.unit,
    minWidth: 120
  },
  selectEmpty: {
    marginTop: theme.spacing.unit * 2
  }
});
function CustomSvgIcon(props) {
  return (
    <SvgIcon {...props}>
      <path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z" />
    </SvgIcon>
  );
}

class SimpleSelect extends React.Component {
  state = {
    age: "",
    name: "hai"
  };

  handleChange = (event) => {
    this.setState({ [event.target.name]: event.target.value });
  };

  render() {
    const { classes } = this.props;

    return (
      <form className={classes.root} autoComplete="off">
        <FormControl className={classes.formControl}>
          <InputLabel htmlFor="age-simple">Age</InputLabel>
          <Select
            value={this.state.age}
            onChange={this.handleChange}
            inputProps={{
              name: "age",
              id: "age-simple"
            }}
            IconComponent={CustomSvgIcon}
          >
            <MenuItem value="">
              <em>None</em>
            </MenuItem>
            <MenuItem value={10}>Ten</MenuItem>
            <MenuItem value={20}>Twenty</MenuItem>
            <MenuItem value={30}>Thirty</MenuItem>
          </Select>
        </FormControl>
      </form>
    );
  }
}

SimpleSelect.propTypes = {
  classes: PropTypes.object.isRequired
};

export default withStyles(styles)(SimpleSelect);

Edit Select custom icon

It is also possible to create a React component from an imported SVG file, but this is dependent on your build configuration. If you are using create-react-app then this will work (see this article for details).

Below is an example using the import approach. This uses import { ReactComponent as TestSvgAsComponent } from "./test.svg"; to get a React component from the SVG file. The other step necessary is to add in the styles that would be applied by SvgIcon (classes.icon from useIconStyles in the example).

import React from "react";
import PropTypes from "prop-types";
import { withStyles, makeStyles } from "@material-ui/core/styles";
import InputLabel from "@material-ui/core/InputLabel";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import Select from "@material-ui/core/Select";
import { ReactComponent as TestSvgAsComponent } from "./test.svg";
import clsx from "clsx";
const styles = (theme) => ({
  root: {
    display: "flex",
    flexWrap: "wrap"
  },
  formControl: {
    margin: theme.spacing.unit,
    minWidth: 120
  },
  selectEmpty: {
    marginTop: theme.spacing.unit * 2
  }
});
const useIconStyles = makeStyles({
  // This is a copy of the styles from https://github.com/mui-org/material-ui/blob/v4.12.3/packages/material-ui/src/SvgIcon/SvgIcon.js#L10
  icon: {
    fill: "currentColor",
    width: "1em",
    height: "1em",
    display: "inline-block",
    fontSize: "1.5rem",
    transition: "fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms",
    flexShrink: 0,
    userSelect: "none"
  }
});

function CustomSvgIcon({ className, ...other }) {
  const classes = useIconStyles();
  return (
    <TestSvgAsComponent className={clsx(classes.icon, className)} {...other} />
  );
}
class SimpleSelect extends React.Component {
  state = {
    age: "",
    name: "hai"
  };

  handleChange = (event) => {
    this.setState({ [event.target.name]: event.target.value });
  };

  render() {
    const { classes } = this.props;

    return (
      <form className={classes.root} autoComplete="off">
        <FormControl className={classes.formControl}>
          <InputLabel htmlFor="age-simple">Age</InputLabel>
          <Select
            value={this.state.age}
            onChange={this.handleChange}
            inputProps={{
              name: "age",
              id: "age-simple"
            }}
            IconComponent={CustomSvgIcon}
          >
            <MenuItem value="">
              <em>None</em>
            </MenuItem>
            <MenuItem value={10}>Ten</MenuItem>
            <MenuItem value={20}>Twenty</MenuItem>
            <MenuItem value={30}>Thirty</MenuItem>
          </Select>
        </FormControl>
      </form>
    );
  }
}

SimpleSelect.propTypes = {
  classes: PropTypes.object.isRequired
};

export default withStyles(styles)(SimpleSelect);

Edit Select custom icon

Related documentation:

Related answer:

Upvotes: 2

Related Questions