Not able to set values on Material UI TextField Select component - reactjs

I am new to React.
I want to build a Form that has a Text field and a dropdown with 2 selections.
The form renders but I am unable to select any values from the dropdown. The handleChange function doesnt run.
const Category = () => {
const theme = useTheme();
const colors = tokens(theme.palette.mode);
const [formValues, setformValues] = useState({
categoryName: "Test",
categoryType: "Expense",
})
const categories = [
{
value: 'Income',
label: 'Income',
},
{
value: 'Expense',
label: 'Expense',
},
];
const handleFormSubmit = (values) => {
console.log(`Submitting : ${JSON.stringify(values)}`);
};
const isNonMobile = useMediaQuery("(min-width:600px)");
const handleCategoryTextChange = (event) => {
console.log(`Category Text Change ${event}`);
setformValues({
...formValues,
categoryName: event.target.value,
})
}
const handleCategoryChange = (event) => {
console.log(`Category Change Event ${event.target.value}`);
setformValues({
...formValues,
categoryType: event.target.value
})
};
return (
<Box>
<Box m="20px">
<Header title="CREATE CATEGORY" subtitle="Create a New Category" />
<form onSubmit={handleFormSubmit}>
<Box
display="grid"
gap="30px"
gridTemplateColumns="repeat(4, minmax(0, 1fr))"
sx={{
"& > div": { gridColumn: isNonMobile ? undefined : "span 4" },
}}
>
<TextField
fullWidth
variant="filled"
type="text"
label="Category Name"
onChange={handleCategoryTextChange}
value={formValues.categoryName}
name="categoryName"
sx={{ gridColumn: "span 2" }}
/>
<TextField
fullWidth
variant="filled"
label="Category Type"
onChange={handleCategoryChange}
value={formValues.categoryType}
name="categoryType"
sx={{ gridColumn: "span 2" }}
select
>
{categories.map((option) => (
<MenuItem key = {option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</TextField>
</Box>
</form>
</Box>
</Box>
);
};
I am expecting the dropdown to be selectable and the handleChange events to be triggered.
Once this is working, I would submit the form values to an API endpoint

Related

can't edit data in Textfield in reactjs

I'm trying to edit the api data on a Mui datagrid. I want a modal form to pop on clicking the edit icon. Everything works fine, I'm getting the data to be edited and all that. thing is the data I'm getting in textfield for edit, is not changing the value. it's not taking any inputs I'm giving, it stays the same. what should I do to make the textfields editable ? Thanks in advance.
export default function ContactsCard(props) {
const [ContactData, setContactData] = useState([]);
const [open, setOpen] = useState(false);
const [formInputData, setformInputData] = useState(
{
name: '',
details: '',
}
);
const handleChange = (evnt) => {
const newInput = (data) => ({
...data,
[evnt.target.name]: evnt.target.value
});
setformInputData(newInput);
}
const showData = () => {
axios.get('http://localhost:8006/api/v2/get/beneficiaries-list').then(function (res) {
try {
var result = res.data;
// console.log(result.data)
setContactData(result.data)
}
catch (error) {
console.log(error)
}
})
}
const handleSubmit = (evnt) => {
evnt.preventDefault();
const formData = new FormData(); //formdata object
formData.append('nickname', formInputData.name); //append the values with key, value pair
formData.append('target', formInputData.details);
const config = {
headers: { 'content-type': 'multipart/form-data' }
}
axios.post('http://localhost:8006/api/v2/save/beneficiary', formData, config)
.then(response => {
if (response.data.success === true) {
showData()
alert(response.data.message)
}
})
.catch(error => {
alert(error.message);
});
setformInputData({ name: "", details: "" });
setOpen(false);
}
const handleEdit = (id) => {
var edit_id = id.row.id
console.log(edit_id)
setOpen(true)
setformInputData(ContactData.find((data) => data.id === edit_id))
setOpen(true)
}
const handleOpen = () => setOpen(true)
const handleClose = () => setOpen(false);
useEffect(() => {
showData();
}, [])
const handleDelete = (id) => {
var del_id = id.row.id
console.log(del_id)
axios.post('http://localhost:8006/api/v2/remove/beneficiary/', { id: del_id }).then(res => {
console.log(res.data)
if (res.data.success === true) {
alert(res.data.message)
showData();
console.log('ajh');
}
})
};
const columns = [
{ field: 'name', headerName: 'Nick Name', width: '130' },
{ field: 'details', headerName: 'Deposit address', width: '130' },
{
field: 'actions',
type: 'actions',
width: '130',
headerName: 'Actions',
cellClassName: 'actions',
renderCell: (id) => {
return (
<>
<IconButton color="primary" onClick={(e) => { handleEdit(id) }}>
<EditIcon />
</IconButton>
<IconButton color="error" onClick={(e) => {
handleDelete(id)
}}>
<DeleteIcon />
</IconButton>
</>
);
},
},
];
return (
<Card {...props}>
<CardContent>
<ContactDataGrid rows={ContactData} columns={columns} />
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<FormControl sx={style} mt={1} >
<Typography sx={{ fontSize: 16, fontWeight: 'bold' }} color="text.secondary" gutterBottom>
Edit Contact
</Typography>
<Stack flexDirection='column' gap={1.5} mt={1}>
<TextField autoFocus required
id="filled-hidden-label-small"
label="Nick Name" variant="outlined" size="small"
onChange={handleChange}
value={formInputData.name}
name="Nick Name"
className="form-control"
/>
<TextField required
id="filled-hidden-label-small"
label="Deposit Address" variant="outlined" size="small"
onChange={handleChange}
value={formInputData.details}
name="Amount"
className="form-control"
/>
</Stack>
<Stack flexDirection='row' justifyContent={'center'} gap={1.5} mt={1}>
<Button
variant="contained"
type="submit" color="error"
onClick={handleClose}
sx={{ alignSelf: 'center' }} >Cancel</Button>
<Button
variant="contained"
type="submit"
onClick={handleSubmit}
sx={{ alignSelf: 'center', backgroundColor: '#000073' }} >Submit</Button>
</Stack>
</FormControl>
</Fade>
</Modal>
</CardContent>
</Card>
);
}
Your handleChange function is updating the new value in event.target.name but, the name doesn't match with the value. You can try changing the name to match the value getter:
<TextField autoFocus required
id="filled-hidden-label-small"
label="Nick Name" variant="outlined" size="small"
onChange={handleChange}
value={formInputData.name}
name="name" // this changed
className="form-control"
/>
<TextField required
id="filled-hidden-label-small"
label="Deposit Address" variant="outlined" size="small"
onChange={handleChange}
value={formInputData.details}
name="details" // this changed
className="form-control"
/>

How to disable Mui-focusVisible Mui-focused for an AccordionSummary element?

I'm using a custom element for changing the group name of an AccordionSummary.
The UI in normal mode
Element code
Upon clicking the edit button for changing name, mui-focused mui-focusVisible property is passed to AccordionSummary which creates a grey background over the element. Upon clicking save, the mui-focused property is removed again. However, when trying to submit pressing enter and not clicking save, mui-focused stays and AccordionSummary is greyed/focused even in non-edit mode.
Mui-focused upon edit
Mui-focused/focusVisible element code
Greyed UI
I have also tried using a modal form instead for changing the group name. Got the exact same issue of a persistent mui-focused upon submitting by pressing enter.
Is there any way where I could override/disable focused property? I don't want to preventDefault for enter keypress because of bad UX. Sharing the relevant code below.
const AccordionSummary = withStyles((theme) => ({
content: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
'&$expanded': {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
},
},
expanded: {},
}))(MuiAccordionSummary)
const useStyles = makeStyles((theme) => ({
turbineIcon: {
marginRight: theme.spacing(2),
},
gap: {
gap: theme.spacing(1),
},
}))
const GroupSummary = ({
onChangeGroupParameter,
}) => {
const classes = useStyles()
const handleGroupNameValidation = (newName) => {
return /^[A-Za-z][A-Za-z0-9\s_\-]+$/.test(newName)
}
const handleGroupNameChange = useCallback(
(newGroupName) => {
onChangeGroupParameter(groupId, 'group', newGroupName)
},
[onChangeGroupParameter, groupId]
)
return (
<AccordionSummary
aria-controls={`${groupId}-content`}
id={`${groupId}-header`}
>
<Box
display="flex"
width="100%"
alignItems="center"
justifyContent="center"
flexWrap="wrap"
className={classes.gap}
>
<TurbineIconButton big visual className={classes.turbineIcon} />
<Tooltip title="Add">
<EditableTypography
variant="h4"
initialValue={groupId}
onValidate={handleGroupNameValidation}
onSave={handleGroupNameChange}
label="group name"
/>
</Tooltip>
</Box>
</AccordionSummary>
)
}
EditableTypography-
const useStyles = makeStyles((theme) => ({
noPadding: {
padding: 0,
},
iconStyle: {
width: theme.spacing(2.5),
height: theme.spacing(2.5),
},
}))
const defaultOnValidate = () => true
const EditableTypography = ({
initialValue,
onSave,
onValidate,
textFieldProps,
iconButtonProps,
containerProps,
...rest
}) => {
const classes = useStyles()
const [value, setValue] = useState(initialValue)
const [editing, setEditing] = useState(false)
const handleKeyPress = useCallback(
(e) => {
if (e.key === 'Enter') {
handleValueSave(value)
} else if (e.key === 'Escape') {
setEditing(false)
setValue(initialValue)
}
},
[handleValueSave, setEditing, setValue, initialValue, value]
)
const isValid = useMemo(() => onValidate(value), [onValidate, value])
const getHelperText = useCallback(() => {
if (value === initialValue) return 'No change'
if (!isValid) return 'Invalid'
return ''
}, [isValid, value, initialValue])
const helperText = useMemo(() => getHelperText(value), [getHelperText, value])
const handleValueSave = useCallback(
(value) => {
setEditing(false)
if (isValid) {
onSave(value)
}
},
[setEditing, onSave, isValid]
)
const handleTextFieldOnChange = useCallback((e) => setValue(e.target.value), [
setValue,
])
const handleSaveClick = useCallback(() => handleValueSave(value), [
handleValueSave,
value,
])
const handleEditClick = useCallback(
(e) => {
e.stopPropagation()
setEditing(true)
},
[setEditing]
)
return (
<Box display="flex" alignItems="center" {...containerProps}>
{editing && (
<>
<TextField
autoFocus
helperText={helperText}
error={!isValid}
value={value}
variant="outlined"
onChange={handleTextFieldOnChange}
onKeyDown={handleKeyPress}
className={classes.noPadding}
{...textFieldProps}
/>
<IconButton onClick={handleSaveClick} {...iconButtonProps}>
<SaveIcon size="small" />
</IconButton>
</>
)}
{!editing && (
<>
<Typography {...rest}>{value}</Typography>
<IconButton onClick={handleEditClick} {...iconButtonProps}>
<EditIcon size="small" className={classes.iconStyle} />
</IconButton>
</>
)}
</Box>
)
}
Modal code-
<EditGroupNameModal
show={editGroupOpen}
initialValue={groupId}
onModalClose={handleModalClose}
onSave={handleGroupNameChange}
/>
const EditGroupNameModal = ({ initialValue, show, onModalClose, onSave }) => {
const classes = useStyles()
const handleClose = () => {
onModalClose(false)
}
const handleSubmit = useCallback((data) => {
onSave(data.groupName)
handleClose()
})
return (
<Modal
className={classes.modal}
open={show}
onClose={handleClose}
onModalClose={handleClose}
>
<EditGroupForm
initialValue={initialValue}
onCancel={handleClose}
onSubmit={handleSubmit}
/>
</Modal>
)
}
const EditGroupForm = ({ onCancel, initialValue, onSubmit }) => {
const validationSchema = Yup.object().shape({
groupName: Yup.string()
.required('Group name is required')
.matches(
/^[A-Za-z][A-Za-z0-9\s_\-]+$/,
'Group name should have min 2 characters and no special characters except dash (-)'
),
})
const {
register,
handleSubmit,
formState: { errors, isDirty },
} = useForm({
defaultValues: {
groupName: initialValue,
},
resolver: yupResolver(validationSchema),
})
const classes = useStyles()
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<Paper className={classes.paper}>
<Box
className={classes.rootBox}
display="flex"
flexDirection="column"
>
<Box mb={3}>
<Typography variant="h4">Edit Group Name</Typography>
</Box>
<Box mb={3}>
<TextField
autoFocus
fullWidth
defaultValue={initialValue}
{...register('groupName')}
// onKeyPress={(e) => {
// e.key === 'Enter' && e.preventDefault()
// }}
/>
<span style={{ color: 'red' }}>{errors.groupName?.message}</span>
</Box>
<Box ml="auto">
<Button
style={{ marginRight: '1vw' }}
variant="contained"
onClick={onCancel}
>
Cancel
</Button>
<Button
variant="contained"
color="primary"
type="submit"
disabled={!isDirty}
>
Save
</Button>
</Box>
</Box>
</Paper>
</form>
</div>
)
}

React : Managing more than one select control inside a Dialog from Material UI

I'm a newbie in React and Typescript and I'm trying to render some dropdowns from a dialog combining the Select(Chip) and Dialog from Material UI.
The dropdowns are displayed well when I open them, but When I click on any of the displayed values, this overwrite the rest of the dropdowns.
Any help with these?
Thanks you all!
[1]: https://i.stack.imgur.com/QkeZQ.png
[2]: https://i.stack.imgur.com/P45sc.png
[3]: https://i.stack.imgur.com/OWp7D.png
export default function DialogSelect(props: { data: Map<string, any[]>; colName: string; }) {
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const [attributeName, setattributeName] = React.useState<string[]>([]);
const handleChangeChip = (event: SelectChangeEvent<typeof attributeName>) => {
const {
target: { value },
} = event;
setattributeName(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value,
);
};
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = (event: React.SyntheticEvent<unknown>, reason?: string) => {
if (reason !== 'backdropClick') {
setOpen(false);
}
};
const handleDelete = (e: React.MouseEvent, value: string) => {
e.preventDefault();
console.log("clicked delete");
setattributeName((current) => _without(current, value));
};
return (
<div>
<Button onClick={handleClickOpen}>Open select dialog</Button>
<Dialog disableEscapeKeyDown open={open} onClose={handleClose}>
<DialogTitle>Fill the form</DialogTitle>
<DialogContent>
<Box component="form" sx={{ display: 'flex', flexWrap: 'wrap' }}>
{Array.from(props.data).map((attribute, index) => (
<FormControl sx={{ m: 1, width: 300}}>
<InputLabel id={"demo-multiple-chip-label"+attribute[0]}>{attribute[0]}</InputLabel>
<Select sx={{backgroundColor:'honeydew'}}
labelId={"demo-multiple-chip-label"+attribute[0]}
id={"demo-multiple-chip"+attribute[0]}
className="attributeValues"
multiple
value={attributeName}
onChange={handleChangeChip}
input={<OutlinedInput id="select-multiple-chip" label={props.colName} />}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value) => (
<Chip clickable variant="outlined" deleteIcon={
<CancelIcon
onMouseDown={(event) => event.stopPropagation()}
/>
} onDelete={(e) => handleDelete(e, value)} key={value} label={value} />
))}
</Box>
)}
MenuProps={MenuProps}
>
{attribute[1].map((name: any) => (
<MenuItem
key={name}
value={name}
style={getStyles(name, attributeName, theme)}
>
{name}
</MenuItem>
))}
</Select>
</FormControl>
))}
</Box>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleClose}>Ok</Button>
</DialogActions>
</Dialog>
</div>
);
}

