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

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.

Related

Passing empty props changes the Button color in ButtonGroup

I'm using a customized IconButton element with ButtonGroup.
While passing an empty props, the color and hover shape of element is also changed. I want to know why did this happen? For me, an HOC shouldn't have different render effect with an empty prop passed.
I hosted a minimal environment with GitHub Pages. The question is shown on the second and third chapter.
Using a new customized component.
Misbehavior: buttons are white and not in group.
See the hover highlight of second and third "G"
The component is based on IconButton without passing {...props}.
Using a new customized component. All good.
The component is also based on IconButton but passing {...props}.
Although the props is not passed, hence should be empty(?).
Maybe useStyles changed color, but the hover background shape is changed too.
Also I made a related issue on official repo, and in my Learning note
It's the opposite, if you don't pass the props to IconButton:
function IconButtonWrapper(props /* unused props */) {
return (
<IconButton>
<DeleteIcon />
</IconButton>
);
}
Then no styles are applied and the IconButton appears in plain white, so nothing changes.
Although the props is not passed
The props are passed automatically from the ButtonGroup, even if you don't pass anything. The way it works is that ButtonGroup clones the child component and pass its own set of props to them, so when you don't pass anything yourself like this:
<ButtonGroup variant="contained">
<IconButtonWrapper />
<IconButtonWrapper />
<IconButtonWrapper />
</ButtonGroup>
The child component still has props provided by the parent, you can see it by logging the props:
function IconButtonWrapper(props) {
console.log(props);
return (
<IconButton {...props}>
<DeleteIcon />
</IconButton>
);
}

'Box' component vs css prop for layout, what to choose?

I'm using styled-system and Emotion now and I can't come to an understanding of which approach is more correct to use. I have some "Box" component, which handles different props like mx, my, px, py, bg, color, and many others from https://styled-system.com/api/.
Sometimes I have the following situations :
I have some "Select" component, which doesn't have styled-system props. And I have to add some margins (for example) to this Select. I can do this in following ways :
import {Select} from 'Select'
<Box
as={Select}
my={32}
ml={10}
/>
Or I can use Emotion's css prop with #styled-system/css and do next :
/** #jsxImportSource #emotion/react */
import {Select} from 'Select'
import css from '#styled-system/css'
<Select
css={css({
my: 32,
ml: 10
)}
/>
For me personally, the css option seems more readable, but I already use the "Box" component in many places of the project and I do not know whether to change their syntax or not.before :
<Box mx={1}/>
after :
div css={css({mx=1})}
Which option in which situation would be more correct?
This is pretty subjective. Your box is not the same as a div. Presumably, you have a border-box rule for the Box component. Let's not get the whole reason for using the box confused. If you just want your select to have all the props of a box, well, it should already have that, assuming your Select definition looks like this
const Select = ({ onClick, ...styleProps }) => <Box {...styleProps}><p>select me</p></Box>
export default Select

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>
</>
);
}

No component displayed when adding a custom label to a ReferenceLine in Recharts

I'm trying to add a custom label with a circular border to the right of a ReferenceLine as shown here https://imgur.com/a/svCsNVZ and as it says you can do in the docs here http://recharts.org/en-US/api/ReferenceLine#label .
The issue we’re having is that whenever we try to pass a component in here <ReferenceLine {...props} label={<CustomizedLabel />} /> nothing ever gets rendered, no matter what I try.
I can’t find any examples where they have specifically done this to a reference line label, but have managed to get the component passing functionality working for the data points, so I’m not sure where we’re going wrong here.
Currently, we can customise the label using an object but when passing our own element in nothing is rendered.
<ReferenceLine
y={dataLimits.lL}
stroke={Colors.red.hex}
strokeDasharray="3 3"
label={{
position: "right",
value: dataLimits.lL,
fill: "#595959",
fontSize: "0.75rem"
}}
ifOverflow="extendDomain"
/>
We want to convert this to
<ReferenceLine
y={dataLimits.lL}
stroke={Colors.red.hex}
strokeDasharray="3 3"
label={<LimitLabel />}
ifOverflow="extendDomain"
/>
where LimitLabel has the properties above but with a circular border.
No error messages appear on the console, and no components appear in the DOM where it should be.
This is a jsfiddle with our current implementation without the custom component, if that helps demonstrate
https://jsfiddle.net/rbyztucn/1/
The docs on recharts are really limited on this, but from my experiments and the idea from #rebecca on using SVG elements, I realised that the label prop on these ReferenceLine components expects an SVG element, not a React DOM element.
I will update this comment when I find out more on positioning these elements; I have a feeling I can use Recharts inbuilt locating utils to make this fairly easy.
A nice side effect of this is that you can pass SVG icons to these labels easily too.
This is probably late, but I ran into the same problem recently and managed do find a solution.
Like J Rhodes mentioned, the documentation is very vague on how to create customized labels using Recharts. The lib is great but the documentation does need some improvement.
As far as I've noticed, any label prop or even the <Label /> component itself can only render SVG elements by default. One way to overcome this limitation is by declaring a customized SVG element using <foreignObject> and a rendered React element as children, like this example:
const renderCustomLabel = ({ viewBox }) => (
<g>
<foreignObject x={0} y={0} width={100} height={100}>
<div>Your custom content goes here...</div>
</foreignObject>
</g>
)}
On the component (<ReferenceLine /> and <ReferenceDot /> as label prop and <Label /> as content prop) call, all you need is to pass the function reference like this:
<ReferenceLine label={renderCustomLabel} />
Ps: The viewBox prop gives dynamic access to the parent component position.

Positioning react-bootstrap popover

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>

Resources