UNPKG

@oslokommune/punkt-elements

Version:

Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo

350 lines (270 loc) 12.9 kB
import '@testing-library/jest-dom' import { fireEvent } from '@testing-library/dom' import './datepicker' import '../calendar/calendar' import { PktDatepicker } from './datepicker' import { PktDatepickerPopup } from './datepicker-popup' const waitForCustomElements = async () => { await customElements.whenDefined('pkt-datepicker') await customElements.whenDefined('pkt-calendar') } // Helper function to create datepicker markup const createDatepicker = async (datepickerProps = '') => { const container = document.createElement('div') container.innerHTML = ` <pkt-datepicker ${datepickerProps}></pkt-datepicker> ` document.body.appendChild(container) await waitForCustomElements() return container } // Cleanup after each test afterEach(() => { document.body.innerHTML = '' }) describe('PktDatepicker', () => { describe('Multiple date selection', () => { test('displays multiple selected dates as tags', async () => { const multipleDates = '2024-06-15,2024-06-20,2024-06-25' const container = await createDatepicker(`value="${multipleDates}" multiple`) const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker await datepicker.updateComplete const tags = datepicker.querySelectorAll('pkt-tag') expect(tags.length).toBe(3) }) test('allows adding dates through calendar in multiple mode', async () => { const container = await createDatepicker('multiple calendar-open') const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker await datepicker.updateComplete const popup = datepicker.querySelector('pkt-datepicker-popup') as PktDatepickerPopup await popup?.updateComplete const calendar = datepicker.querySelector('pkt-calendar') await (calendar as any)?.updateComplete // Select a date const availableDate = datepicker.querySelector('[data-date]:not([data-disabled="disabled"])') if (availableDate) { fireEvent.click(availableDate) await datepicker.updateComplete // Should add tag const tags = datepicker.querySelectorAll('pkt-tag') expect(tags.length).toBe(1) } }) test('removes dates when clicking tag close button', async () => { const multipleDates = '2024-06-15,2024-06-20' const container = await createDatepicker(`value="${multipleDates}" multiple`) const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker await datepicker.updateComplete const closeButtons = datepicker.querySelectorAll('pkt-tag .pkt-tag__close-btn') expect(closeButtons.length).toBe(2) // Click first close button fireEvent.click(closeButtons[0]) await datepicker.updateComplete const remainingTags = datepicker.querySelectorAll('pkt-tag') expect(remainingTags.length).toBe(1) }) test('respects maxlength in multiple mode', async () => { const container = await createDatepicker('multiple maxlength="2"') const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker await datepicker.updateComplete // Open calendar and try to select more than maxlength dates const calendarButton = datepicker.querySelector('button[type="button"]') fireEvent.click(calendarButton!) await datepicker.updateComplete const availableDates = datepicker.querySelectorAll( '.pkt-calendar__date:not(.pkt-calendar__date--disabled)', ) // Select 3 dates fireEvent.click(availableDates[0]) await datepicker.updateComplete fireEvent.click(availableDates[1]) await datepicker.updateComplete fireEvent.click(availableDates[2]) await datepicker.updateComplete // Should only have maxlength tags const tags = datepicker.querySelectorAll('pkt-tag') expect(tags.length).toBeLessThanOrEqual(2) }) test('sorts multiple dates chronologically', async () => { const unsortedDates = '2024-06-25,2024-06-15,2024-06-20' const container = await createDatepicker(`value="${unsortedDates}" multiple`) const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker await datepicker.updateComplete const tags = datepicker.querySelectorAll('pkt-tag') const tagTexts = Array.from(tags).map((tag) => tag.textContent?.trim()) // Should be sorted chronologically expect(tagTexts[0]).toContain('15') expect(tagTexts[1]).toContain('20') expect(tagTexts[2]).toContain('25') }) test('sorts multiple dates chronologically across months and years', async () => { // Test dates that would fail with simple string sorting const complexUnsortedDates = '2024-12-01,2023-01-15,2024-01-01,2023-12-31' const container = await createDatepicker(`value="${complexUnsortedDates}" multiple`) const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker await datepicker.updateComplete const tags = datepicker.querySelectorAll('pkt-tag') const tagTexts = Array.from(tags).map((tag) => tag.querySelector('time')?.getAttribute('datetime'), ) // Should be sorted chronologically: 2023-01-15, 2023-12-31, 2024-01-01, 2024-12-01 expect(tagTexts[0]).toBe('2023-01-15') expect(tagTexts[1]).toBe('2023-12-31') expect(tagTexts[2]).toBe('2024-01-01') expect(tagTexts[3]).toBe('2024-12-01') }) }) describe('Range selection', () => { test('displays range labels when showRangeLabels is true', async () => { const rangeValue = '2024-06-15,2024-06-20' const container = await createDatepicker(`value="${rangeValue}" range show-range-labels`) const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker await datepicker.updateComplete expect(datepicker.showRangeLabels).toBe(true) const rangeLabels = datepicker.querySelectorAll('.pkt-input-prefix') expect(rangeLabels.length).toBeGreaterThan(0) }) test('populates both input fields when initialized with range value', async () => { const rangeValue = '2024-06-15,2024-06-20' const container = await createDatepicker(`value="${rangeValue}" range`) const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker await datepicker.updateComplete const inputs = datepicker.querySelectorAll('input') expect(inputs.length).toBe(2) // Check that both input fields are populated expect(inputs[0].value).toBe('2024-06-15') expect(inputs[1].value).toBe('2024-06-20') // Check internal state expect(datepicker._value).toEqual(['2024-06-15', '2024-06-20']) expect(datepicker.value).toBe('2024-06-15,2024-06-20') }) test('dispatches value-change event with array for range and multiple datepickers', async () => { // Test range datepicker const rangeContainer = await createDatepicker('range') const rangeDatepicker = rangeContainer.querySelector('pkt-datepicker') as PktDatepicker await rangeDatepicker.updateComplete let valueChangeEvent: CustomEvent | null = null rangeDatepicker.addEventListener('value-change', (e: Event) => { valueChangeEvent = e as CustomEvent }) // Set a range value programmatically rangeDatepicker.value = '2024-06-15,2024-06-20' await rangeDatepicker.updateComplete expect(valueChangeEvent).toBeTruthy() expect(valueChangeEvent!.detail).toEqual(['2024-06-15', '2024-06-20']) // Test multiple datepicker const multipleContainer = await createDatepicker('multiple') const multipleDatepicker = multipleContainer.querySelector('pkt-datepicker') as PktDatepicker await multipleDatepicker.updateComplete valueChangeEvent = null multipleDatepicker.addEventListener('value-change', (e: Event) => { valueChangeEvent = e as CustomEvent }) // Set multiple values programmatically multipleDatepicker.value = '2024-06-15,2024-06-20,2024-06-25' await multipleDatepicker.updateComplete expect(valueChangeEvent).toBeTruthy() expect(valueChangeEvent!.detail).toEqual(['2024-06-15', '2024-06-20', '2024-06-25']) // Test single datepicker const singleContainer = await createDatepicker('') const singleDatepicker = singleContainer.querySelector('pkt-datepicker') as PktDatepicker await singleDatepicker.updateComplete valueChangeEvent = null singleDatepicker.addEventListener('value-change', (e: Event) => { valueChangeEvent = e as CustomEvent }) // Set single value programmatically singleDatepicker.value = '2024-06-15' await singleDatepicker.updateComplete expect(valueChangeEvent).toBeTruthy() expect(valueChangeEvent!.detail).toBe('2024-06-15') }) test('handles range selection through calendar', async () => { const container = await createDatepicker('range') const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker await datepicker.updateComplete // Open calendar const calendarButton = datepicker.querySelector('button[type="button"]') fireEvent.click(calendarButton!) await datepicker.updateComplete const availableDates = datepicker.querySelectorAll( '[data-date]:not([data-disabled="disabled"])', ) // Select start date if (availableDates.length > 5) { fireEvent.click(availableDates[5]) await datepicker.updateComplete // Select end date if (availableDates.length > 10) { fireEvent.click(availableDates[10]) await datepicker.updateComplete // Should have range value expect(datepicker.value).toContain(',') const values = (datepicker.value as string).split(',') expect(values.length).toBe(2) } } }) test('validates range order', async () => { const invalidRange = '2024-06-20,2024-06-15' // End before start const container = await createDatepicker(`value="${invalidRange}" range`) const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker await datepicker.updateComplete // Should reject invalid range and clear the value expect(datepicker.value).toBe('') expect(datepicker._value).toEqual([]) // Component should still render without errors expect(datepicker).toBeInTheDocument() // Test with valid range should work datepicker.value = '2024-06-15,2024-06-20' await datepicker.updateComplete expect(datepicker.value).toBe('2024-06-15,2024-06-20') expect(datepicker._value).toEqual(['2024-06-15', '2024-06-20']) }) test('handles range validation edge cases', async () => { const container = await createDatepicker('range') const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker await datepicker.updateComplete // Same start and end date should be valid datepicker.value = '2024-06-15,2024-06-15' await datepicker.updateComplete expect(datepicker.value).toBe('2024-06-15,2024-06-15') // Single date should be valid (incomplete range) datepicker.value = '2024-06-15' await datepicker.updateComplete expect(datepicker.value).toBe('2024-06-15') // Empty value should be valid datepicker.value = '' await datepicker.updateComplete expect(datepicker.value).toBe('') // Multiple invalid attempts should be consistently rejected datepicker.value = '2024-06-25,2024-06-10' await datepicker.updateComplete expect(datepicker.value).toBe('') datepicker.value = '2024-12-31,2024-01-01' await datepicker.updateComplete expect(datepicker.value).toBe('') }) test('shows range preview on hover', async () => { const container = await createDatepicker('range') const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker await datepicker.updateComplete // Open calendar const calendarButton = datepicker.querySelector('button[type="button"]') fireEvent.click(calendarButton!) await datepicker.updateComplete const availableDates = datepicker.querySelectorAll( '.pkt-calendar__date:not(.pkt-calendar__date--disabled)', ) // Select start date fireEvent.click(availableDates[5]) await datepicker.updateComplete // Hover over potential end date fireEvent.mouseOver(availableDates[10]) await datepicker.updateComplete // Should show hover preview const hoverRanges = datepicker.querySelectorAll('.pkt-calendar__date--in-range-hover') expect(hoverRanges.length).toBeGreaterThan(0) }) }) })