Skip to content
This repository was archived by the owner on Mar 25, 2025. It is now read-only.

Commit 6c7dd8e

Browse files
Brian OchanTigge
authored andcommitted
fix(popOver): reposition PopOver when it is resized
- Fixes an issue where a `PopOver` was incorrectly positioned after a resize
1 parent 9676daa commit 6c7dd8e

File tree

2 files changed

+46
-14
lines changed

2 files changed

+46
-14
lines changed

packages/core/src/PopOver/index.tsx

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1-
import React, { useRef, useLayoutEffect, useContext, useCallback } from 'react'
1+
import React, {
2+
useLayoutEffect,
3+
useContext,
4+
useCallback,
5+
useState,
6+
} from 'react'
27
import ReactDOM from 'react-dom'
38
import styled from 'styled-components'
49

510
import { LayerContext } from '../Practical'
6-
import { useOnResizeEffect, useOnScrollEffect, anchorPosition } from './utils'
11+
import {
12+
useOnResizeParentEffect,
13+
useOnResizeEffect,
14+
useOnScrollEffect,
15+
anchorPosition,
16+
} from './utils'
717

818
type BaseElement = HTMLDivElement
919
type BaseProps = React.HTMLAttributes<BaseElement>
@@ -84,23 +94,24 @@ export const PopOver: React.FC<PopOverProps> = ({
8494
children,
8595
...props
8696
}) => {
87-
const popOverContainerRef = useRef<HTMLDivElement>(null)
97+
const [popOverContainer, setPopOverContainer] =
98+
useState<HTMLDivElement | null>(null)
8899

89100
const { el: layerRoot } = useContext(LayerContext)
90101

91102
const position = useCallback(() => {
92103
// Position the pop-over element synchronously
93-
if (anchorEl !== null && popOverContainerRef.current !== null) {
104+
if (anchorEl !== null && popOverContainer !== null) {
94105
if (onPosition === undefined) {
95-
anchorPosition(anchorEl, popOverContainerRef.current, {
106+
anchorPosition(anchorEl, popOverContainer, {
96107
horizontalPosition,
97108
verticalPosition,
98109
horizontalAlignment,
99110
verticalAlignment,
100111
})
101112
return
102113
}
103-
onPosition(anchorEl, popOverContainerRef.current)
114+
onPosition(anchorEl, popOverContainer)
104115
}
105116
}, [
106117
horizontalPosition,
@@ -109,6 +120,7 @@ export const PopOver: React.FC<PopOverProps> = ({
109120
verticalAlignment,
110121
anchorEl,
111122
onPosition,
123+
popOverContainer,
112124
])
113125

114126
useOnScrollEffect(
@@ -119,20 +131,24 @@ export const PopOver: React.FC<PopOverProps> = ({
119131
/** */
120132
}
121133
)
122-
useOnResizeEffect(anchorEl, position)
134+
135+
// Used when resizing the parent anchorEl
136+
useOnResizeParentEffect(anchorEl, position)
137+
138+
useOnResizeEffect(popOverContainer, position)
123139

124140
// Reposition on initialize
125141
useLayoutEffect(() => {
126142
position()
127-
}, [position])
143+
}, [position, popOverContainer])
128144

129145
// When the layer element is not available, there is no layer yet.
130146
if (layerRoot === null) {
131147
return null
132148
}
133149

134150
return ReactDOM.createPortal(
135-
<PopOverContainer ref={popOverContainerRef} {...props}>
151+
<PopOverContainer ref={setPopOverContainer} {...props}>
136152
{children}
137153
</PopOverContainer>,
138154
layerRoot

packages/core/src/PopOver/utils.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@ export const useOnScrollEffect = (
3333
}
3434

3535
// Reposition on resize
36-
export const useOnResizeEffect = (
37-
anchorEl: HTMLElement | null,
36+
export const useOnResizeParentEffect = (
37+
element: HTMLElement | null,
3838
onResize: () => void
3939
): void => {
4040
useLayoutEffect(() => {
41-
if (anchorEl === null) {
41+
if (element === null) {
4242
return undefined
4343
}
4444

45-
let parentEl = anchorEl.parentElement
45+
let parentEl = element.parentElement
4646
const observer = new window.ResizeObserver(onResize)
4747

4848
while (parentEl !== null) {
@@ -51,7 +51,23 @@ export const useOnResizeEffect = (
5151
}
5252

5353
return () => observer.disconnect()
54-
}, [onResize, anchorEl])
54+
}, [onResize, element])
55+
}
56+
57+
export const useOnResizeEffect = (
58+
element: HTMLElement | null,
59+
onResize: () => void
60+
) => {
61+
useLayoutEffect(() => {
62+
if (element === null) {
63+
return undefined
64+
}
65+
66+
const observer = new window.ResizeObserver(onResize)
67+
observer.observe(element)
68+
69+
return () => observer.disconnect()
70+
}, [onResize, element])
5571
}
5672

5773
export const anchorPosition = (

0 commit comments

Comments
 (0)