Skip to content

Commit

Permalink
Ensure unmount on Dialog works in combination with the `transitio…
Browse files Browse the repository at this point in the history
…n` prop on `DialogBackdrop` and `DialogPanel` components (#3352)

* inherit `unmount` from `Dialog` in `DialogBackdrop` and `DialogPanel` components

Only the `Dialog` accepts an `unmount` prop because it's the `Dialog`
that is conditionally rendered and the `DialogBackdrop` and
`DialogPanel` will conditionally show together with the `Dialog`.

However, now that the `Dialog` is wrapped in a `Transition` (which can
be unmounted) and the `DialogBackdrop` and `DialogPanel` will also be
wrapped in a `TransitionChild` (when the `transition` prop is passed)
then we do have to deal with the `unmount` state on the `TransitionChild`.

This is important because if you make the `Dialog` `unmount={false}`, then the
`DialogPanel` will still unmount because the `TransitionChild` is unmounting its
children. This now means that you will lose data (such as form state of inputs).

This commit solves that by inheriting the `unmount` state of the
`Dialog` in the `TransitionChild` wrappers such that they behave the way
you expect them to behave.

* update changelog
  • Loading branch information
RobinMalfait authored Jul 1, 2024
1 parent fbad6a9 commit 990b179
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 6 deletions.
1 change: 1 addition & 0 deletions packages/@headlessui-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- 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))

## [2.1.1] - 2024-06-26

Expand Down
17 changes: 11 additions & 6 deletions packages/@headlessui-react/src/components/dialog/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ let DialogContext = createContext<
| [
{
dialogState: DialogStates
unmount: boolean
close(): void
setTitleId(id: string | null): void
},
Expand Down Expand Up @@ -121,6 +122,7 @@ let InternalDialog = forwardRefWithAs(function InternalDialog<
role = 'dialog',
autoFocus = true,
__demoMode = false,
unmount = false,
...theirProps
} = props

Expand Down Expand Up @@ -248,8 +250,8 @@ let InternalDialog = forwardRefWithAs(function InternalDialog<
let [describedby, DescriptionProvider] = useDescriptions()

let contextBag = useMemo<ContextType<typeof DialogContext>>(
() => [{ dialogState, close, setTitleId }, state],
[dialogState, state, close, setTitleId]
() => [{ dialogState, close, setTitleId, unmount }, state],
[dialogState, state, close, setTitleId, unmount]
)

let slot = useMemo(
Expand All @@ -265,6 +267,7 @@ let InternalDialog = forwardRefWithAs(function InternalDialog<
'aria-modal': __demoMode ? undefined : dialogState === DialogStates.Open ? true : undefined,
'aria-labelledby': state.titleId,
'aria-describedby': describedby,
unmount,
}

let shouldMoveFocusInside = !useIsTouchDevice()
Expand Down Expand Up @@ -421,7 +424,7 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
) {
let internalId = useId()
let { id = `headlessui-dialog-panel-${internalId}`, transition = false, ...theirProps } = props
let [{ dialogState }, state] = useDialogContext('Dialog.Panel')
let [{ dialogState, unmount }, state] = useDialogContext('Dialog.Panel')
let panelRef = useSyncRefs(ref, state.panelRef)

let slot = useMemo(
Expand All @@ -442,9 +445,10 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
}

let Wrapper = transition ? TransitionChild : Fragment
let wrapperProps = transition ? { unmount } : {}

return (
<Wrapper>
<Wrapper {...wrapperProps}>
{render({
ourProps,
theirProps,
Expand Down Expand Up @@ -475,7 +479,7 @@ function BackdropFn<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG>(
ref: Ref<HTMLElement>
) {
let { transition = false, ...theirProps } = props
let [{ dialogState }] = useDialogContext('Dialog.Backdrop')
let [{ dialogState, unmount }] = useDialogContext('Dialog.Backdrop')

let slot = useMemo(
() => ({ open: dialogState === DialogStates.Open }) satisfies BackdropRenderPropArg,
Expand All @@ -485,9 +489,10 @@ function BackdropFn<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG>(
let ourProps = { ref, 'aria-hidden': true }

let Wrapper = transition ? TransitionChild : Fragment
let wrapperProps = transition ? { unmount } : {}

return (
<Wrapper>
<Wrapper {...wrapperProps}>
{render({
ourProps,
theirProps,
Expand Down

0 comments on commit 990b179

Please sign in to comment.