Close React-Bootstrap Popover with a button that's inside popover - reactjs

I'm using React-Bootstrap Popover. I would like the popover to close once a user clicks on a close button located inside the popover. I would prefer a solution that doesn't use refs as facebook doesn't recommend using them. Here is my code
const popoverTop = (
<Popover id="popover-positioned-top" title="Popover top">
<button type="button" className="close">×</button>
<strong>Holy guacamole!</strong> Check this info.
</Popover>
);
<OverlayTrigger trigger="click" placement="top" overlay={popoverTop}>
<Button>Holy guacamole!</Button>
</OverlayTrigger>

Here is what helped me :
<OverlayTrigger
container={this}
trigger="click"
placement="right"
overlay={this.popoverClick}
rootClose={true}
>
<Button aria-label="Get Info" bsSize="medium">
Button Name
</Button>
</OverlayTrigger>
where popoverClick is:
<Popover id="popover-positioned-scrolling-right" className="popover-main">
<div className="popover-custom-header">
<h3 className="popover-title">Your Title</h3>
<IconButton aria-label="Close" className="icon-button"
onClick={() => document.body.click()}>
<Close fontSize="small"/>
</IconButton>
</div>
<div class="popover-custom-content">
{/* ... the content you need */}
</div>
</Popover>
document.body.click() -> does all the work.
Ref: https://stackoverflow.com/a/47636953/9743227
I hope it will help you, too!

I know that a lot of time has passed, today I had this same problem and I arrived here. I found a way to fix it.
<OverlayTrigger trigger = 'your-trigger' placement = 'auto' rootClose
ref = 'overlay'>
<Popover title='' >
------
</Popover>
<Button onClick={ this.hidePopover } ></Button>
</OverlayTrigger>
then in the method
hidePopover = ( ) =>
{
this.refs.overlay.handleHide();
}
I hope I have helped

Related

TypeError: Cannot set properties of undefined (setting 'hook')

TypeError: Cannot set properties of undefined (setting 'hook')
I am working on a Nextjs application but getting this error with antd library. I'm using the modal component and tooltip component which causes a re-render on open and close and spits out this error multiple times. Same goes for the tooltip when I mouse over it. Has anyone experienced this? and how did you fix it?. Answers appreciated!
import { Button, Modal, Tooltip } from 'antd';
const [isModalOpen, setIsModalOpen] = useState(false);
<div className="d-flex gap-2">
<Tooltip title="Edit">
<EditOutlined className="h5 pointer text-warning" />
</Tooltip>
<Tooltip title="Publish">
<CheckOutlined className="h5 pointer text-success ml-2" />
</Tooltip>
</div>
<Button
className="w-100"
onClick={() => setIsModalOpen(true)}
type="primary"
shape="round"
size="large"
icon={<UploadOutlined />}
>
Open modal
</Button>
<Modal
title="Modal title"
centered
open={isModalOpen}
onCancel={() => setIsModalOpen(false)}
footer={null}
>
<p>Some contents...</p>
</Modal>

How to use custom isOpen with a chakra component

