diff --git a/packages/lsd-react/src/components/Calendar/Calendar.tsx b/packages/lsd-react/src/components/Calendar/Calendar.tsx index f6d24e1..ed0c8fe 100644 --- a/packages/lsd-react/src/components/Calendar/Calendar.tsx +++ b/packages/lsd-react/src/components/Calendar/Calendar.tsx @@ -16,6 +16,7 @@ import { omitCommonProps, } from '../../utils/useCommonProps' import { TooltipBase } from '../TooltipBase' +import { useUpdatePositionStyle } from '../../utils/useUpdatePositionStyle' export const CALENDAR_MIN_YEAR = 1850 export const CALENDAR_MAX_YEAR = 2100 @@ -64,7 +65,6 @@ export const Calendar: React.FC & { }) => { const commonProps = useCommonProps(props) const ref = useRef(null) - const [style, setStyle] = useState({}) const [startDate, setStartDate] = useState( startDateProp ? safeConvertDate(startDateProp, minDate, maxDate).date @@ -147,19 +147,7 @@ export const Calendar: React.FC & { } }, [endDate]) - const updateStyle = () => { - const { width, height, top, left } = - handleRef.current!.getBoundingClientRect() - setStyle({ - left, - width, - top: top + height, - }) - } - - useEffect(() => { - updateStyle() - }, [open]) + const positionStyle = useUpdatePositionStyle(handleRef, open) return ( & { disabled && calendarClasses.disabled, )} rootRef={ref} - style={{ ...style, ...(props.style ?? {}) }} + style={{ ...positionStyle, ...(props.style ?? {}) }} arrowOffset={tooltipArrowOffset} >
diff --git a/packages/lsd-react/src/components/DatePicker/DatePicker.tsx b/packages/lsd-react/src/components/DatePicker/DatePicker.tsx index 8e7d617..085baf7 100644 --- a/packages/lsd-react/src/components/DatePicker/DatePicker.tsx +++ b/packages/lsd-react/src/components/DatePicker/DatePicker.tsx @@ -70,7 +70,6 @@ export const DatePicker: React.FC & {
, 'label'> & { @@ -30,7 +31,6 @@ export const DropdownMenu: React.FC & { }) => { const commonProps = useCommonProps(props) const ref = useRef(null) - const [style, setStyle] = useState({}) useClickAway(ref, (event) => { if (!open || event.composedPath().includes(handleRef.current!)) return @@ -38,20 +38,7 @@ export const DropdownMenu: React.FC & { onClose && onClose() }) - const updateStyle = () => { - const { width, height, top, left } = - handleRef.current!.getBoundingClientRect() - - setStyle({ - left, - width, - top: top + height, - }) - } - - useEffect(() => { - updateStyle() - }, [open]) + const positionStyle = useUpdatePositionStyle(handleRef, open) return (
    & { ref={ref} role="listbox" aria-label={label} - style={{ ...style, ...(props.style ?? {}) }} + style={{ ...positionStyle, ...(props.style ?? {}) }} className={clsx( commonProps.className, props.className, diff --git a/packages/lsd-react/src/components/PortalProvider/usePortal.ts b/packages/lsd-react/src/components/PortalProvider/usePortal.ts index 7a5e846..c054513 100644 --- a/packages/lsd-react/src/components/PortalProvider/usePortal.ts +++ b/packages/lsd-react/src/components/PortalProvider/usePortal.ts @@ -13,7 +13,14 @@ export const usePortal = ({ parentId }: Props) => { useEffect(() => { if (typeof window === 'undefined' || !elementRef.current) return - document.getElementById(parentId)?.appendChild(elementRef.current) + + const parentElements = document.querySelectorAll(`#${parentId}`) + + // In some places (e.g. storybook), there may be multiple portal containers when a component + // is rendered multiple times. Here, we append only to the last found parent element. + // This is because usually the last parent is the most recently rendered one. + // Without this, we may append to a parent that is about to be removed from the DOM. + parentElements[parentElements.length - 1]?.appendChild(elementRef.current) return () => { try { diff --git a/packages/lsd-react/src/utils/useUpdatePositionStyle.ts b/packages/lsd-react/src/utils/useUpdatePositionStyle.ts new file mode 100644 index 0000000..e36969d --- /dev/null +++ b/packages/lsd-react/src/utils/useUpdatePositionStyle.ts @@ -0,0 +1,21 @@ +import { useEffect, useState } from 'react' + +export const useUpdatePositionStyle = ( + handleRef: React.RefObject, + tiggerUpdate: boolean | undefined, +): React.CSSProperties => { + const [style, setStyle] = useState({}) + + useEffect(() => { + const { width, height, top, left } = + handleRef.current!.getBoundingClientRect() + + setStyle({ + left: left + window.scrollX, + width, + top: top + height + window.scrollY, + }) + }, [tiggerUpdate]) + + return style +}