I am using material-ui popper.
I want to let the popper go put of container in y-direction. So I set disableportal={false}
.
But after setting disableportal
to false, when I give width: 100%
, popper is occupying the entire browser's width instead of just it's container's width. I don't want the popper to go out of container in x direction but adjust it's width to the width of it's container.
How do I achieve this? Please check below code for reproducing the above issue.
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import Grow from '@material-ui/core/Grow';
import Input from '@material-ui/core/Input';
import MenuItem from '@material-ui/core/MenuItem';
import MenuList from '@material-ui/core/MenuList';
import Paper from '@material-ui/core/Paper';
import Popper from '@material-ui/core/Popper';
import React from 'react';
const items = [
'fsdfsdfsdfs',
'shosjsadsd',
'dsfdjhfdksfhdsf',
'fsdfhdhhhhhhhhh',
];
export function Test() {
const [value, setValue] = React.useState('');
const [anchorEl, setAnchorEl] = React.useState(null);
const handleChange = (event: any) => {
setValue(event.target.value);
};
const renderChildren = () => {
let renderItems = items;
if (value !== '') {
renderItems = items.filter((item: any) => item.toLowerCase().includes(value.toLowerCase()));
}
return renderItems.map((item: any) => {
return (
<MenuItem key={item}>
{item}
</MenuItem>
);
});
};
const onFoucs = (event: any) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const popperTrans = ({ TransitionProps }: any) => {
return (
<Grow
{...TransitionProps}
style={{ transformOrigin: '0 0 0' }}
>
<Paper>
<MenuList>
{renderChildren()}
</MenuList>
</Paper>
</Grow>
);
};
const open = Boolean(anchorEl);
return (
<div style={{width: 1000, height: 500}}>
<ClickAwayListener onClickAway={handleClose}>
<div>
<Input
onChange={handleChange}
onFocus={onFoucs}
value={value}
placeholder='Search'
style={{width: '100%'}}
/>
<Popper
open={open}
anchorEl={anchorEl}
transition={true}
placement='bottom-start'
style={{zIndex: 10000, width: '100%'}}
>
{popperTrans}
</Popper>
</div>
</ClickAwayListener>
</div>
);
}
I am using material-ui popper.
I want to let the popper go put of container in y-direction. So I set disableportal={false}
.
But after setting disableportal
to false, when I give width: 100%
, popper is occupying the entire browser's width instead of just it's container's width. I don't want the popper to go out of container in x direction but adjust it's width to the width of it's container.
How do I achieve this? Please check below code for reproducing the above issue.
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import Grow from '@material-ui/core/Grow';
import Input from '@material-ui/core/Input';
import MenuItem from '@material-ui/core/MenuItem';
import MenuList from '@material-ui/core/MenuList';
import Paper from '@material-ui/core/Paper';
import Popper from '@material-ui/core/Popper';
import React from 'react';
const items = [
'fsdfsdfsdfs',
'shosjsadsd',
'dsfdjhfdksfhdsf',
'fsdfhdhhhhhhhhh',
];
export function Test() {
const [value, setValue] = React.useState('');
const [anchorEl, setAnchorEl] = React.useState(null);
const handleChange = (event: any) => {
setValue(event.target.value);
};
const renderChildren = () => {
let renderItems = items;
if (value !== '') {
renderItems = items.filter((item: any) => item.toLowerCase().includes(value.toLowerCase()));
}
return renderItems.map((item: any) => {
return (
<MenuItem key={item}>
{item}
</MenuItem>
);
});
};
const onFoucs = (event: any) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const popperTrans = ({ TransitionProps }: any) => {
return (
<Grow
{...TransitionProps}
style={{ transformOrigin: '0 0 0' }}
>
<Paper>
<MenuList>
{renderChildren()}
</MenuList>
</Paper>
</Grow>
);
};
const open = Boolean(anchorEl);
return (
<div style={{width: 1000, height: 500}}>
<ClickAwayListener onClickAway={handleClose}>
<div>
<Input
onChange={handleChange}
onFocus={onFoucs}
value={value}
placeholder='Search'
style={{width: '100%'}}
/>
<Popper
open={open}
anchorEl={anchorEl}
transition={true}
placement='bottom-start'
style={{zIndex: 10000, width: '100%'}}
>
{popperTrans}
</Popper>
</div>
</ClickAwayListener>
</div>
);
}
Share
Improve this question
asked Mar 4, 2020 at 12:28
troglodyte07troglodyte07
4,06613 gold badges48 silver badges68 bronze badges
1
- 3 Hi, did you manage to solve this back then? I'm having the same issue. – chaturanga Commented Mar 10, 2021 at 16:57
5 Answers
Reset to default 6Use anchorEl?.clientWidth
:
<Popper
open={Boolean(anchorEl)}
anchorEl={anchorEl}
style={{width: anchorEl?.clientWidth}}
>
{...children}
</Popper>
I'm sure there must be a better way to do this, but since I couldn't find one...
here is my workaround:
const [width, setWidth] = React.useState(DEFAULT_WIDTH);
useEffect(() => {
setWidth(document.getElementById('container')?.offsetWidth);
}, [document.getElementById('container')?.offsetWidth])
<Button id="container">{text}</Button>
<Popper
open={open}
placement="bottom"
style={{ zIndex: 1, width: `${width}px` }}
>
{...children}
</Popper>
I'm not sure about what is causing the issue for you. I can only point you to the example in the docs. I inspired my implementation from there and it worked like a charm. One difference that I see from your code sample is that the <ClickAwayListener>
should be only wrapped around the <MenuList>
. Hope it helps.
The answer comes a bit late but I didn't like the other answers and this may help someone:
Here is how to solve the issue with the useRef hook.
I believe you can do the same with your AnchorEl.
import {useState, useRef, ReactNode} from 'react';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import {
Button,
ButtonGroup,
ButtonGroupProps,
ClickAwayListener,
Grow,
MenuItem,
MenuList,
Paper,
Popper,
} from '@mui/material';
interface Props extends ButtonGroupProps {
primaryButton: ReactNode;
secondaryButtons: Array<ReactNode>;
}
const SecondaryActionsButton = ({primaryButton, secondaryButtons, ...buttonGroupProps}: Props) => {
const [open, setOpen] = useState(false);
const anchorRef = useRef<HTMLDivElement>(null);
const handleToggle = () => {
setOpen(prevOpen => !prevOpen);
};
const handleClose = (event: Event) => {
if (
anchorRef.current &&
anchorRef.current.contains(event.target as HTMLElement)
) {
return;
}
setOpen(false);
};
return (
<>
<ButtonGroup ref={anchorRef} {...buttonGroupProps}>
{primaryButton}
<Button
size="small"
sx={{
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
borderLeft: 0,
}}
onClick={handleToggle}
>
<ArrowDropDownIcon />
</Button>
</ButtonGroup>
<Popper
sx={{
zIndex: 1,
}}
open={open}
anchorEl={anchorRef.current}
placement="bottom-end"
transition
disablePortal
>
{({TransitionProps}) => (
<Grow {...TransitionProps}>
<Paper
sx={{
zIndex: 1,
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
width: anchorRef.current?.clientWidth,
maxWidth: anchorRef.current?.clientWidth,
overflow: 'hidden',
}}
>
<ClickAwayListener onClickAway={handleClose}>
<MenuList id="split-button-menu" autoFocusItem>
{secondaryButtons}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</>
);
};
export default SecondaryActionsButton;
If you are using popper.js that allows you to override middlewares setting, you could do this:
Reference: Match reference width
A common feature of select dropdowns is that the dropdown matches the width of the reference regardless of its contents. You can also use size(){:js} for this, as the Rect{:.class}s get passed in:
// size fn is the part that does the trick
// you should alter other code to fit your usage scenario
popperProps={{
middlewares: (presetMiddlewares) => {
return [
...presetMiddlewares,
size({
apply({ rects, elements }) {
Object.assign(elements.floating.style, {
width: `${rects.reference.width}px`,
});
},
}),
];
}
}}