React - MUI - Autcomplete, store & load checked items

I want to select and store some items. After reloading these values have to be already selected in the list. how i can store and load selected values, that for the next time (reloading, the stored data), the selected values will be selected in the Autocomplete list ?
export default function BuzwordTags(props) {
const tNdsProps = props.titelNdsProps.NotesDashboradProps;
const [selectedOptions, setSelectedOptions] = React.useState("");
const updates = {
buzwords: selectedOptions,
};
React.useEffect(
() => (
setSelectedOptions(tNdsProps.expenses[0].buzwords),
console.log("Selectet options changed", selectedOptions)
),
[tNdsProps.activeNote]
);
return (
<Grid>
<Autocomplete
multiple
onChange={(event, value) => setSelectedOptions(value)}
id="checkboxes-tags-demo"
options={top100Films}
disableCloseOnSelect
getOptionLabel={(option) => option.title}
renderOption={(props, option, { selected }) => (
<li {...props}>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.title}
</li>
)}
style={{ width: 500 }}
renderInput={(params) => (
<TextField
{...params}
label="Checkboxes"
placeholder="Favorites"
value={selectedOptions != "" ? selectedOptions[0].title : ""}
/>
)}
/>
<Button
variant="outlined"
onClick={() => {
setSelectedOptions(tNdsProps.activeNote.id, updates);
}}
>
Add B-Words
</Button>
</Grid>
);
}
const top100Films = [
{ title: "The Shawshank Redemption", year: 1994 },
{ title: "The Godfather", year: 1972 },
{ title: "The Godfather: Part II", year: 1974 },
];

