From 70b6f99ca82368625572bb215f965eb7f78937dd Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 3 Jul 2024 13:52:56 +0200 Subject: [PATCH 1/6] add test that verifies unit test hang --- .../src/components/popover/popover.test.tsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/@headlessui-react/src/components/popover/popover.test.tsx b/packages/@headlessui-react/src/components/popover/popover.test.tsx index 1ca6e7d747..85d00d0f71 100644 --- a/packages/@headlessui-react/src/components/popover/popover.test.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.test.tsx @@ -804,6 +804,26 @@ describe('Rendering', () => { assertActiveElement(getByText('restorable')) }) ) + + it( + 'should be possible to use the `anchor` prop on the `PopoverPanel`', + suppressConsoleLogs(async () => { + render( + + Trigger + Panel open + + ) + + assertPopoverButton({ state: PopoverState.InvisibleUnmounted }) + assertPopoverPanel({ state: PopoverState.InvisibleUnmounted }) + + await click(getPopoverButton()) + + assertPopoverButton({ state: PopoverState.Visible }) + assertPopoverPanel({ state: PopoverState.Visible }) + }) + ) }) describe('Multiple `Popover.Button` warnings', () => { From 3d09fdc38b2192c99591e9afa37f3d7825bd637a Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 3 Jul 2024 13:56:04 +0200 Subject: [PATCH 2/6] bail when parsing the `maxHeight` results in `NaN` --- .../@headlessui-react/src/internal/floating.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/@headlessui-react/src/internal/floating.tsx b/packages/@headlessui-react/src/internal/floating.tsx index 6baaa432df..216e1f9f87 100644 --- a/packages/@headlessui-react/src/internal/floating.tsx +++ b/packages/@headlessui-react/src/internal/floating.tsx @@ -375,9 +375,16 @@ function useFixScrollingPixel(element: HTMLElement | null) { if (!element) return let observer = new MutationObserver(() => { - let maxHeight = element.style.maxHeight - if (parseFloat(maxHeight) !== parseInt(maxHeight)) { - element.style.maxHeight = `${Math.ceil(parseFloat(maxHeight))}px` + let maxHeight = window.getComputedStyle(element).maxHeight + + let maxHeightFloat = parseFloat(maxHeight) + if (isNaN(maxHeightFloat)) return + + let maxHeightInt = parseInt(maxHeight) + if (isNaN(maxHeightInt)) return + + if (maxHeightFloat !== maxHeightInt) { + element.style.maxHeight = `${Math.ceil(maxHeightFloat)}px` } }) From dacb82285eec4825566f9145a7a4b6bc91bd71af Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 3 Jul 2024 14:14:40 +0200 Subject: [PATCH 3/6] playground cleanup Testing using this playground example, so cleaned it up to be more modern using newer components, transition prop and so on. --- .../listbox/listbox-with-pure-tailwind.tsx | 75 ++++++++----------- 1 file changed, 30 insertions(+), 45 deletions(-) diff --git a/playgrounds/react/pages/listbox/listbox-with-pure-tailwind.tsx b/playgrounds/react/pages/listbox/listbox-with-pure-tailwind.tsx index 0d845b098a..de62f6945f 100644 --- a/playgrounds/react/pages/listbox/listbox-with-pure-tailwind.tsx +++ b/playgrounds/react/pages/listbox/listbox-with-pure-tailwind.tsx @@ -1,4 +1,4 @@ -import { Listbox, Transition } from '@headlessui/react' +import { Label, Listbox, ListboxButton, ListboxOption, ListboxOptions } from '@headlessui/react' import { useEffect, useState } from 'react' let people = [ @@ -26,20 +26,12 @@ export default function Home() {
- { - console.log('value:', value) - setActivePerson(value) - }} - > - - Assigned to - + +
- + {active} - + - -
- - {people.map((name) => ( - - - {name} - - - - - - - - ))} - -
-
+ {people.map((name) => ( + + + {name} + + + + + + + + ))} +
From 8a1911380f95273ee6a4fe8708a137f9b85a2fff Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 3 Jul 2024 14:23:10 +0200 Subject: [PATCH 4/6] use CSS instead of JS Let's make it a CSS problem instead of a JS problem. The `round(up, , )` will behave similar to a `Math.ceil()` that we had in the JS implementation. See: https://developer.mozilla.org/en-US/docs/Web/CSS/round --- .../src/internal/floating.tsx | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/packages/@headlessui-react/src/internal/floating.tsx b/packages/@headlessui-react/src/internal/floating.tsx index 216e1f9f87..818c36cb16 100644 --- a/packages/@headlessui-react/src/internal/floating.tsx +++ b/packages/@headlessui-react/src/internal/floating.tsx @@ -175,7 +175,6 @@ export function FloatingProvider({ let overflowRef = useRef(null) let [floatingEl, setFloatingElement] = useState(null) - useFixScrollingPixel(floatingEl) let isEnabled = enabled && config !== null && floatingEl !== null @@ -317,7 +316,7 @@ export function FloatingProvider({ Object.assign(elements.floating.style, { overflow: 'auto', maxWidth: `${availableWidth}px`, - maxHeight: `min(var(--anchor-max-height, 100vh), ${availableHeight}px)`, + maxHeight: `round(up, min(var(--anchor-max-height, 100vh), ${availableHeight}px), 1px)`, }) }, }), @@ -370,35 +369,6 @@ export function FloatingProvider({ ) } -function useFixScrollingPixel(element: HTMLElement | null) { - useIsoMorphicEffect(() => { - if (!element) return - - let observer = new MutationObserver(() => { - let maxHeight = window.getComputedStyle(element).maxHeight - - let maxHeightFloat = parseFloat(maxHeight) - if (isNaN(maxHeightFloat)) return - - let maxHeightInt = parseInt(maxHeight) - if (isNaN(maxHeightInt)) return - - if (maxHeightFloat !== maxHeightInt) { - element.style.maxHeight = `${Math.ceil(maxHeightFloat)}px` - } - }) - - observer.observe(element, { - attributes: true, - attributeFilter: ['style'], - }) - - return () => { - observer.disconnect() - } - }, [element]) -} - function useResolvedConfig( config: (Exclude & InternalFloatingPanelProps) | null, element?: HTMLElement | null From 8ef4d645d364cf8e668076f58a2468e487289655 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 3 Jul 2024 14:37:35 +0200 Subject: [PATCH 5/6] Remove CSS solution for now I want to re-enable this in the future, but unfortunately for now we can't use it because Chrome only introduced support for this in the last 2 months. This reverts commit daac60d45ec3f02b324d0d8b18078a995e885733. --- .../src/internal/floating.tsx | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/@headlessui-react/src/internal/floating.tsx b/packages/@headlessui-react/src/internal/floating.tsx index 818c36cb16..216e1f9f87 100644 --- a/packages/@headlessui-react/src/internal/floating.tsx +++ b/packages/@headlessui-react/src/internal/floating.tsx @@ -175,6 +175,7 @@ export function FloatingProvider({ let overflowRef = useRef(null) let [floatingEl, setFloatingElement] = useState(null) + useFixScrollingPixel(floatingEl) let isEnabled = enabled && config !== null && floatingEl !== null @@ -316,7 +317,7 @@ export function FloatingProvider({ Object.assign(elements.floating.style, { overflow: 'auto', maxWidth: `${availableWidth}px`, - maxHeight: `round(up, min(var(--anchor-max-height, 100vh), ${availableHeight}px), 1px)`, + maxHeight: `min(var(--anchor-max-height, 100vh), ${availableHeight}px)`, }) }, }), @@ -369,6 +370,35 @@ export function FloatingProvider({ ) } +function useFixScrollingPixel(element: HTMLElement | null) { + useIsoMorphicEffect(() => { + if (!element) return + + let observer = new MutationObserver(() => { + let maxHeight = window.getComputedStyle(element).maxHeight + + let maxHeightFloat = parseFloat(maxHeight) + if (isNaN(maxHeightFloat)) return + + let maxHeightInt = parseInt(maxHeight) + if (isNaN(maxHeightInt)) return + + if (maxHeightFloat !== maxHeightInt) { + element.style.maxHeight = `${Math.ceil(maxHeightFloat)}px` + } + }) + + observer.observe(element, { + attributes: true, + attributeFilter: ['style'], + }) + + return () => { + observer.disconnect() + } + }, [element]) +} + function useResolvedConfig( config: (Exclude & InternalFloatingPanelProps) | null, element?: HTMLElement | null From ce412c5aa6e1396f1da0d498c74e6c48e10e094a Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 3 Jul 2024 14:52:25 +0200 Subject: [PATCH 6/6] 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 b4a58bfff3..07b1f16776 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix prematurely added anchoring styles on `ListboxOptions` ([#3337](https://github.com/tailwindlabs/headlessui/pull/3337)) - Ensure `unmount` on `Dialog` works in combination with the `transition` prop on `DialogBackdrop` and `DialogPanel` components ([#3352](https://github.com/tailwindlabs/headlessui/pull/3352)) - Fix crash in `Combobox` component when in `virtual` mode when options are empty ([#3356](https://github.com/tailwindlabs/headlessui/pull/3356)) +- Fix hanging tests when using `anchor` prop ([#3357](https://github.com/tailwindlabs/headlessui/pull/3357)) ## [2.1.1] - 2024-06-26