I am relatively new to react and not sure why isOpen is not working as expected.
Please see the code below for the example that I am working with
I have a menu icon that is using isOpen to open navlinks:
const { isOpen, onOpen, onClose } = useDisclosure();
<IconButton variant={"unstyled"} bgColor={"white"} color={"black"} size={"s"} icon={isOpen ? <Hamburger size={"24"} /> : <Hamburger size={"24"} />} aria-label={"Open Menu"} display={{ md: "none" }} onClick={isOpen ? onClose : onOpen} />
{isOpen ? (
<Box color={"#b8860b"} pb={4} display={{ md: "none" }}>
<Stack as={"nav"} spacing={5}>
{Links.map(link => (
<Link key={link.name} href={link.route}>
<Flex paddingBottom="40px" h="40px" borderBottom="1px" borderColor="black" justifyContent={'left'}>
<Flex paddingLeft={"10px"} paddingTop={"3%"}> {link.icon}</Flex>
<Text p={2} color={"black"} >
{link.name}
</Text>
</Flex>
</Link>
))}
</Stack>
</Box>
) : null}
When I try using a custom isOpen to open a drawer component, i just cant get it to work..
What am I doing wrong?:
const { isOpenMenu, onOpenMenu, onCloseMenu } = useDisclosure()
<Button
bgColor={"white"}
onClick={isOpenMenu}
>
<BsCart4 size={"26px"} color={"black"} />
{cartItemCount > 0 && <Badge ml='1' fontSize='0.9em' colorScheme='green'>{cartItemCount}</Badge>}
</Button>
<Drawer
isOpen={isOpenMenu}
placement='right'
onClose={onCloseMenu}
finalFocusRef={btnRef}
>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader>Create your account</DrawerHeader>
<DrawerBody>
<Input placeholder='Type here...' />
</DrawerBody>
<DrawerFooter>
<Button variant='outline' mr={3} onClick={onCloseMenu}>
Cancel
</Button>
<Button colorScheme='blue'>Save</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
```
When I use isOpen for the drawer it works fine so I thought having another isOpen but custom would open the drawer but its not working as expected.
Can someone help understand why my thinking isnt right based on how to use isOpen correctly?
It seems like only isOpen works when I switch it from using the menu bar and drawer
First of all, if you want to use custom names to the useDisclosure states, you have to set them like this:
const { isOpen: isOpenMenu, onOpen: onOpenMenu, onClose: onCloseMenu } = useDisclosure()
Also, the onClose prop of the drawer component is just a event handler, dont put your onCloseMenu function ther, just call it when you want to close the drawer, as you made on the cancel button, example:
<Button onClick={onCloseMenu} ... />

Why div in a form is a problem for react?

I have a following button which is connected to recaptcha according to docs
<Button
onClick={() => setShowSpinner(true)}
size="lg"
className="g-recaptcha"
data-sitekey="XYZ"
data-callback="handleFormSend"
data-action='submit'
>
{showSpinner && <Spinner
as="span"
animation="grow"
size="sm"
role="status"
aria-hidden="true"
/>}
Send
</Button>
I don't understand why in chrome I got
"Warning: Did not expect server HTML to contain a <div> in <form>."
Of course I checked HTML code and don't see any div? In Mozilla there is no warning.
I use next and react.
What may be wrong?
Looks like a SSR/hydration issue. Try removing spaces between tags.
<Button
onClick={() => setShowSpinner(true)}
size="lg"
className="g-recaptcha"
data-sitekey="XYZ"
data-callback="handleFormSend"
data-action='submit'
>
{showSpinner &&
<Spinner
as="span"
animation="grow"
size="sm"
role="status"
aria-hidden="true"
/>
}
Send
</Button>
Check this answer

Prevent event propagation on row click and dialog in material ui

I'm using material-ui and I have a table with a button inside. The button opens a Dialog and I need to be able to support clicking on the table row.
The problem is that with Portals (in react) - the events are propagated, so clicking inside the Dialog (that was opened after clicking on the button) - the click event on the table-row will get fired.
This is the row:
<TableRow onClick={rowClick}>
<TableCell>Content 1</TableCell>
<TableCell>Row clicked {count} times</TableCell>
<TableCell>
<MyDialog />
</TableCell>
</TableRow>
This is the dialog:
<>
<IconButton onClick={handleClickOpen}>
<EditIcon />
</IconButton>
<Dialog disableBackdropClick open={open} onClose={handleClose}>
<DialogTitle>Dialog</DialogTitle>
<DialogContent>Some content</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleClose}>Save</Button>
</DialogActions>
</Dialog>
</>
Here is a working example:
https://codesandbox.io/s/dazzling-hofstadter-gzwll
And this is an animated gif that shows the issue:
I know I can set the "rowClick" on each cell (and leave the last cell without it) but this is just an example and I'm looking for a more generic solution.
It took some time to find a proper solution, but the only way to prevent the propagation of the event was to add a "click" function on the dialog itself:
<>
<IconButton onClick={handleClickOpen}>
<EditIcon />
</IconButton>
<Dialog
disableBackdropClick
open={open}
onClose={handleClose}
onClick={handleDialogClick}
>
<DialogTitle>Dialog</DialogTitle>
<DialogContent>Some content</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary">
Save
</Button>
</DialogActions>
</Dialog>
</>
And have the handleClickDialog function stop the event propagation:
const handleDialogClick = e => {
e.stopPropagation();
};
Here is a working example:
https://codesandbox.io/s/cocky-violet-19uvd
One option is to add an onClick handler within my-dialog.js.
const handleClick = e => {
e.stopPropagation();
// doesn't do anything except stop the event
};
and then add it to your Dialog:
<Dialog
disableBackdropClick
open={open}
onClose={handleClose}
onClick={handleClick}
>
Fork of your sandbox with the changes: https://codesandbox.io/s/nostalgic-chaplygin-lql5m?fontsize=14&hidenavigation=1&theme=dark
it has nothing to do it e.stopPropagation();. The dialog and edit icon is wrapped with row and row has click function.
I would ask you to check this https://codesandbox.io/s/inspiring-bassi-b9hev
I made some changes. It will open and close the dialog box.
In your existing implementation icon is part of a dialog box which is wrong. you can check my code.
Hint:
<TableBody>
<TableRow onClick={rowClick}>
<TableCell>Content 1</TableCell>
<TableCell>Row clicked {count} times</TableCell>
<TableCell>
<IconButton onClick={() => setIsOpen(true)}>
<EditIcon /> // I change it here
</IconButton>
</TableCell>
</TableRow>
</TableBody>
...
<MyDialog isOpen={isOpen} onClose={() => setIsOpen(false)} />
New DialogBox
<Dialog
disableBackdropClick
open={props.isOpen}
onClose={() => props.onClose()}
>
<DialogTitle>Dialog</DialogTitle>
<DialogContent>Some content</DialogContent>
<DialogActions>
<Button onClick={() => props.onClose()}>Cancel</Button>
<Button onClick={() => props.onClose()}>Save</Button>
</DialogActions>
</Dialog>
you need to disable row event click for action column using stopPropagation
which will fix the issue
const handlerActionColmunClick = (event: React.MouseEvent<HTMLElement>) => {
event.stopPropagation();
};
<TableBodyCell onClick={handlerActionColmunClick }>
<IconButton
aria-label="three-dot-action-menu"
aria-controls="customized-menu"
aria-haspopup="true"
onClick={handleActionMenuClick}
>
...........
...........
...........

React-bootstrap :- is it possible to have MenuItem inside Popover

is it possible to have MenuItem inside Popover component and still retain style. Right now I get menuitem (without style), when I just use MenuItem component. When I wrap it inside , nothing get visible.
<Overlay
show={this.state.isOpen}
target={this.state.target}
placement="bottom"
container={this}
>
<Popover
id="profilePopover"
title={`Hello ${givenName || ''}${surname || ''}`}
>
<strong>Holy guacamole!</strong> Check this info.
<Clearfix>
<ul className="dropdown-menu open">
<MenuItem>link</MenuItem>
</ul>
</Clearfix>
</Popover>
</Overlay>
You are returning more than 1 item inside the popover class.
<Overlay
show={this.state.isOpen}
target={this.state.target}
placement="bottom"
container={this}
>
<Popover
id="profilePopover"
title={`Hello ${givenName || ''}${surname || ''}`}
>
{
<div>
<strong>Holy guacamole!</strong> Check this info.
<Clearfix>
<ul className="dropdown-menu open" style={{display:"block", position: "relative"}}>
<MenuItem>Test</MenuItem>
<MenuItem>Test2</MenuItem>
</ul>
</Clearfix> </div> }
</Popover>
</Overlay>
and it got something to do with the classname "dropdown-menu open" try add a style in it
Thanks guys, I created custom popover element, and threw some custom .css, and I can see menuItem inside popover. Though I am yet to correct arrowOffset values and top alignment not working... separate question for them.
<Overlay
show={this.state.isOpen}
target={this.state.target}
container={this}
placement="bottom"
arrowOffsetTop="-6px;"
arrowOffsetLeft="79%;"
>
<PopoverAligned
links={links}
givenName={givenName}
surname={surname}
greetingText={greetingText}
/>
</Overlay>
const PopoverAligned = props => (
<Popover
id="profilePopover"
title={`${props.greetingText} ${props.givenName || ''} ${props.surname || ''}`}
className="dropdown-menu open"
placement="bottom"
style={{ top: '43px;', left: '-100px' }}
>
{
props.links.map(link => (
<li className="menuitem">
<a href={link.url} target={link.target} className="dropdown-link">
{link.title}
</a>
</li>
))
}
</Popover>
);

Resources