Unexpected useState behaviour in ant design table

I have ant design form and table side by side,When I fill the form data and click on add entity button the data should be displayed in the table. Now the data does get rendered when I click on add entity but again when I fill the form and click on the add entity button the previous data gets disappeared, Now I know that I need to copy the previous data and I did that too but its not working.
My Problem:- The previous form state value which is rendered in the table gets disappeared when new form data is added
My query:- How and Where should I copy the previous object with the spread operator so that it does not get reset.
My code is
import { Card, Table } from "antd";
import { Form, Input, Button, Select, Space, AutoComplete } from "antd";
import { DeleteOutlined, PlusOutlined, SendOutlined } from "#ant-design/icons";
import { useState } from "react";
const ExampleComponent = (props) => {
// Destructuring props
const { intent_data, entity_data } = props;
const [dataSource, setDataSource] = useState([{}]);
// To handle the disable state of Select Entity Select DropDown
const [addentity,setAddEntity] = useState(false)
// Handler function passed to YES/NO Select Option
const addEntityHandler = (addEntity) => {
if(addEntity === 'no'){
setAddEntity(true)
}else{
setAddEntity(false)
}
}
const [form] = Form.useForm();
const onFinish = (values) => {
console.log(values);
form.resetFields();
const dataArr = [];
// Push values to array since dataSource takes array not an object
dataArr.push(values);
setDataSource(dataArr);
};
const columns = [
{
title: "Entity",
dataIndex: "entity_name",
key: "entity_name",
},
{
title: "Entity Value",
dataIndex: "entity_value",
key: "entity_value",
},
{
title: "Operation",
key: "operation",
render: (record: any) => (
<DeleteOutlined
style={{ color: "red" }}
onClick={() => console.log(record)}
/>
),
},
];
return (
<Card className="csi-project-card-0934">
<div className="example-layout">
<div style={{ flexBasis: "100%" }}>
<Form
form={form}
labelCol={{ span: 7 }}
wrapperCol={{ span: 10 }}
layout="horizontal"
colon={true}
onFinish={onFinish}
size="large"
>
{/* <h4>Create Example</h4> */}
<Form.Item
label="Select Intent"
name="intent_name"
className="csi-ant-form-item"
rules={[{ required: true, message: "Intent Cannot be Empty!" }]}
>
<Select>
{/* {intent_data?.map?.((value) => (
<Select.Option
key={value.intent_ID}
value={value.intent_name}
>
{value.intent_name}
</Select.Option>
))} */}
<Select.Option value="intent demo">Intent Demo</Select.Option>
<Select.Option value="intent test">Intent Test</Select.Option>
</Select>
</Form.Item>
<Form.Item
label="Enter Example"
name="example_name"
className="csi-ant-form-item"
hasFeedback
rules={[
{ required: true, message: "This Field Cannot be Empty!" },
({ getFieldValue }) => ({
validator(_, value) {
if (value.length < 4) {
return Promise.reject("Length too short");
}
return Promise.resolve();
},
}),
]}
>
<AutoComplete>
<Input allowClear/>
</AutoComplete>
</Form.Item>
<Form.Item
label="Do you want to add Entity"
name="add_entity"
className="csi-ant-form-item"
rules={[{ required: true, message: "This Cannot be Empty!" }]}
>
<Select placeholder="SELECT" onSelect={(addEntity) => addEntityHandler(addEntity)}>
<Select.Option value="yes">YES</Select.Option>
<Select.Option value="no">NO</Select.Option>
</Select>
</Form.Item>
<Form.Item
label="Select Entity"
name="entity_name"
className="csi-ant-form-item"
>
<Select disabled = {addentity}>
<Select.Option value="entity demo">Entity Demo</Select.Option>
<Select.Option value="entity test">Entity Test</Select.Option>
</Select>
</Form.Item>
<Form.Item
label="Select Value"
name="entity_value"
className="csi-ant-form-item"
hasFeedback
rules={[
{ required: true, message: "This Field Cannot be Empty!" },
]}
>
<AutoComplete>
<Input placeholder="Select Value from Example" />
</AutoComplete>
</Form.Item>
<Form.Item className="csi-ant-form-item">
<Button
key="submit"
type="primary"
htmlType="submit"
shape="round"
>
Add Entity <PlusOutlined />
</Button>
</Form.Item>
</Form>
</div>
<div
style={{
flexBasis: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<Table
bordered
className="ib-table"
dataSource={dataSource}
columns={columns}
pagination={{ pageSize: 6 }}
rowKey={Math.random().toString()}
/>
<Button key="submit" type="primary" htmlType="submit" shape="round">
Submit <SendOutlined />
</Button>
</div>
</div>
</Card>
);
};
export default ExampleComponent;
The form data is stored in values object and the structure of values is
{
add_entity: "yes"
entity_name: "entity demo"
entity_value: "Test"
example_name: "Test"
intent_name: "intent demo"
}
One thing to note here is that dataSource state variable is array of objects, like
[{
add_entity: "yes"
entity_name: "entity demo"
entity_value: "Test"
example_name: "Test"
intent_name: "intent demo"
}]
My Expected Output is Below
Entity
Entity Value
Operation
entity demo
test
delete icon
intent demo
test
delete icon
What if you try with
setDataSource(prevDataSource => [...prevDataSource, values]);
?
In the same way, to delete an item:
{
title: "Operation",
key: "operation",
render: (record) => (
<DeleteOutlined
style={{ color: "red" }}
onClick={() => {
setDataSource(prevDataSource => prevDataSource.filter(item => item.entity_name !== record.entity_name //entity_name or whatever id the item has ))
}}
/>
),
},
By the way, try to avoid using any whenever possible if you're using typescript. Here is how to:
import { Table } from 'antd';
import { ColumnsType } from 'antd/es/table';
interface User {
key: number;
name: string;
}
const columns: ColumnsType<User> = [
{
key: 'name',
title: 'Name',
dataIndex: 'name',
},
];
const data: User[] = [
{
key: 0,
name: 'Jack',
},
];
export default () => (
<>
<Table<User> columns={columns} dataSource={data} />
/* JSX style usage */
<Table<User> dataSource={data}>
<Table.Column<User> key="name" title="Name" dataIndex="name" />
</Table>
</>
);

Resources