Positioning react-bootstrap popover - reactjs

I have a couple of bootstrap rows. When the user clicks on any of the rows, a popover should appear in the middle of the row and show some information. However, at the moment it pops far right, after the end of the row.
Here is my JSBin link showing the code: https://jsbin.com/rogeyufuku/1/edit?css,js,output
I tried to reposition it by setting positionLeft to a negative value as follows:
<ReactBootstrap.Popover className="my-popover"
positionLeft={-300}
positionTop={20}
>
but that did not work.
I also tried to manipulate it using plain old CSS rules as follows:
.my-popover {
position: relative;
left: -200px;
}
but that did not work either.
Is there any idea to render the popover overlay as I desire it?
(Including all the code here for reference, but JSBin link is working)
EDIT: Here is how it looks now: http://i.imgur.com/URuYthN.png
Here is what I want it to look like: http://i.imgur.com/M4Hh1wI.jpg

its a bit hard to see what you are trying to do but you shouldn't be using the positionLeft and positionTop props, they are going to be set by the Overlay component
If you want the popover to appear in the middle, use placement prop and the values "top", or "bottom"instead of "right"
If you want finer grained control over where and how it is positioned you will need to make your own custom Popover component that does something with the positionLeft and positionTop component that are passed in to it by the Overlay component
class MyPopover extends React.Component {
render(){
var left = calculateMyOwnLeftPosition()
return (
<ReactBootstrap.Popover {...this.props} positionLeft={left}>
{ this.props.children }
</ReactBootstrap.Popover>
)
}
}
and then you'd use it like:
<Overlay>
<MyPopover>sweet content</MyPopover>
</Overlay>

Use Popover props positionLeft and positionTop:
<Popover
id="popover-basic"
placement="right"
positionLeft={200}
positionTop={50}
title="Popover right"
>
But if you're using an OverlayTrigger use placement on OverlayTrigger:
React-Bootstrap Popovers With OverlayTrigger:
const popoverTop = (
<Popover id="popover-positioned-top" title="Popover top">
<strong>Holy guacamole!</strong> Check this info.
</Popover>
);
<OverlayTrigger trigger="click" placement="top" overlay={popoverTop}>
<Button>Holy guacamole!</Button>
</OverlayTrigger>

Related

How can I set the Overlay container property when using OverlayTrigger?

I'm using the OverlayTrigger component to get hover behavior for tooltips, and therefore not using the Overlay component directly. The Overlay component has a container property that I'd like to use to remedy the tooltip getting cut off by its natural container element.
I've attempted to pass a popperConfig object, but that's not working. I've also tried adding a container attribute to the OverlayTrigger component.
<Container fluid className='flex-fill' ref={rowContainer}>
...
<OverlayTrigger delay={{show: 500}} overlay={renderUserTooltip}
popperConfig={{container: rowContainer}}>
<FaUser/>
</OverlayTrigger>
How can I set the container for the Overlay when the Overlay component isn't directly used?
React bootstrap doesn't have a container prop or something similar (I mean it has a target prop but as this part of the docs suggests, for the OverlayTrigger the type is null, so it doesn't accept values and I don't think you can trick it to accept (and I don't think it would be wise to try).
But a pretty nice example, that shows some sort of a workaround in my opinion, can also be find in the docs, under customizing-trigger-behavior.
Starting from that example, if you need your trigger to be totally separated from the container an option is to just wrap everything in a big container that receives ({ ref, ...triggerHandler }) and all is left is to give your container the ref, and the trigger to your FaUser component. So something like:
<OverlayTrigger
placement="bottom"
overlay={<Tooltip id="button-tooltip-2">Check out this avatar</Tooltip>}
>
{({ containerRef, ...triggerHandler }) => (
<Container fluid className='flex-fill' ref={containerRef}>
...
<FaUser/>
</Container>
)}
</OverlayTrigger>
I also created a sandbox with a minimal reproducible example.

React useRef to target specific div?

