How to append a hidden submit input in children props (React) - reactjs

I have a common modal with a footer submit button. The modal is able to accept children.
const Modal = ({ children }: PropsWithChildren<Props>) => {
return (
<div>
{children}
<div>
<button type="submit">submit</button>
</div>
</div>
)
}
export default Modal
Usage
const CreateModal = (props: Props) => {
return (
<Modal>
<form>
<input name="test" />
</form>
</Modal>
)
}
export default CreateModal
Currently, that are a lot of files using the Modal component and I don't want to move the form tag in the modal.
My question is how can I modify the children in the common modal component? I want to append a hidden input inside the form so that I can submit the form when press Enter.
Wonder if using react cloneElement is able to solve my problem as I know it only can pass additional props to it.

I am using react hook form btw.
This is how I solve it.
<ModalBody display="flex" flexDirection="column">
<form onSubmit={onSubmit}>
{children}
<input hidden type="submit" />
</form>
</ModalBody>
I still need to edit all files but for future usage, I no need to include a hidden input for all files.
The only thing I worry is the FormProvider now is nested inside the form. By default the <FormProvider> should be the parent of the <form>.

Related

React-Bootstrap input text lose focus when opened from a OverlayTrigger in a Modal

I have a button in a modal that on click opens a popover component, in the popover I have an input text field.
The problem I have is that the input lose focus instantly, I can't type in it because something else is hijacking the focus out of it and I can't figure out what is, here is a working example of the problem.
And here is the code in question:
import "./styles.css";
import { Fragment, useState } from "react";
import { Button, Modal, OverlayTrigger, Popover, Form } from "react-bootstrap";
export default function App() {
const [showModal, setShowModal] = useState(false);
const [inputValue, setInputValue] = useState("");
const popover = (
<Popover id="popover-basic">
<Popover.Content>
<Form.Control
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Enter value"
/>
</Popover.Content>
</Popover>
);
return (
<div className="App">
<Fragment>
<Button variant="primary" onClick={() => setShowModal(true)}>
Open
</Button>
{showModal && (
<Modal
show={showModal}
onHide={() => setShowModal(false)}
centered
backdrop="static"
animation={false}
>
<Modal.Header closeButton>modal</Modal.Header>
<Modal.Body>
<p>Hello</p>
<OverlayTrigger
trigger="click"
placement="right"
overlay={popover}
>
<Button variant="secondary">Open Popover</Button>
</OverlayTrigger>
</Modal.Body>
</Modal>
)}
</Fragment>
</div>
);
}
Modal has a property "enforceFocus", which keeps focus on the Modal component. The property value is set to true per default. Set it to false and you will be able to use your input.
"https://react-bootstrap-v4.netlify.app/components/modal/#modal-props
Disabling the enforceFocus works, but I'm not sure it is the best alternative. If the user navigates the fields using the tab key, he might focus elements outside of the modal, which could create confusion.
Instead, I suggest you play with the overlay's container prop. You could use a ref to the modal itself, or to a container inside the modal. This way, the overlay will be "inside" the modal and won't lose focus.
I created an example to demonstrate it works.

seperate submit component in react-final-form

const Buttons = () => {
return (
<>
<div>
<button
buttonType="submit"
disabled={form.hasValidationErrors || form.pristine}
>
Save
</button>
</>
);
};
export default Buttons;
I have used react-final-form to create form for my react form. But I want to implement a seperate component for submit button. I need to have access to pristine but it gives me an error as there is not any form here in new component.
Does anyone have any solution?
Have you tried send the form as a parameter to the Buttons component?
Also, I think you were missing a closing div in your code.
const Buttons = ({form}) => {
return (
<>
<div>
<button
buttonType="submit"
disabled={form.hasValidationErrors || form.pristine}
>
Save
</button>
</div>
</>
);
};
export default Buttons;
As I use Typescript I do not know what is the type of form which i want to send it through props to my Buttons component. Also, II do not know if this is a right solution or not. Or does react-final-form has anything to help solving the issue?

Reusable Modal Component React Typescript

