I am using the Material-UI Popper component (which in turn uses popper.js) to create a hovering toolbar. For the most part it is working well, except for one odd behavior:
- Select some text: the hovering toolbar appears above the text - as expected.
- Select any button in the toolbar: the appropriate action is performed. However the toolbar jumps to the top-left corner of the window. See below.
You can try out this behavior in my Storybook - just select some text and click on one of the "T" buttons.
The basic issue centers around positioning of the popper:
- When user selects some text, a fake virtual element is created and passed to the popper as an anchor element. Popper uses this
anchorEl
to position the hovering toolbar. So far so good. - When the user clicks on a button in the toolbar, the hovering toolbar jumps to the top-left of the window.
I am guessing this is happening because the anchor element is somehow lost when the underlying component re-renders. I don't know why, but this is just my theory. Can someone help me solve this issue?
The code that computes the anchorEl
sits inside a React useEffect()
. I have made sure that the dependency list for the useEffect
is accurate. I can see that when the toolbar jumps, the useEffect()
is NOT being called, which means that anchorEl
is not being recomputed. This leads me to believe that the toolbar should stay intact in its current position and not jump to (0,0). But that's not happening :-(.
Here's the useEffect()
code inside the toolbar component. You can find the full code in my repo. Any help would be much appreciated.
useEffect(() => {
if (editMode === 'toolbar') {
if (isTextSelected) {
const domSelection = window.getSelection();
if (domSelection === null || domSelection.rangeCount === 0) {
return;
}
const domRange = domSelection.getRangeAt(0);
const rect = domRange.getBoundingClientRect();
setAnchorEl({
clientWidth: rect.width,
clientHeight: rect.height,
getBoundingClientRect: () =>
domRange.getBoundingClientRect(),
});
setToolbarOpen(true);
} else {
setToolbarOpen(false);
}
} else {
setToolbarOpen(false);
}
}, [editMode, isTextSelected, selection, selectionStr]);