Skip to content

Commit

Permalink
Ensure anchored components are properly stacked on top of Dialog co…
Browse files Browse the repository at this point in the history
…mponents (#3111)

* ensure `Dialog` knows about `Modal`s via the `StackProvider`

When you render a `Listbox` in a `Dialog`, then clicking outside of the
`Listbox` will only close the `Listbox` and not the `Dialog`.

This is because the `Listbox` is rendered _inside_ the `Dialog`, and the
`useOutsideClick` hook will prevent the event from propagating to the
`Dialog` therefore it stays open.

Then, if you add the `anchor` prop to the `ListboxOptions` then a few
things will happen:

1. We will render the `ListboxOptions` in a `Modal`, which portals the
   component to the end of the `body` (aka, it won't be in the `Dialog`
   anymore).
2. The `anchor` prop, will use Floating UI to position the element
   correctly.

If you now click outside of the open `Listbox`, then the `Dialog` will
receive the click event (because it is rendered somewhere else in the
DOM) and therefore the `Listbox` **and** the `Dialog` will close.

The `Dialog` also uses a `StackProvider` to know if it is the top-level
`Dialog` or not. The problem is that the `Modal` doesn't use that
`StackProvider` to tell the `Dialog` that something is stacked on top of
the current `Dialog`.

That's what this commit fixes, the `Modal` will now use a
`StackProvider` to tell the `Dialog` that it's not the top-most element
anymore so it shouldn't enable the `useOutsideClick` behavior.

That said, this is one of the things that will be changed in the future
to make "parallel" dialogs possible. Essentially, we will track a global
stack and the top-most element (last one that was "opened") will win.

Then hooks such as `useOutsideClick` and `useScrollLock` will use that
information to know if they should undo scroll locking for example if
another element is still open.

* update CHANGELOG
  • Loading branch information
RobinMalfait authored Apr 19, 2024
1 parent 8fa5caf commit b517a39
Show file tree
Hide file tree
Showing 3 changed files with 5 additions and 3 deletions.
1 change: 1 addition & 0 deletions packages/@headlessui-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Render hidden form input fields for `Checkbox`, `Switch` and `RadioGroup` components ([#3095](https://github.com/tailwindlabs/headlessui/pull/3095))
- Ensure the `multiple` prop is typed correctly when passing explicit types to the `Combobox` component ([#3099](https://github.com/tailwindlabs/headlessui/pull/3099))
- Omit `nullable` prop from `Combobox` component ([#3100](https://github.com/tailwindlabs/headlessui/pull/3100))
- Ensure anchored components are properly stacked on top of `Dialog` components ([#3111](https://github.com/tailwindlabs/headlessui/pull/3111))

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ function DialogFn<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG>(
enabled={dialogState === DialogStates.Open}
element={internalDialogRef}
onUpdate={useEvent((message, type) => {
if (type !== 'Dialog') return
if (type !== 'Dialog' && type !== 'Modal') return

match(message, {
[StackMessage.Add]: () => setNestedDialogCount((count) => count + 1),
Expand Down
5 changes: 3 additions & 2 deletions packages/@headlessui-react/src/internal/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
type RefProp,
} from '../utils/render'
import { ForcePortalRoot } from './portal-force-root'
import { StackProvider } from './stack-context'

function useScrollLock(
ownerDocument: Document | null,
Expand Down Expand Up @@ -171,7 +172,7 @@ function ModalFn<TTag extends ElementType = typeof DEFAULT_MODAL_TAG>(
}

return (
<>
<StackProvider type="Modal" enabled={enabled} element={internalModalRef}>
<ForcePortalRoot force={true}>
<Portal>
<FocusTrap
Expand Down Expand Up @@ -201,7 +202,7 @@ function ModalFn<TTag extends ElementType = typeof DEFAULT_MODAL_TAG>(
<HoistFormFields>
<MainTreeNode />
</HoistFormFields>
</>
</StackProvider>
)
}

Expand Down

0 comments on commit b517a39

Please sign in to comment.