Give <Select> options a border - reactjs

Is it possible to give the container of the <Select> options a border? The <Select> tag implements a <Popover> with a <Menu> component on which the <MenuItem>s options are rendered, but the documentation does not detail how this container can be styled.
There is an option to apply props to the menu via MenuProps, but it is not clear in the examples/documentation how a border could be applied to it.
https://codesandbox.io/s/material-demo-2cdqs?fontsize=14
import ...;
const useStyles = makeStyles((theme: Theme) =>
createStyles({
containerWithBorder: {
paper: {
border: "1px solid red"
}
}
})
);
function SimpleSelect() {
const classes = useStyles();
return (
<form className={classes.root} autoComplete="off">
<FormControl className={classes.formControl}>
<InputLabel htmlFor="age-simple">Age</InputLabel>
<Select
MenuProps={{ // Can this be used to give the options a border?
classes: classes.containerWithBorder
}}
value={values.age}
onChange={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>
</form>
);
}
export default SimpleSelect;

Yes, what you would do is write your style in the in the override useStyles section
const useStyles = makeStyles((theme: Theme) =>
createStyles({
select: {
border: "1px solid black",
},
}),
);
like this and then you would pass it to the Select component:
<Select className={classes.select} />
like this

Related

Mui select - change background color once a menuItem is selected

I have a Mui Select with different menu items. I want to be able to change the background color of the select once a user has chosen a menu item.
This is one of my selects with the menu items:
<p className="text-md font-nunito font-medium">Role</p>
<FormControl>
<Select
displayEmpty
variant="outlined"
id="role"
name="role"
className="border text-white"
sx={{
bgColor: '#393939',
color: 'white',
'& .MuiSelect-iconOutlined': {
color: '#393939',
},
}}
value={role}
onChange={(event) => {
handleChange(event);
handleRoleSwitch(event);
}}
onBlur={handleBlur('role')}
MenuProps={{
PaperProps: {
sx: {
bgcolor: '#393939',
'& .MuiMenuItem-root': {
padding: 2,
color: 'white',
},
},
},
}}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem className="text-white" value="admin">
Admin
</MenuItem>
<MenuItem className="text-white" value="user">
User
</MenuItem>
<MenuItem className="text-white" value="viewer">
Viewer
</MenuItem>
</Select>
</FormControl>
How about using a useState variable for representing the bgColor that changes when clicking a menu item?
Here is a quick example on how you could do it.
const [bgColorValue, setBgColorValue] = useState('#393939')
const handleBgColorChange = (newValue) => {
setBgColorValue(newValue)
}
<Select
displayEmpty
variant="outlined"
id="role"
name="role"
className="border text-white"
sx={{
bgColor: '#393939',
color: 'white',
'& .MuiSelect-iconOutlined': {
color: '#393939',
},
}}
value={role}
onChange={(event) => {
handleChange(event);
handleRoleSwitch(event);
}}
onBlur={handleBlur('role')}
MenuProps={{
PaperProps: {
sx: {
bgcolor: bgColorValue,
'& .MuiMenuItem-root': {
padding: 2,
color: 'white',
},
},
},
}}
>
```
And then just use the onClick={() => handleBgColorChange('#404040')} where you want the new bgValue to be set. Obviously, i just used #404040 as a example. Hope this helps in some sort of way.
You can create a variable to set if a selection happened using useState. Then control it using the onChange of the Select.
Have a look at the code below and in this working codesandbox
Here is a working codesandbox with your code.
I have used styled-components in both.
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";
import styled from "#emotion/styled";
export interface StyledSelectProps {
isSelected: boolean;
}
const StyledSelect = styled(Select)`
background: ${({ isSelected }: StyledSelectProps) =>
isSelected ? "#f38713" : "white"};
`;
export default function BasicSelect() {
const [age, setAge] = React.useState("");
const [itemSelected, setItemSelected] = React.useState(false);
const handleChange = (event: SelectChangeEvent) => {
setAge(event.target.value as string);
setItemSelected(true);
};
return (
<Box sx={{ minWidth: 120 }}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Age</InputLabel>
<StyledSelect
labelId="demo-simple-select-label"
id="demo-simple-select"
value={age}
label="Age"
onChange={handleChange}
isSelected={itemSelected}
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</StyledSelect>
</FormControl>
</Box>
);
}

Matrial UI change border of outlined select

I want to change the default outline color of the Select component in Material UI.
This is what I've tried but did not work:
const useStyles = makeStyles(theme => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
borderColor: "green",
},
selectEmpty: {
marginTop: theme.spacing(2)
},
outlined: {
'&:before': {
borderColor: "green",
},
'&:after': {
borderColor: "green",
}
},
}));
export default function SimpleSelect() {
const classes = useStyles();
const [age, setAge] = React.useState("");
const handleChange = event => {
setAge(event.target.value);
};
return (
<div>
<FormControl variant="outlined" className={classes.formControl}>
<InputLabel id="demo-simple-select-outlined-label">Age</InputLabel>
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
value={age}
onChange={handleChange}
label="Age"
className={classes.outlined}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</div>
);
}
Thank you in advance for the help!
P.S. This is how it looks when focused/selected
Try with deep nesting or with important.
borderColor: "green !important",

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

Select, OutlineInput label with shrink property not same as TextField when empty

If I use a TextField component, and set InputLabelProps={{shrink: true}}, the Label stays at the top of the TextField, and the outline is cut to show the Label correctly.
However, if I use Select component, as follows :
<FormControl variant={ this.props.variant } className={ classes.formControl } fullWidth>
<InputLabel
ref={ (input) =>{ this.inputLabel = input }}
htmlFor={ this.props.id }
shrink={ true }>
{ this.props.label }
</InputLabel>
<Select
id={ this.props.id }
value={ this.props.value }
onChange={ this.onChange }
input={
<OutlinedInput
labelWidth={ this.state.labelWidth }
name={ this.props.id }
id={ this.props.id }
/>
}
>
{ this.props.options.map(option => (
<MenuItem key={ option.value } value={ option.value }>
<em>{ option.label }</em>
</MenuItem>
))}
</Select>
</FormControl>
The Label stays at the top of the outline just like TextField, however, the outline is not cut out to display the Label nicely, instead it looks like it is "crossed out"
What am I doing wrong?
(Please not, that the this.state.labelWidth has a correct value, even if I hard code this, it still does not work)
Thanks in advance for your help
Cheers
Jason
The default behavior is for the shrink property of InputLabel to be automatically managed by Material-UI. Generally shrink is only applied when the Select has a non-empty value or when it has focus. If you want to have shrink applied all the time, then you also need to specify notched on OutlinedInput since that is what controls leaving a space for the label along the outline.
The code below shows both cases (1. always apply shrink and notched, 2. let Material-UI manage shrink and notched):
import React from "react";
import { makeStyles } 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 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 [values, setValues] = React.useState({
age: ""
});
const inputLabel = React.useRef(null);
const [labelWidth, setLabelWidth] = React.useState(0);
React.useEffect(() => {
setLabelWidth(inputLabel.current.offsetWidth);
}, []);
function handleChange(event) {
setValues(oldValues => ({
...oldValues,
[event.target.name]: event.target.value
}));
}
return (
<form className={classes.root} autoComplete="off">
<FormControl variant="outlined" className={classes.formControl}>
<InputLabel
shrink
ref={inputLabel}
htmlFor="outlined-age-always-notched"
>
Age
</InputLabel>
<Select
value={values.age}
onChange={handleChange}
input={
<OutlinedInput
notched
labelWidth={labelWidth}
name="age"
id="outlined-age-always-notched"
/>
}
>
<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={inputLabel} htmlFor="outlined-age-simple">
Age
</InputLabel>
<Select
value={values.age}
onChange={handleChange}
input={
<OutlinedInput
labelWidth={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>
);
}
export default SimpleSelect;
Last answer has a deprecated prop labelWidth
For the newer MUI just add shrink prop to InputLabel component and notched prop to Select component.
More on the issue and solution here. https://github.com/mui/material-ui/issues/22799

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