How to make select label always appear at top in MUI? - reactjs

I want to create a material ui selector that label always appear at top. I added placeholder to select component. Here is what I want to do.
When not focusing to select component or at the beginning, it looks like this.Label overlaps the place holder.
What I want to create select label always seem at the top and not overlaps the placeholder.
Here is the code at codesandbox
CodeSandbox
import * as React from "react";
import MenuItem from "#mui/material/MenuItem";
import FormControl from "#mui/material/FormControl";
import Select from "#mui/material/Select";
import InputLabel from "#mui/material/InputLabel";
const names = ["Oliver Hansen", "Van Henry", "April Tucker", "Ralph Hubbard"];
export default function MultipleSelectPlaceholder() {
const [personName, setPersonName] = React.useState(null);
const handleChange = (event) => {
setPersonName(event.target.value);
};
return (
<div>
<FormControl sx={{ m: 1, width: 300, mt: 3 }}>
<InputLabel id="demo-simple-select-label">Age</InputLabel>
<Select
labelId="demo-simple-select-label"
label="Age"
displayEmpty
value={personName}
onChange={handleChange}
renderValue={(selected) => {
if (selected === null) {
return <em>Please Choose Name</em>;
}
return selected;
}}
>
<MenuItem value={null}>
<em>Clear</em>
</MenuItem>
{names.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
<script src="https://unpkg.com/#material-ui/core/umd/material-ui.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.1/umd/react-dom.production.min.js"></script>

https://codesandbox.io/s/multipleselectplaceholder-material-demo-forked-t06gm?file=/demo.js
Just add to Select:
notched={true} // Makes space between lines
and to InputLabel:
shrink={true} // Keeps label at top

For this purpose, you can simply pass shrink prop to the InputLabel component

Related

MUI, ReactSuite: Uncaught RangeError: Maximum call stack size exceeded

I'm using Modal component from React Suite and Select component from MUI
Basically I have an open button that opens a modal popup which has inside, a select component
However, when I open console, it produces the following error when I open the modal and select something from the Select menu:
at HTMLDocument.contain (TrapFocus.js:214:1)
at Object.current (Modal.js:131:1)
at useEventCallback.js:19:1
at Object.current (Modal.js:139:1)
at HTMLDocument.<anonymous> (useEventCallback.js:19:1)
at HTMLDocument.contain (TrapFocus.js:214:1)
at Object.current (Modal.js:131:1)
at useEventCallback.js:19:1
at Object.current (Modal.js:139:1)
at HTMLDocument.<anonymous> (useEventCallback.js:19:1)
I have not edited anything except just copying and pasting the sample code from both the Modal and Select components(pasting it inside the <Modal.Body>) from React Suite and MUI respectively, any idea what's causing the error?
Thanks in advance
import React from "react";
import { Button, Modal, Toggle, ButtonToolbar } from "rsuite";
import OutlinedInput from "#mui/material/OutlinedInput";
import InputLabel from "#mui/material/InputLabel";
import MenuItem from "#mui/material/MenuItem";
import FormControl from "#mui/material/FormControl";
import ListItemText from "#mui/material/ListItemText";
import Select from "#mui/material/Select";
import Checkbox from "#mui/material/Checkbox";
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};
const names = [
"Oliver Hansen",
"Van Henry",
"April Tucker",
"Ralph Hubbard",
"Omar Alexander",
"Carlos Abbott",
"Miriam Wagner",
"Bradley Wilkerson",
"Virginia Andrews",
"Kelly Snyder",
];
function Box() {
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const [personName, setPersonName] = React.useState([]);
const handleChange = (event) => {
const {
target: { value },
} = event;
setPersonName(
// On autofill we get a stringified value.
typeof value === "string" ? value.split(",") : value
);
};
return (
<div className="modal-container">
<ButtonToolbar>
<Button onClick={handleOpen}>Open</Button>
</ButtonToolbar>
<Modal open={open} onClose={handleClose}>
<Modal.Header>
<Modal.Title>Modal Title</Modal.Title>
</Modal.Header>
<Modal.Body>
<div>
<FormControl sx={{ m: 1, width: 300 }}>
<InputLabel id="demo-multiple-checkbox-label">Tag</InputLabel>
<Select
labelId="demo-multiple-checkbox-label"
id="demo-multiple-checkbox"
multiple
value={personName}
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.join(", ")}
MenuProps={MenuProps}
>
{names.map((name) => (
<MenuItem key={name} value={name}>
<Checkbox checked={personName.indexOf(name) > -1} />
<ListItemText primary={name} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
</Modal.Body>
<Modal.Footer>
<Button onClick={handleClose} appearance="primary">
Ok
</Button>
<Button onClick={handleClose} appearance="subtle">
Cancel
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
export default Box;
Not better way to solve this, but set this attribute disableEnforceFocus in your Modal tag.

Material UI Select Multiple Selection in Array

I created a Select using React that allows the user to select multiple options.
The problem is that the Select displays the ID of the selected item, instead of their name.
How can I change the code in a way that the Select display the names separated by commas (now shows the IDs separated by commas), while keeping the array of ids for later processing.
Any idea how to fix it? Here is the code to CodeSanbox
I have the following array in a Material UI Select:
const names = [
{ id: "1", value: "Oliver Hansen" },
{ id: "2", value: "Van Henry" },
{ id: "3", value: "Omar Alexander" }
];
This is the code that renders the Multiple Select:
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
name="first"
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.join(", ")}
>
{names.map((name) => (
<MenuItem key={name.id} value={name.id}>
<Checkbox checked={personName.indexOf(name.id) > -1} />
<ListItemText primary={name.value} />
</MenuItem>
))}
</Select>
I found one possible solution for your issue.
check if it works for you.
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import { Input, OutlinedInput } from "#material-ui/core";
import InputLabel from "#material-ui/core/InputLabel";
import FormControl from "#material-ui/core/FormControl";
import Select from "#material-ui/core/Select";
import MenuItem from "#material-ui/core/MenuItem";
import ListItemText from "#material-ui/core/ListItemText";
import Checkbox from "#material-ui/core/Checkbox";
const useStyles = makeStyles((theme) => ({
formControl: {
margin: theme.spacing(1),
minWidth: 300
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
const names = [
{ id: "1", value: "Oliver Hansen" },
{ id: "2", value: "Van Henry" },
{ id: "3", value: "Van Henry" }
];
export default function NativeSelects() {
const classes = useStyles();
const [personName, setPersonName] = React.useState([]);
const handleChange = (event) => {
const {
target: { value }
} = event;
setPersonName(
// On autofill we get a the stringified value.
typeof value === "string" ? value.split(",") : value
);
};
return (
<div>
<FormControl className={classes.formControl}>
<InputLabel htmlFor="age-native-simple">
Names here to select from
</InputLabel>
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
name="first"
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.map(obj=> names[obj - 1].value).join(", ")}
>
{names.map((name) => (
<MenuItem key={name.id} value={name.id}>
<Checkbox checked={personName.indexOf(name.id) > -1} />
<ListItemText primary={name.value} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
Updated Code
just you can do this:
renderValue={
(selected) =>
names.filter( name => selected.includes(name.id) )
.map( record => record.name )
.join(", ")
}
To show the selected user's names, you can update the map part, where currently, you're using id.
You can update this to use the name.value to store/show the person's names.
{names.map((name) => (
<MenuItem key={name.value} value={name.value}>
<Checkbox checked={personName.indexOf(name.value) > -1} />
<ListItemText primary={name.value} />
</MenuItem>
))}
Updated Sandbox
the simple steps I did, just focus on the renderValue property, on the Select component:
renderValue={(selected) => names.find((val) => val.id === selected).value}
the logic I use, find the value in the 'names' array, where the id is the selected id, then take the 'value' value in the 'names' array, to display.
Update the Menu Items' value to be a object instead of an id.
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
name="first"
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.map((item) => item.value)?.join(",")}
>
{names.map((name) => (
<MenuItem key={name.id} value={name}>
<Checkbox
checked={personName.find((p) => p.id === name.id) !== undefined}
/>
<ListItemText primary={name.value} />
</MenuItem>
))}
</Select>

Material ui list of select item need to be selected

i have created selected box..using functional component...i am getting list of items from the back end, i am looping that list and showing in the front end....now i need to be selected item need to show in the box what i have selected...could any one help on this...
Please see my example code in this link https://codesandbox.io/s/gallant-water-fnqiv
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 => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
const lists = ["Ten", "twenty", "Thirty", "Fourty", "Fifity", "Sixty"];
export default function SimpleSelect() {
const classes = useStyles();
const [age, setAge] = React.useState("");
const inputLabel = React.useRef(null);
const [labelWidth, setLabelWidth] = React.useState(0);
React.useEffect(() => {
setLabelWidth(inputLabel.current.offsetWidth);
}, []);
const handleChange = event => {
setAge(event.target.value);
};
return (
<div>
<FormControl variant="outlined" className={classes.formControl}>
<InputLabel ref={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}
labelWidth={labelWidth}
>
<MenuItem value={""}>
{lists.map(item => {
<li>{item.list}</li>;
})}
</MenuItem>
</Select>
</FormControl>
</div>
);
}
There is no property in the array named, item.list , so you need to use item alone which itself gives the value you want.
Also make sure <MenuItem value={item}> is inside the lists.map() method..
Include return statement of the MenuItemis inside ofthe lists.map() method which throws error in your codesandbox link
And hence,
Change:
<MenuItem value={""}>
{lists.map(item => {
<li>{item.list}</li>;
})}
</MenuItem>
To:
{lists.map(item => {
return (
<MenuItem value={item}>
<li>{item}</li>
</MenuItem>
);
})}
Forked Codesandbox
you can try this-
<div>
<FormControl variant="outlined" className={classes.formControl}>
<InputLabel ref={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}
labelWidth={labelWidth}
>
{lists.map(item => (
<MenuItem value={item}>{item}</MenuItem>
))}
</Select>
</FormControl>
</div>
https://codesandbox.io/s/material-ui-select-m8lgt
Updated code sandbox link
Your MenuItem was wrong.
{lists.map(item => (
<MenuItem value={item}>{item}</MenuItem>
))}

Ant Design - Vertical Group of Checkboxes

I am trying to make a column of checkboxes using Ant design. I have tried:
<Checkbox.Group>
<Checkbox>Hello</Checkbox>
<Checkbox>Hello1</Checkbox>
<Checkbox>Hello2</Checkbox>
</Checkbox.Group>
And:
<CheckboxGroup options={plainOptions} value={this.state.checkedList} onChange={this.onChange} />
However, there does not seem to be an option to make checkboxes appear in a column instead of a row. I know I can use flexbox, but I was hoping Ant design would have a builtin method for making checkboxes appear vertically (in a column) instead of appearing in a horizontal row.
Is there a simple solution for stacking the checkboxes vertically?
Doesn't appear like it does since the <label>s it renders are display: inline-block. The simplest solution (other than rewriting the component) is to change .ant-checkbox-group-item's attributes from (you can always wrap the CheckBox with another class name to apply selectively, instead of globally):
.ant-checkbox-group-item {
display: inline-block;
margin-right: 8px;
}
to:
.ant-checkbox-group-item {
display: block;
margin-right: 0;
}
Which will make it appear like so:
Optionally, you can also add a display: inline-block attribute to .ant-checkbox-group's styles.
Which will then make it appear like so:
Another solution is to create some reusable components and apply the above via the style property (admittedly, not an elegant solution since CheckboxGroup's onChange function expects to handle options, but you'd have to intercept them in order to alter the Checkbox's style property).
Working example: https://codesandbox.io/s/w0voxxxzm5
Checkbox.js
import React from "react";
import { Checkbox } from "antd";
export default ({ disabled, label, value, handleChange }) => (
<Checkbox
style={{ display: "block", marginLeft: 0 }}
disabled={disabled || false}
label={label}
checked={value}
onChange={handleChange}
>
{label}
</Checkbox>
);
CheckboxGroup.js
import React from "react";
import Checkbox from "./Checkbox";
export default ({ options, ...props }) => (
<div
className="ant-checkbox-group"
style={{ display: "inline-block", marginRight: 10 }}
>
{options.map(label => (
<Checkbox
key={label}
label={label}
disabled={props.disabled}
handleChange={props.handleChange}
value={props[label]}
/>
))}
</div>
);
Form.js
import React, { Component } from "react";
import CheckboxGroup from "./CheckboxGroup";
const options1 = ["Apple", "Pear", "Orange"];
const options2 = ["Strawberry", "Grape", "Mango"];
const options3 = ["Kiwi", "Banana", "Cherry"];
export default class Form extends Component {
state = {};
handleChange = ({ target: { label, checked } }) =>
this.setState({ [label]: checked });
handleSubmit = e => {
e.preventDefault();
alert(JSON.stringify(this.state, null, 4));
};
render = () => (
<form onSubmit={this.handleSubmit}>
<CheckboxGroup
{...this.state}
options={options1}
handleChange={this.handleChange}
/>
<CheckboxGroup
{...this.state}
options={options2}
handleChange={this.handleChange}
/>
<CheckboxGroup
{...this.state}
options={options3}
handleChange={this.handleChange}
disabled
/>
<div style={{ marginTop: 20 }}>
<button className="ant-btn ant-btn-primary" type="submit">
Submit
</button>
</div>
</form>
);
}
you can do this, and the content in item is also aligned and right!
.ant-checkbox-group {
display: flex;
flex-direction: column;
}
Following Matt Carlotta example if you are using a variant of styled component, you can achieve that with
export const ColumnCheckbox = styled(Checkbox.Group)`
.ant-checkbox-group-item {
display: block;
margin-right: 0;
}
`
<ColumnCheckbox options={['Hello','Hello1','Hello2']}/>
Not sure if this was always working (since the question is 2 years old), but it is possible to arrange the checkboxes within a group vertically without tweaking the styles by simply wrapping each of them in a <Row> component:
import { Checkbox, Row, Form } from 'antd';
function onChange(checkedValues) {
console.log('checked = ', checkedValues);
}
const GoodFruitForm = () =>
<Form>
<Form.Item label="Good Fruit">
<Checkbox.Group onChange={onChange}>
<Row><Checkbox value='Apple'>Apple</Checkbox></Row>
<Row><Checkbox value='Pear'>Pear</Checkbox></Row>
</Checkbox.Group>
</Form.Item>
</Form>
And the content renders as expected:
render() {
return (
<EnhanceSubContent title={TITLE}>
<Alert
className={styles.alertMargin}
message="Select the Events below to receive notification."
type="info"
showIcon
/>
<div className={styles.content}>
<Checkbox.Group style={{ width: '100%' }} onChange={this.onChange}>
<Row>
<Col span={12}>
<Space direction="vertical">
<Checkbox value="Link Up">Link Up</Checkbox>
<Checkbox value="Link Down">Link Down</Checkbox>
<Checkbox value="Power On">Power On</Checkbox>
<Checkbox value="Power Off">Power Off</Checkbox>
<Checkbox value="Cold Start">Cold Start</Checkbox>
<Checkbox value="Warm Start">Warm Start</Checkbox>
<Checkbox value="Port Link Up">Port Link Up</Checkbox>
</Space>
</Col>
<Col span={12}>
<Space direction="vertical">
<Checkbox value="Port Link Down">Port Link Down</Checkbox>
<Checkbox value="Authentication Failure">
Authentication Failure
</Checkbox>
<Checkbox value="Power1 ok">Power1 ok</Checkbox>
<Checkbox value="Power1 failure">Power1 failure</Checkbox>
<Checkbox value="Power2 ok">Power2 ok</Checkbox>
<Checkbox value="Power2 failure">Power2 failure</Checkbox>
</Space>
</Col>
</Row>
</Checkbox.Group>
<Divider />
</div>
<div className={styles.buttonsContianer}>
<Space align="end">
<Button type="primary"> Apply </Button>
<Button className={styles.cancelButton}> Reset </Button>
</Space>
</div>
</EnhanceSubContent>
);
}
}
Or you can use styled-components, so you will not change .ant-checkbox-group-item style globally.
import { Checkbox } from 'antd';
import styled from 'styled-components'
const VerticalCheckboxGroup = styled(Checkbox.Group)`
.ant-checkbox-group-item {
display: block!important;
}
`

Select MenuItem doesn't show when JSX saved to state

Working from the demo here: https://material-ui.com/demos/selects/ and I'm receiving some weird results. Specifically, when I select an item from the drop-down menu, the this.props.value updates fine... but the MenuItem does not show up.
If I put the <FormControl> tag directly in render, it works fine. If I put in an a variable, then setState with that and insert that into the render... it does NOT work.
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';
const styles = theme => ({
root: {
display: 'flex',
flexWrap: 'wrap',
},
formControl: {
margin: theme.spacing.unit,
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing.unit * 2,
},
});
class Question extends React.Component {
state = {
age: '',
age2: '',
slct: ''
};
componentDidMount() {
const { classes } = this.props;
var mySelect =
<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>
this.setState({ slct: mySelect });
}
handleChange = event => {
this.setState({ [event.target.name]: event.target.value });
};
render() {
const { classes } = this.props;
return (
<div>
{this.state.slct}
<p>Value:</p>{this.state.age}
<FormControl className={classes.formControl}>
<InputLabel htmlFor="age-simple2">Age</InputLabel>
<Select
value={this.state.age2}
onChange={this.handleChange}
inputProps={{
name: 'age2',
id: 'age-simple2',
}}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
<p>Value:</p>{this.state.age2}
</div>
);
}
}
export default withStyles(styles)(Question);
You can see how the 'value' updates correctly for both based on which answer I select in the drop down... but visually, the MenuItem label never appears to show up for the one that's coming from state:
Help?
(fyi: these items are in a parents' <form>.)
it has to be a function or an Array for rendering. To make your code work, it should be:
state = {
slct: <FormControl>...</FormControl>
}
slctRenderer = () => this.state.slct;
render() {
return (
<div>
{this.slctRenderer()}
</div>
);
}
Using the information from #Jee Mok ... the idea that it might work as a function (even though other items don't seem to care) made me try the following changes to the initial code:
Added this function:
selectRenderer() {
const { classes } = this.props;
return (
<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>
);
};
And then in the component's render function, I call it:
render() {
return (
<div>
{this.selectRenderer()}
<p>Value:</p>{this.state.age}
</div>
);
}
This works. (and thus I'll mark it answered in two days)
I am curious, however, WHY this particular chunk of JSX needs to be brought in as a function whereas other chunks of JSX do not. (specifically, a material-ui text field works just fine being assigned to state as I did in the OP) I'd like to be able to build out this question once (preferably as part of the componentDidMount lifecycle) and then pass the built-out question somehow to the render function. State seems to be the way to do that, but I can't with Select/MenuItems for some reason.

Resources