960 lines
29 KiB
TypeScript
960 lines
29 KiB
TypeScript
import { render } from '@testing-library/react'
|
|
import React, { createElement, Suspense, useEffect, useRef } from 'react'
|
|
import {
|
|
assertActiveElement,
|
|
assertDisclosureButton,
|
|
assertDisclosurePanel,
|
|
DisclosureState,
|
|
getByText,
|
|
getDisclosureButton,
|
|
getDisclosurePanel,
|
|
} from '../../test-utils/accessibility-assertions'
|
|
import { click, focus, Keys, MouseButton, press } from '../../test-utils/interactions'
|
|
import { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'
|
|
import { Transition } from '../transitions/transition'
|
|
import { Disclosure } from './disclosure'
|
|
|
|
jest.mock('../../hooks/use-id')
|
|
|
|
afterAll(() => jest.restoreAllMocks())
|
|
|
|
function nextFrame() {
|
|
return new Promise<void>((resolve) => {
|
|
requestAnimationFrame(() => {
|
|
requestAnimationFrame(() => {
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
describe('Safe guards', () => {
|
|
it.each([
|
|
['Disclosure.Button', Disclosure.Button],
|
|
['Disclosure.Panel', Disclosure.Panel],
|
|
])(
|
|
'should error when we are using a <%s /> without a parent <Disclosure />',
|
|
suppressConsoleLogs((name, Component) => {
|
|
expect(() => render(createElement<typeof Component>(Component))).toThrowError(
|
|
`<${name} /> is missing a parent <Disclosure /> component.`
|
|
)
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should be possible to render a Disclosure without crashing',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Contents</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
})
|
|
)
|
|
})
|
|
|
|
describe('Rendering', () => {
|
|
describe('Disclosure', () => {
|
|
it(
|
|
'should be possible to render a Disclosure using a render prop',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
{({ open }) => (
|
|
<>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Panel is: {open ? 'open' : 'closed'}</Disclosure.Panel>
|
|
</>
|
|
)}
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
await click(getDisclosureButton())
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.Visible,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.Visible, textContent: 'Panel is: open' })
|
|
})
|
|
)
|
|
|
|
it('should be possible to render a Disclosure in an open state by default', async () => {
|
|
render(
|
|
<Disclosure defaultOpen>
|
|
{({ open }) => (
|
|
<>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Panel is: {open ? 'open' : 'closed'}</Disclosure.Panel>
|
|
</>
|
|
)}
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.Visible,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.Visible, textContent: 'Panel is: open' })
|
|
|
|
await click(getDisclosureButton())
|
|
|
|
assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })
|
|
})
|
|
|
|
it(
|
|
'should expose a close function that closes the disclosure',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
{({ close }) => (
|
|
<>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>
|
|
<button onClick={() => close()}>Close me</button>
|
|
</Disclosure.Panel>
|
|
</>
|
|
)}
|
|
</Disclosure>
|
|
)
|
|
|
|
// Focus the button
|
|
await focus(getDisclosureButton())
|
|
|
|
// Ensure the button is focused
|
|
assertActiveElement(getDisclosureButton())
|
|
|
|
// Open the disclosure
|
|
await click(getDisclosureButton())
|
|
|
|
// Ensure we can click the close button
|
|
await click(getByText('Close me'))
|
|
|
|
// Ensure the disclosure is closed
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Ensure the Disclosure.Button got the restored focus
|
|
assertActiveElement(getByText('Trigger'))
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should expose a close function that closes the disclosure and restores to a specific element',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<>
|
|
<button id="test">restoreable</button>
|
|
<Disclosure>
|
|
{({ close }) => (
|
|
<>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>
|
|
<button onClick={() => close(document.getElementById('test')!)}>
|
|
Close me
|
|
</button>
|
|
</Disclosure.Panel>
|
|
</>
|
|
)}
|
|
</Disclosure>
|
|
</>
|
|
)
|
|
|
|
// Focus the button
|
|
await focus(getDisclosureButton())
|
|
|
|
// Ensure the button is focused
|
|
assertActiveElement(getDisclosureButton())
|
|
|
|
// Open the disclosure
|
|
await click(getDisclosureButton())
|
|
|
|
// Ensure we can click the close button
|
|
await click(getByText('Close me'))
|
|
|
|
// Ensure the disclosure is closed
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Ensure the restoreable button got the restored focus
|
|
assertActiveElement(getByText('restoreable'))
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should expose a close function that closes the disclosure and restores to a ref',
|
|
suppressConsoleLogs(async () => {
|
|
function Example() {
|
|
let elementRef = useRef(null)
|
|
return (
|
|
<>
|
|
<button ref={elementRef}>restoreable</button>
|
|
<Disclosure>
|
|
{({ close }) => (
|
|
<>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>
|
|
<button onClick={() => close(elementRef)}>Close me</button>
|
|
</Disclosure.Panel>
|
|
</>
|
|
)}
|
|
</Disclosure>
|
|
</>
|
|
)
|
|
}
|
|
|
|
render(<Example />)
|
|
|
|
// Focus the button
|
|
await focus(getDisclosureButton())
|
|
|
|
// Ensure the button is focused
|
|
assertActiveElement(getDisclosureButton())
|
|
|
|
// Open the disclosure
|
|
await click(getDisclosureButton())
|
|
|
|
// Ensure we can click the close button
|
|
await click(getByText('Close me'))
|
|
|
|
// Ensure the disclosure is closed
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Ensure the restoreable button got the restored focus
|
|
assertActiveElement(getByText('restoreable'))
|
|
})
|
|
)
|
|
|
|
it('should not crash when using Suspense boundaries', async () => {
|
|
render(
|
|
<Disclosure defaultOpen={true}>
|
|
<Disclosure.Button>Click me!</Disclosure.Button>
|
|
<Disclosure.Panel>
|
|
<Suspense fallback={null}>
|
|
<p>Hi there</p>
|
|
</Suspense>
|
|
</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('Disclosure.Button', () => {
|
|
it(
|
|
'should be possible to render a Disclosure.Button using a render prop',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>{JSON.stringify}</Disclosure.Button>
|
|
<Disclosure.Panel></Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
textContent: JSON.stringify({ open: false }),
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
await click(getDisclosureButton())
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.Visible,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
textContent: JSON.stringify({ open: true }),
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.Visible })
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should be possible to render a Disclosure.Button using a render prop and an `as` prop',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button as="div" role="button">
|
|
{JSON.stringify}
|
|
</Disclosure.Button>
|
|
<Disclosure.Panel />
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
textContent: JSON.stringify({ open: false }),
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
await click(getDisclosureButton())
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.Visible,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
textContent: JSON.stringify({ open: true }),
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.Visible })
|
|
})
|
|
)
|
|
|
|
describe('`type` attribute', () => {
|
|
it('should set the `type` to "button" by default', async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
</Disclosure>
|
|
)
|
|
|
|
expect(getDisclosureButton()).toHaveAttribute('type', 'button')
|
|
})
|
|
|
|
it('should not set the `type` to "button" if it already contains a `type`', async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button type="submit">Trigger</Disclosure.Button>
|
|
</Disclosure>
|
|
)
|
|
|
|
expect(getDisclosureButton()).toHaveAttribute('type', 'submit')
|
|
})
|
|
|
|
it('should set the `type` to "button" when using the `as` prop which resolves to a "button"', async () => {
|
|
let CustomButton = React.forwardRef<HTMLButtonElement>((props, ref) => (
|
|
<button ref={ref} {...props} />
|
|
))
|
|
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button as={CustomButton}>Trigger</Disclosure.Button>
|
|
</Disclosure>
|
|
)
|
|
|
|
expect(getDisclosureButton()).toHaveAttribute('type', 'button')
|
|
})
|
|
|
|
it('should not set the type if the "as" prop is not a "button"', async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button as="div">Trigger</Disclosure.Button>
|
|
</Disclosure>
|
|
)
|
|
|
|
expect(getDisclosureButton()).not.toHaveAttribute('type')
|
|
})
|
|
|
|
it('should not set the `type` to "button" when using the `as` prop which resolves to a "div"', async () => {
|
|
let CustomButton = React.forwardRef<HTMLDivElement>((props, ref) => (
|
|
<div ref={ref} {...props} />
|
|
))
|
|
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button as={CustomButton}>Trigger</Disclosure.Button>
|
|
</Disclosure>
|
|
)
|
|
|
|
expect(getDisclosureButton()).not.toHaveAttribute('type')
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('Disclosure.Panel', () => {
|
|
it(
|
|
'should be possible to render Disclosure.Panel using a render prop',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>{JSON.stringify}</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
await click(getDisclosureButton())
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.Visible,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({
|
|
state: DisclosureState.Visible,
|
|
textContent: JSON.stringify({ open: true }),
|
|
})
|
|
})
|
|
)
|
|
|
|
it('should be possible to always render the Disclosure.Panel if we provide it a `static` prop', () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel static>Contents</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
// Let's verify that the Disclosure is already there
|
|
expect(getDisclosurePanel()).not.toBe(null)
|
|
})
|
|
|
|
it('should be possible to use a different render strategy for the Disclosure.Panel', async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel unmount={false}>Contents</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({ state: DisclosureState.InvisibleHidden })
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleHidden })
|
|
|
|
// Let's open the Disclosure, to see if it is not hidden anymore
|
|
await click(getDisclosureButton())
|
|
|
|
assertDisclosureButton({ state: DisclosureState.Visible })
|
|
assertDisclosurePanel({ state: DisclosureState.Visible })
|
|
|
|
// Let's re-click the Disclosure, to see if it is hidden again
|
|
await click(getDisclosureButton())
|
|
|
|
assertDisclosureButton({ state: DisclosureState.InvisibleHidden })
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleHidden })
|
|
})
|
|
|
|
it(
|
|
'should expose a close function that closes the disclosure',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>
|
|
{({ close }) => <button onClick={() => close()}>Close me</button>}
|
|
</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
// Focus the button
|
|
await focus(getDisclosureButton())
|
|
|
|
// Ensure the button is focused
|
|
assertActiveElement(getDisclosureButton())
|
|
|
|
// Open the disclosure
|
|
await click(getDisclosureButton())
|
|
|
|
// Ensure we can click the close button
|
|
await click(getByText('Close me'))
|
|
|
|
// Ensure the disclosure is closed
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Ensure the Disclosure.Button got the restored focus
|
|
assertActiveElement(getByText('Trigger'))
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should expose a close function that closes the disclosure and restores to a specific element',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<>
|
|
<button id="test">restoreable</button>
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>
|
|
{({ close }) => (
|
|
<button onClick={() => close(document.getElementById('test')!)}>Close me</button>
|
|
)}
|
|
</Disclosure.Panel>
|
|
</Disclosure>
|
|
</>
|
|
)
|
|
|
|
// Focus the button
|
|
await focus(getDisclosureButton())
|
|
|
|
// Ensure the button is focused
|
|
assertActiveElement(getDisclosureButton())
|
|
|
|
// Open the disclosure
|
|
await click(getDisclosureButton())
|
|
|
|
// Ensure we can click the close button
|
|
await click(getByText('Close me'))
|
|
|
|
// Ensure the disclosure is closed
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Ensure the restoreable button got the restored focus
|
|
assertActiveElement(getByText('restoreable'))
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should expose a close function that closes the disclosure and restores to a ref',
|
|
suppressConsoleLogs(async () => {
|
|
function Example() {
|
|
let elementRef = useRef(null)
|
|
return (
|
|
<>
|
|
<button ref={elementRef}>restoreable</button>
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>
|
|
{({ close }) => <button onClick={() => close(elementRef)}>Close me</button>}
|
|
</Disclosure.Panel>
|
|
</Disclosure>
|
|
</>
|
|
)
|
|
}
|
|
|
|
render(<Example />)
|
|
|
|
// Focus the button
|
|
await focus(getDisclosureButton())
|
|
|
|
// Ensure the button is focused
|
|
assertActiveElement(getDisclosureButton())
|
|
|
|
// Open the disclosure
|
|
await click(getDisclosureButton())
|
|
|
|
// Ensure we can click the close button
|
|
await click(getByText('Close me'))
|
|
|
|
// Ensure the disclosure is closed
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Ensure the restoreable button got the restored focus
|
|
assertActiveElement(getByText('restoreable'))
|
|
})
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('Composition', () => {
|
|
function Debug({ fn, name }: { fn: (text: string) => void; name: string }) {
|
|
useEffect(() => {
|
|
fn(`Mounting - ${name}`)
|
|
return () => {
|
|
fn(`Unmounting - ${name}`)
|
|
}
|
|
}, [fn, name])
|
|
return null
|
|
}
|
|
|
|
it(
|
|
'should be possible to control the Disclosure.Panel by wrapping it in a Transition component',
|
|
suppressConsoleLogs(async () => {
|
|
let orderFn = jest.fn()
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Debug name="Disclosure" fn={orderFn} />
|
|
<Transition>
|
|
<Debug name="Transition" fn={orderFn} />
|
|
<Disclosure.Panel>
|
|
<Transition.Child>
|
|
<Debug name="Transition.Child" fn={orderFn} />
|
|
</Transition.Child>
|
|
</Disclosure.Panel>
|
|
</Transition>
|
|
</Disclosure>
|
|
)
|
|
|
|
// Verify the Disclosure is hidden
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Open the Disclosure component
|
|
await click(getDisclosureButton())
|
|
|
|
// Verify the Disclosure is visible
|
|
assertDisclosurePanel({ state: DisclosureState.Visible })
|
|
|
|
// Unmount the full tree
|
|
await click(getDisclosureButton())
|
|
|
|
// Wait for all transitions to finish
|
|
await nextFrame()
|
|
await nextFrame()
|
|
|
|
// Verify that we tracked the `mounts` and `unmounts` in the correct order
|
|
expect(orderFn.mock.calls).toEqual([
|
|
['Mounting - Disclosure'],
|
|
['Mounting - Transition'],
|
|
['Mounting - Transition.Child'],
|
|
['Unmounting - Transition'],
|
|
['Unmounting - Transition.Child'],
|
|
])
|
|
})
|
|
)
|
|
})
|
|
|
|
describe('Keyboard interactions', () => {
|
|
describe('`Enter` key', () => {
|
|
it(
|
|
'should be possible to open the Disclosure with Enter',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Contents</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Focus the button
|
|
await focus(getDisclosureButton())
|
|
|
|
// Open disclosure
|
|
await press(Keys.Enter)
|
|
|
|
// Verify it is open
|
|
assertDisclosureButton({ state: DisclosureState.Visible })
|
|
assertDisclosurePanel({
|
|
state: DisclosureState.Visible,
|
|
attributes: { id: 'headlessui-disclosure-panel-2' },
|
|
})
|
|
|
|
// Close disclosure
|
|
await press(Keys.Enter)
|
|
assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should not be possible to open the disclosure with Enter when the button is disabled',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button disabled>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Content</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Focus the button
|
|
await focus(getDisclosureButton())
|
|
|
|
// Try to open the disclosure
|
|
await press(Keys.Enter)
|
|
|
|
// Verify it is still closed
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should be possible to close the disclosure with Enter when the disclosure is open',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Contents</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Focus the button
|
|
await focus(getDisclosureButton())
|
|
|
|
// Open disclosure
|
|
await press(Keys.Enter)
|
|
|
|
// Verify it is open
|
|
assertDisclosureButton({ state: DisclosureState.Visible })
|
|
assertDisclosurePanel({
|
|
state: DisclosureState.Visible,
|
|
attributes: { id: 'headlessui-disclosure-panel-2' },
|
|
})
|
|
|
|
// Close disclosure
|
|
await press(Keys.Enter)
|
|
|
|
// Verify it is closed again
|
|
assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
})
|
|
)
|
|
})
|
|
|
|
describe('`Space` key', () => {
|
|
it(
|
|
'should be possible to open the disclosure with Space',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Contents</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Focus the button
|
|
await focus(getDisclosureButton())
|
|
|
|
// Open disclosure
|
|
await press(Keys.Space)
|
|
|
|
// Verify it is open
|
|
assertDisclosureButton({ state: DisclosureState.Visible })
|
|
assertDisclosurePanel({
|
|
state: DisclosureState.Visible,
|
|
attributes: { id: 'headlessui-disclosure-panel-2' },
|
|
})
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should not be possible to open the disclosure with Space when the button is disabled',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button disabled>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Contents</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Focus the button
|
|
await focus(getDisclosureButton())
|
|
|
|
// Try to open the disclosure
|
|
await press(Keys.Space)
|
|
|
|
// Verify it is still closed
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should be possible to close the disclosure with Space when the disclosure is open',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Contents</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Focus the button
|
|
await focus(getDisclosureButton())
|
|
|
|
// Open disclosure
|
|
await press(Keys.Space)
|
|
|
|
// Verify it is open
|
|
assertDisclosureButton({ state: DisclosureState.Visible })
|
|
assertDisclosurePanel({
|
|
state: DisclosureState.Visible,
|
|
attributes: { id: 'headlessui-disclosure-panel-2' },
|
|
})
|
|
|
|
// Close disclosure
|
|
await press(Keys.Space)
|
|
|
|
// Verify it is closed again
|
|
assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
})
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('Mouse interactions', () => {
|
|
it(
|
|
'should be possible to open a disclosure on click',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Contents</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Open disclosure
|
|
await click(getDisclosureButton())
|
|
|
|
// Verify it is open
|
|
assertDisclosureButton({ state: DisclosureState.Visible })
|
|
assertDisclosurePanel({
|
|
state: DisclosureState.Visible,
|
|
attributes: { id: 'headlessui-disclosure-panel-2' },
|
|
})
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should not be possible to open a disclosure on right click',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Contents</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Open disclosure
|
|
await click(getDisclosureButton(), MouseButton.Right)
|
|
|
|
// Verify it is still closed
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should not be possible to open a disclosure on click when the button is disabled',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button disabled>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Contents</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Try to open the disclosure
|
|
await click(getDisclosureButton())
|
|
|
|
// Verify it is still closed
|
|
assertDisclosureButton({
|
|
state: DisclosureState.InvisibleUnmounted,
|
|
attributes: { id: 'headlessui-disclosure-button-1' },
|
|
})
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should be possible to close a disclosure on click',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Trigger</Disclosure.Button>
|
|
<Disclosure.Panel>Contents</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
// Open disclosure
|
|
await click(getDisclosureButton())
|
|
|
|
// Verify it is open
|
|
assertDisclosureButton({ state: DisclosureState.Visible })
|
|
|
|
// Click to close
|
|
await click(getDisclosureButton())
|
|
|
|
// Verify it is closed
|
|
assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
})
|
|
)
|
|
|
|
it(
|
|
'should be possible to close the Disclosure by clicking on a Disclosure.Button inside a Disclosure.Panel',
|
|
suppressConsoleLogs(async () => {
|
|
render(
|
|
<Disclosure>
|
|
<Disclosure.Button>Open</Disclosure.Button>
|
|
<Disclosure.Panel>
|
|
<Disclosure.Button>Close</Disclosure.Button>
|
|
</Disclosure.Panel>
|
|
</Disclosure>
|
|
)
|
|
|
|
// Open the disclosure
|
|
await click(getDisclosureButton())
|
|
|
|
let closeBtn = getByText('Close')
|
|
|
|
expect(closeBtn).not.toHaveAttribute('id')
|
|
expect(closeBtn).not.toHaveAttribute('aria-controls')
|
|
expect(closeBtn).not.toHaveAttribute('aria-expanded')
|
|
|
|
// The close button should close the disclosure
|
|
await click(closeBtn)
|
|
|
|
// Verify it is closed
|
|
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
|
|
|
|
// Verify we restored the Open button
|
|
assertActiveElement(getDisclosureButton())
|
|
})
|
|
)
|
|
})
|