I have a component which has a button within it, like so -
<Button variant="primary" disabled={checkAccepted} onClick={openModal}>Send</Button>
I would like this button to, when it is active, to open up a modal when clicked. I am unsure how to do this and have been messing around with props but can't seem to figure it out. I also want the modal to be reusable so that any content can be passed in the modal body.I am thinking how do I open up the modal from within my openModal function?
I tried returning it like so -
const openModal = () => {
return (
<Modal>
<ModalBody>*Pass in swappable content here*</ModalBody>
</Modal>
)
}
But that doesn't seem to work. I am sure I am missing something.
You can't return components from an event handler. The way to handle events in react is almost always to alter the state of your application which triggers a re-render. In your case you need to keep track of the open state of your modal.
This can be done either in a controlled way (you keep track of the open state yourself and pass it to your <Modal> component as a prop) or in an uncontrolled way (the <Modal> component manages the open state itself). The second approach requires that you provide e.g. an element to render to your Modal component that acts as a trigger:
const MyModal = ({ children, trigger }) => {
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
return (
<div>
{React.cloneElement(trigger, { onClick: toggle })}
<Modal isOpen={modal} toggle={toggle}>
<ModalBody>{children}</ModalBody>
</Modal>
</div>
);
};
Then you can use it like that:
<MyModal trigger={<Button variant="primary">Send</Button>}>
<p>This is the content.</p>
</MyModal>
Or you can implement it in a controlled way. This is more flexible as it allows you to render the triggering element anywhere:
const MyModal = ({ children, isOpen, toggle }) => (
<div>
<Modal isOpen={isOpen} toggle={toggle}>
<ModalBody>{children}</ModalBody>
</Modal>
</div>
);
Usage Example:
function App() {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
return (
<div className="App">
<Button variant="primary" onClick={toggle}>
Send
</Button>
<MyModal isOpen={isOpen} toggle={toggle}>
<p>This is the content.</p>
</MyModal>
</div>
);
}
You should pass the function which triggers the modal to your <Button /> component as prop. Then, in your component, you want to add the onClick event. You can't set an onClick event to the <Button />. It will think of onClick as a prop being passed to <Button />. Within <Button /> you can set the onClick event to an actual <button> element, and use the function which was passed in as a prop on that event.
You can use state to keep track of when the modal button is clicked. Your function can look like: (I am using class based components here, but you can do the same thing with functional components)
buttonClickedHandler = () => {
this.setState({isModalButtonClicked: !this.state.isModalButtonClicked});
}
Then, you can set the Modal component,
<Modal isShow={this.state.isModalButtonClicked} modalButton={this.buttonClickedHandler}>
<div> ...set contents of modal</div>
</Modal>
<button onClick={this.buttonClickedHandler}>Show Modal</button>
So, within the Modal component, you can have something like this:
<React.Fragment>
<Backdrop showModal={this.props.isShow} clicked={this.props.modalButton}/>
{this.props.children}
</React.Fragment>
Backdrop is basically the greyed out background. You can also set an onClick event to listen to when the backdrop is clicked.

Office Fluent UI / Fabric UI Modal - how can I close it from the body component?

I'm using the Modal component from the fluent-ui-react
https://developer.microsoft.com/en-us/fluentui#/controls/web/modal
like this:
function ModalExtended(props) {
const [isModalOpen, { setTrue: showModal, setFalse: hideModal }] = useBoolean(
false
);
const isDraggable = useBoolean(false);
const titleId = useId("title");
return (
<div>
<DefaultButton onClick={showModal} text={props.buttonText} />
<Modal
titleAriaId={titleId}
isOpen={isModalOpen}
onDismiss={hideModal}
isBlocking={false}
containerClassName={contentStyles.container}
>
<div className={contentStyles.header}>
<span id={titleId}>{props.gridHeader}</span>
<IconButton
styles={iconButtonStyles}
iconProps={cancelIcon}
ariaLabel="Close popup modal"
onClick={hideModal}
/>
</div>
<div className={contentStyles.body}>{props.body}</div>
</Modal>
</div>
);
}
Then i call the ModalExtended component from other components like this:
<ModalExtended
buttonText="Open modal button text"
gridHeader="Modal header text"
body={
<GenericTreeGridContainer/>
}
/>
In the body prop i send another component (GenericTreeGridContainer) that i would like to render when the Modal opens.
In this body component i have a click event which, when it finishes its work should also close the Modal.
Is there a way to pass the hideModal function from the ModalExtended components to my body component so i can close the Modal from the body component?
Define a parent component with isModalOpen and hideModal, and pass them into both the modal and body as props. You also might be able to render the {props.body} instead like <props.body hideModal={...} /> but I haven't tried that to see how good of a pattern it is.

Submit Final Form from modal

I have issue with my final form. I have a Modal with a react-final-form in it and would like to submit the form with the button which is in the footer of the modal. Submit button has onSubmit event, when I clicked this button I saw that my onSubmit function doesn't work. It works when I click button which opens modal.... What is going wrong here?
Advert.js
class Advert extends React.Component {
showLoginMenu = (e) => {
e.preventDefault();
this.props.loadModal(LOGIN_MODAL);
};
....
<button onClick={this.showLoginMenu.bind(this)}>Order</button>
}
Modal.js
<Modal onClose={this.onClose.bind(this)}>
<Form
onSubmit={this.onSubmit}
initialValues={initValues}
decorators={[calculator]}
render={({handleSubmit}) => (
<form onSubmit={handleSubmit}>
<some fields>
<button type="submit" onSubmit=
{this.onSubmit}>Order</button>
</form>
)}
/>
As far as I know, onSubmit is a prop of form tag, not button tag. You should use onClick instead.
I know this has probably been resolved by now as this issue is about 2 years old.
But to resolve this issue you need to include the Modal inside of the Form component, but before the form tag
<Form
onSubmit={this.onSubmit}
initialValues={initValues}
decorators={[calculator]}
render={({handleSubmit}) => (
<Modal onClose={this.onClose.bind(this)}>
<form onSubmit={handleSubmit}>
<button type="submit" onSubmit={this.onSubmit}>Order</button>
</form>
<Modal>
)}
/>

Resources