If I press escape button data will be saved to database. How to prevent user to submit data if user press escape button? Following code is from material ui Dialog component. I tried with e.keyCode === 27. In material ui e.keyCode === 27 is not working. I want to prevent user submitting data if they press escape button.
import React from 'react';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import {getCookie} from "../../../../actions/02 user/01 auth"
import { userInfoEdit,createUserProfile } from '../../../../actions/02 user/02 profile';
export default function FormDialog() {
const [open, setOpen] = React.useState(false);
const[name,setName] = React.useState(null)
const handleClickOpen = () => {
setOpen(true);
};
const token = getCookie("token")
const handleClose1 = ()=>{
setOpen(false)
}
const handleClose = async() => {
setOpen(false);
await userInfoEdit({name}, token); //api call
await createUserProfile(token,{name}) // api call
setName("");
};
return (
<div>
<Button size ="small" variant="outlined" color="primary" onClick={handleClickOpen}>
Edit
</Button>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Edit Name</DialogTitle>
<DialogContent>
<DialogContentText>
Edit name
</DialogContentText>
<TextField
autoFocus
label="Name"
value={name}
type="name"
margin="normal"
onChange={(e)=>setName(e.target.value)}
variant="outlined"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose1} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary"
disabled={name ? name.length <= 6:!name}
>
Submit
</Button>
</DialogActions>
</Dialog>
</div>
);
}
Why you are giving handleClose function for Submit and onClose. Give separately like below.
export default function FormDialog() {
const [open, setOpen] = React.useState(false);
const[name,setName] = React.useState(null)
const handleClickOpen = () => {
setOpen(true);
};
const token = getCookie("token")
const handleClose1 = ()=>{
setOpen(false)
}
const popupClose = () => {
setOpen(false);
}
const submitHandleClose = async() => {
popupClose();
await userInfoEdit({name}, token); //api call
await createUserProfile(token,{name}) // api call
setName("");
};
return (
<div>
<Button size ="small" variant="outlined" color="primary" onClick={handleClickOpen}>
Edit
</Button>
<Dialog open={open} onClose={popupClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Edit Name</DialogTitle>
<DialogContent>
<DialogContentText>
Edit name
</DialogContentText>
<TextField
autoFocus
label="Name"
value={name}
type="name"
margin="normal"
onChange={(e)=>setName(e.target.value)}
variant="outlined"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={popupClose} color="primary">
Cancel
</Button>
<Button onClick={submitHandleClose} color="primary"
disabled={name ? name.length <= 6:!name}
>
Submit
</Button>
</DialogActions>
</Dialog>
</div>
);
}
Related
I am trying to implement a mui dialog box that contains a checkbox that the user has to check/acknowledge first before they can select the "Yes" option.
I currently have a working dialog box component that I am calling that doesn't have the checkbox, but I have added the checkbox part as follows:
export default function ConfirmCheckboxDialog(props) {
const { confirmDialog, setConfirmDialog } = props;
const classes = useStyles();
const [checked, setChecked] = React.useState(false);
const handleChange = (event) => {
setChecked(event.target.checked);
};
return (
<Dialog open={confirmDialog.isOpen} classes={{ paper: classes.dialog }}>
<DialogTitle className={classes.dialogTitle}>
<IconButton disableRipple className={classes.titleIcon} size="large">
<NotListedLocationIcon />
</IconButton>
</DialogTitle>
<DialogContent className={classes.dialogContent}>
<DialogContentText id="alert-dialog-description">
<Typography variant="h6">{confirmDialog.title}</Typography>
<FormControlLabel
control={
<Checkbox checked={checked} onChange={handleChange} />
}
label="Please acknowledge"
/>
</DialogContentText>
</DialogContent>
<DialogActions className={classes.dialogAction}>
<Controls.Button
text="No"
color="primary"
onClick={() => setConfirmDialog({ ...confirmDialog, isOpen: false })}
/>
<Controls.Button text="Yes" color="success" onClick={confirmDialog.onConfirm} />
</DialogActions>
</Dialog>
);
}
Now I am calling this ConfirmCheckboxDialog component above from another component where I have set the following details:
const [confirmDialog, setConfirmDialog] = useState({ isOpen: false, title: '' });
I also have the following button:
<Button
color="secondary"
onClick={() => {
setConfirmDialog({
isOpen: true,
title: `Delete?`,
onConfirm: () => {
console.log("Deleted......";
}
});
>
{<span>Delete</span>}
</Button>
<ConfirmCheckboxDialog confirmDialog={confirmDialog} setConfirmDialog={setConfirmDialog} />
Based on the above ConfirmCheckboxDialog component, I'm not sure how to validate the checkbox against the Yes button as the only means of the onClick={confirmDialog.onConfirm} /> being set is if the checkbox is set/true
Any help would be good.
Try to define a dedicate function to handle the Yes button click:
const handleYes = () => {
if (checked && confirmDialog.onConfirm) {
setConfirmDialog({ ...confirmDialog, isOpen: false })
confirmDialog.onConfirm();
}
}
...
<Controls.Button text="Yes" color="success" onClick={() => handleYes()} />
I would like to open a modal (or dialogue) when a user selects an option from a dropdown menu.
Eventually there will be a few options in the dropdown, and different dialogues/modals will be called and rendered depending on which dropdown option has been clicked. For now - how do I get the modal/dialogue to open with dropdown menu events?
I'm currently using the handleClose handler to attempt to open a dialogue since that event should be easy to grab and use right out of the docs for MUI Menu and MenuItem.
The origination call to the DropdownMenu component (which works well and shows the dropdown menu) occurs in a table through the click of an icon
<DropdownMenu options={['Show modal or dialogue to the user']}>
<MoreHorizIcon />
</DropdownMenu>
The DropdownMenu component (also working well itself, except for not triggering the dialogue/modal) looks like this
interface IProps extends Omit<unknown, 'children'> {
children: any;
options: string[];
}
const ITEM_HEIGHT = 48;
const DropdownMenu = ({ children, options }: IProps) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const showModal = () => {
return (
<AlertDialog />
)
}
const handleClose = () => {
//the native alert dialogue works well
alert('I want to show a dialog or modal the same way this alert shows up and ask the user if they are sure they want to delete something')
//why isn't the custom alert dialog being called?
showModal();
setAnchorEl(null);
};
return (
<div>
<IconButton
aria-label="more"
id="more-button"
aria-controls="long-menu"
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
onClick={handleClick}
>
{children}
</IconButton>
<Menu
id="dropdownmenu"
MenuListProps={{
'aria-labelledby': 'more-button'
}}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
PaperProps={{
style: {
maxHeight: ITEM_HEIGHT * 4.5,
width: '20ch'
}
}}
>
{options.map(option => (
<MenuItem key={option} onClick={handleClose} >
{option}
</MenuItem>
))}
</Menu>
</div>
);
};
export default DropdownMenu;
And the example starter dialogue I am using to get the ball rolling looks like this
const AlertDialog = () => {
const [open, setOpen] = React.useState(true);
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Sweet Filler Dialog"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>NO</Button>
<Button onClick={handleClose} autoFocus>
YES
</Button>
</DialogActions>
</Dialog>
</div>
);
}
You can use a state variable to trigger the modal in the DropdownMenu component, whenever you wanted to show the dialog/modal.
const [isModalOpen, setIsModalOpen] = React.useState(false);
and then in the handleClose, you can update the modal state to open the modal.
const handleClose = () => {
setIsModalOpen(true)
setAnchorEl(null);
};
Then somewhere in your JSX of DropdownMenu, you can conditionally render the AlertDialog component like this
{isModalOpen ? <AlertDialog open={isModalOpen} setOpen={setIsModalOpen} /> : null}
Finally, update your AlertDialog component to use props to handle the closing of the modal.
const AlertDialog = ({ open, setOpen }) => {
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Sweet Filler Dialog"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>NO</Button>
<Button onClick={handleClose} autoFocus>
YES
</Button>
</DialogActions>
</Dialog>
</div>
);
}
I have a subscription dialog form. I want the email field to be required, but I am currently able to submit my form with a blank email address (which would be a major problem for the client!). I have it marked as required in my code, but that doesn't seem to be translating to my UI.
I am using Material UI for styling.
Any pointers are sincerely appreciated :)
In the picture, see how I was able to click subscribe with no email address (the submit message appears after clicking subscribe).
import React from 'react';
import Button from '#material-ui/core/Button';
import { makeStyles } from '#material-ui/core/styles';
import TextField from '#material-ui/core/TextField';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import Grid from '#material-ui/core/Grid';
import { Typography } from '#material-ui/core';
import { Form } from 'react-final-form';
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
padding: theme.spacing(2)
},
divider: {
marginBottom: theme.spacing(2)
}
}));
export default function SubscribeFormResults() {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const [formSubmitted, setFormSubmitted] = React.useState(false);
const onSubmit = async values => {
console.log('Submitting subscribe form!');
console.log('Subscribe form values:', values);
setFormSubmitted(true);
};
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
setFormSubmitted(false);
};
const validate = values => {
const errors = {};
if (!values.userEmail) {
errors.userEmail = 'Required';
}
return errors;
};
return (
<div>
<Button size="small" color="primary" onClick={handleClickOpen}>
Subscribe
</Button>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Subscribe</DialogTitle>
<DialogContent>
<Form
onSubmit={onSubmit}
initialValues={{ userEmail: 'johndoe#example.com', arn: 'Connect to Backend!' }}
validate={validate}
render={({ handleSubmit, form, submitting, pristine, values }) => (
<form onSubmit={handleSubmit} noValidate>
<DialogContentText>
To subscribe to this website, please enter your email address here. We will send updates
occasionally.
</DialogContentText>
<TextField
label="Email Address"
name="userEmail"
margin="none"
required={true}
fullWidth
/>
{formSubmitted && <Grid item xs={12}>
<Typography name='submitMessage' variant='subtitle1'>You have subscribed to AA-01-23-45-678901-2. {/* Connect to backend here */}</Typography>
</Grid>}
<DialogActions>
<Button /* onClick={handleClose} */ color="primary" type="submit" disabled={submitting}>
Subscribe
</Button>
<Button onClick={handleClose} color="primary">
Close
</Button>
</DialogActions>
</form>
)}
/>
</DialogContent>
</Dialog>
</div>
);
}
For future readers, I fixed this by removing the validation parameter from the Material UI, uppercase Form tag and enforced validation using the standard, lowercase form tag.
I'm wondering if anyone else has had success editing text inside a Bootstrap React Modal and could share some wisdom. I'm using a Bootstrap React Modal to display data once the user clicks on the task title. The data is displaying beautifully, but the user cannot edit the data. The cursor changes to an editing cursor, but no amount of keyboard pounding gets an edit. I tried adding a Bootstrap React Form as you can see below. When I click on the "Save Changes" button, I get my toastify error.
import React, { useState } from "react";
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";
import Modal from "react-bootstrap/Modal";
import taskAPI from "../../utils/taskAPI";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import "./style.css";
function TaskModal({ onHide, onTaskUpdated, task }) {
const [title, setTitle] = useState("");
const [textBody, setTextBody] = useState("");
const [isPending, setIsPending] = useState(false);
const notify = () => toast.warn("You cannot save an empty task!");
const updateTask=(event)=> {
event.preventDefault();
setIsPending(true);
taskAPI
.updateTask({ title, textBody })
.then((response) => {
response.setTitle("");
response.setTextBody("");
})
.then((response) => {
onTaskUpdated();
setIsPending(false);
})
.catch((error) => {
console.log(error);
setIsPending(false);
notify();
});
}
return (
<>
<Modal show={true} onHide={onHide}>
<Modal.Header closeButton>
<Modal.Title>
<Form>
<Form.Group controlId="TaskTitleText">
<Form.Control
className="TaskTitleText"
as="textarea"
size="lg"
rows="1"
value={task.title}
onChange={(event)=> setTitle(event.target.value)}
/>
</Form.Group>
</Form>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group controlId="TaskBodyText">
<Form.Control
as="textarea"
multiple
value={task.textBody}
onChange={(event)=> setTextBody(event.target.value)}
></Form.Control>
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button
variant="primary"
value={isPending ? "Saving..." : "Submit"}
onClick={updateTask}
>
Save Changes
<ToastContainer />
</Button>
<Button variant="secondary"
value={isPending ? "Saving..." : "Submit"}
onClick={onHide}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
);
}
export default TaskModal;
Update your code to below, I have checked it and update the solutions with you.
It will perfectly works everytime.
import React, { useState } from "react";
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";
import Modal from "react-bootstrap/Modal";
import taskAPI from "../../utils/taskAPI";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import "./style.css";
function TaskModal({ onHide, onTaskUpdated, task }) {
const [title, setTitle] = useState(task.title || "");
const [textBody, setTextBody] = useState(task.textBody || "");
const [isPending, setIsPending] = useState(false);
const notify = () => toast.warn("You cannot save an empty task!");
const updateTask=(event)=> {
event.preventDefault();
setIsPending(true);
taskAPI
.updateTask({ title, textBody })
.then((response) => {
response.setTitle("");
response.setTextBody("");
})
.then((response) => {
onTaskUpdated();
setIsPending(false);
})
.catch((error) => {
console.log(error);
setIsPending(false);
notify();
});
}
return (
<>
<Modal show={true} onHide={onHide}>
<Modal.Header closeButton>
<Modal.Title>
<Form>
<Form.Group controlId="TaskTitleText">
<Form.Control
className="TaskTitleText"
as="textarea"
size="lg"
rows="1"
value={title}
onChange={(event)=> setTitle(event.target.value)}
/>
</Form.Group>
</Form>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group controlId="TaskBodyText">
<Form.Control
as="textarea"
multiple
value={textBody}
onChange={(event)=> setTextBody(event.target.value)}
></Form.Control>
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button
variant="primary"
value={isPending ? "Saving..." : "Submit"}
onClick={updateTask}
>
Save Changes
<ToastContainer />
</Button>
<Button variant="secondary"
value={isPending ? "Saving..." : "Submit"}
onClick={onHide}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
);
}
export default TaskModal;
Note that you are setting the value of your Form.Control to task.textBody and not textBody. task.Textbody is a prop that is passed into your element from the parent element, while taskBody is the local state (from useState("")) that you are actually updating in your onChange function.
You probably want to manage the state in a parent component, in which case you can pass down an onChange method that this component can call whenever the text changes.
I'm not a React expert yet thus I have a question for you - how to invoke my popup window from:
import {options, columns,convertToArray} from './consts'
const index = () => {
const {data, loading, error, performFetch} = fetchHook({path: "/xxx/yyy", fetchOnMount: true})
return (
<div className={classes.Container}>
<h1>List of products</h1>
<Divider className={classes.Divider} />
<ProductTable data={convertToArray(data)} options={options} columns={columns}/>
</div>
)
}
export default index;
consts.js
export const actions = (productPropertyId, showModal) => {
const productDetails = (productPropertyId) => {
}
const removeProduct = (productPropertyId, showModal) => {
actions(productPropertyId, showModal);
return (
<div className={classes.actionsContainer}>
<Button
onClick={() => productDetails(productPropertyId)}
> {"More"}
</Button>
<Button
const removeProduct = (productPropertyId, showModal) => {
actions(productPropertyId, showModal);
>{"Remove"}
</Button>
</div>
)
};
export const convertToArray = (productList) => {
let products = []
if (productList != null) {
productList.map(product => {
column1, column2, column3, actions(product.id)]
products.push(prod)
})
}
return products;
};
My popup is --> <FormDialog/> based on react Materials.
Is it possible to invoke popup in this place?
I have a react material table with some columns. The last column contains 2 buttons, one of them is "Remove" (row). Here I want to invoke my popup. Maybe I should rebuild my structure?
UPDATE
Below is my popup - I wonder how to run this popup from the place above:
const formDialog = (popupOpen) => {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
{/*<Button variant="outlined" color="primary" onClick={handleClickOpen}>*/}
{/* Open alert dialog*/}
{/*</Button>*/}
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Subscribe</DialogTitle>
<DialogContent>
<DialogContentText>
To subscribe to this website, please enter your email address here. We will send updates
occasionally.
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="name"
label="Email Address"
type="email"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary">
Subscribe
</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default formDialog;
UPDATE 2
I updated my code taking into cosideration the tips from your response, see above. Can I add a parameter showModal in my export const actions = (productPropertyId, showModal) and then invoke this component with different showModal value? UNfortunately my popup doesn't appear when I click on Remove button :(
You can invoke it conditionally and controle it using some state variable. Like this:
const [removeModal, removeToggle] = useState(false);
return (<>
<div className={classes.actionsContainer}>
<Button
onClick={() => productDetails(productPropertyId)}
> {"More"}
</Button>
<Button
onClick={() => removeToggle(true)}
>{"Remove"}
</Button>
</div>
{removeModal ? <YourComponent /> : ''}
</>
)
I'm using a react fragment there <></> just to position the modal div outside the main div, but you can also invoke it inside your main div as well (I usually do this and set the position: fixed so the modal/popup coud appear in top of everything).