I'm working on a calendar app.
The problem: clicking popper of a popper closes both poppers, because it fires the click outside event of the first popper which closes it.
I have a component <Event />
which uses Material-UI React <Popper />
and it works fine with it. combining it with the <AwayClickListener />
it closes when clicking out side, and stay open when clicking inside the popper. I created <Events />
which is a list of <Event />
.
when click the + more text, popper with all the events in that day should appear, on top of the cell.
the popper children is also <Events />
:
clicking an event should open a popper with the event details, as clicking it in the cell was.
since i use the same component <Events />
it does that, but not fully as expected:
clicking the event details popper closes both poppers.
That is the issue: the requirement is that clicking out side of the poppers will close the poppers, but clicking inside will leave them open and interactive
debugging shows that clicking the second popper, fires the outside clicked event of the first popper which closes it. also, taking out the click away listener function from the first popper leave the second popper open for most of the clicks - clicking some places in it, fires it's clicked away function which closes it. e.g: clicking the title closes it, clicking the location or summary divs does not.
<ClickAwayListener />
.<ClickAwayListener />
CODE
<Popper>
- costume wrapper for the material ui popper
const popper = ({
placement,
open,
anchorEl,
handleClickAway=null,
title,
handleCloseClick=null,
children,
popperStyle = {},
calendarPopoverClass = ''
}) => {
const useStyles = makeStyles({
Popper: popperStyle
})
const styles = useStyles();
return (
<Popper modifiers={{
flip: {
enabled: false,
},
preventOverflow: {
enabled: false,
boundariesElement: 'scrollParent',
}
}}
className={styles.Popper}
placement={placement}
open={open}
anchorEl={anchorEl}
>
<ClickAwayListener onClickAway={handleClickAway}>
<CalendarPopover className={st(classes[calendarPopoverClass])} isShown withArrow={false} title={title} onClose={handleCloseClick}>
{children}
</CalendarPopover>
</ClickAwayListener>
</Popper>
)
}
<Event />
const event = ({ PROPS }) => {
const [expanded, setExpanded] = React.useState(null);
const closeExpanded = () => setExpanded(null)
return (
<>
<div
className={st(classes.Event, { isTimeShown, isNextWeekFirstFiller, isLastFiller, isMultiDay, isAllDay, isFiller })}
style={inlineStyle}
onClick={onEventClick}
>
<div className={classes.Time}>{timeToDisplay}</div>
<div className={classes.Title}>{title}</div>
</div>
<Popper
placement={popperPlacement}
title={title}
handleCloseClick={closeExpanded}
handleClickAway={closeExpanded}
open={Boolean(expanded)}
anchorEl={expanded}
popperStyle={popperStyle}
calendarPopoverClass='Event'
>
<ExpandedEvent
startDate={startDate}
endDate={endDate}
location={location}
summary={summary}
/>
</Popper>
</>
);
}
<Events />
const Events = ({ events, isTimeShown, localeToggle, popperPlacement, popperStyle, handleShowMoreClick=null }) => {
const eventsToShow: JSX.Element[] = [];
if (events.length > 0) {
let eventsToShowAmount = 3;
const moreEventsCount = events.length - eventsToShowAmount;
eventsToShowAmount = moreEventsCount > 0 ? eventsToShowAmount : events.length;
for (let i = 0; i < eventsToShowAmount; i++) {
eventsToShow.push(
<Event
key={events[i].id}
{...events[i]}
isTimeShown={isTimeShown}
popperPlacement={popperPlacement}
popperStyle={popperStyle}
/>
)
}
if (moreEventsCount > 0) {
eventsToShow.push(<ShowMore key='ShowMore' handleClick={handleShowMoreClick} moreEventsCount={moreEventsCount} />)
}
}
return (
<div className={classes.Events}>
{eventsToShow}
</div>
);
}
<MonthlyCell />
const MonthlyCell = ({
events,
isTimeShown,
popperPlacement,
popperStyle
}) => {
const [expandedEvents, setExpandedEvents] = React.useState(null);
const cell = React.useRef<HTMLDivElement>(null)
const eventsList = (handleShowMoreClick = null) => (
<Events
events={events}
isTimeShown={isTimeShown}
localeToggle={true}
popperPlacement={popperPlacement}
popperStyle={popperStyle}
handleShowMoreClick={handleShowMoreClick}
/>
);
const handleShowMoreClick = () => setExpandedEvents(eventsList());
const closeExpandedEvents = () => {
setExpandedEvents(null);
}
return (
<>
<div ref={cell} className={classes.MonthlyCell} >
{eventsList(handleShowMoreClick)}
</div>
<Popper
placement='left'
open={Boolean(expandedEvents)}
title='hello'
handleClickAway={closeExpandedEvents}
anchorEl={cell.current}
popperStyle={{ left: '17% !important' }}
handleCloseClick={closeExpandedEvents}
>
{eventsList()}
</Popper>
</>
);
}
hope it was clear enough. let me know if anything else is needed. Thank you
EDIT 1
another attempt was giving the parent popper bigger z-index, but it didn't work
the solution was surrounding the popper children in a div. component i used caused this un-wanted behaviour, because it didn't had forwardRef support. so adding div wrapper solved that.
also, dropping the modifiers attribute:
<Popper
// modifiers={{
// flip: {
// enabled: false,
// },
// preventOverflow: {
// enabled: false,
// boundariesElement: 'scrollParent',
// }
// }}
link for working solution: https://codesandbox.io/s/popper-in-a-popper-s6dfr?file=/src/Popper/Popper.js:372-519