So I have this modal that is wrapped with an entire background div. My issue is that I added a close function on the background, so no matter where I click, it will close the modal.
I would like to have the useRef only target the background div and not work if I click any of the children inside of it
Here is the code
const modalRef = useRef();
const closeModal = e => {
if (modalRef.current.contains(e.target)) {
setShowModal(false);
}
}
return (
<>
{showModal ? (
<Background onClick={closeModal} ref={modalRef}>
<animated.div style={animation}>
<ModalWrapper showModal={showModal}>
<div>hi</div>
<CloseModalButton
aria-label='Close modal'
// onClick={() => setShowModal(!showModal)}
/>
</ModalWrapper>
</animated.div>
</Background>
) : null}
</>
);
};
So right now the ref is attached to the background, but if I console.log(modalRef.current) it will show me the entire jsx with all my children divs inside, but I only want to target the outside div aka the background
So whenever I click outside of my modal it will close
Note: when I console.log(modalRef.current) this is what shows up in the console aka my entire JSX
I'm also using styled-components, so I only want to target the top div which I called Background but shows sc-bdnylx iEsAwc so I have no idea how to target it since it doesn't have any ids or classNames
I tried to add this console.log(modalRef.current.children[0]) but when I implemented it into my function, it didn't work properly. This console.log does only show the divs below my Background div, but I haven't found anything about targeting just the Background only
You don't actually need to use refs here. Just add onClick={e => e.stopPropagation()} to one of the divs inside Background, like on ModalWrapper. It'll stop the click from passing through.

How to use override Button using Box component in Material-UI?

I've been trying to understand and write code on the Box component in material-UI. (https://material-ui.com/components/box/#box)
I've been trying to override a Button component the two ways it describes in the documentation, but I have no idea how. When I run the code segment using both methods, the button appears but no color change. Then when I try to add an extra Button underneath the clone element code segment I get an error saying 'Cannot read property 'className' of undefined'.
<Box color="primary" clone>
<Button>Click</Button>
<Button>Click</Button>
</Box>
When I add a Button component underneath in the second render props way, the first button just disappears from the DOM completely.
<Box color="secondary">
{props => <Button {...props} > Click </Button>}
<Button color="secondary">Click</Button>
</Box>
Would appreciate an explanation of how overriding underlying DOM elements work.
There are a few issues with the code you've shown in your question.
primary and secondary are not valid colors within the palette. They are valid options for the color prop of Button, but here you are trying to reference colors within the theme's palette object. For this purpose, you need primary.main and secondary.main (which is what Button uses when you specify <Button color="primary">).
Box only supports a single child when using the clone property and it only supports a single child when using the render props approach. In both of your examples you have two children.
Here is the Material-UI source code that deals with the clone option:
if (clone) {
return React.cloneElement(children, {
className: clsx(children.props.className, className),
...spread,
});
}
This is creating a new child element that combines the className generated by Box with any existing class name on the child. It gets at this existing class name via children.props.className, but when there are multiple children then children will be an array of elements and will not have a props property so you get the error:
Cannot read property 'className' of undefined
Here is the Material-UI source code that deals with the render props approach:
if (typeof children === 'function') {
return children({ className, ...spread });
}
When you have more than one child, then typeof children === 'function' will not be true and it won't use the render props approach. In this case, both children just get normal react rendering and trying to render a function doesn't render anything.
Below is a working example that fixes all of these problems by using a single Button child in the clone case and a single function child in the render props case (a function that then renders two Button elements).
import React from "react";
import Button from "#material-ui/core/Button";
import Box from "#material-ui/core/Box";
export default function App() {
return (
<>
<Box color="primary.main" clone>
<Button>Click</Button>
</Box>
<Box color="secondary.main">
{props => (
<>
<Button {...props}> Click </Button>
<Button color="secondary">Click</Button>
</>
)}
</Box>
</>
);
}

Can't get Popover to display in correct position in Dialog

