Select with chip input not displaying the selected value - reactjs

I have a Select and the inputs are in Chip Format. I tried console log of the value selected and it is getting it fine. But for some reason, it does not get displayed on the select box. What am I doing wrong here?
handleChange = event => {
this.setState({ badge : event.target.value });
};
const chipOptions = [
{key: 1, 'text': 'text1', 'value': 'text1'},
{key: 2, 'text':'text2', 'value':'text2'},
{key: 3, 'text':'text3', 'value':'text3'}
]
<FormControl className={classes.formControl}>
<Select
value={this.state.badge}
onChange={this.handleChange}
inputProps={{
name: 'badge',
id: 'badge-simple',
}}
>
{chipOptions && chipOptions.map(option=> (
<Chip key={option.value} label={option.value} className={classes.chip} value={option.value} />
))}
</Select>
</FormControl>

The default manner in which Select renders the selected value is to render its children. In the Select source code as it is looping through the children of the Select, it does the following:
selected = areEqualValues(value, child.props.value);
if (selected && computeDisplay) {
displaySingle = child.props.children;
}
This is based on the assumption of the Select having MenuItem children. For instance, in the following example the first MenuItem would be selected and that MenuItem's children would be the text "Item 1":
<Select value={1}>
<MenuItem value={1}>Item 1</MenuItem>
<MenuItem value={2}>Item 2</MenuItem>
</Select>
Your Chips don't have children, so nothing is displayed. You can customize this behavior by specifying the renderValue property on Select. This is a function that receives the value and can decide what to render.
The following example shows using the renderValue prop to render a Chip:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import FormControl from "#material-ui/core/FormControl";
import Chip from "#material-ui/core/Chip";
import Select from "#material-ui/core/Select";
import { withStyles } from "#material-ui/core/styles";
const styles = theme => ({
formControl: {
margin: theme.spacing.unit,
minWidth: 120
}
});
const chipOptions = [
{ key: 1, text: "text1", value: "text1" },
{ key: 2, text: "text2", value: "text2" },
{ key: 3, text: "text3", value: "text3" }
];
function App({ classes }) {
const [value, setValue] = useState("text1");
const renderChip = value => {
return <Chip label={value} className={classes.chip} />;
};
return (
<>
<FormControl className={classes.formControl}>
<Select
inputProps={{
name: "badge",
id: "badge-simple"
}}
renderValue={renderChip}
value={value}
onChange={event => {
console.log(event.target.value);
setValue(event.target.value);
}}
>
{chipOptions &&
chipOptions.map(option => (
<Chip
key={option.value}
label={option.value}
className={classes.chip}
value={option.value}
/>
))}
</Select>
</FormControl>
</>
);
}
const StyledApp = withStyles(styles)(App);
const rootElement = document.getElementById("root");
ReactDOM.render(<StyledApp />, rootElement);

Related

boolean input to setState from mapped data

I've got this .map to map a list of checkboxes.
I'd like to map the setState as well, and make in sort it dynamically points out the value binded to each column object:
const [activeColumns, setActiveColumns] = useState<any>({
creationDate: false,
destination: false,
})
const columns = [
{ label: 'Creation_date', value: "creationDate" },
{ label: "Destination", value: "destination" }
]
return (
// ...
{
columns.map((column: any, index: number) => (
<MenuItem onClick={() => setActiveColumns({...activeColumns, [column.value]: !activeColumns[column.value]})}>
<FormGroup>
<FormControlLabel control={<Checkbox checked={activeColumns[column.value]}/>} label={column.label} />
</FormGroup>
</MenuItem>
))
})
I don't know how to dynamically point to activeColmumns keys from columns values.
I'm using React TS and MUI
Your code is working as intended, just wrap your expression with JSX fragments or any parent element
import React, { useState } from "react";
import FormGroup from "#mui/material/FormGroup";
import FormControlLabel from "#mui/material/FormControlLabel";
import Checkbox from "#mui/material/Checkbox";
import { MenuItem } from "#mui/material";
export default function CheckboxLabels() {
const [activeColumns, setActiveColumns] = useState<any>({
creationDate: false,
destination: false
});
const columns = [
{ label: "Creation_date", value: "creationDate" },
{ label: "Destination", value: "destination" }
];
return (
// ...
<>
{columns.map((column: any, index: number) => (
<MenuItem
key={column.value}
onClick={() =>
setActiveColumns({
...activeColumns,
[column.value]: !activeColumns[column.value]
})
}
>
<FormGroup>
<FormControlLabel
control={<Checkbox checked={activeColumns[column.value]} />}
label={column.label}
/>
</FormGroup>
</MenuItem>
))}
</>
);
}

I keep getting violation from chrome console and it does laggy animation when selecting a controlled component with MUI textfield select props

