import { mount } from '@vue/test-utils' import { afterEach, describe, expect, it } from 'vitest' import { nextTick } from 'vue' import DatePicker from '../../components/DatePicker.vue' function dropdown(): HTMLElement | null { return document.body.querySelector('.mint-date-picker__dropdown') } describe('DatePicker', () => { afterEach(() => { document.body.innerHTML = '' }) it('uses shared dropdown state with teleported calendar content', async () => { const wrapper = mount(DatePicker, { props: { modelValue: '2026-04-22', }, attachTo: document.body, }) await wrapper.get('input').trigger('click') expect(dropdown()).not.toBeNull() document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })) await nextTick() expect(dropdown()).toBeNull() await wrapper.get('input').trigger('click') expect(dropdown()).not.toBeNull() const nextButton = document.body.querySelector('[aria-label="Next month"]') expect(nextButton).not.toBeNull() nextButton?.dispatchEvent(new MouseEvent('click', { bubbles: true })) await nextTick() expect(dropdown()).not.toBeNull() document.body.dispatchEvent(new MouseEvent('click', { bubbles: true })) await nextTick() expect(dropdown()).toBeNull() wrapper.unmount() }) it('opens from the input with Enter, Space and ArrowDown', async () => { const wrapper = mount(DatePicker, { attachTo: document.body }) for (const key of ['Enter', ' ', 'ArrowDown']) { await wrapper.get('input').trigger('keydown', { key }) expect(dropdown()).not.toBeNull() document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })) await nextTick() expect(dropdown()).toBeNull() } wrapper.unmount() }) it('moves focus into the calendar grid and back to the input on close', async () => { const wrapper = mount(DatePicker, { props: { modelValue: '2026-04-22' }, attachTo: document.body, }) const input = wrapper.get('input').element await wrapper.get('input').trigger('click') await nextTick() await nextTick() await nextTick() expect(document.activeElement).toBe( document.body.querySelector('.mint-date-picker__day--selected'), ) document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })) await nextTick() await nextTick() expect(document.activeElement).toBe(input) wrapper.unmount() }) it('marks the panel as a non-modal dialog', async () => { const wrapper = mount(DatePicker, { attachTo: document.body }) await wrapper.get('input').trigger('click') const panel = dropdown() expect(panel?.getAttribute('role')).toBe('dialog') expect(panel?.hasAttribute('aria-modal')).toBe(false) expect(wrapper.get('input').attributes('aria-haspopup')).toBe('dialog') expect(wrapper.get('input').attributes('aria-expanded')).toBe('true') wrapper.unmount() }) it('keeps the panel open while the page scrolls, repositioning instead', async () => { const wrapper = mount(DatePicker, { attachTo: document.body }) await wrapper.get('input').trigger('click') window.dispatchEvent(new Event('scroll')) await nextTick() expect(dropdown()).not.toBeNull() wrapper.unmount() }) it('does not open from the keyboard while disabled', async () => { const wrapper = mount(DatePicker, { props: { disabled: true }, attachTo: document.body }) await wrapper.get('input').trigger('keydown', { key: 'Enter' }) expect(dropdown()).toBeNull() wrapper.unmount() }) it('should render the calendar on the shared popover surface when opened', async () => { const wrapper = mount(DatePicker, { attachTo: document.body }) await wrapper.get('input').trigger('click') expect(dropdown()?.classList.contains('mint-popover')).toBe(true) wrapper.unmount() }) })