diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index c896cab80..4c69e2df2 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix `Transition` component's incorrect cleanup and order of events ([#1803](https://github.com/tailwindlabs/headlessui/pull/1803)) - Ensure enter transitions work when using `unmount={false}` ([#1811](https://github.com/tailwindlabs/headlessui/pull/1811)) - Improve accessibility when announcing `Listbox.Option` and `Combobox.Option` components ([#1812](https://github.com/tailwindlabs/headlessui/pull/1812)) +- Fix `ref` stealing from children ([#1820](https://github.com/tailwindlabs/headlessui/pull/1820)) ## [1.6.6] - 2022-07-07 diff --git a/packages/@headlessui-react/src/components/transitions/transition.test.tsx b/packages/@headlessui-react/src/components/transitions/transition.test.tsx index 0b3bf7d46..ae8072c4d 100644 --- a/packages/@headlessui-react/src/components/transitions/transition.test.tsx +++ b/packages/@headlessui-react/src/components/transitions/transition.test.tsx @@ -6,6 +6,29 @@ import { Transition } from './transition' import { executeTimeline } from '../../test-utils/execute-timeline' +function nextFrame() { + return new Promise((resolve) => { + requestAnimationFrame(() => { + requestAnimationFrame(() => { + resolve() + }) + }) + }) +} + +it('should not steal the ref from the child', async () => { + let fn = jest.fn() + render( + +
...
+
+ ) + + await nextFrame() + + expect(fn).toHaveBeenCalled() +}) + it('should render without crashing', () => { render( diff --git a/packages/@headlessui-react/src/utils/render.ts b/packages/@headlessui-react/src/utils/render.ts index ebcde6675..2978f46f2 100644 --- a/packages/@headlessui-react/src/utils/render.ts +++ b/packages/@headlessui-react/src/utils/render.ts @@ -175,7 +175,8 @@ function _render( // Filter out undefined values so that they don't override the existing values mergeProps(resolvedChildren.props, compact(omit(rest, ['ref']))), dataAttributes, - refRelatedProps + refRelatedProps, + mergeRefs((resolvedChildren as any).ref, refRelatedProps.ref) ) ) } @@ -193,6 +194,20 @@ function _render( ) } +function mergeRefs(...refs: any[]) { + return { + ref: refs.every((ref) => ref == null) + ? undefined + : (value: any) => { + for (let ref of refs) { + if (ref == null) continue + if (typeof ref === 'function') ref(value) + else ref.current = value + } + }, + } +} + function mergeProps(...listOfProps: Props[]) { if (listOfProps.length === 0) return {} if (listOfProps.length === 1) return listOfProps[0]