I'm rendering from database over 300 choices to the MUI textfield select. I used the controller component from react hook forms how do I solve this?
import { Menu, MenuItem, TextField } from "#mui/material";
import React from "react";
import { Controller } from "react-hook-form";
import { useSelector } from "react-redux";
const DropdownState = ({ controller, textField, type, isLabelAsId = true }) => {
const position = useSelector((state) => state.position);
let data = [];
if (type === "position") {
data = position.positions.map((x) => ({
id: x.PosID,
label: x.Positn,
}));
} else {
console.error(
'Please choose a type between "position" and "test" '
);
return;
}
if (isLabelAsId) data = data.map((x) => ({ ...x, id: x?.label }));
return (
<Controller
{...controller}
render={({ field, fieldState }) => (
<TextField
select
size="small"
margin="normal"
sx={{
flex: 1,
"& fieldset": {
borderRadius: 3,
},
}}
{...field}
{...textField}
error={!!fieldState.error}
helperText={fieldState.error?.message}
>
{data
.sort((a, b) =>
a.label.toLowerCase() < b.label.toLowerCase()
? -1
: a.label.toLowerCase() > b.label.toLowerCase()
? 1
: 0
)
.map((x, index) => (
<MenuItem key={index} value={x.id}>
{x.label}
</MenuItem>
))}
</TextField>
)}
/>
);
};
export default DropdownState;
What I want is to avoid getting this warning message from the console and stop the laggy animation when clicking the select input field "Position"

Mui Select not changing value when defaultValue is set