I have a Dialog and have a ListItem that when you click on it goes into edit mode by showing a Popover. This was working in an older version of MUI using a Modal but since getting on the latest that didn't work and I'm trying to use a Popover. I tried to make a simple example on CodeSandox but that works. What happens is the Popover is always in the upper left of the page instead of the ListItem.
I have simplified my code to a simple Button and Popover in the Dialog and still have the same problem and have ran out of ideas on what to try next. The error I get in the console is
[Warning] Material-UI: the `anchorEl` prop provided to the component is invalid.
The anchor element should be part of the document layout.
Make sure the element is present in the document or that it's not display none.
When the item is clicked I do event.currentTarget just like in the examples and this is what the console.log looks like for it.
[Log] <button class="MuiButtonBase-root MuiButton-root MuiButton-text" tabindex="0" type="button"> (main.chunk.js, line 26437)
<span class="MuiButton-label">Click Me</span>
<span class="MuiTouchRipple-root">
<span class="MuiTouchRipple-ripple MuiTouchRipple-rippleVisible" style="width: 117.2006825918689px; height: 117.2006825918689px; top: -34.60034129593445px; left: -25.60034129593445px;">
<span class="MuiTouchRipple-child MuiTouchRipple-childLeaving"></span>
</span>
</span>
</button>
I even tried doing disablePortal in the Dialog which didn't fix it. I also tried using refs which fixed the anchorEl warning but still displays relative to the page and not the element. Any ideas?
For anyone that comes across this issue with Material UI, there are a couple of things that you can do.
One is to make sure that if you have multiple nested functional components, that your anchorEl / click handlers for the popover are defined within the specific functional component that holds the popover. If you have nested functional components and the parent component holds the state, it will rerender the children on every state change, which can reset the anchorEl reference.
Second - React.memo can prevent unnecessary rerenders on functional components (it only works if props don't change but should still reap performance benefits in child components).
I have nested elements this is how I solved this without doing anything too extra.
So my main functional component simply returned something like this
const filters = () => {
const [anchorEl, setAnchorEl] = useState(null)
const popoverOpen = Boolean(anchorEl)
const handleTogglePopover = (event: any) => {
event.preventDefault()
if (anchorEl === null) {
setAnchorEl(event.currentTarget)
} else {
setAnchorEl(null)
}
}
const SortActions = () => {
return (
<Box>
<MyCustomRadioButton/>
</Box>
)
}
const FilterButtons = () => {
return (
<Box>
<ButtonBase
onClick={handleTogglePopover}
aria-owns={popoverOpen ? 'my-popover-id-name' : undefined}
aria-haspopup={true}
>
{/* contents (this is a comment in html in react) */}
</ButtonBase>
<Popover
id={'my-popover-id-name'}
open={popoverOpen}
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left'
}}
onClose={handleTogglePopover}
>
<SortActions/>
</Popover>
</Box>
)
}
return (
<Box>
{/* THIS LINE IS THE LINE I CHANGED TO GET IT TO WORK */}
<FilterButtons/>
</Box>
)
}
I changed that line to {FilterButtons()} instead. It looks like the component that is rendering the popover needs to exist within the functional component calling it.
However any nested components under do not need to be declared within the calling functional component.
From what I have gathered in looking at many peoples solutions is to use React.memo for this but this worked for me. Something about React re-rendering the popover losing the state when its called as a nested component rather than a function within the component causes the state to be lost? I assume it has to do with how JavaScript works in terms of encapsulation within a function.
I know this is an older question but I know people will still run by this question eventually.
It's also possible to get this error due to what it's saying - you might be trying to use an element that has display: none style as an achorEl for your component, which isn't supported as the underlying logic calculating the position of the anchor element needs it to be visible on screen.
Check if there is any display: none; style
May be anchorEl used in multiple nested functional components problem
Try to use memo concept to prevent component rerender

Adding button text / label inside SpeedDialAction - Material-UI v1 / React

I am trying to add labels to nested <SpeedDialAction /> components, and have button text displayed next to icons like so:
but it seems like children do not get rendered:
...
<SpeedDialAction
key={action.name}
icon={action.icon}
tooltipTitle={action.name}
onClick={this.handleClick}
>
Foo
</SpeedDialAction>
...
I also tried using the ButtonProps prop as listed in the docs but that did not do the trick either.
I take a look at the SpeedDialAction source code https://github.com/mui-org/material-ui/blob/6f9eecf48baca339a6b15c5fcfb683cba11e4871/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.js
The title of Tooltip only shows on hover, but it can be easily done by changing default state to true, eg: state={ tooltipOpen: true } in SpeedDialAction.js file.
However, Tooltip component in SpeedDialAction has no reference, so there is no easy way to setState from outside.
The easiest solution is to create a custom SpeedDialAction component.
SpeedDialAction component contents only Tooltip and Button, which it's hard to modify.
There is the codesandbox https://codesandbox.io/s/9zpyj4o0zo
You can simply add SpeedDialAction.js file to your project.
Update:
Removed onClose event in Tooltip in codesandobox. Fixed the problem where title disappear after click.

Resources