UNPKG

@oslokommune/punkt-elements

Version:

Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo

465 lines (344 loc) 17.7 kB
import '@testing-library/jest-dom' import { axe, toHaveNoViolations } from 'jest-axe' import { fireEvent } from '@testing-library/dom' expect.extend(toHaveNoViolations) import './helptext' import { PktHelptext } from './helptext' const waitForCustomElements = async () => { await customElements.whenDefined('pkt-helptext') await customElements.whenDefined('pkt-icon') } // Helper function to create helptext markup const createHelptext = async (helptextProps = '', content = '') => { const container = document.createElement('div') container.innerHTML = ` <pkt-helptext ${helptextProps}> ${content} </pkt-helptext> ` document.body.appendChild(container) await waitForCustomElements() return container } // Cleanup after each test afterEach(() => { document.body.innerHTML = '' }) describe('PktHelptext', () => { describe('Rendering and basic functionality', () => { test('renders without errors', async () => { const container = await createHelptext() const helptext = container.querySelector('pkt-helptext') as PktHelptext expect(helptext).toBeInTheDocument() await helptext.updateComplete expect(helptext).toBeTruthy() }) test('renders with basic helptext', async () => { const helptextContent = 'This is helpful information' const container = await createHelptext(`helptext="${helptextContent}"`) const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete expect(helptext.helptext).toBe(helptextContent) const helptextContainer = helptext.querySelector('.pkt-inputwrapper__helptext-container') expect(helptextContainer).toBeInTheDocument() expect(helptextContainer).toHaveClass('pkt-inputwrapper__has-helptext') const helptextDiv = helptext.querySelector('.pkt-inputwrapper__helptext') expect(helptextDiv).toBeInTheDocument() expect(helptextDiv?.textContent?.trim()).toContain(helptextContent) }) test('renders with slot content', async () => { const slotContent = '<p>Slot helptext content</p>' const container = await createHelptext('', slotContent) const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const contentSlot = helptext.querySelector('.pkt-contents') expect(contentSlot).toBeInTheDocument() expect(helptext.textContent).toContain('Slot helptext content') }) test('renders with dropdown helptext', async () => { const dropdownContent = 'This is expandable helptext content' const container = await createHelptext(`helptextDropdown="${dropdownContent}"`) const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete expect(helptext.helptextDropdown).toBe(dropdownContent) const expandableContainer = helptext.querySelector('.pkt-inputwrapper__helptext-expandable') expect(expandableContainer).toBeInTheDocument() const button = expandableContainer?.querySelector('button') expect(button).toBeInTheDocument() expect(button).toHaveClass('pkt-link') const icon = button?.querySelector('pkt-icon') expect(icon).toBeInTheDocument() }) }) describe('Properties and attributes', () => { test('applies default properties correctly', async () => { const container = await createHelptext() const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete expect(helptext.forId).toBeTruthy() // Should have a generated ID expect(helptext.helptext).toBe('') expect(helptext.helptextDropdown).toBe('') expect(helptext.helptextDropdownButton).toBeTruthy() // Should have default from specs expect(helptext.isHelpTextOpen).toBe(false) }) test('sets forId correctly', async () => { const customId = 'custom-helptext-id' const container = await createHelptext(`forId="${customId}"`) const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete expect(helptext.forId).toBe(customId) const helptextDiv = helptext.querySelector('.pkt-inputwrapper__helptext') expect(helptextDiv?.id).toBe(`${customId}-helptext`) }) test('sets helptext content correctly', async () => { const helptextContent = 'Custom helptext message' const container = await createHelptext(`helptext="${helptextContent}"`) const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete expect(helptext.helptext).toBe(helptextContent) expect(helptext.textContent).toContain(helptextContent) }) test('sets dropdown helptext correctly', async () => { const dropdownContent = 'Dropdown helptext content' const container = await createHelptext(`helptextDropdown="${dropdownContent}"`) const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete expect(helptext.helptextDropdown).toBe(dropdownContent) }) test('sets dropdown button text correctly', async () => { const buttonText = 'Custom button text' const container = await createHelptext(`helptextDropdownButton="${buttonText}"`) const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete expect(helptext.helptextDropdownButton).toBe(buttonText) }) }) describe('Dropdown functionality', () => { test('does not render dropdown when no dropdown content', async () => { const container = await createHelptext('helptext="Regular helptext"') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const expandableContainer = helptext.querySelector('.pkt-inputwrapper__helptext-expandable') expect(expandableContainer).not.toBeInTheDocument() }) test('renders dropdown when dropdown content is provided', async () => { const container = await createHelptext('helptextDropdown="Dropdown content"') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const expandableContainer = helptext.querySelector('.pkt-inputwrapper__helptext-expandable') expect(expandableContainer).toBeInTheDocument() const button = expandableContainer?.querySelector('button') expect(button).toBeInTheDocument() const expandableContent = expandableContainer?.querySelector( '.pkt-inputwrapper__helptext-expandable-closed', ) expect(expandableContent).toBeInTheDocument() }) test('toggles dropdown state on button click', async () => { const container = await createHelptext('helptextDropdown="Dropdown content"') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete expect(helptext.isHelpTextOpen).toBe(false) const button = helptext.querySelector('.pkt-inputwrapper__helptext-expandable button') expect(button).toBeInTheDocument() // Click to open fireEvent.click(button!) await helptext.updateComplete expect(helptext.isHelpTextOpen).toBe(true) const openContent = helptext.querySelector('.pkt-inputwrapper__helptext-expandable-open') expect(openContent).toBeInTheDocument() const closedContent = helptext.querySelector('.pkt-inputwrapper__helptext-expandable-closed') expect(closedContent).not.toBeInTheDocument() // Click to close fireEvent.click(button!) await helptext.updateComplete expect(helptext.isHelpTextOpen).toBe(false) }) test('changes icon when dropdown is toggled', async () => { const container = await createHelptext('helptextDropdown="Dropdown content"') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const icon = helptext.querySelector('pkt-icon') expect(icon?.getAttribute('name')).toBe('chevron-thin-down') const button = helptext.querySelector('.pkt-inputwrapper__helptext-expandable button') fireEvent.click(button!) await helptext.updateComplete expect(icon?.getAttribute('name')).toBe('chevron-thin-up') }) test('dispatches toggleHelpText event on dropdown toggle', async () => { const container = await createHelptext('helptextDropdown="Dropdown content"') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete let eventFired = false let eventDetail: any = null helptext.addEventListener('toggleHelpText', (e: Event) => { eventFired = true eventDetail = (e as CustomEvent).detail }) const button = helptext.querySelector('.pkt-inputwrapper__helptext-expandable button') fireEvent.click(button!) expect(eventFired).toBe(true) expect(eventDetail).toEqual({ isOpen: true }) }) }) describe('CSS classes and styling', () => { test('applies correct classes when no content', async () => { const container = await createHelptext() const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const helptextContainer = helptext.querySelector('.pkt-inputwrapper__helptext-container') expect(helptextContainer).toBeInTheDocument() expect(helptextContainer).not.toHaveClass('pkt-inputwrapper__has-helptext') }) test('applies correct classes when helptext is provided', async () => { const container = await createHelptext('helptext="Some helptext"') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const helptextContainer = helptext.querySelector('.pkt-inputwrapper__helptext-container') expect(helptextContainer).toHaveClass('pkt-inputwrapper__has-helptext') }) test('applies correct classes when dropdown is provided', async () => { const container = await createHelptext('helptextDropdown="Dropdown content"') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const helptextContainer = helptext.querySelector('.pkt-inputwrapper__helptext-container') expect(helptextContainer).toHaveClass('pkt-inputwrapper__has-helptext') }) test('applies correct classes for closed dropdown', async () => { const container = await createHelptext('helptextDropdown="Dropdown content"') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const dropdownContent = helptext.querySelector( '.pkt-inputwrapper__helptext-expandable .pkt-inputwrapper__helptext', ) expect(dropdownContent).toHaveClass('pkt-inputwrapper__helptext-expandable-closed') expect(dropdownContent).not.toHaveClass('pkt-inputwrapper__helptext-expandable-open') }) test('applies correct classes for open dropdown', async () => { const container = await createHelptext('helptextDropdown="Dropdown content"') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const button = helptext.querySelector('.pkt-inputwrapper__helptext-expandable button') fireEvent.click(button!) await helptext.updateComplete const dropdownContent = helptext.querySelector( '.pkt-inputwrapper__helptext-expandable .pkt-inputwrapper__helptext', ) expect(dropdownContent).toHaveClass('pkt-inputwrapper__helptext-expandable-open') expect(dropdownContent).not.toHaveClass('pkt-inputwrapper__helptext-expandable-closed') }) }) describe('Content rendering', () => { test('renders HTML content safely in helptext', async () => { const htmlContent = '<strong>Important</strong> information' const container = await createHelptext(`helptext="${htmlContent}"`) const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const strong = helptext.querySelector('strong') expect(strong).toBeInTheDocument() expect(strong?.textContent).toBe('Important') }) test('renders HTML content safely in dropdown', async () => { const htmlContent = '<em>Emphasized</em> dropdown content' const container = await createHelptext(`helptextDropdown="${htmlContent}"`) const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const button = helptext.querySelector('.pkt-inputwrapper__helptext-expandable button') fireEvent.click(button!) await helptext.updateComplete const em = helptext.querySelector('em') expect(em).toBeInTheDocument() expect(em?.textContent).toBe('Emphasized') }) test('renders both regular and dropdown content simultaneously', async () => { const regularContent = 'Regular helptext' const dropdownContent = 'Dropdown helptext' const container = await createHelptext( `helptext="${regularContent}" helptextDropdown="${dropdownContent}"`, ) const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete expect(helptext.textContent).toContain(regularContent) const button = helptext.querySelector('.pkt-inputwrapper__helptext-expandable button') fireEvent.click(button!) await helptext.updateComplete expect(helptext.textContent).toContain(dropdownContent) }) }) describe('Slot management', () => { test('detects slot content via hasSlotContent()', async () => { const container = await createHelptext('', '<span>Slotted content</span>') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete expect(helptext.hasSlotContent()).toBe(true) }) test('applies has-helptext class when slots are filled', async () => { const container = await createHelptext('', '<span>Slotted content</span>') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const helptextContainer = helptext.querySelector('.pkt-inputwrapper__helptext-container') expect(helptextContainer).toHaveClass('pkt-inputwrapper__has-helptext') }) }) describe('Accessibility', () => { test('basic helptext is accessible', async () => { const container = await createHelptext('helptext="Accessible helptext" forId="test-input"') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const results = await axe(container) expect(results).toHaveNoViolations() const helptextDiv = helptext.querySelector('.pkt-inputwrapper__helptext') expect(helptextDiv?.id).toBe('test-input-helptext') }) test('dropdown helptext is accessible', async () => { const container = await createHelptext( 'helptextDropdown="Dropdown content" helptextDropdownButton="Show more info"', ) const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const results = await axe(container) expect(results).toHaveNoViolations() const button = helptext.querySelector('button') expect(button).toBeInTheDocument() expect(button?.type).toBe('button') expect(button?.textContent?.trim()).toContain('Show more info') }) test('dropdown button has proper aria state', async () => { const container = await createHelptext('helptextDropdown="Dropdown content"') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete const button = helptext.querySelector('button') expect(button).toBeInTheDocument() // Button should be focusable and have proper role expect(button?.tagName).toBe('BUTTON') expect(button?.type).toBe('button') }) }) describe('Integration', () => { test('works with input wrapper context', async () => { const container = await createHelptext('forId="input-123" helptext="Field help information"') const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete // The ID should match what an input would use for aria-describedby const helptextDiv = helptext.querySelector('.pkt-inputwrapper__helptext') expect(helptextDiv?.id).toBe('input-123-helptext') }) test('manages multiple content sources correctly', async () => { const container = await createHelptext( 'helptext="Property text" helptextDropdown="Dropdown text"', '<span>Slot text</span>', ) const helptext = container.querySelector('pkt-helptext') as PktHelptext await helptext.updateComplete // All content should be present but in correct locations expect(helptext.textContent).toContain('Property text') expect(helptext.textContent).toContain('Slot text') // Dropdown content should be present but hidden initially const dropdownContent = helptext.querySelector( '.pkt-inputwrapper__helptext-expandable-closed', ) expect(dropdownContent).toBeInTheDocument() expect(dropdownContent?.textContent?.trim()).toContain('Dropdown text') }) }) })