@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
809 lines (617 loc) • 30.8 kB
text/typescript
import '@testing-library/jest-dom'
import { axe, toHaveNoViolations } from 'jest-axe'
import { fireEvent } from '@testing-library/dom'
import { vi } from 'vitest'
expect.extend(toHaveNoViolations)
import './combobox'
import { PktCombobox } from './combobox'
import type { IPktComboboxOption } from './combobox'
const waitForCustomElements = async () => {
await customElements.whenDefined('pkt-combobox')
}
// Helper function to create combobox markup
const createCombobox = async (comboboxProps = '', options = '') => {
const container = document.createElement('div')
container.innerHTML = `
<pkt-combobox ${comboboxProps}>
${options}
</pkt-combobox>
`
document.body.appendChild(container)
await waitForCustomElements()
return container
}
// Helper function to create options
const createOptions = (optionData: { value: string; label?: string; disabled?: boolean }[]) => {
return optionData
.map(
(opt) =>
`<option value="${opt.value}" ${opt.disabled ? 'disabled' : ''}>${opt.label || opt.value}</option>`,
)
.join('')
}
// Cleanup after each test
afterEach(() => {
document.body.innerHTML = ''
})
describe('PktCombobox', () => {
describe('Rendering and basic functionality', () => {
test('renders without errors', async () => {
const container = await createCombobox('id="test-combobox" name="test" label="Test Combobox"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
expect(combobox).toBeInTheDocument()
await combobox.updateComplete
expect(combobox).toBeTruthy()
const wrapper = combobox.querySelector('pkt-input-wrapper')
const comboboxDiv = combobox.querySelector('.pkt-combobox')
expect(wrapper).toBeInTheDocument()
expect(comboboxDiv).toBeInTheDocument()
})
test('renders with correct structure', async () => {
const container = await createCombobox('id="test" name="test" label="Test Label"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const wrapper = combobox.querySelector('pkt-input-wrapper')
const comboboxDiv = wrapper?.querySelector('.pkt-combobox')
const inputDiv = comboboxDiv?.querySelector('.pkt-combobox__input')
const arrowIcon = inputDiv?.querySelector('.pkt-combobox__arrow-icon')
const listbox = comboboxDiv?.querySelector('pkt-listbox')
expect(wrapper).toBeInTheDocument()
expect(comboboxDiv).toBeInTheDocument()
expect(inputDiv).toBeInTheDocument()
expect(arrowIcon).toBeInTheDocument()
expect(listbox).toBeInTheDocument()
})
test('renders select-only combobox with correct ARIA attributes', async () => {
const container = await createCombobox('id="test-arrow" name="test" label="Test"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const comboboxInput = combobox.querySelector('.pkt-combobox__input')
const icon = comboboxInput?.querySelector('pkt-icon')
expect(comboboxInput?.getAttribute('id')).toBe('test-arrow-combobox')
expect(comboboxInput?.getAttribute('aria-expanded')).toBe('false')
expect(comboboxInput?.getAttribute('aria-controls')).toBe('test-arrow-listbox')
expect(comboboxInput?.getAttribute('aria-haspopup')).toBe('listbox')
expect(comboboxInput?.getAttribute('aria-labelledby')).toBe('test-arrow-combobox-label')
expect(comboboxInput?.getAttribute('role')).toBe('combobox')
expect(icon?.getAttribute('name')).toBe('chevron-thin-down')
})
})
describe('Properties and attributes', () => {
test('applies default properties correctly', async () => {
const container = await createCombobox('id="test" name="test"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox.value).toBe('')
expect(combobox.allowUserInput).toBe(false)
expect(combobox.typeahead).toBe(false)
expect(combobox.includeSearch).toBe(false)
expect(combobox.multiple).toBe(false)
expect(combobox.disabled).toBe(false)
expect(combobox.options).toEqual([])
expect(combobox.tagPlacement).toBe(null)
expect(combobox.maxlength).toBe(null)
expect(combobox.displayValueAs).toBe('label')
})
test('handles multiple property correctly', async () => {
const container = await createCombobox('id="test" name="test" multiple')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox.multiple).toBe(true)
const listbox = combobox.querySelector('pkt-listbox')
expect(listbox?.hasAttribute('is-multi-select')).toBe(true)
})
test('handles allowUserInput property correctly', async () => {
const container = await createCombobox('id="test" name="test" allow-user-input')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox.allowUserInput).toBe(true)
const input = combobox.querySelector('input[type="text"]')
expect(input).toBeInTheDocument()
expect(input?.getAttribute('role')).toBe('combobox')
})
test('handles typeahead property correctly', async () => {
const container = await createCombobox('id="test" name="test" typeahead')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox.typeahead).toBe(true)
const input = combobox.querySelector('input[type="text"]')
expect(input).toBeInTheDocument()
expect(input?.getAttribute('aria-autocomplete')).toBe('both')
})
test('handles includeSearch property correctly', async () => {
const container = await createCombobox('id="test" name="test" include-search')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox.includeSearch).toBe(true)
const listbox = combobox.querySelector('pkt-listbox')
expect(listbox?.hasAttribute('include-search')).toBe(true)
})
test('handles disabled property correctly', async () => {
const container = await createCombobox('id="test" name="test" disabled')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox.disabled).toBe(true)
const comboboxInput = combobox.querySelector('.pkt-combobox__input')
expect(comboboxInput).toHaveClass('pkt-combobox__input--disabled')
expect(comboboxInput?.getAttribute('tabindex')).toBe('-1')
})
test('handles fullwidth property correctly', async () => {
const container = await createCombobox('id="test" name="test" fullwidth')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox.fullwidth).toBe(true)
const inputDiv = combobox.querySelector('.pkt-combobox__input')
expect(inputDiv).toHaveClass('pkt-combobox__input--fullwidth')
})
})
describe('Options handling', () => {
test('handles options provided via property', async () => {
const testOptions: IPktComboboxOption[] = [
{ value: 'option1', label: 'Option 1' },
{ value: 'option2', label: 'Option 2' },
{ value: 'option3', label: 'Option 3', disabled: true },
]
const container = await createCombobox('id="test" name="test"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
// Set options via property
combobox.options = testOptions
await combobox.updateComplete
// Ensure listbox has also updated
const listbox = combobox.querySelector('pkt-listbox') as any
await listbox?.updateComplete
expect(combobox.options).toHaveLength(3)
expect(combobox.options[0].value).toBe('option1')
expect(combobox.options[0].label).toBe('Option 1')
expect(combobox.options[2].disabled).toBe(true)
expect(listbox?.options).toEqual(
expect.arrayContaining([expect.objectContaining({ value: 'option1', label: 'Option 1' })]),
)
})
test('handles options provided via slot', async () => {
const optionsMarkup = createOptions([
{ value: 'value1', label: 'Label 1' },
{ value: 'value2', label: 'Label 2' },
])
const container = await createCombobox('id="test" name="test"', optionsMarkup)
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
// Options should be extracted from slot
expect(combobox.options.length).toBeGreaterThan(0)
expect(combobox.options.some((opt) => opt.value === 'value1')).toBe(true)
expect(combobox.options.some((opt) => opt.value === 'value2')).toBe(true)
})
test('handles defaultOptions property', async () => {
const defaultOptions: IPktComboboxOption[] = [
{ value: 'default1', label: 'Default 1' },
{ value: 'default2', label: 'Default 2' },
]
const container = await createCombobox('id="test" name="test"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
combobox.defaultOptions = defaultOptions
await combobox.updateComplete
expect(combobox.options).toHaveLength(2)
expect(combobox.options[0].value).toBe('default1')
expect(combobox.options[1].value).toBe('default2')
})
test('preserves userAdded options when defaultOptions change', async () => {
const container = await createCombobox('id="test" name="test" allow-user-input')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
// Add a user option
const userOption: IPktComboboxOption = {
value: 'user-added',
label: 'User Added',
userAdded: true,
}
combobox.options = [userOption]
await combobox.updateComplete
// Set default options
const defaultOptions: IPktComboboxOption[] = [{ value: 'default1', label: 'Default 1' }]
combobox.defaultOptions = defaultOptions
await combobox.updateComplete
// Should have both user-added and default options
expect(combobox.options).toHaveLength(2)
expect(combobox.options.some((opt) => opt.userAdded)).toBe(true)
expect(combobox.options.some((opt) => opt.value === 'default1')).toBe(true)
})
test('preserves userAdded option when parent replaces options by overwriting options property', async () => {
const container = await createCombobox('id="test" name="test" allow-user-input')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
;(combobox as any).addNewUserValue('userAdded')
await combobox.updateComplete
expect(combobox.options.some((o: any) => o.value === 'userAdded' && o.userAdded)).toBe(true)
expect(combobox['_value']).toContain('userAdded')
combobox.options = [{ value: 'external1', label: 'External 1' }]
await combobox.updateComplete
const hasUserAdded = combobox.options.some((o: any) => o.value === 'userAdded' && o.userAdded)
const hasExternal = combobox.options.some((o: any) => o.value === 'external1')
expect(hasExternal).toBe(true)
expect(hasUserAdded).toBe(true)
expect(combobox['_value']).toContain('userAdded')
})
})
describe('Value handling', () => {
test('handles single value correctly', async () => {
const container = await createCombobox('id="test" name="test" value="test-value"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox.value).toBe('test-value')
// Internal _value should be an array with single item
expect(combobox['_value']).toEqual(['test-value'])
})
test('handles multiple values correctly', async () => {
const container = await createCombobox('id="test" name="test" value="value1,value2" multiple')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox.value).toBe('value1,value2')
expect(combobox['_value']).toEqual(['value1', 'value2'])
})
test('handles array value correctly', async () => {
const container = await createCombobox('id="test" name="test" multiple')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
combobox.value = ['array1', 'array2']
await combobox.updateComplete
expect(combobox['_value']).toEqual(['array1', 'array2'])
})
test('limits to single value when not multiple', async () => {
const container = await createCombobox('id="test" name="test"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
combobox.value = ['value1', 'value2']
await combobox.updateComplete
// Should only keep the first value when not multiple
expect(combobox['_value']).toEqual(['value1'])
})
})
describe('Placeholder functionality', () => {
test('shows placeholder when no value selected', async () => {
const container = await createCombobox('id="test" name="test" placeholder="Select an option"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox.placeholder).toBe('Select an option')
const placeholder = combobox.querySelector('.pkt-combobox__placeholder')
expect(placeholder).toBeInTheDocument()
expect(placeholder?.textContent).toBe('Select an option')
})
test('hides placeholder when value is selected', async () => {
const testOptions: IPktComboboxOption[] = [{ value: 'option1', label: 'Option 1' }]
const container = await createCombobox(
'id="test" name="test" placeholder="Select an option" value="option1"',
)
const combobox = container.querySelector('pkt-combobox') as PktCombobox
combobox.options = testOptions
await combobox.updateComplete
const placeholder = combobox.querySelector('.pkt-combobox__placeholder')
expect(placeholder).not.toBeInTheDocument()
})
test('shows placeholder in multiple mode with outside tag placement', async () => {
const container = await createCombobox(
'id="test" name="test" placeholder="Select options" multiple tag-placement="outside"',
)
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const placeholder = combobox.querySelector('.pkt-combobox__placeholder')
expect(placeholder).toBeInTheDocument()
expect(placeholder?.textContent).toBe('Select options')
})
})
describe('Tag placement functionality', () => {
test('renders tags inside input by default in multiple mode', async () => {
const testOptions: IPktComboboxOption[] = [{ value: 'option1', label: 'Option 1' }]
const container = await createCombobox('id="test" name="test" multiple value="option1"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
combobox.options = testOptions
await combobox.updateComplete
const outsideTags = combobox.querySelector('.pkt-combobox__tags-outside')
expect(outsideTags).not.toBeInTheDocument()
})
test('renders tags outside input when tagPlacement is outside', async () => {
const testOptions: IPktComboboxOption[] = [{ value: 'option1', label: 'Option 1' }]
const container = await createCombobox(
'id="test" name="test" multiple tag-placement="outside" value="option1"',
)
const combobox = container.querySelector('pkt-combobox') as PktCombobox
combobox.options = testOptions
await combobox.updateComplete
const outsideTags = combobox.querySelector('.pkt-combobox__tags-outside')
expect(outsideTags).toBeInTheDocument()
})
})
describe('Input field functionality', () => {
test('renders hidden input when not allowUserInput or typeahead', async () => {
const container = await createCombobox('id="test" name="test-name"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const hiddenInput = combobox.querySelector('input[type="hidden"]')
const textInput = combobox.querySelector('input[type="text"]')
expect(hiddenInput).toBeInTheDocument()
expect(textInput).not.toBeInTheDocument()
expect(hiddenInput?.getAttribute('id')).toBe('test-input')
expect(hiddenInput?.getAttribute('name')).toBe('test-name-input')
})
test('renders text input when allowUserInput is true', async () => {
const container = await createCombobox('id="test" name="test-name" allow-user-input')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const textInput = combobox.querySelector('input[type="text"]')
const hiddenInput = combobox.querySelector('input[type="hidden"]')
expect(textInput).toBeInTheDocument()
expect(hiddenInput).not.toBeInTheDocument()
expect(textInput?.getAttribute('id')).toBe('test-input')
expect(textInput?.getAttribute('name')).toBe('test-name-input')
expect(textInput?.getAttribute('role')).toBe('combobox')
expect(textInput?.getAttribute('aria-controls')).toBe('test-listbox')
})
test('renders text input when typeahead is true', async () => {
const container = await createCombobox('id="test" name="test" typeahead')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const textInput = combobox.querySelector('input[type="text"]')
expect(textInput).toBeInTheDocument()
expect(textInput?.getAttribute('aria-autocomplete')).toBe('both')
})
test('sets correct aria-autocomplete for allowUserInput', async () => {
const container = await createCombobox('id="test" name="test" allow-user-input')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const textInput = combobox.querySelector('input[type="text"]')
expect(textInput?.getAttribute('aria-autocomplete')).toBe('list')
})
})
describe('Dropdown functionality', () => {
test('opens dropdown when arrow button is clicked', async () => {
const container = await createCombobox('id="test" name="test"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const arrowButton = combobox.querySelector('.pkt-combobox__input')
const inputDiv = combobox.querySelector('.pkt-combobox__input')
expect(combobox['_isOptionsOpen']).toBe(false)
expect(arrowButton?.getAttribute('aria-expanded')).toBe('false')
expect(inputDiv).not.toHaveClass('pkt-combobox__input--open')
// Click arrow to open
fireEvent.click(arrowButton!)
await combobox.updateComplete
expect(combobox['_isOptionsOpen']).toBe(true)
expect(arrowButton?.getAttribute('aria-expanded')).toBe('true')
expect(inputDiv).toHaveClass('pkt-combobox__input--open')
})
test('toggles dropdown state with multiple clicks', async () => {
const container = await createCombobox('id="test" name="test"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const arrowButton = combobox.querySelector('.pkt-combobox__input')
// Click to open
fireEvent.click(arrowButton!)
await combobox.updateComplete
expect(combobox['_isOptionsOpen']).toBe(true)
// Click to close
fireEvent.click(arrowButton!)
await combobox.updateComplete
expect(combobox['_isOptionsOpen']).toBe(false)
})
test('does not open when disabled', async () => {
const container = await createCombobox('id="test" name="test" disabled')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const arrowButton = combobox.querySelector('.pkt-combobox__input')
// Try to click when disabled
fireEvent.click(arrowButton!)
await combobox.updateComplete
expect(combobox['_isOptionsOpen']).toBe(false)
})
})
describe('Search functionality', () => {
test('handles search input when includeSearch is true', async () => {
const container = await createCombobox('id="test" name="test" include-search')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const listbox = combobox.querySelector('pkt-listbox')
expect(listbox?.hasAttribute('include-search')).toBe(true)
})
test('sets search placeholder correctly', async () => {
const container = await createCombobox(
'id="test" name="test" include-search search-placeholder="Search items..."',
)
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox.searchPlaceholder).toBe('Search items...')
const listbox = combobox.querySelector('pkt-listbox') as any
expect(listbox?.searchPlaceholder).toBe('Search items...')
})
})
describe('Keyboard navigation', () => {
test('handles keyboard events on arrow button', async () => {
const container = await createCombobox('id="test" name="test"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const arrowButton = combobox.querySelector('.pkt-combobox__input')
// Test Enter key
fireEvent.keyDown(arrowButton!, { key: 'Enter' })
await combobox.updateComplete
expect(combobox['_isOptionsOpen']).toBe(true)
// Test Space key
fireEvent.keyDown(arrowButton!, { key: ' ' })
await combobox.updateComplete
expect(combobox['_isOptionsOpen']).toBe(false)
})
test('handles input focus for text inputs', async () => {
const container = await createCombobox('id="test" name="test" allow-user-input')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const textInput = combobox.querySelector('input[type="text"]')
// Test focus event
fireEvent.focus(textInput!)
await combobox.updateComplete
// Test blur event
fireEvent.blur(textInput!)
await combobox.updateComplete
// Should not throw errors
expect(combobox).toBeInTheDocument()
})
})
describe('Max length functionality', () => {
test('handles maxlength property correctly', async () => {
const container = await createCombobox('id="test" name="test" multiple maxlength="3"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox.maxlength).toBe(3)
const listbox = combobox.querySelector('pkt-listbox') as any
expect(listbox?.maxLength).toBe(3)
})
test('disables user input when max is reached', async () => {
const testOptions: IPktComboboxOption[] = [
{ value: 'option1', label: 'Option 1' },
{ value: 'option2', label: 'Option 2' },
{ value: 'option3', label: 'Option 3' },
]
const container = await createCombobox(
'id="test" name="test" multiple allow-user-input maxlength="2"',
)
const combobox = container.querySelector('pkt-combobox') as PktCombobox
combobox.options = testOptions
combobox.value = ['option1', 'option2'] // Set to max
await combobox.updateComplete
const listbox = combobox.querySelector('pkt-listbox') as any
await listbox?.updateComplete
expect(listbox?.maxIsReached).toBe(true)
})
})
describe('Error handling', () => {
test('applies error styling when hasError is true', async () => {
const container = await createCombobox('id="test" name="test"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
combobox.hasError = true
await combobox.updateComplete
const inputDiv = combobox.querySelector('.pkt-combobox__input')
expect(inputDiv).toHaveClass('pkt-combobox__input--error')
})
test('passes error state to input wrapper', async () => {
const container = await createCombobox('id="test" name="test" error-message="Test error"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
combobox.hasError = true
await combobox.updateComplete
const wrapper = combobox.querySelector('pkt-input-wrapper')
expect(wrapper?.hasAttribute('haserror')).toBe(true)
})
})
describe('Accessibility', () => {
test('has no accessibility violations', async () => {
const container = await createCombobox(
'id="accessible-combobox" name="test" label="Accessible Combobox"',
)
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const results = await axe(combobox)
expect(results).toHaveNoViolations()
})
test('sets correct ARIA attributes on select-only combobox', async () => {
const container = await createCombobox('id="test-aria" name="test" label="Test"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const comboboxInput = combobox.querySelector('.pkt-combobox__input')
expect(comboboxInput?.getAttribute('role')).toBe('combobox')
expect(comboboxInput?.getAttribute('aria-controls')).toBe('test-aria-listbox')
expect(comboboxInput?.getAttribute('aria-haspopup')).toBe('listbox')
expect(comboboxInput?.getAttribute('aria-expanded')).toBe('false')
expect(comboboxInput?.getAttribute('aria-labelledby')).toBe('test-aria-combobox-label')
})
test('sets correct ARIA attributes on text input', async () => {
const container = await createCombobox(
'id="test-input" name="test" label="Test Label" allow-user-input',
)
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const textInput = combobox.querySelector('input[type="text"]')
expect(textInput?.getAttribute('role')).toBe('combobox')
expect(textInput?.getAttribute('aria-controls')).toBe('test-input-listbox')
expect(textInput?.getAttribute('aria-label')).toBe('Test Label')
expect(textInput?.getAttribute('aria-autocomplete')).toBe('list')
})
test('sets correct aria-activedescendant when value is selected', async () => {
const testOptions: IPktComboboxOption[] = [{ value: 'option1', label: 'Option 1' }]
const container = await createCombobox(
'id="test" name="test" allow-user-input value="option1"',
)
const combobox = container.querySelector('pkt-combobox') as PktCombobox
combobox.options = testOptions
await combobox.updateComplete
const textInput = combobox.querySelector('input[type="text"]')
expect(textInput?.getAttribute('aria-activedescendant')).toBe('test-listbox--1')
})
})
describe('Event handling', () => {
test('dispatches search event when search value changes', async () => {
const container = await createCombobox('id="test" name="test" include-search')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const searchEventSpy = vi.fn()
combobox.addEventListener('search', searchEventSpy)
// Simulate search change
combobox['_search'] = 'test search'
await combobox.updateComplete
expect(searchEventSpy).toHaveBeenCalledWith(
expect.objectContaining({
detail: 'test search',
}),
)
})
test('handles listbox events correctly', async () => {
const container = await createCombobox('id="test" name="test"')
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
const listbox = combobox.querySelector('pkt-listbox')
// Test close-options event
fireEvent(listbox!, new CustomEvent('close-options'))
await combobox.updateComplete
expect(combobox['_isOptionsOpen']).toBe(false)
})
})
describe('Dynamic slot options', () => {
test('picks up dynamically added <option> children', async () => {
const container = await createCombobox(
'id="dynamic" name="dynamic"',
createOptions([
{ value: 'a', label: 'A' },
{ value: 'b', label: 'B' },
]),
)
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox['_options']).toHaveLength(2)
// Add a new option element to the DOM
const newOption = document.createElement('option')
newOption.value = 'c'
newOption.textContent = 'C'
combobox.appendChild(newOption)
// Wait for MutationObserver + Lit update
await combobox.updateComplete
await new Promise((r) => setTimeout(r, 0))
await combobox.updateComplete
expect(combobox['_options']).toHaveLength(3)
expect(combobox['_options'].some((o: IPktComboboxOption) => o.value === 'c')).toBe(true)
})
test('picks up dynamically removed <option> children', async () => {
const container = await createCombobox(
'id="dynamic" name="dynamic"',
createOptions([
{ value: 'a', label: 'A' },
{ value: 'b', label: 'B' },
{ value: 'c', label: 'C' },
]),
)
const combobox = container.querySelector('pkt-combobox') as PktCombobox
await combobox.updateComplete
expect(combobox['_options']).toHaveLength(3)
// Remove the last option element
const options = combobox.querySelectorAll('option')
combobox.removeChild(options[options.length - 1])
// Wait for MutationObserver + Lit update
await combobox.updateComplete
await new Promise((r) => setTimeout(r, 0))
await combobox.updateComplete
expect(combobox['_options']).toHaveLength(2)
expect(combobox['_options'].some((o: IPktComboboxOption) => o.value === 'c')).toBe(false)
})
})
})