@codecademy/gamut

68.2.268.2.3-alpha.809fdd.0
dist/PopoverContainer/hooks.js
~dist/PopoverContainer/hooks.jsModified
+31−24
Index: package/dist/PopoverContainer/hooks.js
===================================================================
--- package/dist/PopoverContainer/hooks.js
+++ package/dist/PopoverContainer/hooks.js
@@ -1,19 +1,33 @@
 import { useEffect, useMemo } from 'react';
 import { findAllAdditionalScrollingParents, findResizingParent } from './utils';
+
+/**
+ * Minimal element shape required for popover positioning.
+ * Accepts both HTMLElement and TargetRef so Popover and PopoverContainer can share hooks.
+ */
+
+/** Resolves Ref to current element; returns null for RefCallback or null ref. */
+export function getRefElement(ref) {
+  if (ref == null) return null;
+  if (typeof ref === 'function') return null;
+  return ref.current;
+}
+
+/** Casts minimal target to HTMLElement for utils that need full DOM (e.g. parentElement). */
+export function getTargetAsElement(target) {
+  return target;
+}
 export const useScrollingParentsEffect = (targetRef, setTargetRect) => {
   useEffect(() => {
-    if (!targetRef.current) {
-      return;
-    }
-    const target = targetRef.current;
-    const scrollingParents = findAllAdditionalScrollingParents(target);
+    const target = getRefElement(targetRef);
+    if (!target) return;
+    const scrollingParents = findAllAdditionalScrollingParents(getTargetAsElement(target));
     const updatePosition = () => {
-      setTargetRect(targetRef?.current?.getBoundingClientRect());
+      const el = getRefElement(targetRef);
+      setTargetRect(el?.getBoundingClientRect());
     };
     const cleanup = [];
-
-    // Add listeners to all scrolling parents (window scroll handled by useWindowScroll)
     scrollingParents.forEach(parent => {
       if (parent.addEventListener) {
         parent.addEventListener('scroll', updatePosition, {
           passive: true
@@ -27,18 +41,14 @@
   }, [targetRef, setTargetRect]);
 };
 export const useResizingParentEffect = (targetRef, setTargetRect) => {
   useEffect(() => {
-    // handles movement of target within a clipped container e.g. Drawer
-    if (!targetRef.current || typeof ResizeObserver === 'undefined') {
-      return;
-    }
-    const resizingParent = findResizingParent(targetRef.current);
-    if (!resizingParent?.addEventListener) {
-      return;
-    }
+    const target = getRefElement(targetRef);
+    if (!target || typeof ResizeObserver === 'undefined') return;
+    const resizingParent = findResizingParent(getTargetAsElement(target));
+    if (!resizingParent?.addEventListener) return;
     const handler = () => {
-      setTargetRect(targetRef?.current?.getBoundingClientRect());
+      setTargetRect(getRefElement(targetRef)?.getBoundingClientRect());
     };
     const ro = new ResizeObserver(handler);
     ro.observe(resizingParent);
     return () => ro.unobserve(resizingParent);
@@ -46,16 +56,13 @@
 };
 
 /**
  * Memoizes the list of scrolling parent elements for a target element.
- * This avoids expensive DOM traversals and getComputedStyle calls on every render.
  * Returns an empty array if the target element is not available.
  */
 export const useScrollingParents = targetRef => {
   return useMemo(() => {
-    if (!targetRef.current) {
-      return [];
-    }
-    return findAllAdditionalScrollingParents(targetRef.current);
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [targetRef.current]);
+    const target = getRefElement(targetRef);
+    if (!target) return [];
+    return findAllAdditionalScrollingParents(getTargetAsElement(target));
+  }, [targetRef]);
 };
\ No newline at end of file