Skip to content

Commit 9885008

Browse files
authored
Make the ref optional in the Popover component (#1465)
* make the ref optional in the `Popover` component We "required" the prop to calculate the `ownerDocument`. But if you don't provide a ref, then we will use the `Popover.Button` to calculate it. If that's not defined, then we can fallback to the default `document`. * update changelog
1 parent 8ca73eb commit 9885008

File tree

3 files changed

+43
-4
lines changed

3 files changed

+43
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727
- Simplify `Popover` Tab logic by using sentinel nodes instead of keydown event interception ([#1440](https://github.com/tailwindlabs/headlessui/pull/1440))
2828
- Ensure the `Popover.Panel` is clickable without closing the `Popover` ([#1443](https://github.com/tailwindlabs/headlessui/pull/1443))
2929
- Improve "Scroll lock" scrollbar width for `Dialog` component ([#1457](https://github.com/tailwindlabs/headlessui/pull/1457))
30+
- Make the `ref` optional in the `Popover` component ([#1465](https://github.com/tailwindlabs/headlessui/pull/1465))
3031

3132
## [@headlessui/react@1.6.1] - 2022-05-03
3233

packages/@headlessui-react/src/components/popover/popover.test.tsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { createElement, useEffect, useRef } from 'react'
1+
import React, { createElement, useEffect, useRef, Fragment } from 'react'
22
import { render } from '@testing-library/react'
33

44
import { Popover } from './popover'
@@ -259,6 +259,38 @@ describe('Rendering', () => {
259259
assertActiveElement(getByText('restoreable'))
260260
})
261261
)
262+
263+
describe('refs', () => {
264+
it(
265+
'should be possible to get a ref to the Popover',
266+
suppressConsoleLogs(async () => {
267+
let popoverRef = { current: null }
268+
269+
render(
270+
<Popover as="div" ref={popoverRef}>
271+
<Popover.Button>Trigger</Popover.Button>
272+
<Popover.Panel>Popover</Popover.Panel>
273+
</Popover>
274+
)
275+
276+
expect(popoverRef.current).not.toBeNull()
277+
})
278+
)
279+
280+
it(
281+
'should be possible to use a Fragment with an optional ref',
282+
suppressConsoleLogs(async () => {
283+
render(
284+
<Popover as={Fragment}>
285+
<Popover.Button>Trigger</Popover.Button>
286+
<Popover.Panel>Popover</Popover.Panel>
287+
</Popover>
288+
)
289+
290+
// It should not throw
291+
})
292+
)
293+
})
262294
})
263295

264296
describe('Popover.Button', () => {

packages/@headlessui-react/src/components/popover/popover.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import React, {
2222
import { Props } from '../../types'
2323
import { match } from '../../utils/match'
2424
import { forwardRefWithAs, render, Features, PropsForFeatures } from '../../utils/render'
25-
import { useSyncRefs } from '../../hooks/use-sync-refs'
25+
import { optionalRef, useSyncRefs } from '../../hooks/use-sync-refs'
2626
import { useId } from '../../hooks/use-id'
2727
import { Keys } from '../keyboard'
2828
import { isDisabledReactIssue7711 } from '../../utils/bugs'
@@ -185,8 +185,12 @@ let PopoverRoot = forwardRefWithAs(function Popover<
185185
let buttonId = `headlessui-popover-button-${useId()}`
186186
let panelId = `headlessui-popover-panel-${useId()}`
187187
let internalPopoverRef = useRef<HTMLElement | null>(null)
188-
let popoverRef = useSyncRefs(ref, internalPopoverRef)
189-
let ownerDocument = useOwnerDocument(internalPopoverRef)
188+
let popoverRef = useSyncRefs(
189+
ref,
190+
optionalRef((ref) => {
191+
internalPopoverRef.current = ref
192+
})
193+
)
190194

191195
let reducerBag = useReducer(stateReducer, {
192196
popoverState: PopoverStates.Closed,
@@ -200,6 +204,8 @@ let PopoverRoot = forwardRefWithAs(function Popover<
200204
let [{ popoverState, button, panel, beforePanelSentinel, afterPanelSentinel }, dispatch] =
201205
reducerBag
202206

207+
let ownerDocument = useOwnerDocument(internalPopoverRef.current ?? button)
208+
203209
useEffect(() => dispatch({ type: ActionTypes.SetButtonId, buttonId }), [buttonId, dispatch])
204210
useEffect(() => dispatch({ type: ActionTypes.SetPanelId, panelId }), [panelId, dispatch])
205211

0 commit comments

Comments
 (0)