@oslokommune/punkt-elements 13.5.1 → 13.5.3

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.
@@ -0,0 +1,505 @@
1
+ import '@testing-library/jest-dom'
2
+ import { axe, toHaveNoViolations } from 'jest-axe'
3
+ import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
4
+ import { CustomElementFor } from '../../tests/component-registry'
5
+ import './input-wrapper'
6
+
7
+ expect.extend(toHaveNoViolations)
8
+
9
+ export interface InputWrapperTestConfig extends BaseTestConfig {
10
+ label?: string
11
+ helptext?: string
12
+ errorMessage?: string
13
+ hasError?: boolean
14
+ counter?: boolean
15
+ counterCurrent?: number
16
+ counterMaxLength?: number
17
+ counterError?: boolean
18
+ inline?: boolean
19
+ useWrapper?: boolean
20
+ disabled?: boolean
21
+ optionalTag?: boolean
22
+ requiredTag?: boolean
23
+ hasFieldset?: boolean
24
+ inFieldset?: boolean
25
+ readonly?: boolean
26
+ forId?: string
27
+ ariaDescribedBy?: string
28
+ helptextDropdown?: string
29
+ helptextDropdownButton?: string
30
+ optionalText?: string
31
+ requiredText?: string
32
+ tagText?: string
33
+ role?: string
34
+ }
35
+
36
+ // Use shared framework
37
+ export const createInputWrapperTest = async (config: InputWrapperTestConfig = {}) => {
38
+ const { container, element } = await createElementTest<
39
+ CustomElementFor<'pkt-input-wrapper'>,
40
+ InputWrapperTestConfig
41
+ >('pkt-input-wrapper', config)
42
+
43
+ return {
44
+ container,
45
+ wrapper: element,
46
+ }
47
+ }
48
+
49
+ describe('PktInputWrapper', () => {
50
+ describe('Rendering and basic functionality', () => {
51
+ test('renders without errors', async () => {
52
+ const { wrapper } = await createInputWrapperTest()
53
+
54
+ expect(wrapper).toBeInTheDocument()
55
+ await wrapper.updateComplete
56
+ expect(wrapper).toBeTruthy()
57
+ })
58
+
59
+ test('renders with basic structure', async () => {
60
+ const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
61
+ await wrapper.updateComplete
62
+
63
+ expect(wrapper).toBeInTheDocument()
64
+ expect(wrapper.label).toBe('Test Label')
65
+ const label = wrapper.querySelector('label')
66
+ expect(label).toBeInTheDocument()
67
+ expect(label?.textContent).toContain('Test Label')
68
+ })
69
+
70
+ test('renders slotted content', async () => {
71
+ const { wrapper } = await createInputWrapperTest({
72
+ label: 'Test Label',
73
+ content: '<input type="text" placeholder="Custom input" />',
74
+ })
75
+ await wrapper.updateComplete
76
+
77
+ const input = wrapper.querySelector('input')
78
+ expect(input).toBeInTheDocument()
79
+ expect(input?.placeholder).toBe('Custom input')
80
+ })
81
+ })
82
+
83
+ describe('Properties and attributes', () => {
84
+ test('applies default properties correctly', async () => {
85
+ const { wrapper } = await createInputWrapperTest()
86
+ await wrapper.updateComplete
87
+
88
+ expect(wrapper.label).toBe('')
89
+ expect(wrapper.hasError).toBe(false)
90
+ expect(wrapper.disabled).toBe(false)
91
+ expect(wrapper.inline).toBe(false)
92
+ expect(wrapper.counter).toBe(false)
93
+ expect(wrapper.optionalTag).toBe(false)
94
+ expect(wrapper.requiredTag).toBe(false)
95
+ })
96
+
97
+ test('sets label property correctly', async () => {
98
+ const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
99
+ await wrapper.updateComplete
100
+
101
+ expect(wrapper.label).toBe('Test Label')
102
+ })
103
+
104
+ test('sets helptext property correctly', async () => {
105
+ const { wrapper } = await createInputWrapperTest({ helptext: 'This is help text' })
106
+ await wrapper.updateComplete
107
+
108
+ expect(wrapper.helptext).toBe('This is help text')
109
+ })
110
+
111
+ test('sets error state correctly', async () => {
112
+ const { wrapper } = await createInputWrapperTest({
113
+ hasError: true,
114
+ errorMessage: 'Error message',
115
+ })
116
+ await wrapper.updateComplete
117
+
118
+ expect(wrapper.hasError).toBe(true)
119
+ expect(wrapper.errorMessage).toBe('Error message')
120
+ })
121
+
122
+ test('sets disabled state correctly', async () => {
123
+ const { wrapper } = await createInputWrapperTest({ disabled: true })
124
+ await wrapper.updateComplete
125
+
126
+ expect(wrapper.disabled).toBe(true)
127
+ })
128
+
129
+ test('sets inline layout correctly', async () => {
130
+ const { wrapper } = await createInputWrapperTest({ inline: true })
131
+ await wrapper.updateComplete
132
+
133
+ expect(wrapper.inline).toBe(true)
134
+ })
135
+
136
+ test('sets forId property correctly', async () => {
137
+ const { wrapper } = await createInputWrapperTest({ forId: 'custom-id' })
138
+ await wrapper.updateComplete
139
+
140
+ expect(wrapper.forId).toBe('custom-id')
141
+ })
142
+ })
143
+
144
+ describe('Tag functionality', () => {
145
+ test('renders optional tag when enabled', async () => {
146
+ const { wrapper } = await createInputWrapperTest({
147
+ label: 'Test Label',
148
+ optionalTag: true,
149
+ })
150
+ await wrapper.updateComplete
151
+
152
+ expect(wrapper.optionalTag).toBe(true)
153
+ const tag = wrapper.querySelector('.pkt-tag')
154
+ expect(tag).toBeInTheDocument()
155
+ })
156
+
157
+ test('renders required tag when enabled', async () => {
158
+ const { wrapper } = await createInputWrapperTest({
159
+ label: 'Test Label',
160
+ requiredTag: true,
161
+ })
162
+ await wrapper.updateComplete
163
+
164
+ expect(wrapper.requiredTag).toBe(true)
165
+ const tag = wrapper.querySelector('.pkt-tag')
166
+ expect(tag).toBeInTheDocument()
167
+ })
168
+
169
+ test('renders custom tag text when provided', async () => {
170
+ const { wrapper } = await createInputWrapperTest({
171
+ label: 'Test Label',
172
+ tagText: 'Custom Tag',
173
+ })
174
+ await wrapper.updateComplete
175
+
176
+ expect(wrapper.tagText).toBe('Custom Tag')
177
+ const tag = wrapper.querySelector('.pkt-tag')
178
+ expect(tag).toBeInTheDocument()
179
+ expect(tag?.textContent).toContain('Custom Tag')
180
+ })
181
+ })
182
+
183
+ describe('Counter functionality', () => {
184
+ test('renders counter when enabled', async () => {
185
+ const { wrapper } = await createInputWrapperTest({
186
+ label: 'Test Label',
187
+ counter: true,
188
+ counterCurrent: 5,
189
+ counterMaxLength: 100,
190
+ })
191
+ await wrapper.updateComplete
192
+
193
+ expect(wrapper.counter).toBe(true)
194
+ expect(wrapper.counterCurrent).toBe(5)
195
+ expect(wrapper.counterMaxLength).toBe(100)
196
+
197
+ const counter = wrapper.querySelector('.pkt-input__counter')
198
+ expect(counter).toBeInTheDocument()
199
+ })
200
+
201
+ test('updates counter current value', async () => {
202
+ const { wrapper } = await createInputWrapperTest({
203
+ label: 'Test Label',
204
+ counter: true,
205
+ counterCurrent: 10,
206
+ counterMaxLength: 100,
207
+ })
208
+ await wrapper.updateComplete
209
+
210
+ wrapper.counterCurrent = 25
211
+ await wrapper.updateComplete
212
+
213
+ expect(wrapper.counterCurrent).toBe(25)
214
+ })
215
+
216
+ test('handles counter error state', async () => {
217
+ const { wrapper } = await createInputWrapperTest({
218
+ label: 'Test Label',
219
+ counter: true,
220
+ counterCurrent: 105,
221
+ counterMaxLength: 100,
222
+ })
223
+ await wrapper.updateComplete
224
+
225
+ // Counter should indicate error when current exceeds max
226
+ expect(wrapper.counterCurrent).toBe(105)
227
+ expect(wrapper.counterMaxLength).toBe(100)
228
+ })
229
+ })
230
+
231
+ describe('Helptext functionality', () => {
232
+ test('renders dropdown helptext', async () => {
233
+ const { wrapper } = await createInputWrapperTest({
234
+ label: 'Test Label',
235
+ helptextDropdown: 'Dropdown help content',
236
+ })
237
+ await wrapper.updateComplete
238
+
239
+ expect(wrapper.helptextDropdown).toBe('Dropdown help content')
240
+ const helptext = wrapper.querySelector('pkt-helptext')
241
+ expect(helptext).toBeInTheDocument()
242
+ })
243
+
244
+ test('sets dropdown button text', async () => {
245
+ const { wrapper } = await createInputWrapperTest({
246
+ label: 'Test Label',
247
+ helptextDropdown: 'Dropdown help',
248
+ helptextDropdownButton: 'Custom Button',
249
+ })
250
+ await wrapper.updateComplete
251
+
252
+ expect(wrapper.helptextDropdownButton).toBe('Custom Button')
253
+ })
254
+
255
+ test('renders helptext from slot', async () => {
256
+ const { wrapper } = await createInputWrapperTest({
257
+ label: 'Test Label',
258
+ content: '<input type="text" /><div slot="helptext">Slotted help content</div>',
259
+ })
260
+ await wrapper.updateComplete
261
+
262
+ const slottedContent = wrapper.querySelector('[name="helptext"]')
263
+ expect(slottedContent).toBeInTheDocument()
264
+ expect(slottedContent?.textContent).toContain('Slotted help content')
265
+ })
266
+ })
267
+
268
+ describe('Error handling', () => {
269
+ test('displays error message when hasError is true', async () => {
270
+ const { wrapper } = await createInputWrapperTest({
271
+ label: 'Test Label',
272
+ hasError: true,
273
+ errorMessage: 'This field is required',
274
+ })
275
+ await wrapper.updateComplete
276
+
277
+ expect(wrapper.hasError).toBe(true)
278
+ expect(wrapper.errorMessage).toBe('This field is required')
279
+
280
+ const errorMessage = wrapper.querySelector('.pkt-alert--error')
281
+ expect(errorMessage).toBeInTheDocument()
282
+ expect(errorMessage?.textContent).toContain('This field is required')
283
+ })
284
+
285
+ test('does not display error message when hasError is false', async () => {
286
+ const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
287
+ await wrapper.updateComplete
288
+
289
+ expect(wrapper.hasError).toBe(false)
290
+ const errorMessage = wrapper.querySelector('.pkt-alert--error')
291
+ expect(errorMessage).not.toBeInTheDocument()
292
+ })
293
+
294
+ test('applies error styling when hasError is true', async () => {
295
+ const { wrapper } = await createInputWrapperTest({
296
+ label: 'Test Label',
297
+ hasError: true,
298
+ })
299
+ await wrapper.updateComplete
300
+
301
+ expect(wrapper.hasError).toBe(true)
302
+ // Check for error-related classes based on actual component implementation
303
+ const inputWrapper = wrapper.querySelector('.pkt-inputwrapper')
304
+ expect(inputWrapper?.classList.contains('pkt-inputwrapper--error')).toBe(true)
305
+ })
306
+ })
307
+
308
+ describe('Fieldset functionality', () => {
309
+ test('handles fieldset mode', async () => {
310
+ const { wrapper } = await createInputWrapperTest({
311
+ label: 'Test Label',
312
+ hasFieldset: true,
313
+ })
314
+ await wrapper.updateComplete
315
+
316
+ expect(wrapper.hasFieldset).toBe(true)
317
+ })
318
+
319
+ test('sets role attribute correctly', async () => {
320
+ const { wrapper } = await createInputWrapperTest({
321
+ label: 'Test Label',
322
+ role: 'radiogroup',
323
+ })
324
+ await wrapper.updateComplete
325
+
326
+ expect(wrapper.role).toBe('radiogroup')
327
+ expect(wrapper.getAttribute('role')).toBe('radiogroup')
328
+ })
329
+ })
330
+
331
+ describe('Wrapper functionality', () => {
332
+ test('handles useWrapper property values', async () => {
333
+ // Test true value
334
+ const { wrapper: wrapperTrue } = await createInputWrapperTest({
335
+ label: 'Test Label',
336
+ useWrapper: true,
337
+ })
338
+ await wrapperTrue.updateComplete
339
+
340
+ expect(wrapperTrue.useWrapper).toBe(true)
341
+ expect(wrapperTrue.getAttribute('useWrapper')).toBe('true')
342
+
343
+ // Test false value explicitly
344
+ const { wrapper: wrapperFalse } = await createInputWrapperTest()
345
+ wrapperFalse.setAttribute('useWrapper', 'false')
346
+ wrapperFalse.label = 'Test Label'
347
+ await wrapperFalse.updateComplete
348
+
349
+ expect(wrapperFalse.useWrapper).toBe(false)
350
+ expect(wrapperFalse.getAttribute('useWrapper')).toBe('false')
351
+ })
352
+ })
353
+
354
+ describe('ARIA and accessibility attributes', () => {
355
+ test('sets aria-describedby correctly', async () => {
356
+ const { wrapper } = await createInputWrapperTest({
357
+ label: 'Test Label',
358
+ ariaDescribedBy: 'help-text-id',
359
+ })
360
+ await wrapper.updateComplete
361
+
362
+ expect(wrapper.ariaDescribedby).toBe('help-text-id')
363
+ })
364
+
365
+ test('associates label with input using forId', async () => {
366
+ const { wrapper } = await createInputWrapperTest({
367
+ label: 'Test Label',
368
+ forId: 'test-input',
369
+ content: '<input id="test-input" type="text" />',
370
+ })
371
+ await wrapper.updateComplete
372
+
373
+ const label = wrapper.querySelector('label')
374
+ const input = wrapper.querySelector('input')
375
+
376
+ expect(label?.getAttribute('for')).toBe('test-input')
377
+ expect(input?.id).toBe('test-input')
378
+ })
379
+ })
380
+
381
+ describe('Dynamic updates', () => {
382
+ test('updates label dynamically', async () => {
383
+ const { wrapper } = await createInputWrapperTest({ label: 'Original Label' })
384
+ await wrapper.updateComplete
385
+
386
+ wrapper.label = 'Updated Label'
387
+ await wrapper.updateComplete
388
+
389
+ expect(wrapper.label).toBe('Updated Label')
390
+ const label = wrapper.querySelector('label')
391
+ expect(label?.textContent).toContain('Updated Label')
392
+ })
393
+
394
+ test('updates error state dynamically', async () => {
395
+ const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
396
+ await wrapper.updateComplete
397
+
398
+ wrapper.hasError = true
399
+ wrapper.errorMessage = 'Dynamic error'
400
+ await wrapper.updateComplete
401
+
402
+ expect(wrapper.hasError).toBe(true)
403
+ expect(wrapper.errorMessage).toBe('Dynamic error')
404
+ })
405
+
406
+ test('updates helptext dynamically', async () => {
407
+ const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
408
+ await wrapper.updateComplete
409
+
410
+ wrapper.helptext = 'Dynamic help text'
411
+ await wrapper.updateComplete
412
+
413
+ expect(wrapper.helptext).toBe('Dynamic help text')
414
+ })
415
+ })
416
+
417
+ describe('Accessibility', () => {
418
+ test('basic input wrapper is accessible', async () => {
419
+ const { container } = await createInputWrapperTest({
420
+ label: 'Accessible Label',
421
+ forId: 'test-input',
422
+ content: '<input id="test-input" type="text" />',
423
+ })
424
+ await new Promise((resolve) => setTimeout(resolve, 100))
425
+
426
+ const results = await axe(container)
427
+ expect(results).toHaveNoViolations()
428
+ })
429
+
430
+ test('input wrapper with helptext is accessible', async () => {
431
+ const { container } = await createInputWrapperTest({
432
+ label: 'Test Label',
433
+ helptext: 'This is helpful information',
434
+ forId: 'test-input-2',
435
+ content: '<input id="test-input-2" type="text" />',
436
+ })
437
+ await new Promise((resolve) => setTimeout(resolve, 100))
438
+
439
+ const results = await axe(container)
440
+ expect(results).toHaveNoViolations()
441
+ })
442
+
443
+ test('comprehensive input wrapper accessibility', async () => {
444
+ const { container } = await createInputWrapperTest({
445
+ label: 'Test Label',
446
+ helptext: 'Help text',
447
+ hasError: true,
448
+ errorMessage: 'Error message',
449
+ optionalTag: true,
450
+ forId: 'test-input-comprehensive',
451
+ content: '<input id="test-input-comprehensive" type="text" />',
452
+ })
453
+ await new Promise((resolve) => setTimeout(resolve, 100))
454
+
455
+ const results = await axe(container)
456
+ expect(results).toHaveNoViolations()
457
+ })
458
+ })
459
+
460
+ describe('Integration scenarios', () => {
461
+ test('works with complex form elements', async () => {
462
+ const { wrapper } = await createInputWrapperTest({
463
+ label: 'Complex Field',
464
+ helptext: 'Help text',
465
+ counter: true,
466
+ counterMaxLength: 100,
467
+ optionalTag: true,
468
+ content: '<textarea></textarea>',
469
+ })
470
+ await wrapper.updateComplete
471
+
472
+ expect(wrapper.label).toBe('Complex Field')
473
+ expect(wrapper.helptext).toBe('Help text')
474
+ expect(wrapper.counter).toBe(true)
475
+ expect(wrapper.optionalTag).toBe(true)
476
+
477
+ const textarea = wrapper.querySelector('textarea')
478
+ expect(textarea).toBeInTheDocument()
479
+ })
480
+
481
+ test('handles multiple input wrappers on same page', async () => {
482
+ const { container } = await createInputWrapperTest({
483
+ label: 'Field 1',
484
+ content: '<input type="text" />',
485
+ })
486
+
487
+ // Add additional wrappers to the container
488
+ container.innerHTML += `
489
+ <pkt-input-wrapper label="Field 2"><input type="email" /></pkt-input-wrapper>
490
+ <pkt-input-wrapper label="Field 3"><textarea></textarea></pkt-input-wrapper>
491
+ `
492
+
493
+ // Wait for all components to be defined
494
+ await customElements.whenDefined('pkt-input-wrapper')
495
+
496
+ const wrappers = container.querySelectorAll('pkt-input-wrapper')
497
+ expect(wrappers).toHaveLength(3)
498
+
499
+ for (const wrapper of wrappers) {
500
+ await (wrapper as any).updateComplete
501
+ expect(wrapper.querySelector('label')).toBeInTheDocument()
502
+ }
503
+ })
504
+ })
505
+ })