i have a select compontent from the mui react libary.
I also use react-hook-form with controller.
In the controller i will set defaultValue becouse i want to use the reset function von react-hook-form. But when i set default value i cant change the value. When there is no default value everthing works as expected.
Here my Code:
import * as React from "react";
import { useTheme } from "#mui/material/styles";
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 Select from "#mui/material/Select";
import { useFormContext, Controller } from "react-hook-form";
import Box from '#mui/material/Box';
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};
function getStyles(name, personName, theme) {
return {
fontWeight:
personName.indexOf(name) === -1
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium,
};
}
function MySelect({ formname, name, reset, setSelected, data = [] }) {
const {
control,
formState: { errors },
setValue,
} = useFormContext();
let isError = false;
let errorMessage = "";
if (errors && errors.hasOwnProperty(name)) {
isError = true;
errorMessage = errors[name].message;
}
const handleChange = (event) => {
const {
target: { value },
} = event;
setSelected(
// On autofill we get a stringified value.
typeof value === "string" ? value.split(",") : value
);
};
return (
<Box sx={{minWidth: 120}}>
<InputLabel id="demo-multiple-name-label">{name}</InputLabel>
<Controller
name={formname}
style={{ width: 200 }}
control={control}
defaultValue=""
render={({ field: { onChange, value } }) => (
<Select
sx={{ width: 200 }}
labelId="demo-multiple-name-label"
id="demo-multiple-name"
multiple={false}
value={value}
onChange={onChange}
input={<OutlinedInput label={name} />}
MenuProps={MenuProps}
error={isError}
helperText={errorMessage}
>
{data.map((element, i) => {
if (element.bsa_maskenkey)
return (
<MenuItem value={element} key={i}>
{`${element.bsa_maskenkey} ${element.we_menge}`}
</MenuItem>
);
else if (element.blager_maskenkey)
return (
<MenuItem value={element} key={i}>
{`${element.blager_maskenkey} ${element.blagerpo_maskenkey} | Menge: ${element.summe}`}
</MenuItem>
);
else
return (
<MenuItem value={element} key={i}>
{`${element.objekt} | Menge: ${element.menge}`}
</MenuItem>
);
})}
</Select>
)}
/>
</Box>
);
}
export default React.memo(MySelect)
```
Input elements must be either controlled or uncontrolled (specify either the value prop, or the defaultValue prop, but not both). You should decide between using a controlled or uncontrolled input element and remove one of these props. I assume you need controlled inputs since you probably maintain input value in some type of state of your form.
It should be just enough to declare defaultValues in useForm hook, and those will be automatically bind to their related input fields inside form. For example:
useForm({
mode: 'onSubmit',
reValidateMode: 'onChange',
defaultValues: { FieldA: "foo", FieldB: "xai" },
shouldFocusError: true,
shouldUnregister: false,
shouldUseNativeValidation: false
})
When using like this, those default values for FieldA and FieldB will be automatically set as initial values in their related fields inside form, you just need to be sure that you are properly binding those fields with related form state.

Delete Item in "Select Box" on State Change in React-Select (MultiSelect)

I want to delete items in Select Box as my state gets updated.
As of my current implementation, items gets deleted from the Options List Dropdown but the Select Box still shows that deleted value.
The Setup
Curernt Behaviour
Even after deleting item it still shows up in Select Box
Goal
Update Select Box as well on State Change.
Here's the SandBox
Thanks in Advance :)
You will need to sync your updated option to your Select component too:
You will need to use a new state and update them accordingly.
So it's become:
import "./styles.css";
import { useState } from "react";
import Select from "react-select";
import { Button } from "#material-ui/core";
export default function App() {
const [items, setItems] = useState([
{ id: 1, text: "Text 1" },
{ id: 2, text: "Text 2" },
{ id: 3, text: "Text 3" }
]);
const [options, setOptions] = useState([]);
const deleteItem = (getID) => {
setItems(items.filter((single) => single.id !== getID));
setOptions(options.filter((single) => single.id !== getID));
};
return (
<div className="App">
<div style={{ marginTop: 40, marginBottom: 40 }}>
{items.map((data) => (
<li>
{data.text}
<Button
onClick={() => deleteItem(data.id)}
color="primary"
style={{ marginLeft: 20 }}
>
Delete
</Button>
</li>
))}
</div>
<Select
isMulti
isSearchable
maxMenuHeight={200}
isClearable={false}
options={items}
getOptionLabel={(option) => option.text}
getOptionValue={(option) => option.text}
value={options}
onChange={(options) => setOptions(options)}
/>
</div>
);
}

Is there a Material UI based tree select component?

I have been looking for a reliable component with a clear API documentation that would allow me to display a "tree view" structure for a select input as part of a form. The closest I have came across is vue-treeselect with many supported features such as: disabling branch nodes, disable item selection and more; the issue is that it's only available on Vue JS. My project is using Material UI as its design system, any component that supports it would be very great. Thanks
I needed a to deal with tree data in a project as well. I ended up creating MUI Tree Select.
You can demo it in this sandbox.
I searched a lot for that in the end I made this by myself sandbox.
you can choose to parent or child with this and you can custom it easily.
import { ThemeProvider, createTheme } from "#mui/material/styles";
import React, { useState } from "react";
import ReactDOM from "react-dom";
import TreeItem from "#mui/lab/TreeItem";
import { Popover, TextField, Typography } from "#mui/material";
import clsx from "clsx";
import { TreeView, useTreeItem } from "#mui/lab";
import ExpandMoreIcon from "#mui/icons-material/ExpandMore";
import ChevronRightIcon from "#mui/icons-material/ChevronRight";
import { useMediaQuery } from "#mui/material";
const data = [
{
id: "root",
name: "Parent",
children: [
{
id: "1",
name: "Child - 1"
},
{
id: "3",
name: "Child - 3",
children: [
{
id: "4",
name: "Child - 4"
}
]
}
]
},
{
id: "1root",
name: "Parent1",
children: [
{
id: "5",
name: "Child - 1-1"
},
{
id: "7",
name: "Child - 3-1",
children: [
{
id: "8",
name: "Child - 4-1"
}
]
}
]
}
];
const CustomContent = React.forwardRef(function CustomContent(props, ref) {
const {
classes,
className,
label,
nodeId,
icon: iconProp,
expansionIcon,
displayIcon
} = props;
const {
disabled,
expanded,
selected,
focused,
handleExpansion,
handleSelection,
preventSelection
} = useTreeItem(nodeId);
const icon = iconProp || expansionIcon || displayIcon;
const handleMouseDown = (event) => {
preventSelection(event);
};
const handleExpansionClick = (event) => {
handleExpansion(event);
};
const handleSelectionClick = (event) => {
handleSelection(event);
};
return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div
className={clsx(className, classes.root, {
[classes.expanded]: expanded,
[classes.selected]: selected,
[classes.focused]: focused,
[classes.disabled]: disabled
})}
onMouseDown={handleMouseDown}
ref={ref}
style={{ padding: "3px 0" }}
>
<div onClick={handleExpansionClick} className={classes.iconContainer}>
{icon}
</div>
<Typography
onClick={handleSelectionClick}
component="div"
className={classes.label}
>
{label}
</Typography>
</div>
);
});
const CustomTreeItem = (props) => (
<TreeItem ContentComponent={CustomContent} {...props} />
);
export default function RichObjectTreeView({ formik, edit }) {
const [anchorEl, setAnchorEl] = React.useState(null);
const [equipmentItem, setEquipmentItem] = useState("");
const [equipmentId, setEquipmentId] = useState("");
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "simple-popover" : undefined;
const renderTree = (nodes) => (
<CustomTreeItem key={nodes.id} nodeId={nodes.id} label={nodes.name}>
{Array.isArray(nodes.children)
? nodes.children.map((node) => renderTree(node))
: null}
</CustomTreeItem>
);
return (
<>
<TextField
variant="standard"
required={false}
label="Equipment Item"
name="equipmentItem"
id="equipmentItem"
defaultValue={equipmentItem}
value={equipmentItem}
className="w-100"
inputProps={{ readOnly: !edit }}
onClick={handleClick}
/>
<Popover
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "left"
}}
>
<TreeView
aria-label="icon expansion"
defaultSelected={equipmentId}
selected={equipmentId}
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
onNodeSelect={(e, id) => {
setEquipmentId(id);
setEquipmentItem(e.target.innerText);
}}
sx={{
height: 200,
flexGrow: 1,
minWidth: "200px",
overflowY: "auto"
}}
>
{data.map((item, i) => renderTree(item))}
</TreeView>
</Popover>
</>
);
}
You can install this library here.
it cover both reactive forms and ngforms too
Check this out https://www.npmjs.com/package/mat-tree-select-input
Probably this is what you are looking for:
https://github.com/dowjones/react-dropdown-tree-select (it also has a theme for mui like style)

Resources