UNPKG

@mozaic-ds/vue

Version:

Mozaic-Vue is the Vue.js implementation of ADEO Design system

252 lines (208 loc) 7.44 kB
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { ref, type ShallowRef } from 'vue'; import { useFloatingItem } from './use-floating-item.composable'; describe('useFloatingItem', () => { let trigger: ShallowRef<HTMLElement | null>; let floating: ShallowRef<HTMLElement | null>; let triggerEl: HTMLElement; let floatingEl: HTMLElement; let childButton: HTMLButtonElement; beforeEach(() => { triggerEl = document.createElement('button'); floatingEl = document.createElement('div'); childButton = document.createElement('button'); childButton.textContent = 'item'; floatingEl.appendChild(childButton); // start hidden floatingEl.classList.add('mc-sidebar__floating-item--hidden'); // attach to DOM so focus and events behave normally document.body.appendChild(triggerEl); document.body.appendChild(floatingEl); trigger = ref(triggerEl); floating = ref(floatingEl); }); afterEach(() => { vi.restoreAllMocks(); document.body.innerHTML = ''; }); it('showFloatingItem sets position, removes hidden class, sets flag and adds mousedown listener', () => { const { showFloatingItem, floatingItemIsDisplayed } = useFloatingItem( trigger, floating, ); // ensure getBoundingClientRect returns a known top vi.spyOn(triggerEl, 'getBoundingClientRect').mockReturnValue({ top: 42, left: 0, bottom: 0, right: 0, width: 0, height: 0, x: 0, y: 0, toJSON: () => {}, } as unknown as DOMRect); const addSpy = vi.spyOn(document, 'addEventListener'); showFloatingItem(); expect( floatingEl.classList.contains('mc-sidebar__floating-item--hidden'), ).toBe(false); expect(floatingItemIsDisplayed.value).toBe(true); expect(addSpy).toHaveBeenCalledWith('mousedown', expect.any(Function)); }); it('hideFloatingItem adds hidden class, clears flag and removes mousedown listener', () => { const { showFloatingItem, hideFloatingItem, floatingItemIsDisplayed } = useFloatingItem(trigger, floating); // make visible first vi.spyOn(triggerEl, 'getBoundingClientRect').mockReturnValue({ top: 1, left: 0, bottom: 0, right: 0, width: 0, height: 0, x: 0, y: 0, toJSON: () => {}, } as unknown as DOMRect); const removeSpy = vi.spyOn(document, 'removeEventListener'); showFloatingItem(); // sanity expect(floatingItemIsDisplayed.value).toBe(true); expect( floatingEl.classList.contains('mc-sidebar__floating-item--hidden'), ).toBe(false); hideFloatingItem(); expect( floatingEl.classList.contains('mc-sidebar__floating-item--hidden'), ).toBe(true); expect(floatingItemIsDisplayed.value).toBe(false); expect(removeSpy).toHaveBeenCalledWith('mousedown', expect.any(Function)); }); it('hideFloatingItem does not hide when relatedTarget is inside floating or trigger if allowItemHover true', () => { const { showFloatingItem, hideFloatingItem, floatingItemIsDisplayed } = useFloatingItem(trigger, floating, { allowItemHover: true }); vi.spyOn(triggerEl, 'getBoundingClientRect').mockReturnValue({ top: 1, left: 0, bottom: 0, right: 0, width: 0, height: 0, x: 0, y: 0, toJSON: () => {}, } as unknown as DOMRect); showFloatingItem(); expect(floatingItemIsDisplayed.value).toBe(true); // simulate relatedTarget inside floating const fakeEvent = { relatedTarget: childButton, } as unknown as FocusEvent; hideFloatingItem(fakeEvent); expect(floatingItemIsDisplayed.value).toBe(true); expect( floatingEl.classList.contains('mc-sidebar__floating-item--hidden'), ).toBe(false); }); it('clicking outside after showFloatingItem hides the floating item', () => { const result = useFloatingItem(trigger, floating); const { showFloatingItem, floatingItemIsDisplayed } = result; vi.spyOn(triggerEl, 'getBoundingClientRect').mockReturnValue({ top: 5, left: 0, bottom: 0, right: 0, width: 0, height: 0, x: 0, y: 0, toJSON: () => {}, } as unknown as DOMRect); showFloatingItem(); expect(floatingItemIsDisplayed.value).toBe(true); // Dispatch mousedown on an outside element const outside = document.createElement('div'); document.body.appendChild(outside); const event = new MouseEvent('mousedown', { bubbles: true }); // dispatch on outside so event.target is outside outside.dispatchEvent(event); // after event loop, listener should have executed synchronously expect(floatingItemIsDisplayed.value).toBe(false); expect( floatingEl.classList.contains('mc-sidebar__floating-item--hidden'), ).toBe(true); }); it('onTriggerKeydown opens on ArrowDown/Enter/Space and focuses first listbox item', () => { const { onTriggerKeydown, floatingItemIsDisplayed } = useFloatingItem( trigger, floating, ); // ensure getBoundingClientRect returns value so showFloatingItem proceeds vi.spyOn(triggerEl, 'getBoundingClientRect').mockReturnValue({ top: 2, left: 0, bottom: 0, right: 0, width: 0, height: 0, x: 0, y: 0, toJSON: () => {}, } as unknown as DOMRect); const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowDown' }); const preventSpy = vi.spyOn(keyEvent, 'preventDefault'); const focusSpy = vi.spyOn(childButton, 'focus'); onTriggerKeydown(keyEvent); expect(preventSpy).toHaveBeenCalled(); expect(floatingItemIsDisplayed.value).toBe(true); expect(focusSpy).toHaveBeenCalled(); }); it('onTriggerKeydown with Tab hides the floating item', () => { const { onTriggerKeydown, showFloatingItem, floatingItemIsDisplayed } = useFloatingItem(trigger, floating); vi.spyOn(triggerEl, 'getBoundingClientRect').mockReturnValue({ top: 3, left: 0, bottom: 0, right: 0, width: 0, height: 0, x: 0, y: 0, toJSON: () => {}, } as unknown as DOMRect); showFloatingItem(); expect(floatingItemIsDisplayed.value).toBe(true); const tabEvent = new KeyboardEvent('keydown', { key: 'Tab' }); onTriggerKeydown(tabEvent); expect(floatingItemIsDisplayed.value).toBe(false); expect( floatingEl.classList.contains('mc-sidebar__floating-item--hidden'), ).toBe(true); }); it('onListboxKeydown Escape hides and focuses trigger', () => { const { onListboxKeydown, showFloatingItem, floatingItemIsDisplayed } = useFloatingItem(trigger, floating); vi.spyOn(triggerEl, 'getBoundingClientRect').mockReturnValue({ top: 6, left: 0, bottom: 0, right: 0, width: 0, height: 0, x: 0, y: 0, toJSON: () => {}, } as unknown as DOMRect); const triggerFocusSpy = vi.spyOn(triggerEl, 'focus'); showFloatingItem(); expect(floatingItemIsDisplayed.value).toBe(true); const escEvent = new KeyboardEvent('keydown', { key: 'Escape' }); const preventSpy = vi.spyOn(escEvent, 'preventDefault'); onListboxKeydown(escEvent); expect(preventSpy).toHaveBeenCalled(); expect(floatingItemIsDisplayed.value).toBe(false); expect(triggerFocusSpy).toHaveBeenCalled(); }); });