feat(web): add workspace-aware session flow
This commit is contained in:
@@ -170,6 +170,71 @@ describe('Menu', () => {
|
||||
fireEvent.mouseLeave(wrap)
|
||||
expect(screen.queryByRole('menuitem', { name: 'Create ok' })).toBeNull()
|
||||
})
|
||||
|
||||
it('portal mode prefers getAnchorRect over measuring its own wrapper', () => {
|
||||
const rect = { left: 40, right: 72, top: 100, bottom: 128, width: 32, height: 28, x: 40, y: 100, toJSON: () => ({}) } as DOMRect
|
||||
render(
|
||||
<Menu
|
||||
portal
|
||||
open
|
||||
getAnchorRect={() => rect}
|
||||
anchor={null}
|
||||
items={items}
|
||||
onSelect={() => {}}
|
||||
onClose={() => {}}
|
||||
/>)
|
||||
const menu = screen.getByRole('menu')
|
||||
// side=bottom, align=start: below the host-supplied rect, left-aligned.
|
||||
expect(menu.style.left).toBe('40px')
|
||||
expect(menu.style.top).toBe('132px')
|
||||
})
|
||||
|
||||
it('portal mode skips the frame when getAnchorRect returns null (no menu until a rect exists)', () => {
|
||||
render(
|
||||
<Menu
|
||||
portal
|
||||
open
|
||||
getAnchorRect={() => null}
|
||||
anchor={null}
|
||||
items={items}
|
||||
onSelect={() => {}}
|
||||
onClose={() => {}}
|
||||
/>)
|
||||
expect(screen.queryByRole('menu')).toBeNull()
|
||||
})
|
||||
|
||||
it('portal mode renders the list under body, positions it fixed, and still closes on outside pointerdown', () => {
|
||||
const onSelect = vi.fn()
|
||||
const onClose = vi.fn()
|
||||
const { container } = render(
|
||||
<Menu portal open anchor={<span>trigger</span>} items={items} onSelect={onSelect} onClose={onClose} />)
|
||||
const menu = screen.getByRole('menu')
|
||||
// Outside the anchor wrapper subtree — overflow-clipping ancestors can't crop it.
|
||||
expect(container.contains(menu)).toBe(false)
|
||||
expect(menu.parentElement).toBe(document.body)
|
||||
expect(menu.style.top).not.toBe('')
|
||||
fireEvent.click(screen.getByRole('menuitem', { name: 'Alpha' }))
|
||||
expect(onSelect).toHaveBeenCalledWith('a')
|
||||
fireEvent.pointerDown(menu)
|
||||
expect(onClose).not.toHaveBeenCalled()
|
||||
// Non-Node targets (e.g. window itself) are ignored, not treated as outside.
|
||||
const nonNodeTarget = new Event('pointerdown', { bubbles: true })
|
||||
Object.defineProperty(nonNodeTarget, 'target', { value: window })
|
||||
document.dispatchEvent(nonNodeTarget)
|
||||
expect(onClose).not.toHaveBeenCalled()
|
||||
fireEvent.pointerDown(document.body)
|
||||
expect(onClose).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('portal mode positions from the opposite edges for align=end / side=top', () => {
|
||||
render(
|
||||
<Menu portal open align="end" side="top" anchor={<span>trigger</span>} items={items} onSelect={() => {}} onClose={() => {}} />)
|
||||
const menu = screen.getByRole('menu')
|
||||
expect(menu.style.right).not.toBe('')
|
||||
expect(menu.style.bottom).not.toBe('')
|
||||
expect(menu.style.left).toBe('')
|
||||
expect(menu.style.top).toBe('')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Modal', () => {
|
||||
|
||||
@@ -104,6 +104,26 @@ describe('Tooltip', () => {
|
||||
expect(screen.queryByRole('tooltip')).toBeNull()
|
||||
})
|
||||
|
||||
it('forwards the anchor element to the child ref (object and callback)', () => {
|
||||
const objectRef = { current: null as HTMLButtonElement | null }
|
||||
const callbackRef = vi.fn()
|
||||
const { rerender } = render(
|
||||
<Tooltip label="Add">
|
||||
<button type="button" ref={objectRef}>anchor</button>
|
||||
</Tooltip>,
|
||||
)
|
||||
expect(objectRef.current).toBe(screen.getByText('anchor'))
|
||||
// Tooltip's own positioning still works through the merged ref.
|
||||
fireEvent.mouseEnter(screen.getByText('anchor'))
|
||||
expect(screen.getByRole('tooltip')).toBeTruthy()
|
||||
rerender(
|
||||
<Tooltip label="Add">
|
||||
<button type="button" ref={callbackRef}>anchor</button>
|
||||
</Tooltip>,
|
||||
)
|
||||
expect(callbackRef).toHaveBeenCalledWith(screen.getByText('anchor'))
|
||||
})
|
||||
|
||||
it('drops an already-visible bubble when disabled flips mid-hover', () => {
|
||||
const { rerender } = render(
|
||||
<Tooltip label="Rail">
|
||||
|
||||
Reference in New Issue
Block a user