Template
fix(SearchPalette): return null while closed
The Overlay refactor dropped the early return, so the fixed-inset backdrop stayed mounted permanently: the palette covered the page and swallowed every click. Restore the guard and cover it with tests.
This commit is contained in:
1 parent
f6e81afe5e
commit
72ff61f94d
2 files changed
+44
No files matched your search
@@ -197,6 +197,11 @@ export function SearchPalette({ isOpen, onClose, onSelect, initialQuery = '' }:
|
|||||||
trapTabKey(e, panelRef.current)
|
trapTabKey(e, panelRef.current)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Closed palette must not render at all: the backdrop is `position: fixed;
|
||||||
|
inset: 0`, so rendering it unconditionally left the modal permanently on
|
||||||
|
screen (and ate every click on the page underneath). */
|
||||||
|
if (!isOpen) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="search-palette-backdrop" onClick={onClose}>
|
<div className="search-palette-backdrop" onClick={onClose}>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useState } from 'react'
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { Dialog, DialogContent, DialogDescription, DialogTitle } from '../src/components/ui/Dialog'
|
import { Dialog, DialogContent, DialogDescription, DialogTitle } from '../src/components/ui/Dialog'
|
||||||
import { Sheet, SheetClose, SheetContent, SheetTitle } from '../src/components/ui/Sheet'
|
import { Sheet, SheetClose, SheetContent, SheetTitle } from '../src/components/ui/Sheet'
|
||||||
|
import { SearchPalette } from '../src/components/SearchPalette'
|
||||||
|
|
||||||
describe('Dialog (Overlay primitive)', () => {
|
describe('Dialog (Overlay primitive)', () => {
|
||||||
it('renders a modal dialog with the panel focused when open', () => {
|
it('renders a modal dialog with the panel focused when open', () => {
|
||||||
@@ -168,3 +169,41 @@ describe('Sheet (Overlay skin)', () => {
|
|||||||
expect(last).toHaveFocus()
|
expect(last).toHaveFocus()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('SearchPalette (Overlay behavior)', () => {
|
||||||
|
it('renders nothing while closed and no backdrop is left over the page', () => {
|
||||||
|
const { rerender } = render(
|
||||||
|
<SearchPalette isOpen={false} onClose={() => {}} onSelect={() => {}} />,
|
||||||
|
)
|
||||||
|
expect(screen.queryByRole('dialog')).toBeNull()
|
||||||
|
expect(document.querySelector('.search-palette-backdrop')).toBeNull()
|
||||||
|
expect(document.body.style.overflow).toBe('')
|
||||||
|
|
||||||
|
rerender(<SearchPalette isOpen onClose={() => {}} onSelect={() => {}} />)
|
||||||
|
expect(screen.getByRole('dialog')).toHaveClass('search-palette-modal')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('closes on Escape / backdrop click and unlocks body scroll', () => {
|
||||||
|
function Controlled() {
|
||||||
|
const [open, setOpen] = useState(true)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button onClick={() => setOpen(true)}>Open</button>
|
||||||
|
<SearchPalette isOpen={open} onClose={() => setOpen(false)} onSelect={() => {}} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const { container } = render(<Controlled />)
|
||||||
|
expect(document.body.style.overflow).toBe('hidden')
|
||||||
|
|
||||||
|
fireEvent.keyDown(window, { key: 'Escape' })
|
||||||
|
expect(screen.queryByRole('dialog')).toBeNull()
|
||||||
|
expect(document.body.style.overflow).toBe('')
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: 'Open' }))
|
||||||
|
expect(screen.getByRole('dialog')).toBeInTheDocument()
|
||||||
|
fireEvent.click(container.querySelector('.search-palette-backdrop')!)
|
||||||
|
expect(screen.queryByRole('dialog')).toBeNull()
|
||||||
|
expect(document.body.style.overflow).toBe('')
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in new issue
Block a user