Skip to content

Commit 5a3d556

Browse files
Lazily resolve default containers in <Dialog> (#2697)
* Lazily resolve default containers in `<Dialog>` * Update changelog
1 parent 6a88fd5 commit 5a3d556

File tree

3 files changed

+51
-1
lines changed

3 files changed

+51
-1
lines changed

packages/@headlessui-react/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Fixed
1111

1212
- Don't call `<Dialog>`'s `onClose` twice on mobile devices ([#2690](https://github.com/tailwindlabs/headlessui/pull/2690))
13+
- Lazily resolve default containers in `<Dialog>` ([#2697](https://github.com/tailwindlabs/headlessui/pull/2697))
1314

1415
## [1.7.17] - 2023-08-17
1516

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,6 +1525,43 @@ describe('Mouse interactions', () => {
15251525
})
15261526
)
15271527

1528+
// NOTE: This test doesn't actually fail in JSDOM when it's supposed to
1529+
// We're keeping it around for documentation purposes
1530+
it(
1531+
'should not close the Dialog if it starts open and we click inside the Dialog when it has only a panel',
1532+
suppressConsoleLogs(async () => {
1533+
function Example() {
1534+
let [isOpen, setIsOpen] = useState(true)
1535+
return (
1536+
<>
1537+
<button id="trigger" onClick={() => setIsOpen((v) => !v)}>
1538+
Trigger
1539+
</button>
1540+
<Dialog open={isOpen} onClose={() => setIsOpen(false)}>
1541+
<Dialog.Panel>
1542+
<p id="inside">My content</p>
1543+
<button>close</button>
1544+
</Dialog.Panel>
1545+
</Dialog>
1546+
</>
1547+
)
1548+
}
1549+
1550+
render(<Example />)
1551+
1552+
// Open the dialog
1553+
await click(document.getElementById('trigger'))
1554+
1555+
assertDialog({ state: DialogState.Visible })
1556+
1557+
// Click the p tag inside the dialog
1558+
await click(document.getElementById('inside'))
1559+
1560+
// It should not have closed
1561+
assertDialog({ state: DialogState.Visible })
1562+
})
1563+
)
1564+
15281565
it(
15291566
'should close the Dialog if we click outside the Dialog.Panel',
15301567
suppressConsoleLogs(async () => {

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import React, {
1414
ContextType,
1515
ElementType,
1616
MouseEvent as ReactMouseEvent,
17+
RefObject,
1718
MutableRefObject,
1819
Ref,
1920
} from 'react'
@@ -210,13 +211,24 @@ function DialogFn<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG>(
210211
let hasNestedDialogs = nestedDialogCount > 1 // 1 is the current dialog
211212
let hasParentDialog = useContext(DialogContext) !== null
212213
let [portals, PortalWrapper] = useNestedPortals()
214+
215+
// We use this because reading these values during iniital render(s)
216+
// can result in `null` rather then the actual elements
217+
// This doesn't happen when using certain components like a
218+
// `<Dialog.Title>` because they cause the parent to re-render
219+
let defaultContainer: RefObject<HTMLElement> = {
220+
get current() {
221+
return state.panelRef.current ?? internalDialogRef.current
222+
},
223+
}
224+
213225
let {
214226
resolveContainers: resolveRootContainers,
215227
mainTreeNodeRef,
216228
MainTreeNode,
217229
} = useRootContainers({
218230
portals,
219-
defaultContainers: [state.panelRef.current ?? internalDialogRef.current],
231+
defaultContainers: [defaultContainer],
220232
})
221233

222234
// If there are multiple dialogs, then you can be the root, the leaf or one

0 commit comments

Comments
 (0)