Remove deprecated DialogBackdrop and DialogOverlay components (#3171)
* remove `DialogBackdrop` and `DialogOverlay` We deprecated those components in v1.6, since they are no longer documented and this is a major release, we can safely get rid of it. * update changelog * migrate playground example to use `Dialog.Panel`
This commit is contained in:
@@ -53,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Deprecate the `entered` prop on the `Transition` component ([#3089](https://github.com/tailwindlabs/headlessui/pull/3089))
|
||||
- Deprecate dot notation for components ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887), [#3170](https://github.com/tailwindlabs/headlessui/pull/3170))
|
||||
- Add frozen value to `ComboboxOptions` component ([#3126](https://github.com/tailwindlabs/headlessui/pull/3126))
|
||||
- Remove deprecated `DialogBackdrop` and `DialogOverlay` components ([#3171](https://github.com/tailwindlabs/headlessui/pull/3171))
|
||||
|
||||
## [1.7.19] - 2024-04-15
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// Next.js barrel file improvements (GENERATED FILE)
|
||||
export type * from '../dialog/dialog'
|
||||
export { DialogBackdrop } from '../dialog/dialog'
|
||||
@@ -1,3 +0,0 @@
|
||||
// Next.js barrel file improvements (GENERATED FILE)
|
||||
export type * from '../dialog/dialog'
|
||||
export { DialogOverlay } from '../dialog/dialog'
|
||||
@@ -6,15 +6,11 @@ import {
|
||||
assertActiveElement,
|
||||
assertDialog,
|
||||
assertDialogDescription,
|
||||
assertDialogOverlay,
|
||||
assertDialogTitle,
|
||||
assertPopoverPanel,
|
||||
DialogState,
|
||||
getByText,
|
||||
getDialog,
|
||||
getDialogBackdrop,
|
||||
getDialogOverlay,
|
||||
getDialogOverlays,
|
||||
getDialogs,
|
||||
getPopoverButton,
|
||||
PopoverState,
|
||||
@@ -46,9 +42,7 @@ function TabSentinel(props: PropsOf<'button'>) {
|
||||
|
||||
describe('Safe guards', () => {
|
||||
it.each([
|
||||
['Dialog.Overlay', Dialog.Overlay],
|
||||
['Dialog.Title', Dialog.Title],
|
||||
['Dialog.Backdrop', Dialog.Backdrop],
|
||||
['Dialog.Panel', Dialog.Panel],
|
||||
])(
|
||||
'should error when we are using a <%s /> without a parent <Dialog />',
|
||||
@@ -66,7 +60,6 @@ describe('Safe guards', () => {
|
||||
render(
|
||||
<Dialog autoFocus={false} open={false} onClose={console.log}>
|
||||
<button>Trigger</button>
|
||||
<Dialog.Overlay />
|
||||
<Dialog.Title />
|
||||
<p>Contents</p>
|
||||
<Dialog.Description />
|
||||
@@ -553,148 +546,6 @@ describe('Rendering', () => {
|
||||
)
|
||||
})
|
||||
|
||||
describe('Dialog.Overlay', () => {
|
||||
it(
|
||||
'should be possible to render Dialog.Overlay using a render prop',
|
||||
suppressConsoleLogs(async () => {
|
||||
let overlay = jest.fn().mockReturnValue(null)
|
||||
function Example() {
|
||||
let [isOpen, setIsOpen] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<button id="trigger" onClick={() => setIsOpen((v) => !v)}>
|
||||
Trigger
|
||||
</button>
|
||||
<Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>
|
||||
<Dialog.Overlay>{overlay}</Dialog.Overlay>
|
||||
<TabSentinel />
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
render(<Example />)
|
||||
|
||||
assertDialogOverlay({
|
||||
state: DialogState.InvisibleUnmounted,
|
||||
attributes: { id: 'headlessui-dialog-overlay-2' },
|
||||
})
|
||||
|
||||
await click(document.getElementById('trigger'))
|
||||
|
||||
assertDialogOverlay({
|
||||
state: DialogState.Visible,
|
||||
attributes: { id: 'headlessui-dialog-overlay-2' },
|
||||
})
|
||||
expect(overlay).toHaveBeenCalledWith({ open: true })
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
describe('Dialog.Backdrop', () => {
|
||||
it(
|
||||
'should throw an error if a Dialog.Backdrop is used without a Dialog.Panel',
|
||||
suppressConsoleLogs(async () => {
|
||||
function Example() {
|
||||
let [isOpen, setIsOpen] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<button id="trigger" onClick={() => setIsOpen((v) => !v)}>
|
||||
Trigger
|
||||
</button>
|
||||
<Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>
|
||||
<Dialog.Backdrop />
|
||||
<TabSentinel />
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
render(<Example />)
|
||||
|
||||
try {
|
||||
await click(document.getElementById('trigger'))
|
||||
|
||||
expect(true).toBe(false)
|
||||
} catch (e: unknown) {
|
||||
expect((e as Error).message).toBe(
|
||||
'A <Dialog.Backdrop /> component is being used, but a <Dialog.Panel /> component is missing.'
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should not throw an error if a Dialog.Backdrop is used with a Dialog.Panel',
|
||||
suppressConsoleLogs(async () => {
|
||||
function Example() {
|
||||
let [isOpen, setIsOpen] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<button id="trigger" onClick={() => setIsOpen((v) => !v)}>
|
||||
Trigger
|
||||
</button>
|
||||
<Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>
|
||||
<Dialog.Backdrop />
|
||||
<Dialog.Panel>
|
||||
<TabSentinel />
|
||||
</Dialog.Panel>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
render(<Example />)
|
||||
|
||||
await click(document.getElementById('trigger'))
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should portal the Dialog.Backdrop',
|
||||
suppressConsoleLogs(async () => {
|
||||
function Example() {
|
||||
let [isOpen, setIsOpen] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<button id="trigger" onClick={() => setIsOpen((v) => !v)}>
|
||||
Trigger
|
||||
</button>
|
||||
<Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>
|
||||
<Dialog.Backdrop />
|
||||
<Dialog.Panel>
|
||||
<TabSentinel />
|
||||
</Dialog.Panel>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
render(<Example />)
|
||||
|
||||
await click(document.getElementById('trigger'))
|
||||
|
||||
let dialog = getDialog()
|
||||
let backdrop = getDialogBackdrop()
|
||||
|
||||
expect(dialog).not.toBe(null)
|
||||
dialog = dialog as HTMLElement
|
||||
|
||||
expect(backdrop).not.toBe(null)
|
||||
backdrop = backdrop as HTMLElement
|
||||
|
||||
// It should not be nested
|
||||
let position = dialog.compareDocumentPosition(backdrop)
|
||||
expect(position & Node.DOCUMENT_POSITION_CONTAINED_BY).not.toBe(
|
||||
Node.DOCUMENT_POSITION_CONTAINED_BY
|
||||
)
|
||||
|
||||
// It should be a sibling
|
||||
expect(position & Node.DOCUMENT_POSITION_FOLLOWING).toBe(Node.DOCUMENT_POSITION_FOLLOWING)
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
describe('Dialog.Title', () => {
|
||||
it(
|
||||
'should be possible to render Dialog.Title using a render prop',
|
||||
@@ -1134,76 +985,6 @@ describe('Keyboard interactions', () => {
|
||||
})
|
||||
|
||||
describe('Mouse interactions', () => {
|
||||
it(
|
||||
'should be possible to close a Dialog using a click on the Dialog.Overlay',
|
||||
suppressConsoleLogs(async () => {
|
||||
function Example() {
|
||||
let [isOpen, setIsOpen] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<button id="trigger" onClick={() => setIsOpen((v) => !v)}>
|
||||
Trigger
|
||||
</button>
|
||||
<Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>
|
||||
<Dialog.Overlay />
|
||||
Contents
|
||||
<TabSentinel />
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
render(<Example />)
|
||||
|
||||
// Open dialog
|
||||
await click(document.getElementById('trigger'))
|
||||
|
||||
// Verify it is open
|
||||
assertDialog({ state: DialogState.Visible })
|
||||
|
||||
// Click to close
|
||||
await click(getDialogOverlay())
|
||||
|
||||
// Verify it is closed
|
||||
assertDialog({ state: DialogState.InvisibleUnmounted })
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should not close the Dialog when clicking on contents of the Dialog.Overlay',
|
||||
suppressConsoleLogs(async () => {
|
||||
function Example() {
|
||||
let [isOpen, setIsOpen] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<button id="trigger" onClick={() => setIsOpen((v) => !v)}>
|
||||
Trigger
|
||||
</button>
|
||||
<Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>
|
||||
<Dialog.Overlay>
|
||||
<button>hi</button>
|
||||
</Dialog.Overlay>
|
||||
Contents
|
||||
<TabSentinel />
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
render(<Example />)
|
||||
|
||||
// Open dialog
|
||||
await click(document.getElementById('trigger'))
|
||||
|
||||
// Verify it is open
|
||||
assertDialog({ state: DialogState.Visible })
|
||||
|
||||
// Click on an element inside the overlay
|
||||
await click(getByText('hi'))
|
||||
|
||||
// Verify it is still open
|
||||
assertDialog({ state: DialogState.Visible })
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should be possible to close the dialog, and re-focus the button when we click outside on the body element',
|
||||
suppressConsoleLogs(async () => {
|
||||
@@ -1273,44 +1054,6 @@ describe('Mouse interactions', () => {
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should stop propagating click events when clicking on the Dialog.Overlay',
|
||||
suppressConsoleLogs(async () => {
|
||||
let wrapperFn = jest.fn()
|
||||
function Example() {
|
||||
let [isOpen, setIsOpen] = useState(true)
|
||||
return (
|
||||
<div onClick={wrapperFn}>
|
||||
<Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>
|
||||
Contents
|
||||
<Dialog.Overlay />
|
||||
<TabSentinel />
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render(<Example />)
|
||||
|
||||
await nextFrame()
|
||||
|
||||
// Verify it is open
|
||||
assertDialog({ state: DialogState.Visible })
|
||||
|
||||
// Verify that the wrapper function has not been called yet
|
||||
expect(wrapperFn).toHaveBeenCalledTimes(0)
|
||||
|
||||
// Click the Dialog.Overlay to close the Dialog
|
||||
await click(getDialogOverlay())
|
||||
|
||||
// Verify it is closed
|
||||
assertDialog({ state: DialogState.InvisibleUnmounted })
|
||||
|
||||
// Verify that the wrapper function has not been called yet
|
||||
expect(wrapperFn).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should be possible to submit a form inside a Dialog',
|
||||
suppressConsoleLogs(async () => {
|
||||
@@ -1667,7 +1410,6 @@ describe('Mouse interactions', () => {
|
||||
Trigger
|
||||
</button>
|
||||
<Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>
|
||||
<Dialog.Backdrop />
|
||||
<Dialog.Panel>
|
||||
<TabSentinel />
|
||||
</Dialog.Panel>
|
||||
@@ -1700,7 +1442,6 @@ describe('Mouse interactions', () => {
|
||||
Trigger
|
||||
</button>
|
||||
<Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>
|
||||
<Dialog.Backdrop />
|
||||
<Dialog.Panel>
|
||||
<button id="inside">Inside</button>
|
||||
<TabSentinel />
|
||||
@@ -1733,7 +1474,6 @@ describe('Mouse interactions', () => {
|
||||
Trigger
|
||||
</button>
|
||||
<Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>
|
||||
<Dialog.Backdrop />
|
||||
<Dialog.Panel>
|
||||
<button id="inside">Inside</button>
|
||||
<TabSentinel />
|
||||
@@ -1767,7 +1507,6 @@ describe('Mouse interactions', () => {
|
||||
</button>
|
||||
<div id="i-am-outside">this thing</div>
|
||||
<Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>
|
||||
<Dialog.Backdrop />
|
||||
<Dialog.Panel>
|
||||
<button id="inside">Inside</button>
|
||||
<TabSentinel />
|
||||
@@ -1816,8 +1555,6 @@ describe('Nesting', () => {
|
||||
|
||||
return (
|
||||
<Dialog autoFocus={false} open={open} onClose={onClose}>
|
||||
<Dialog.Overlay />
|
||||
|
||||
<div>
|
||||
<p>Level: {level}</p>
|
||||
<button onClick={() => setShowChild(true)}>Open {level + 1} a</button>
|
||||
@@ -1850,12 +1587,11 @@ describe('Nesting', () => {
|
||||
}
|
||||
|
||||
it.each`
|
||||
strategy | when | action
|
||||
${'with `Escape`'} | ${'mounted'} | ${() => press(Keys.Escape)}
|
||||
${'with `Outside Click`'} | ${'mounted'} | ${() => click(document.body)}
|
||||
${'with `Click on Dialog.Overlay`'} | ${'mounted'} | ${() => click(getDialogOverlays().pop()!)}
|
||||
${'with `Escape`'} | ${'always'} | ${() => press(Keys.Escape)}
|
||||
${'with `Outside Click`'} | ${'always'} | ${() => click(document.body)}
|
||||
strategy | when | action
|
||||
${'with `Escape`'} | ${'mounted'} | ${() => press(Keys.Escape)}
|
||||
${'with `Outside Click`'} | ${'mounted'} | ${() => click(document.body)}
|
||||
${'with `Escape`'} | ${'always'} | ${() => press(Keys.Escape)}
|
||||
${'with `Outside Click`'} | ${'always'} | ${() => click(document.body)}
|
||||
`(
|
||||
'should be possible to open nested Dialog components (visible when $when) and close them $strategy',
|
||||
async ({ when, action }) => {
|
||||
|
||||
@@ -35,7 +35,6 @@ import { State, useOpenClosed } from '../../internal/open-closed'
|
||||
import { ForcePortalRoot } from '../../internal/portal-force-root'
|
||||
import { StackMessage, StackProvider } from '../../internal/stack-context'
|
||||
import type { Props } from '../../types'
|
||||
import { isDisabledReactIssue7711 } from '../../utils/bugs'
|
||||
import { match } from '../../utils/match'
|
||||
import {
|
||||
RenderFeatures,
|
||||
@@ -426,115 +425,6 @@ function DialogFn<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG>(
|
||||
|
||||
// ---
|
||||
|
||||
let DEFAULT_OVERLAY_TAG = 'div' as const
|
||||
type OverlayRenderPropArg = {
|
||||
open: boolean
|
||||
}
|
||||
type OverlayPropsWeControl = 'aria-hidden'
|
||||
|
||||
export type DialogOverlayProps<TTag extends ElementType = typeof DEFAULT_OVERLAY_TAG> = Props<
|
||||
TTag,
|
||||
OverlayRenderPropArg,
|
||||
OverlayPropsWeControl
|
||||
>
|
||||
|
||||
function OverlayFn<TTag extends ElementType = typeof DEFAULT_OVERLAY_TAG>(
|
||||
props: DialogOverlayProps<TTag>,
|
||||
ref: Ref<HTMLElement>
|
||||
) {
|
||||
let internalId = useId()
|
||||
let { id = `headlessui-dialog-overlay-${internalId}`, ...theirProps } = props
|
||||
let [{ dialogState, close }] = useDialogContext('Dialog.Overlay')
|
||||
let overlayRef = useSyncRefs(ref)
|
||||
|
||||
let handleClick = useEvent((event: ReactMouseEvent) => {
|
||||
if (event.target !== event.currentTarget) return
|
||||
if (isDisabledReactIssue7711(event.currentTarget)) return event.preventDefault()
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
close()
|
||||
})
|
||||
|
||||
let slot = useMemo(
|
||||
() => ({ open: dialogState === DialogStates.Open }) satisfies OverlayRenderPropArg,
|
||||
[dialogState]
|
||||
)
|
||||
|
||||
let ourProps = {
|
||||
ref: overlayRef,
|
||||
id,
|
||||
'aria-hidden': true,
|
||||
onClick: handleClick,
|
||||
}
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_OVERLAY_TAG,
|
||||
name: 'Dialog.Overlay',
|
||||
})
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
let DEFAULT_BACKDROP_TAG = 'div' as const
|
||||
type BackdropRenderPropArg = {
|
||||
open: boolean
|
||||
}
|
||||
type BackdropPropsWeControl = 'aria-hidden'
|
||||
|
||||
export type DialogBackdropProps<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG> = Props<
|
||||
TTag,
|
||||
BackdropRenderPropArg,
|
||||
BackdropPropsWeControl
|
||||
>
|
||||
|
||||
function BackdropFn<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG>(
|
||||
props: DialogBackdropProps<TTag>,
|
||||
ref: Ref<HTMLElement>
|
||||
) {
|
||||
let internalId = useId()
|
||||
let { id = `headlessui-dialog-backdrop-${internalId}`, ...theirProps } = props
|
||||
let [{ dialogState }, state] = useDialogContext('Dialog.Backdrop')
|
||||
let backdropRef = useSyncRefs(ref)
|
||||
|
||||
useEffect(() => {
|
||||
if (state.panelRef.current === null) {
|
||||
throw new Error(
|
||||
`A <Dialog.Backdrop /> component is being used, but a <Dialog.Panel /> component is missing.`
|
||||
)
|
||||
}
|
||||
}, [state.panelRef])
|
||||
|
||||
let slot = useMemo(
|
||||
() => ({ open: dialogState === DialogStates.Open }) satisfies BackdropRenderPropArg,
|
||||
[dialogState]
|
||||
)
|
||||
|
||||
let ourProps = {
|
||||
ref: backdropRef,
|
||||
id,
|
||||
'aria-hidden': true,
|
||||
}
|
||||
|
||||
return (
|
||||
<ForcePortalRoot force>
|
||||
<Portal>
|
||||
{render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_BACKDROP_TAG,
|
||||
name: 'Dialog.Backdrop',
|
||||
})}
|
||||
</Portal>
|
||||
</ForcePortalRoot>
|
||||
)
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
let DEFAULT_PANEL_TAG = 'div' as const
|
||||
type PanelRenderPropArg = {
|
||||
open: boolean
|
||||
@@ -631,24 +521,12 @@ export interface _internal_ComponentDialog extends HasDisplayName {
|
||||
): JSX.Element
|
||||
}
|
||||
|
||||
export interface _internal_ComponentDialogBackdrop extends HasDisplayName {
|
||||
<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG>(
|
||||
props: DialogBackdropProps<TTag> & RefProp<typeof BackdropFn>
|
||||
): JSX.Element
|
||||
}
|
||||
|
||||
export interface _internal_ComponentDialogPanel extends HasDisplayName {
|
||||
<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
props: DialogPanelProps<TTag> & RefProp<typeof PanelFn>
|
||||
): JSX.Element
|
||||
}
|
||||
|
||||
export interface _internal_ComponentDialogOverlay extends HasDisplayName {
|
||||
<TTag extends ElementType = typeof DEFAULT_OVERLAY_TAG>(
|
||||
props: DialogOverlayProps<TTag> & RefProp<typeof OverlayFn>
|
||||
): JSX.Element
|
||||
}
|
||||
|
||||
export interface _internal_ComponentDialogTitle extends HasDisplayName {
|
||||
<TTag extends ElementType = typeof DEFAULT_TITLE_TAG>(
|
||||
props: DialogTitleProps<TTag> & RefProp<typeof TitleFn>
|
||||
@@ -659,21 +537,15 @@ export interface _internal_ComponentDialogDescription extends _internal_Componen
|
||||
|
||||
let DialogRoot = forwardRefWithAs(DialogFn) as _internal_ComponentDialog
|
||||
/** @deprecated use a plain `<div>` instead of `<DialogBackdrop>` */
|
||||
export let DialogBackdrop = forwardRefWithAs(BackdropFn) as _internal_ComponentDialogBackdrop
|
||||
export let DialogPanel = forwardRefWithAs(PanelFn) as _internal_ComponentDialogPanel
|
||||
/** @deprecated use a plain `<div>` instead of `<DialogOverlay>` */
|
||||
export let DialogOverlay = forwardRefWithAs(OverlayFn) as _internal_ComponentDialogOverlay
|
||||
export let DialogTitle = forwardRefWithAs(TitleFn) as _internal_ComponentDialogTitle
|
||||
/** @deprecated use `<Description>` instead of `<DialogDescription>` */
|
||||
export let DialogDescription = Description as _internal_ComponentDialogDescription
|
||||
|
||||
export let Dialog = Object.assign(DialogRoot, {
|
||||
/** @deprecated use a plain `<div>` instead of `<Dialog.Backdrop>` */
|
||||
Backdrop: DialogBackdrop,
|
||||
/** @deprecated use `<DialogPanel>` instead of `<Dialog.Panel>` */
|
||||
Panel: DialogPanel,
|
||||
/** @deprecated use a plain `<div>` instead of `<Dialog.Overlay>` */
|
||||
Overlay: DialogOverlay,
|
||||
/** @deprecated use `<DialogTitle>` instead of `<Dialog.Title>` */
|
||||
Title: DialogTitle,
|
||||
/** @deprecated use `<Description>` instead of `<Dialog.Description>` */
|
||||
|
||||
@@ -24,9 +24,7 @@ it('should expose the correct components', () => {
|
||||
'Description',
|
||||
|
||||
'Dialog',
|
||||
'DialogBackdrop',
|
||||
'DialogDescription',
|
||||
'DialogOverlay',
|
||||
'DialogPanel',
|
||||
'DialogTitle',
|
||||
|
||||
|
||||
@@ -1490,18 +1490,6 @@ export function getDialogDescription(): HTMLElement | null {
|
||||
return document.querySelector('[id^="headlessui-description-"]')
|
||||
}
|
||||
|
||||
export function getDialogOverlay(): HTMLElement | null {
|
||||
return document.querySelector('[id^="headlessui-dialog-overlay-"]')
|
||||
}
|
||||
|
||||
export function getDialogBackdrop(): HTMLElement | null {
|
||||
return document.querySelector('[id^="headlessui-dialog-backdrop-"]')
|
||||
}
|
||||
|
||||
export function getDialogOverlays(): HTMLElement[] {
|
||||
return Array.from(document.querySelectorAll('[id^="headlessui-dialog-overlay-"]'))
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
export enum DialogState {
|
||||
@@ -1682,53 +1670,6 @@ export function assertDialogDescription(
|
||||
}
|
||||
}
|
||||
|
||||
export function assertDialogOverlay(
|
||||
options: {
|
||||
attributes?: Record<string, string | null>
|
||||
textContent?: string
|
||||
state: DialogState
|
||||
},
|
||||
overlay = getDialogOverlay()
|
||||
) {
|
||||
try {
|
||||
switch (options.state) {
|
||||
case DialogState.InvisibleHidden:
|
||||
if (overlay === null) return expect(overlay).not.toBe(null)
|
||||
|
||||
assertHidden(overlay)
|
||||
|
||||
if (options.textContent) expect(overlay).toHaveTextContent(options.textContent)
|
||||
|
||||
for (let attributeName in options.attributes) {
|
||||
expect(overlay).toHaveAttribute(attributeName, options.attributes[attributeName])
|
||||
}
|
||||
break
|
||||
|
||||
case DialogState.Visible:
|
||||
if (overlay === null) return expect(overlay).not.toBe(null)
|
||||
|
||||
assertVisible(overlay)
|
||||
|
||||
if (options.textContent) expect(overlay).toHaveTextContent(options.textContent)
|
||||
|
||||
for (let attributeName in options.attributes) {
|
||||
expect(overlay).toHaveAttribute(attributeName, options.attributes[attributeName])
|
||||
}
|
||||
break
|
||||
|
||||
case DialogState.InvisibleUnmounted:
|
||||
expect(overlay).toBe(null)
|
||||
break
|
||||
|
||||
default:
|
||||
assertNever(options.state)
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof Error) Error.captureStackTrace(err, assertDialogOverlay)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
export function getRadioGroup(): HTMLElement | null {
|
||||
|
||||
@@ -21,8 +21,8 @@ function Nested({ onClose, level = 0 }) {
|
||||
return (
|
||||
<>
|
||||
<Dialog open={true} onClose={onClose} className="fixed inset-0 z-10">
|
||||
<Dialog.Overlay className="fixed inset-0 bg-gray-500 opacity-25" />
|
||||
<div
|
||||
<div className="fixed inset-0 bg-gray-500 opacity-25" />
|
||||
<Dialog.Panel
|
||||
className="fixed left-12 top-24 z-10 w-96 bg-white p-4"
|
||||
style={{
|
||||
transform: `translate(calc(50px * ${level}), calc(50px * ${level}))`,
|
||||
@@ -34,7 +34,7 @@ function Nested({ onClose, level = 0 }) {
|
||||
<Button onClick={() => setShowChild(true)}>Open (2)</Button>
|
||||
<Button onClick={() => setShowChild(true)}>Open (3)</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
{showChild && <Nested onClose={() => setShowChild(false)} level={level + 1} />}
|
||||
</Dialog>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user