@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
368 lines (280 loc) • 13.1 kB
text/typescript
import '@testing-library/jest-dom'
import { fireEvent } from '@testing-library/dom'
import { parseISODateString } from 'shared-utils/date-utils'
import './calendar'
import { PktCalendar } from './calendar'
const waitForCustomElements = async () => {
await customElements.whenDefined('pkt-calendar')
}
// Helper function to create calendar markup
const createCalendar = async (calendarProps = '') => {
const container = document.createElement('div')
container.innerHTML = `
<pkt-calendar ${calendarProps}></pkt-calendar>
`
document.body.appendChild(container)
await waitForCustomElements()
return container
}
// Cleanup after each test
afterEach(() => {
document.body.innerHTML = ''
})
describe('PktCalendar', () => {
describe('Rendering and basic functionality', () => {
test('renders without errors', async () => {
const container = await createCalendar()
const calendar = container.querySelector('pkt-calendar') as PktCalendar
expect(calendar).toBeInTheDocument()
await calendar.updateComplete
expect(calendar).toBeTruthy()
})
test('renders with correct structure', async () => {
const container = await createCalendar()
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
const calendarElement = calendar.querySelector('.pkt-calendar')
const calendarTable = calendar.querySelector('.pkt-cal-days')
const monthNav = calendar.querySelector('.pkt-cal-month-nav')
expect(calendarElement).toBeInTheDocument()
expect(calendarTable).toBeInTheDocument()
expect(monthNav).toBeInTheDocument()
})
test('renders month picker when withcontrols is true', async () => {
const container = await createCalendar('withcontrols')
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
// Navigation buttons should always be present
const prevButton = calendar.querySelector('.pkt-calendar__prev-month')
const nextButton = calendar.querySelector('.pkt-calendar__next-month')
expect(prevButton).toBeInTheDocument()
expect(nextButton).toBeInTheDocument()
// Should show month picker controls, not static title
const monthPicker = calendar.querySelector('.pkt-cal-month-picker')
const monthTitle = calendar.querySelector('.pkt-calendar__month-title')
expect(monthPicker).toBeInTheDocument()
expect(monthTitle).not.toBeInTheDocument()
// Should have month and year inputs
const monthSelect = monthPicker?.querySelector('select')
const yearInput = monthPicker?.querySelector('input[type="number"]')
expect(monthSelect).toBeInTheDocument()
expect(yearInput).toBeInTheDocument()
})
test('renders static month title when withcontrols is false', async () => {
const container = await createCalendar()
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
// Navigation buttons should always be present
const prevButton = calendar.querySelector('.pkt-calendar__prev-month')
const nextButton = calendar.querySelector('.pkt-calendar__next-month')
expect(prevButton).toBeInTheDocument()
expect(nextButton).toBeInTheDocument()
// Should show static month title, not controls
const monthTitle = calendar.querySelector('.pkt-calendar__month-title')
const monthPicker = calendar.querySelector('.pkt-cal-month-picker')
expect(monthTitle).toBeInTheDocument()
expect(monthPicker).not.toBeInTheDocument()
})
})
describe('Properties and attributes', () => {
test('applies default properties correctly', async () => {
const container = await createCalendar()
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
expect(calendar.multiple).toBe(false)
expect(calendar.range).toBe(false)
expect(calendar.weeknumbers).toBe(false)
expect(calendar.withcontrols).toBe(false)
expect(calendar.selected).toEqual([])
expect(calendar.earliest).toBe(null)
expect(calendar.latest).toBe(null)
})
test('handles multiple property correctly', async () => {
const container = await createCalendar('multiple')
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
expect(calendar.multiple).toBe(true)
})
test('handles range property correctly', async () => {
const container = await createCalendar('range')
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
expect(calendar.range).toBe(true)
})
test('handles weeknumbers property correctly', async () => {
const container = await createCalendar('weeknumbers')
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
expect(calendar.weeknumbers).toBe(true)
const weekNumbers = calendar.querySelectorAll('.pkt-calendar__week-number')
expect(weekNumbers.length).toBeGreaterThan(0)
})
test('handles maxMultiple property correctly', async () => {
const container = await createCalendar('multiple max-multiple="3"')
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
expect(calendar.maxMultiple).toBe(3)
})
test('handles currentmonth property correctly', async () => {
const testDate = '2024-03-15'
const container = await createCalendar(`currentmonth="${testDate}"`)
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
expect(calendar.currentmonth).toEqual(parseISODateString(testDate))
})
})
describe('Month navigation', () => {
it('should navigate to previous month when clicking previous button', async () => {
const calendar = document.createElement('pkt-calendar') as PktCalendar
document.body.appendChild(calendar)
await waitForCustomElements()
const navElement = calendar.querySelector('.pkt-cal-month-nav')
const prevButton = navElement?.querySelector('button')
expect(prevButton).toBeInTheDocument()
const initialMonth = calendar.currentmonth!.getMonth()
const initialYear = calendar.currentmonth!.getFullYear()
fireEvent.click(prevButton!)
await calendar.updateComplete
// Should go to previous month (or December of previous year if we're in January)
if (initialMonth === 0) {
expect(calendar.currentmonth!.getMonth()).toBe(11)
expect(calendar.currentmonth!.getFullYear()).toBe(initialYear - 1)
} else {
expect(calendar.currentmonth!.getMonth()).toBe(initialMonth - 1)
expect(calendar.currentmonth!.getFullYear()).toBe(initialYear)
}
document.body.removeChild(calendar)
})
it('should navigate to next month when clicking next button', async () => {
const calendar = document.createElement('pkt-calendar') as PktCalendar
document.body.appendChild(calendar)
await waitForCustomElements()
const navElement = calendar.querySelector('.pkt-cal-month-nav')
const buttons = navElement?.querySelectorAll('button')
const nextButton = buttons?.[1] // Second button is next
expect(nextButton).toBeInTheDocument()
const initialMonth = calendar.currentmonth!.getMonth()
const initialYear = calendar.currentmonth!.getFullYear()
fireEvent.click(nextButton!)
await calendar.updateComplete
// Should go to next month (or January of next year if we're in December)
if (initialMonth === 11) {
expect(calendar.currentmonth!.getMonth()).toBe(0)
expect(calendar.currentmonth!.getFullYear()).toBe(initialYear + 1)
} else {
expect(calendar.currentmonth!.getMonth()).toBe(initialMonth + 1)
expect(calendar.currentmonth!.getFullYear()).toBe(initialYear)
}
document.body.removeChild(calendar)
})
test('navigates to next month', async () => {
const container = await createCalendar('withcontrols currentmonth="2024-06-15"')
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
const nextButton = calendar.querySelector('.pkt-calendar__next-month')
expect(nextButton).toBeInTheDocument()
// With controls=true, check the month select dropdown instead of title
const monthSelect = calendar.querySelector(
'.pkt-cal-month-picker select',
) as HTMLSelectElement
const initialMonth = monthSelect?.value
fireEvent.click(nextButton!)
await calendar.updateComplete
const newMonth = monthSelect?.value
expect(newMonth).not.toBe(initialMonth)
})
test('month navigation updates visible dates', async () => {
const container = await createCalendar('withcontrols currentmonth="2024-06-15"')
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
const initialDates = Array.from(calendar.querySelectorAll('.pkt-calendar__date')).map(
(el) => el.textContent,
)
const nextButton = calendar.querySelector('.pkt-calendar__next-month')
fireEvent.click(nextButton!)
await calendar.updateComplete
const newDates = Array.from(calendar.querySelectorAll('.pkt-calendar__date')).map(
(el) => el.textContent,
)
expect(newDates).not.toEqual(initialDates)
})
})
describe('Date formatting and localization', () => {
test('displays day names correctly', async () => {
const container = await createCalendar()
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
const dayHeaders = calendar.querySelectorAll('.pkt-calendar__day-name')
expect(dayHeaders.length).toBe(7)
// Check that day names are displayed
dayHeaders.forEach((header) => {
expect(header.textContent).toBeTruthy()
expect(header.textContent!.length).toBeGreaterThan(0)
})
})
test('displays month names correctly', async () => {
const container = await createCalendar('currentmonth="2024-06-15"')
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
const monthTitle = calendar.querySelector('.pkt-calendar__month-title')
expect(monthTitle).toBeInTheDocument()
expect(monthTitle?.textContent).toBeTruthy()
})
test('handles custom day strings', async () => {
const container = await createCalendar()
const calendar = container.querySelector('pkt-calendar') as PktCalendar
// Set custom day strings
calendar.dayStrings = ['S', 'M', 'T', 'W', 'T', 'F', 'S']
await calendar.updateComplete
const dayHeaders = calendar.querySelectorAll('.pkt-calendar__day-name')
expect(dayHeaders[0].textContent?.trim()).toBe('S')
expect(dayHeaders[1].textContent?.trim()).toBe('M')
})
test('handles custom month strings', async () => {
const container = await createCalendar('withcontrols')
const calendar = container.querySelector('pkt-calendar') as PktCalendar
// Set custom month strings
calendar.monthStrings = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
]
await calendar.updateComplete
// When withcontrols=true, check the month select dropdown instead of title
const monthSelect = calendar.querySelector('.pkt-cal-month-picker select')
expect(monthSelect).toBeInTheDocument()
// Check that the custom month string appears in the dropdown options
const options = monthSelect?.querySelectorAll('option')
expect(options?.[0]?.textContent).toContain('Jan')
})
})
describe('Today highlighting', () => {
test('highlights today date', async () => {
const container = await createCalendar()
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
const todayDate = calendar.querySelector('.pkt-calendar__date--today')
expect(todayDate).toBeInTheDocument()
})
test('today date is selectable unless excluded', async () => {
const container = await createCalendar()
const calendar = container.querySelector('pkt-calendar') as PktCalendar
await calendar.updateComplete
const todayDate = calendar.querySelector('.pkt-calendar__date--today')
if (todayDate && !todayDate.classList.contains('pkt-calendar__date--disabled')) {
fireEvent.click(todayDate)
await calendar.updateComplete
expect(todayDate).toHaveClass('pkt-calendar__date--selected')
}
})
})
})