@oslokommune/punkt-elements 13.5.2 → 13.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/{alert-DQNBDKjT.cjs → alert-7rUOhlNi.cjs} +2 -1
- package/dist/{alert-B07oUpkq.js → alert-cUBtwi2k.js} +12 -11
- package/dist/{loader-CHPxY9c6.cjs → loader-DNidjwH-.cjs} +6 -1
- package/dist/{loader-Da4IOk_T.js → loader-h3d-3D7s.js} +6 -1
- package/dist/{messagebox-DwGdcdm7.js → messagebox-C8KQgCl_.js} +14 -13
- package/dist/{messagebox-CqUBJs_D.cjs → messagebox-CjPtPPrW.cjs} +1 -0
- package/dist/pkt-alert.cjs +1 -1
- package/dist/pkt-alert.js +1 -1
- package/dist/pkt-index.cjs +1 -1
- package/dist/pkt-index.js +3 -3
- package/dist/pkt-loader.cjs +1 -1
- package/dist/pkt-loader.js +1 -1
- package/dist/pkt-messagebox.cjs +1 -1
- package/dist/pkt-messagebox.js +1 -1
- package/package.json +6 -2
- package/src/components/alert/alert.test.ts +64 -79
- package/src/components/alert/alert.ts +1 -0
- package/src/components/backlink/backlink.test.ts +50 -96
- package/src/components/button/button.test.ts +211 -249
- package/src/components/calendar/calendar.accessibility.test.ts +30 -43
- package/src/components/card/card.test.ts +71 -121
- package/src/components/checkbox/checkbox.test.ts +231 -156
- package/src/components/consent/consent.test.ts +87 -91
- package/src/components/icon/icon.test.ts +368 -0
- package/src/components/input-wrapper/input-wrapper.test.ts +505 -0
- package/src/components/link/link.test.ts +224 -0
- package/src/components/linkcard/linkcard.test.ts +14 -12
- package/src/components/listbox/listbox.test.ts +225 -0
- package/src/components/loader/loader.test.ts +257 -0
- package/src/components/loader/loader.ts +6 -1
- package/src/components/messagebox/messagebox.test.ts +241 -0
- package/src/components/messagebox/messagebox.ts +1 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
3
|
+
import { fireEvent } from '@testing-library/dom'
|
|
4
|
+
import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
|
|
5
|
+
import { CustomElementFor } from '../../tests/component-registry'
|
|
6
|
+
import './link'
|
|
7
|
+
|
|
8
|
+
expect.extend(toHaveNoViolations)
|
|
9
|
+
|
|
10
|
+
export interface LinkTestConfig extends BaseTestConfig {
|
|
11
|
+
href?: string
|
|
12
|
+
target?: string
|
|
13
|
+
skin?: string
|
|
14
|
+
variant?: string
|
|
15
|
+
iconName?: string
|
|
16
|
+
iconPosition?: string
|
|
17
|
+
openInNewTab?: boolean
|
|
18
|
+
external?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Use shared framework
|
|
22
|
+
export const createLinkTest = async (config: LinkTestConfig = {}) => {
|
|
23
|
+
const { container, element } = await createElementTest<
|
|
24
|
+
CustomElementFor<'pkt-link'>,
|
|
25
|
+
LinkTestConfig
|
|
26
|
+
>('pkt-link', config)
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
container,
|
|
30
|
+
link: element,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('PktLink', () => {
|
|
35
|
+
describe('Rendering and basic functionality', () => {
|
|
36
|
+
test('renders without errors', async () => {
|
|
37
|
+
const { link } = await createLinkTest()
|
|
38
|
+
|
|
39
|
+
expect(link).toBeInTheDocument()
|
|
40
|
+
await link.updateComplete
|
|
41
|
+
expect(link).toBeTruthy()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('renders with correct structure', async () => {
|
|
45
|
+
const { link } = await createLinkTest({
|
|
46
|
+
href: 'https://example.com',
|
|
47
|
+
content: 'Click me',
|
|
48
|
+
})
|
|
49
|
+
await link.updateComplete
|
|
50
|
+
|
|
51
|
+
expect(link).toBeInTheDocument()
|
|
52
|
+
const anchor = link.querySelector('a')
|
|
53
|
+
expect(anchor).toBeInTheDocument()
|
|
54
|
+
expect(anchor?.href).toBe('https://example.com/')
|
|
55
|
+
expect(anchor?.textContent).toContain('Click me')
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
describe('Properties and attributes', () => {
|
|
60
|
+
test('applies default properties correctly', async () => {
|
|
61
|
+
const { link } = await createLinkTest()
|
|
62
|
+
await link.updateComplete
|
|
63
|
+
|
|
64
|
+
expect(link.href).toBe('#')
|
|
65
|
+
expect(link.external).toBe(false)
|
|
66
|
+
expect(link.iconName).toBeUndefined()
|
|
67
|
+
expect(link.target).toBe('_self')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('sets href property correctly', async () => {
|
|
71
|
+
const { link } = await createLinkTest({
|
|
72
|
+
href: 'https://example.com',
|
|
73
|
+
})
|
|
74
|
+
await link.updateComplete
|
|
75
|
+
|
|
76
|
+
expect(link.href).toBe('https://example.com')
|
|
77
|
+
const anchor = link.querySelector('a')
|
|
78
|
+
expect(anchor?.href).toBe('https://example.com/')
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('sets external property correctly', async () => {
|
|
82
|
+
const { link } = await createLinkTest({
|
|
83
|
+
href: 'https://example.com',
|
|
84
|
+
external: true,
|
|
85
|
+
})
|
|
86
|
+
await link.updateComplete
|
|
87
|
+
|
|
88
|
+
expect(link.external).toBe(true)
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe('Icon functionality', () => {
|
|
93
|
+
test('renders icon when iconName is provided', async () => {
|
|
94
|
+
const { link } = await createLinkTest({
|
|
95
|
+
href: '#',
|
|
96
|
+
iconName: 'arrow-right',
|
|
97
|
+
})
|
|
98
|
+
await link.updateComplete
|
|
99
|
+
|
|
100
|
+
const icon = link.querySelector('pkt-icon')
|
|
101
|
+
expect(icon).toBeInTheDocument()
|
|
102
|
+
expect(icon?.getAttribute('name')).toBe('arrow-right')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test('positions icon correctly', async () => {
|
|
106
|
+
const { link } = await createLinkTest({
|
|
107
|
+
href: '#',
|
|
108
|
+
iconName: 'arrow-right',
|
|
109
|
+
iconPosition: 'right',
|
|
110
|
+
})
|
|
111
|
+
await link.updateComplete
|
|
112
|
+
|
|
113
|
+
const anchor = link.querySelector('a')
|
|
114
|
+
expect(anchor?.classList.contains('pkt-link--icon-right')).toBe(true)
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
describe('External link functionality', () => {
|
|
119
|
+
test('applies external class and rel attribute', async () => {
|
|
120
|
+
const { link } = await createLinkTest({
|
|
121
|
+
href: 'https://example.com',
|
|
122
|
+
external: true,
|
|
123
|
+
})
|
|
124
|
+
await link.updateComplete
|
|
125
|
+
|
|
126
|
+
const anchor = link.querySelector('a')
|
|
127
|
+
expect(anchor?.classList.contains('pkt-link--external')).toBe(true)
|
|
128
|
+
expect(anchor?.rel).toBe('noopener noreferrer')
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('does not set rel attribute for internal links', async () => {
|
|
132
|
+
const { link } = await createLinkTest({
|
|
133
|
+
href: '/internal-page',
|
|
134
|
+
})
|
|
135
|
+
await link.updateComplete
|
|
136
|
+
|
|
137
|
+
const anchor = link.querySelector('a')
|
|
138
|
+
expect(anchor?.rel).toBe('')
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
describe('Event handling', () => {
|
|
143
|
+
test('handles click events', async () => {
|
|
144
|
+
const { link } = await createLinkTest({
|
|
145
|
+
href: '#test',
|
|
146
|
+
})
|
|
147
|
+
await link.updateComplete
|
|
148
|
+
|
|
149
|
+
const anchor = link.querySelector('a')
|
|
150
|
+
const clickHandler = jest.fn()
|
|
151
|
+
anchor?.addEventListener('click', clickHandler)
|
|
152
|
+
|
|
153
|
+
fireEvent.click(anchor!)
|
|
154
|
+
expect(clickHandler).toHaveBeenCalled()
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
describe('Dynamic updates', () => {
|
|
159
|
+
test('updates href dynamically', async () => {
|
|
160
|
+
const { link } = await createLinkTest({
|
|
161
|
+
href: 'https://example.com',
|
|
162
|
+
})
|
|
163
|
+
await link.updateComplete
|
|
164
|
+
|
|
165
|
+
link.href = 'https://updated.com'
|
|
166
|
+
await link.updateComplete
|
|
167
|
+
|
|
168
|
+
expect(link.href).toBe('https://updated.com')
|
|
169
|
+
const anchor = link.querySelector('a')
|
|
170
|
+
expect(anchor?.href).toBe('https://updated.com/')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test('updates external property dynamically', async () => {
|
|
174
|
+
const { link } = await createLinkTest({
|
|
175
|
+
href: 'https://example.com',
|
|
176
|
+
})
|
|
177
|
+
await link.updateComplete
|
|
178
|
+
|
|
179
|
+
link.external = true
|
|
180
|
+
await link.updateComplete
|
|
181
|
+
|
|
182
|
+
expect(link.external).toBe(true)
|
|
183
|
+
const anchor = link.querySelector('a')
|
|
184
|
+
expect(anchor?.classList.contains('pkt-link--external')).toBe(true)
|
|
185
|
+
expect(anchor?.rel).toBe('noopener noreferrer')
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
describe('Accessibility', () => {
|
|
190
|
+
test('basic link is accessible', async () => {
|
|
191
|
+
const { container } = await createLinkTest({
|
|
192
|
+
href: 'https://example.com',
|
|
193
|
+
content: 'Accessible Link',
|
|
194
|
+
})
|
|
195
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
196
|
+
|
|
197
|
+
const results = await axe(container)
|
|
198
|
+
expect(results).toHaveNoViolations()
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
describe('Integration scenarios', () => {
|
|
203
|
+
test('works with complex configurations', async () => {
|
|
204
|
+
const { link } = await createLinkTest({
|
|
205
|
+
href: 'https://external-site.com',
|
|
206
|
+
iconName: 'external',
|
|
207
|
+
iconPosition: 'right',
|
|
208
|
+
external: true,
|
|
209
|
+
target: '_blank',
|
|
210
|
+
content: 'Complex External Link',
|
|
211
|
+
})
|
|
212
|
+
await link.updateComplete
|
|
213
|
+
|
|
214
|
+
expect(link.href).toBe('https://external-site.com')
|
|
215
|
+
expect(link.iconName).toBe('external')
|
|
216
|
+
expect(link.external).toBe(true)
|
|
217
|
+
expect(link.target).toBe('_blank')
|
|
218
|
+
|
|
219
|
+
const anchor = link.querySelector('a')
|
|
220
|
+
expect(anchor?.classList.contains('pkt-link--external')).toBe(true)
|
|
221
|
+
expect(anchor?.classList.contains('pkt-link--icon-right')).toBe(true)
|
|
222
|
+
})
|
|
223
|
+
})
|
|
224
|
+
})
|
|
@@ -6,14 +6,11 @@ import { PktLinkCard, type TLinkCardSkin } from './linkcard'
|
|
|
6
6
|
import type { IPktLinkCard } from './linkcard'
|
|
7
7
|
import './linkcard'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
// Enhanced test configuration extends the component interface with additional test-specific properties
|
|
11
9
|
export interface LinkCardTestConfig extends IPktLinkCard, BaseTestConfig {}
|
|
12
10
|
|
|
13
|
-
|
|
14
11
|
// Use shared framework
|
|
15
12
|
export const createLinkCardTest = async (config: LinkCardTestConfig = {}) => {
|
|
16
|
-
const { container, element} = await createElementTest<
|
|
13
|
+
const { container, element } = await createElementTest<
|
|
17
14
|
CustomElementFor<'pkt-linkcard'>,
|
|
18
15
|
LinkCardTestConfig
|
|
19
16
|
>('pkt-linkcard', config)
|
|
@@ -30,7 +27,14 @@ expect.extend(toHaveNoViolations)
|
|
|
30
27
|
|
|
31
28
|
// Test data constants
|
|
32
29
|
const VALID_SKINS: TLinkCardSkin[] = [
|
|
33
|
-
'normal',
|
|
30
|
+
'normal',
|
|
31
|
+
'no-padding',
|
|
32
|
+
'blue',
|
|
33
|
+
'beige',
|
|
34
|
+
'green',
|
|
35
|
+
'gray',
|
|
36
|
+
'beige-outline',
|
|
37
|
+
'gray-outline',
|
|
34
38
|
]
|
|
35
39
|
|
|
36
40
|
const SAMPLE_ICONS = ['arrow-right', 'external-link', 'download', 'info']
|
|
@@ -238,7 +242,7 @@ describe('PktLinkCard', () => {
|
|
|
238
242
|
iconName: 'arrow-right',
|
|
239
243
|
openInNewTab: true,
|
|
240
244
|
skin: 'blue',
|
|
241
|
-
content: '<p>Complete content</p>'
|
|
245
|
+
content: '<p>Complete content</p>',
|
|
242
246
|
}
|
|
243
247
|
|
|
244
248
|
const { linkCard, link } = await createLinkCardTest(config)
|
|
@@ -283,7 +287,7 @@ describe('PktLinkCard', () => {
|
|
|
283
287
|
const { linkCard } = await createLinkCardTest({
|
|
284
288
|
title: '',
|
|
285
289
|
href: '',
|
|
286
|
-
iconName: ''
|
|
290
|
+
iconName: '',
|
|
287
291
|
})
|
|
288
292
|
|
|
289
293
|
expect(linkCard.title).toBe('')
|
|
@@ -315,7 +319,7 @@ describe('PktLinkCard', () => {
|
|
|
315
319
|
test('has no accessibility violations', async () => {
|
|
316
320
|
const { linkCard } = await createLinkCardTest({
|
|
317
321
|
title: 'Accessible Link Card',
|
|
318
|
-
href: '/accessible'
|
|
322
|
+
href: '/accessible',
|
|
319
323
|
})
|
|
320
324
|
|
|
321
325
|
const results = await axe(linkCard)
|
|
@@ -325,7 +329,7 @@ describe('PktLinkCard', () => {
|
|
|
325
329
|
test('maintains proper link semantics', async () => {
|
|
326
330
|
const { link } = await createLinkCardTest({
|
|
327
331
|
title: 'Semantic Link',
|
|
328
|
-
href: '/semantic'
|
|
332
|
+
href: '/semantic',
|
|
329
333
|
})
|
|
330
334
|
|
|
331
335
|
expect(link.tagName).toBe('A')
|
|
@@ -337,7 +341,7 @@ describe('PktLinkCard', () => {
|
|
|
337
341
|
const { link } = await createLinkCardTest({
|
|
338
342
|
title: 'External Link',
|
|
339
343
|
href: 'https://example.com',
|
|
340
|
-
openInNewTab: true
|
|
344
|
+
openInNewTab: true,
|
|
341
345
|
})
|
|
342
346
|
|
|
343
347
|
expect(link.getAttribute('target')).toBe('_blank')
|
|
@@ -345,7 +349,6 @@ describe('PktLinkCard', () => {
|
|
|
345
349
|
})
|
|
346
350
|
})
|
|
347
351
|
|
|
348
|
-
|
|
349
352
|
describe('Type Safety', () => {
|
|
350
353
|
test('validates interface implementation', () => {
|
|
351
354
|
const linkCard = new PktLinkCard()
|
|
@@ -359,4 +362,3 @@ describe('PktLinkCard', () => {
|
|
|
359
362
|
})
|
|
360
363
|
})
|
|
361
364
|
})
|
|
362
|
-
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
3
|
+
import { fireEvent } from '@testing-library/dom'
|
|
4
|
+
import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
|
|
5
|
+
import { CustomElementFor } from '../../tests/component-registry'
|
|
6
|
+
import { type IPktListbox } from './listbox'
|
|
7
|
+
import './listbox'
|
|
8
|
+
|
|
9
|
+
export interface ListboxTestConfig extends Partial<IPktListbox>, BaseTestConfig {
|
|
10
|
+
label?: string
|
|
11
|
+
id?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Use shared framework
|
|
15
|
+
export const createListboxTest = async (config: ListboxTestConfig = {}) => {
|
|
16
|
+
const { container, element } = await createElementTest<
|
|
17
|
+
CustomElementFor<'pkt-listbox'>,
|
|
18
|
+
ListboxTestConfig
|
|
19
|
+
>('pkt-listbox', { ...config, options: undefined })
|
|
20
|
+
|
|
21
|
+
// Set complex properties directly on the element after creation
|
|
22
|
+
if (config.options) {
|
|
23
|
+
element.options = config.options
|
|
24
|
+
await element.updateComplete
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
container,
|
|
29
|
+
listbox: element,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
expect.extend(toHaveNoViolations)
|
|
34
|
+
|
|
35
|
+
// Cleanup after each test
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
document.body.innerHTML = ''
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('PktListbox', () => {
|
|
41
|
+
describe('Rendering and basic functionality', () => {
|
|
42
|
+
test('renders without errors', async () => {
|
|
43
|
+
const { listbox } = await createListboxTest()
|
|
44
|
+
|
|
45
|
+
expect(listbox).toBeInTheDocument()
|
|
46
|
+
expect(listbox).toBeTruthy()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('renders with options', async () => {
|
|
50
|
+
const options = [
|
|
51
|
+
{ value: 'option1', label: 'Option 1' },
|
|
52
|
+
{ value: 'option2', label: 'Option 2' },
|
|
53
|
+
]
|
|
54
|
+
const { listbox } = await createListboxTest({
|
|
55
|
+
label: 'Test Listbox',
|
|
56
|
+
options,
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const listboxElement = listbox.querySelector('.pkt-listbox')
|
|
60
|
+
expect(listboxElement).toBeInTheDocument()
|
|
61
|
+
|
|
62
|
+
const optionElements = listbox.querySelectorAll('.pkt-listbox__option')
|
|
63
|
+
expect(optionElements).toHaveLength(2)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe('Properties and attributes', () => {
|
|
68
|
+
test('applies default properties correctly', async () => {
|
|
69
|
+
const { listbox } = await createListboxTest()
|
|
70
|
+
|
|
71
|
+
expect(listbox.isOpen).toBe(false)
|
|
72
|
+
expect(listbox.disabled).toBe(false)
|
|
73
|
+
expect(listbox.includeSearch).toBe(false)
|
|
74
|
+
expect(listbox.isMultiSelect).toBe(false)
|
|
75
|
+
expect(listbox.allowUserInput).toBe(false)
|
|
76
|
+
expect(listbox.maxLength).toBe(0)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('sets properties correctly', async () => {
|
|
80
|
+
const { listbox } = await createListboxTest({
|
|
81
|
+
isOpen: true,
|
|
82
|
+
disabled: true,
|
|
83
|
+
includeSearch: true,
|
|
84
|
+
isMultiSelect: true,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
expect(listbox.isOpen).toBe(true)
|
|
88
|
+
expect(listbox.disabled).toBe(true)
|
|
89
|
+
expect(listbox.includeSearch).toBe(true)
|
|
90
|
+
expect(listbox.isMultiSelect).toBe(true)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
describe('Option handling', () => {
|
|
95
|
+
test('renders single select options correctly', async () => {
|
|
96
|
+
const options = [
|
|
97
|
+
{ value: 'option1', label: 'Option 1', selected: true },
|
|
98
|
+
{ value: 'option2', label: 'Option 2' },
|
|
99
|
+
]
|
|
100
|
+
const { listbox } = await createListboxTest({ options })
|
|
101
|
+
|
|
102
|
+
const selectedOption = listbox.querySelector('.pkt-listbox__option--selected')
|
|
103
|
+
expect(selectedOption).toBeInTheDocument()
|
|
104
|
+
|
|
105
|
+
const checkIcon = listbox.querySelector('pkt-icon[name="check-big"]')
|
|
106
|
+
expect(checkIcon).toBeInTheDocument()
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('renders multi-select options with checkboxes', async () => {
|
|
110
|
+
const options = [
|
|
111
|
+
{ value: 'option1', label: 'Option 1', selected: true },
|
|
112
|
+
{ value: 'option2', label: 'Option 2' },
|
|
113
|
+
]
|
|
114
|
+
const { listbox } = await createListboxTest({
|
|
115
|
+
isMultiSelect: true,
|
|
116
|
+
options,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const checkboxes = listbox.querySelectorAll('input[type="checkbox"]')
|
|
120
|
+
expect(checkboxes).toHaveLength(2)
|
|
121
|
+
expect(checkboxes[0]).toBeChecked()
|
|
122
|
+
expect(checkboxes[1]).not.toBeChecked()
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test('handles option click', async () => {
|
|
126
|
+
const options = [
|
|
127
|
+
{ value: 'option1', label: 'Option 1' },
|
|
128
|
+
{ value: 'option2', label: 'Option 2' },
|
|
129
|
+
]
|
|
130
|
+
const { listbox } = await createListboxTest({ options })
|
|
131
|
+
|
|
132
|
+
// Listen for the option-toggle event
|
|
133
|
+
let toggledValue: string | null = null
|
|
134
|
+
listbox.addEventListener('option-toggle', (e: any) => {
|
|
135
|
+
toggledValue = e.detail
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
const optionElement = listbox.querySelector('.pkt-listbox__option')
|
|
139
|
+
fireEvent.click(optionElement!)
|
|
140
|
+
|
|
141
|
+
await listbox.updateComplete
|
|
142
|
+
expect(toggledValue).toBe('option1')
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
describe('Search functionality', () => {
|
|
147
|
+
test('renders search when includeSearch is true', async () => {
|
|
148
|
+
const { listbox } = await createListboxTest({ includeSearch: true })
|
|
149
|
+
|
|
150
|
+
const searchInput = listbox.querySelector('[role="searchbox"]')
|
|
151
|
+
expect(searchInput).toBeInTheDocument()
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('filters options based on search', async () => {
|
|
155
|
+
const options = [
|
|
156
|
+
{ value: 'apple', label: 'Apple' },
|
|
157
|
+
{ value: 'banana', label: 'Banana' },
|
|
158
|
+
]
|
|
159
|
+
const { listbox } = await createListboxTest({
|
|
160
|
+
includeSearch: true,
|
|
161
|
+
options,
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// Set search value and trigger filtering
|
|
165
|
+
listbox.searchValue = 'app'
|
|
166
|
+
listbox.filterOptions()
|
|
167
|
+
await listbox.updateComplete
|
|
168
|
+
|
|
169
|
+
// Should filter to only show Apple - check filtered options
|
|
170
|
+
const visibleOptions = listbox.querySelectorAll('.pkt-listbox__option')
|
|
171
|
+
expect(visibleOptions).toHaveLength(1)
|
|
172
|
+
expect(visibleOptions[0].textContent?.trim()).toContain('Apple')
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
describe('User input functionality', () => {
|
|
177
|
+
test('renders new option banner when allowUserInput is true', async () => {
|
|
178
|
+
const { listbox } = await createListboxTest({
|
|
179
|
+
allowUserInput: true,
|
|
180
|
+
customUserInput: 'New',
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const newOptionBanner = listbox.querySelector('.pkt-listbox__banner--new-option')
|
|
184
|
+
expect(newOptionBanner).toBeInTheDocument()
|
|
185
|
+
expect(newOptionBanner?.getAttribute('data-value')).toBe('New')
|
|
186
|
+
// Check that the text contains the basic structure
|
|
187
|
+
expect(newOptionBanner?.textContent).toMatch(/Legg til.*New/)
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
describe('Maximum selection', () => {
|
|
192
|
+
test('shows maximum reached banner', async () => {
|
|
193
|
+
const options = [
|
|
194
|
+
{ value: 'option1', label: 'Option 1', selected: true },
|
|
195
|
+
{ value: 'option2', label: 'Option 2', selected: true },
|
|
196
|
+
]
|
|
197
|
+
const { listbox } = await createListboxTest({
|
|
198
|
+
isMultiSelect: true,
|
|
199
|
+
maxLength: 3,
|
|
200
|
+
options,
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
const banner = listbox.querySelector('.pkt-listbox__banner--maximum-reached')
|
|
204
|
+
expect(banner).toBeInTheDocument()
|
|
205
|
+
expect(banner?.textContent).toContain('2 av maks 3')
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
describe('Accessibility', () => {
|
|
210
|
+
test('basic listbox is accessible', async () => {
|
|
211
|
+
const options = [
|
|
212
|
+
{ value: 'option1', label: 'Option 1' },
|
|
213
|
+
{ value: 'option2', label: 'Option 2' },
|
|
214
|
+
]
|
|
215
|
+
const { container } = await createListboxTest({
|
|
216
|
+
label: 'Accessible Listbox',
|
|
217
|
+
options,
|
|
218
|
+
})
|
|
219
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
220
|
+
|
|
221
|
+
const results = await axe(container)
|
|
222
|
+
expect(results).toHaveNoViolations()
|
|
223
|
+
})
|
|
224
|
+
})
|
|
225
|
+
})
|