Open Select after a click on label - reactjs

I'm looking for a way to open a Select (not native) component from Material UI after a click on the label with htmlFor specified.
<label htmlFor='menu'>Label to open Menu</label>
<MUISelect inputProps={{id: 'menu'}} native={false}>{options}</MUISelect>
Obviously it doesn't work

There are two main aspects to this problem:
You need to get the id onto the same element that has the click-handler for opening the menu.
The element with the click-handler is a div and not an input. Labels do not automatically trigger the click event of the labelled element when it is not an input element.
The first aspect can be solved by using SelectDisplayProps:
SelectDisplayProps={{ id: "menu" }}
The second aspect can be solved with a custom label component:
import React from "react";
const LabelForClickableDiv = ({ htmlFor, ...other }) => {
const onClick = () => {
document.getElementById(htmlFor).click();
};
return <label htmlFor={htmlFor} onClick={onClick} {...other} />;
};
export default LabelForClickableDiv;
Here's a working example:
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import MenuItem from "#material-ui/core/MenuItem";
import FormControl from "#material-ui/core/FormControl";
import Select from "#material-ui/core/Select";
import LabelForClickableDiv from "./LabelForClickableDiv";
const useStyles = makeStyles(theme => ({
root: {
display: "flex",
flexWrap: "wrap"
},
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
function SimpleSelect() {
const classes = useStyles();
const [value, setValue] = React.useState("");
return (
<>
<form className={classes.root} autoComplete="off">
<FormControl className={classes.formControl}>
<LabelForClickableDiv htmlFor="menu">
Label to open Menu
</LabelForClickableDiv>
<Select
value={value}
onChange={event => {
setValue(event.target.value);
}}
SelectDisplayProps={{
id: "menu"
}}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</form>
</>
);
}
export default SimpleSelect;

Related

How to set left value of popover in mui to 0 or make it equal to left edge of anchor?

This is images problem
image 1
image 2
import * as React from 'react';
import Box from '#mui/material/Box';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import FormControl from '#mui/material/FormControl';
import Select, { SelectChangeEvent } from '#mui/material/Select';
export default function BasicSelect() {
const [age, setAge] = React.useState('');
const [anchorEl, setAnchorEl] = React.useState()
const selectRef = React.useRef()
const handleChange = (event: SelectChangeEvent) => {
setAge(event.target.value as string);
};
React.useEffect(() => {
setAnchorEl(selectRef.current)
}, [selectRef])
return (
<Box sx={{ minWidth: 120 }}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Age</InputLabel>
<Select
ref={selectRef}
labelId="demo-simple-select-label"
id="demo-simple-select"
value={age}
label="Age"
onChange={handleChange}
MenuProps={{
anchorEl: anchorEl,
anchorOrigin: { horizontal: 'left', vertical: 'bottom' },
}}
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</Box>
);
}
How to set left value of popover in mui to 0 or make it equal to left edge of anchor?
code default: https://codesandbox.io/s/yzgs66?file=/demo.tsx

Why my 'Select' components in react are synchronized together?

I am currently using materialui to create a submission page. I have created two 'Select' components for the page, and there is a problem that if I choose an option from the second 'Select' component, the option from the first 'Select' component will be refreshed. I was thinking there is something wrong with the 'onChange', yet I have no idea about the cause for this problem. I want to select all the options from the 'Select' components before submitting the data at once.
Also, is there a way to disable the second 'Select' components before entering an option other than 'None' in the first 'Select' components, and enable it after a valid option has been entered?
Thank you very much for your time and help!
import React, { Component } from 'react';
import { Form, Field } from 'react-final-form';
import { OnChange } from 'react-final-form-listeners';
import { makeStyles, InputLabel, FormControl, Typography, Paper, Link, Grid, Button, CssBaseline, MenuItem, TextField, Select, Box, FormHelperText} from '#material-ui/core';
const useStyples = makeStyles((theme) => ({
formControl: {
margin: theme.spacing(2),
minWidth: 400
},
typo1: {
padding: 30,
margin: 'auto',
maxWidth: 600
},
typo2: {
padding: 0,
margin: 'auto',
maxWidth: 600
},
}))
export default function CreatePage() {
const classes = useStyples()
const [value, setValue] = React.useState('')
const handleChange = (event) => {
setValue(event.target.value)
}
return (
<div>
<Typography variant='h4' align='center' component='h1' gutterBottom className={classes.typo1}>
Data Submission Form
</Typography>
<Typography variant='h5' align='center' component='h2' gutterBottom className={classes.typo2}>
Material-UI
</Typography>
<Box display='flex' flexDirection='row'>
<Box>
<FormControl required variant='filled' className={classes.formControl}>
<InputLabel shrink>Defect</InputLabel>
<Select
labelId='defect-select1'
id='defect-select1'
value={value}
displayEmpty
onChange={handleChange}>
<MenuItem value=''>None</MenuItem>
<MenuItem value={'Crack'}>Crack</MenuItem>
<MenuItem value={'Scratch'}>Scratch</MenuItem>
<MenuItem value={'Rough Surface'}>Rough Surface</MenuItem>
<MenuItem value={'Spalling'}>Spalling</MenuItem>
</Select>
<FormHelperText>Select Defect for Position 1 - Required</FormHelperText>
</FormControl>
</Box>
<Box>
<FormControl required variant='filled' className={classes.formControl}>
<InputLabel shrink>Tool</InputLabel>
<Select
labelId='tool1'
id='tool1'
displayEmpty
value={value}
onChange={handleChange}>
<MenuItem value=''>None</MenuItem>
<MenuItem value={'Tool 1'}>Tool 1</MenuItem>
<MenuItem value={'Tool 2'}>Tool 2</MenuItem>
<MenuItem value={'Tool 3'}>Tool 3</MenuItem>
<MenuItem value={'Tool 4'}>Tool 4</MenuItem>
</Select>
<FormHelperText>Select Tool Decision for Position 1 - Required</FormHelperText>
</FormControl>
</Box>
</Box>
</div>
);
}
I would be glad if someone can tell me the root cause for the problem. An additon of a solution for my question would be better!
Well, it seems that you only have 1 value :
const [value, setValue] = React.useState('')
And although you have duplicated the <Select /> component you didn't duplicate the value...
You'll need a second "value" and a second handleChange which will update that second value
const [value2, setValue2] = React.useState('');
const handleChange2 = (event) => {
setValue2(event.target.value)
}

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?
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);
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);
Related documentation:
https://material-ui.com/components/icons/#svgicon
Related answer:
Select is not working onClick IconComponent(dropdown-arrow) in react material ui

Showing a default value for a select control using react and material-ui control

I am working on a react page using material-ui. I am retrieving data
from the database to populate my select control.
I want to be able to have a default value like "Select the value". The section highlighted in yellow is just blank.
How can I achieve this? An image of what exist is attached.
<FormControl id="ron" className="form-control">
<InputLabel htmlFor="productDescription" shrink>Product Code/Description</InputLabel>
<Select
value={this.state.productCode}
onChange={this.handleChangeProductCode}
name='productcode'
>
<MenuItem value="">
select the value
</MenuItem>
{this.dataForProductCodeControl()}
</Select>
</FormControl>
dataForProductCodeControl() {
if(this.props.groupedData != undefined){
return this.props.groupedData.map((dt, i) => {
return (
<MenuItem key={i} value={dt.productCode}>
{dt.productCode} | {dt.productDescription}
</MenuItem>
);
});
}
}
You need to specify the displayEmpty prop on the Select.
Here's a working example:
import React from "react";
import { 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";
const useStyles = makeStyles(theme => ({
root: {
display: "flex",
flexWrap: "wrap"
},
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
export default function SimpleSelect() {
const classes = useStyles();
const [values, setValues] = React.useState({
age: ""
});
function handleChange(event) {
setValues(oldValues => ({
...oldValues,
[event.target.name]: event.target.value
}));
}
return (
<form className={classes.root} autoComplete="off">
<FormControl className={classes.formControl}>
<InputLabel htmlFor="age-simple" shrink>
Age
</InputLabel>
<Select
value={values.age}
displayEmpty
onChange={handleChange}
inputProps={{
name: "age",
id: "age-simple"
}}
>
<MenuItem value={""}>Select Age</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</form>
);
}
You can add a disabled valueless item like this:
<MenuItem value="" disabled>
Placeholder
</MenuItem>
But the item will be displayed as an option. See https://material-ui.com/components/selects/#simple-select

How to customize the select drop down MUI paper CSS

I am using Material UI for my react project and using the select dropdown that has been shown in the Material UI documentation. On clicking the first set of select drop down a Material UI paper pops up with options overlapping the select element itself. How can I bring the pop up a little below the select element so that it doesn't overlap using custom theme?
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import OutlinedInput from '#material-ui/core/OutlinedInput';
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';
const styles = theme => ({
root: {
display: 'flex',
flexWrap: 'wrap',
},
formControl: {
margin: theme.spacing.unit,
minWidth: 120,
},
});
class SimpleSelect extends React.Component {
state = {
age: '',
labelWidth: 0,
};
componentDidMount() {
this.setState({
labelWidth: ReactDOM.findDOMNode(this.InputLabelRef).offsetWidth,
});
}
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',
}}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
<FormControl variant="outlined" className={classes.formControl}>
<InputLabel
ref={ref => {
this.InputLabelRef = ref;
}}
htmlFor="outlined-age-simple"
>
Age
</InputLabel>
<Select
value={this.state.age}
onChange={this.handleChange}
input={
<OutlinedInput
labelWidth={this.state.labelWidth}
name="age"
id="outlined-age-simple"
/>
}
>
<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);
There's a property called MenuProps for Select. Set variant to menu within MenuProps and adjust CSS as per your requirement.
<Select
classes={{
root: classes.root,
}}
MenuProps={{ classes: { paper: classes.dropdownStyle },
variant: 'menu'
//setting variant to menu makes it appear below the element
}}
onChange={()=>{}}
{...rest}
>
{list.map(item => (
<MenuItem value={item.value}>
<Typography variant="body2" color="textSecondary">
{item.label}
</Typography>
</MenuItem>
))}
</Select>
You can get this look via the anchorOrigin and getContentAnchorEl Menu props which Menu inherits from Popover.
Here is a working example of customizing a single Select:
import React from "react";
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";
export default function SimpleSelect() {
const [age, setAge] = React.useState("");
const handleChange = (event) => {
setAge(event.target.value);
};
const menuProps = {
getContentAnchorEl: null,
anchorOrigin: {
vertical: "bottom",
horizontal: "left"
}
};
return (
<div>
<FormControl style={{ margin: "8px", minWidth: "120px" }}>
<InputLabel id="demo-simple-select-label">Age</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={age}
onChange={handleChange}
MenuProps={menuProps}
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</div>
);
}
Here is example of doing the same thing via the theme:
import React from "react";
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 { ThemeProvider, createMuiTheme } from "#material-ui/core/styles";
const menuProps = {
getContentAnchorEl: null,
anchorOrigin: {
vertical: "bottom",
horizontal: "left"
}
};
const theme = createMuiTheme({
props: {
MuiSelect: {
MenuProps: menuProps
}
}
});
export default function SimpleSelect() {
const [age, setAge] = React.useState("");
const handleChange = (event) => {
setAge(event.target.value);
};
return (
<ThemeProvider theme={theme}>
<div>
<FormControl style={{ margin: "8px", minWidth: "120px" }}>
<InputLabel id="demo-simple-select-label">Age</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={age}
onChange={handleChange}
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</div>
</ThemeProvider>
);
}
Related answer: How to make a drop-down menu appear exactly below the bar in Material-UI?

Resources