From 433236eae7094274d45acc17551e1cba453479cc Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Tue, 26 Mar 2024 18:22:27 +0100 Subject: [PATCH 1/5] allow to pass direct values to `useElementSize` Currently we require a `MutableRefObject`, but in some situations (Popover) we don't have a ref, but the direct element. This now supports passing in both. Another fix could be to wrap the raw value in a ref but that feels like an incorrect workaround. --- .../@headlessui-react/src/hooks/use-element-size.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/@headlessui-react/src/hooks/use-element-size.ts b/packages/@headlessui-react/src/hooks/use-element-size.ts index 09e3d969d2..bfd501b882 100644 --- a/packages/@headlessui-react/src/hooks/use-element-size.ts +++ b/packages/@headlessui-react/src/hooks/use-element-size.ts @@ -7,11 +7,14 @@ function computeSize(element: HTMLElement | null) { return { width, height } } -export function useElementSize(ref: React.MutableRefObject, unit = false) { - let [size, setSize] = useState(() => computeSize(ref.current)) +export function useElementSize( + ref: React.MutableRefObject | HTMLElement | null, + unit = false +) { + let element = ref === null ? null : ref instanceof HTMLElement ? ref : ref.current + let [size, setSize] = useState(() => computeSize(element)) useIsoMorphicEffect(() => { - let element = ref.current if (!element) return let observer = new ResizeObserver(() => { @@ -23,7 +26,7 @@ export function useElementSize(ref: React.MutableRefObject, return () => { observer.disconnect() } - }, [ref]) + }, [element]) if (unit) { return { From 7ec181a9c4181b610a99e6aaf115f10bdc26bb73 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Tue, 26 Mar 2024 18:28:05 +0100 Subject: [PATCH 2/5] mock `ResizeObserver` This is necessary because jsdom doesn't implement `ResizeObserver` and then it would just crash. --- .../src/components/popover/popover.test.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/@headlessui-react/src/components/popover/popover.test.tsx b/packages/@headlessui-react/src/components/popover/popover.test.tsx index 92302340b5..cdcc77525c 100644 --- a/packages/@headlessui-react/src/components/popover/popover.test.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.test.tsx @@ -22,6 +22,12 @@ let act = _act as unknown as (fn: () => T) => PromiseLike jest.mock('../../hooks/use-id') +// @ts-expect-error +global.ResizeObserver = class FakeResizeObserver { + observe() {} + disconnect() {} +} + afterAll(() => jest.restoreAllMocks()) function nextFrame() { From 12646382eed4e286e1f8cc1c081dd1ef7b17ebee Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Tue, 26 Mar 2024 18:28:24 +0100 Subject: [PATCH 3/5] expose `--button-width` on the `PopoverPanel` --- .../@headlessui-react/src/components/popover/popover.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/@headlessui-react/src/components/popover/popover.tsx b/packages/@headlessui-react/src/components/popover/popover.tsx index e90bb960a4..6a26b2168a 100644 --- a/packages/@headlessui-react/src/components/popover/popover.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.tsx @@ -23,6 +23,7 @@ import React, { type Ref, } from 'react' import { useActivePress } from '../../hooks/use-active-press' +import { useElementSize } from '../../hooks/use-element-size' import { useEvent } from '../../hooks/use-event' import { useEventListener } from '../../hooks/use-event-listener' import { useId } from '../../hooks/use-id' @@ -913,7 +914,10 @@ function PanelFn( } : undefined, tabIndex: -1, - ...(style ? { style } : {}), + style: { + ...style, + '--button-width': useElementSize(state.button, true).width, + } as React.CSSProperties, }) let direction = useTabDirection() From 9d9241b23a22262fba8b8b8d49ecf3d1903de11c Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Tue, 26 Mar 2024 18:31:46 +0100 Subject: [PATCH 4/5] update changelog --- packages/@headlessui-react/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index 7f70211249..e010ef6c5f 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Accept optional `strategy` for the `anchor` prop ([#3034](https://github.com/tailwindlabs/headlessui/pull/3034)) - Expose `--input-width` and `--button-width` CSS variables on the `ComboboxOptions` component ([#3057](https://github.com/tailwindlabs/headlessui/pull/3057)) +- Expose the `--button-width` CSS variable on the `PopoverPanel` component ([#3058](https://github.com/tailwindlabs/headlessui/pull/3058)) ## [2.0.0-alpha.4] - 2024-01-03 From 82bb9c3576b163bdfeb6e0050ee002d65782b168 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Tue, 26 Mar 2024 18:33:16 +0100 Subject: [PATCH 5/5] replace `instanceof HTMLElement` check The `instanceof HTMLElement` cannot be used because this also runs on the server where `HTMLElement` is not available as a concrete class. --- packages/@headlessui-react/src/hooks/use-element-size.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@headlessui-react/src/hooks/use-element-size.ts b/packages/@headlessui-react/src/hooks/use-element-size.ts index bfd501b882..c05fea93db 100644 --- a/packages/@headlessui-react/src/hooks/use-element-size.ts +++ b/packages/@headlessui-react/src/hooks/use-element-size.ts @@ -11,7 +11,7 @@ export function useElementSize( ref: React.MutableRefObject | HTMLElement | null, unit = false ) { - let element = ref === null ? null : ref instanceof HTMLElement ? ref : ref.current + let element = ref === null ? null : 'current' in ref ? ref.current : ref let [size, setSize] = useState(() => computeSize(element)) useIsoMorphicEffect(() => {