@oslokommune/punkt-elements 13.4.0 → 13.4.2
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-B0OUR9oD.js → alert-B07oUpkq.js} +5 -4
- package/dist/{alert-BH0lJ2ny.cjs → alert-DQNBDKjT.cjs} +1 -1
- package/dist/{backlink-B13JTp9D.js → backlink-C2jbzu0U.js} +16 -15
- package/dist/backlink-JbBNi3qg.cjs +13 -0
- package/dist/{button-CDocR7iN.cjs → button-B8rdtaHB.cjs} +1 -1
- package/dist/{button-DrrEvUy9.js → button-DhispFOY.js} +1 -0
- package/dist/{card-uccD6Pnv.cjs → card-BUITGoqX.cjs} +10 -10
- package/dist/{card-BI1NZONj.js → card-Dtw26f7i.js} +96 -76
- package/dist/checkbox-Gn7Wtk9h.cjs +31 -0
- package/dist/checkbox-ym7z6cpt.js +142 -0
- package/dist/{combobox-BhcqC30d.cjs → combobox-DjO0RMUB.cjs} +1 -1
- package/dist/{combobox-D9dGKWuZ.js → combobox-yE4aYhTi.js} +1 -1
- package/dist/{consent-CftYu8Di.js → consent-BpcQFvbi.js} +1 -1
- package/dist/{consent-DrS71kvz.cjs → consent-hYeFWNFr.cjs} +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/pkt-alert.cjs +1 -1
- package/dist/pkt-alert.js +1 -1
- package/dist/pkt-backlink.cjs +1 -1
- package/dist/pkt-backlink.js +1 -1
- package/dist/pkt-button.cjs +1 -1
- package/dist/pkt-button.js +1 -1
- package/dist/pkt-card.cjs +1 -1
- package/dist/pkt-card.js +1 -1
- package/dist/pkt-checkbox.cjs +1 -1
- package/dist/pkt-checkbox.js +1 -1
- package/dist/pkt-combobox.cjs +1 -1
- package/dist/pkt-combobox.js +1 -1
- package/dist/pkt-consent.cjs +1 -1
- package/dist/pkt-consent.js +1 -1
- package/dist/pkt-index.cjs +1 -1
- package/dist/pkt-index.js +7 -7
- package/package.json +2 -2
- package/src/components/accordion/accordion.test.ts +0 -4
- package/src/components/alert/alert.test.ts +348 -0
- package/src/components/alert/alert.ts +1 -0
- package/src/components/backlink/backlink.test.ts +286 -0
- package/src/components/backlink/backlink.ts +5 -3
- package/src/components/button/button.test.ts +514 -0
- package/src/components/button/button.ts +2 -0
- package/src/components/card/card.test.ts +592 -0
- package/src/components/card/card.ts +24 -1
- package/src/components/checkbox/checkbox.test.ts +535 -0
- package/src/components/checkbox/checkbox.ts +44 -1
- package/src/components/combobox/combobox.test.ts +737 -0
- package/src/components/combobox/combobox.ts +1 -1
- package/dist/backlink-C5jQRMwJ.cjs +0 -13
- package/dist/checkbox-CTRbpbye.js +0 -120
- package/dist/checkbox-wJ26voZd.cjs +0 -30
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
3
|
+
import { fireEvent } from '@testing-library/dom'
|
|
4
|
+
|
|
5
|
+
expect.extend(toHaveNoViolations)
|
|
6
|
+
|
|
7
|
+
import './checkbox'
|
|
8
|
+
import { PktCheckbox } from './checkbox'
|
|
9
|
+
|
|
10
|
+
const waitForCustomElements = async () => {
|
|
11
|
+
await customElements.whenDefined('pkt-checkbox')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Helper function to create checkbox markup
|
|
15
|
+
const createCheckbox = async (checkboxProps = '') => {
|
|
16
|
+
const container = document.createElement('div')
|
|
17
|
+
container.innerHTML = `
|
|
18
|
+
<pkt-checkbox ${checkboxProps}></pkt-checkbox>
|
|
19
|
+
`
|
|
20
|
+
document.body.appendChild(container)
|
|
21
|
+
await waitForCustomElements()
|
|
22
|
+
return container
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Cleanup after each test
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
document.body.innerHTML = ''
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe('PktCheckbox', () => {
|
|
31
|
+
describe('Rendering and basic functionality', () => {
|
|
32
|
+
test('renders without errors', async () => {
|
|
33
|
+
const container = await createCheckbox('id="test-checkbox" name="test" label="Test Checkbox"')
|
|
34
|
+
|
|
35
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
36
|
+
expect(checkbox).toBeInTheDocument()
|
|
37
|
+
|
|
38
|
+
await checkbox.updateComplete
|
|
39
|
+
expect(checkbox).toBeTruthy()
|
|
40
|
+
|
|
41
|
+
const inputElement = checkbox.querySelector('input[type="checkbox"]')
|
|
42
|
+
expect(inputElement).toBeInTheDocument()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('renders with correct structure', async () => {
|
|
46
|
+
const container = await createCheckbox('id="test" name="test" label="Test Label"')
|
|
47
|
+
|
|
48
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
49
|
+
await checkbox.updateComplete
|
|
50
|
+
|
|
51
|
+
const wrapper = checkbox.querySelector('.pkt-input-check')
|
|
52
|
+
const inputDiv = wrapper?.querySelector('.pkt-input-check__input')
|
|
53
|
+
const input = inputDiv?.querySelector('.pkt-input-check__input-checkbox')
|
|
54
|
+
const label = inputDiv?.querySelector('.pkt-input-check__input-label')
|
|
55
|
+
|
|
56
|
+
expect(wrapper).toBeInTheDocument()
|
|
57
|
+
expect(inputDiv).toBeInTheDocument()
|
|
58
|
+
expect(input).toBeInTheDocument()
|
|
59
|
+
expect(label).toBeInTheDocument()
|
|
60
|
+
expect(label?.textContent?.trim()).toBe('Test Label')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('renders input with correct attributes', async () => {
|
|
64
|
+
const container = await createCheckbox('id="test-id" name="test-name" value="test-value"')
|
|
65
|
+
|
|
66
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
67
|
+
await checkbox.updateComplete
|
|
68
|
+
|
|
69
|
+
const input = checkbox.querySelector('input[type="checkbox"]')
|
|
70
|
+
expect(input?.getAttribute('id')).toBe('test-id-internal')
|
|
71
|
+
expect(input?.getAttribute('name')).toBe('test-name-internal')
|
|
72
|
+
expect(input?.getAttribute('type')).toBe('checkbox')
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe('Properties and attributes', () => {
|
|
77
|
+
test('applies default properties correctly', async () => {
|
|
78
|
+
const container = await createCheckbox('id="test" name="test"')
|
|
79
|
+
|
|
80
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
81
|
+
await checkbox.updateComplete
|
|
82
|
+
|
|
83
|
+
expect(checkbox.value).toBe('')
|
|
84
|
+
expect(checkbox.checked).toBe(false)
|
|
85
|
+
expect(checkbox.defaultChecked).toBe(false)
|
|
86
|
+
expect(checkbox.hasTile).toBe(false)
|
|
87
|
+
expect(checkbox.isSwitch).toBe(false)
|
|
88
|
+
expect(checkbox.labelPosition).toBe('right')
|
|
89
|
+
expect(checkbox.hideLabel).toBe(false)
|
|
90
|
+
expect(checkbox.disabled).toBe(false)
|
|
91
|
+
expect(checkbox.optionalTag).toBe(false)
|
|
92
|
+
expect(checkbox.requiredTag).toBe(false)
|
|
93
|
+
|
|
94
|
+
const input = checkbox.querySelector('input[type="checkbox"]')
|
|
95
|
+
expect(input?.getAttribute('role')).toBe('checkbox')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('handles value property correctly', async () => {
|
|
99
|
+
const container = await createCheckbox('id="test" name="test" value="test-value"')
|
|
100
|
+
|
|
101
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
102
|
+
await checkbox.updateComplete
|
|
103
|
+
|
|
104
|
+
expect(checkbox.value).toBe('test-value')
|
|
105
|
+
expect(checkbox.getAttribute('value')).toBe('test-value')
|
|
106
|
+
|
|
107
|
+
// Test value updates
|
|
108
|
+
checkbox.value = 'updated-value'
|
|
109
|
+
await checkbox.updateComplete
|
|
110
|
+
|
|
111
|
+
expect(checkbox.value).toBe('updated-value')
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('handles checked property correctly', async () => {
|
|
115
|
+
const container = await createCheckbox('id="test" name="test" checked')
|
|
116
|
+
|
|
117
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
118
|
+
await checkbox.updateComplete
|
|
119
|
+
|
|
120
|
+
expect(checkbox.checked).toBe(true)
|
|
121
|
+
|
|
122
|
+
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
|
|
123
|
+
expect(input?.checked).toBe(true)
|
|
124
|
+
|
|
125
|
+
// Test unchecked
|
|
126
|
+
checkbox.checked = false
|
|
127
|
+
await checkbox.updateComplete
|
|
128
|
+
|
|
129
|
+
expect(input?.checked).toBe(false)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('handles defaultChecked property correctly', async () => {
|
|
133
|
+
const container = await createCheckbox('id="test" name="test"')
|
|
134
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
135
|
+
|
|
136
|
+
// Set defaultChecked before checked is set
|
|
137
|
+
checkbox.defaultChecked = true
|
|
138
|
+
await checkbox.updateComplete
|
|
139
|
+
|
|
140
|
+
expect(checkbox.defaultChecked).toBe(true)
|
|
141
|
+
expect(checkbox.checked).toBe(true)
|
|
142
|
+
|
|
143
|
+
// Wait for another update cycle to ensure DOM is updated
|
|
144
|
+
await checkbox.updateComplete
|
|
145
|
+
|
|
146
|
+
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
|
|
147
|
+
expect(input?.checked).toBe(true)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('handles disabled property correctly', async () => {
|
|
151
|
+
const container = await createCheckbox('id="test" name="test" disabled')
|
|
152
|
+
|
|
153
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
154
|
+
await checkbox.updateComplete
|
|
155
|
+
|
|
156
|
+
expect(checkbox.disabled).toBe(true)
|
|
157
|
+
|
|
158
|
+
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
|
|
159
|
+
const label = checkbox.querySelector('.pkt-input-check__input-label')
|
|
160
|
+
expect(input?.disabled).toBe(true)
|
|
161
|
+
expect(label).toHaveClass('pkt-input-check__input-label--disabled')
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
test('handles labelPosition property correctly', async () => {
|
|
165
|
+
// Test left position
|
|
166
|
+
const container = await createCheckbox('id="test" name="test" label="Test"')
|
|
167
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
168
|
+
checkbox.labelPosition = 'left'
|
|
169
|
+
await checkbox.updateComplete
|
|
170
|
+
|
|
171
|
+
expect(checkbox.labelPosition).toBe('left')
|
|
172
|
+
|
|
173
|
+
let label = checkbox.querySelector('.pkt-input-check__input-label')
|
|
174
|
+
expect(label).toHaveClass('pkt-input-check__input-label--left')
|
|
175
|
+
|
|
176
|
+
// Test right position (default)
|
|
177
|
+
checkbox.labelPosition = 'right'
|
|
178
|
+
await checkbox.updateComplete
|
|
179
|
+
|
|
180
|
+
// Re-query the label element after DOM change
|
|
181
|
+
label = checkbox.querySelector('.pkt-input-check__input-label')
|
|
182
|
+
expect(label).toHaveClass('pkt-input-check__input-label--right')
|
|
183
|
+
expect(label).not.toHaveClass('pkt-input-check__input-label--left')
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('handles hideLabel property correctly', async () => {
|
|
187
|
+
const container = await createCheckbox('id="test" name="test" label="Hidden Label"')
|
|
188
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
189
|
+
checkbox.hideLabel = true
|
|
190
|
+
await checkbox.updateComplete
|
|
191
|
+
|
|
192
|
+
expect(checkbox.hideLabel).toBe(true)
|
|
193
|
+
|
|
194
|
+
const label = checkbox.querySelector('.pkt-input-check__input-label')
|
|
195
|
+
expect(label).toHaveClass('pkt-sr-only')
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('handles isSwitch property correctly', async () => {
|
|
199
|
+
const container = await createCheckbox('id="test" name="test"')
|
|
200
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
201
|
+
checkbox.isSwitch = true
|
|
202
|
+
await checkbox.updateComplete
|
|
203
|
+
|
|
204
|
+
expect(checkbox.isSwitch).toBe(true)
|
|
205
|
+
|
|
206
|
+
const input = checkbox.querySelector('input[type="checkbox"]')
|
|
207
|
+
expect(input?.getAttribute('role')).toBe('switch')
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
test('handles hasTile property correctly', async () => {
|
|
211
|
+
const container = await createCheckbox('id="test" name="test"')
|
|
212
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
213
|
+
checkbox.hasTile = true
|
|
214
|
+
await checkbox.updateComplete
|
|
215
|
+
|
|
216
|
+
expect(checkbox.hasTile).toBe(true)
|
|
217
|
+
|
|
218
|
+
const inputDiv = checkbox.querySelector('.pkt-input-check__input')
|
|
219
|
+
expect(inputDiv).toHaveClass('pkt-input-check__input--tile')
|
|
220
|
+
|
|
221
|
+
// Test disabled with tile
|
|
222
|
+
checkbox.disabled = true
|
|
223
|
+
await checkbox.updateComplete
|
|
224
|
+
|
|
225
|
+
expect(inputDiv).toHaveClass('pkt-input-check__input--tile-disabled')
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
describe('Label and helptext functionality', () => {
|
|
230
|
+
test('renders label correctly', async () => {
|
|
231
|
+
const container = await createCheckbox('id="test" name="test" label="Checkbox Label"')
|
|
232
|
+
|
|
233
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
234
|
+
await checkbox.updateComplete
|
|
235
|
+
|
|
236
|
+
expect(checkbox.label).toBe('Checkbox Label')
|
|
237
|
+
|
|
238
|
+
const label = checkbox.querySelector('.pkt-input-check__input-label')
|
|
239
|
+
expect(label?.textContent?.trim()).toBe('Checkbox Label')
|
|
240
|
+
expect(label?.getAttribute('for')).toBe('test-internal')
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
test('renders checkHelptext when provided', async () => {
|
|
244
|
+
const container = await createCheckbox(
|
|
245
|
+
'id="test" name="test" label="Test" checkHelptext="This is help text"',
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
249
|
+
await checkbox.updateComplete
|
|
250
|
+
|
|
251
|
+
expect(checkbox.checkHelptext).toBe('This is help text')
|
|
252
|
+
|
|
253
|
+
const helptext = checkbox.querySelector('.pkt-input-check__input-helptext')
|
|
254
|
+
expect(helptext).toBeInTheDocument()
|
|
255
|
+
expect(helptext?.textContent).toBe('This is help text')
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
test('does not render helptext when not provided', async () => {
|
|
259
|
+
const container = await createCheckbox('id="test" name="test" label="Test"')
|
|
260
|
+
|
|
261
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
262
|
+
await checkbox.updateComplete
|
|
263
|
+
|
|
264
|
+
const helptext = checkbox.querySelector('.pkt-input-check__input-helptext')
|
|
265
|
+
expect(helptext).not.toBeInTheDocument()
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
describe('Tag functionality', () => {
|
|
270
|
+
test('renders custom tagText when provided', async () => {
|
|
271
|
+
const container = await createCheckbox(
|
|
272
|
+
'id="test" name="test" label="Test" tagText="Custom Tag"',
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
276
|
+
await checkbox.updateComplete
|
|
277
|
+
|
|
278
|
+
expect(checkbox.tagText).toBe('Custom Tag')
|
|
279
|
+
|
|
280
|
+
const tagElement = checkbox.querySelector('.pkt-tag--gray')
|
|
281
|
+
expect(tagElement).toBeInTheDocument()
|
|
282
|
+
expect(tagElement?.textContent).toBe('Custom Tag')
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
test('renders optional tag when optionalTag is true', async () => {
|
|
286
|
+
const container = await createCheckbox('id="test" name="test" label="Test" optionalTag')
|
|
287
|
+
|
|
288
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
289
|
+
await checkbox.updateComplete
|
|
290
|
+
|
|
291
|
+
expect(checkbox.optionalTag).toBe(true)
|
|
292
|
+
expect(checkbox.optionalText).toBe('Valgfritt')
|
|
293
|
+
|
|
294
|
+
const optionalTag = checkbox.querySelector('.pkt-tag--blue-light')
|
|
295
|
+
expect(optionalTag).toBeInTheDocument()
|
|
296
|
+
expect(optionalTag?.textContent).toBe('Valgfritt')
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
test('renders required tag when requiredTag is true', async () => {
|
|
300
|
+
const container = await createCheckbox('id="test" name="test" label="Test" requiredTag')
|
|
301
|
+
|
|
302
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
303
|
+
await checkbox.updateComplete
|
|
304
|
+
|
|
305
|
+
expect(checkbox.requiredTag).toBe(true)
|
|
306
|
+
expect(checkbox.requiredText).toBe('Må fylles ut')
|
|
307
|
+
|
|
308
|
+
const requiredTag = checkbox.querySelector('.pkt-tag--beige')
|
|
309
|
+
expect(requiredTag).toBeInTheDocument()
|
|
310
|
+
expect(requiredTag?.textContent).toBe('Må fylles ut')
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
test('renders custom optional and required text', async () => {
|
|
314
|
+
const container = await createCheckbox(
|
|
315
|
+
'id="test" name="test" label="Test" optionalTag optionalText="Custom Optional" requiredTag requiredText="Custom Required"',
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
319
|
+
await checkbox.updateComplete
|
|
320
|
+
|
|
321
|
+
expect(checkbox.optionalText).toBe('Custom Optional')
|
|
322
|
+
expect(checkbox.requiredText).toBe('Custom Required')
|
|
323
|
+
|
|
324
|
+
const optionalTag = checkbox.querySelector('.pkt-tag--blue-light')
|
|
325
|
+
const requiredTag = checkbox.querySelector('.pkt-tag--beige')
|
|
326
|
+
expect(optionalTag?.textContent).toBe('Custom Optional')
|
|
327
|
+
expect(requiredTag?.textContent).toBe('Custom Required')
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
test('renders multiple tags when multiple are enabled', async () => {
|
|
331
|
+
const container = await createCheckbox(
|
|
332
|
+
'id="test" name="test" label="Test" tagText="Custom" optionalTag requiredTag',
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
336
|
+
await checkbox.updateComplete
|
|
337
|
+
|
|
338
|
+
const customTag = checkbox.querySelector('.pkt-tag--gray')
|
|
339
|
+
const optionalTag = checkbox.querySelector('.pkt-tag--blue-light')
|
|
340
|
+
const requiredTag = checkbox.querySelector('.pkt-tag--beige')
|
|
341
|
+
|
|
342
|
+
expect(customTag).toBeInTheDocument()
|
|
343
|
+
expect(optionalTag).toBeInTheDocument()
|
|
344
|
+
expect(requiredTag).toBeInTheDocument()
|
|
345
|
+
})
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
describe('User interaction', () => {
|
|
349
|
+
test('toggles checked state when clicked', async () => {
|
|
350
|
+
const container = await createCheckbox('id="test" name="test" label="Test"')
|
|
351
|
+
|
|
352
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
353
|
+
await checkbox.updateComplete
|
|
354
|
+
|
|
355
|
+
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
|
|
356
|
+
expect(input.checked).toBe(false)
|
|
357
|
+
|
|
358
|
+
// Click to check
|
|
359
|
+
fireEvent.click(input)
|
|
360
|
+
await checkbox.updateComplete
|
|
361
|
+
|
|
362
|
+
expect(input.checked).toBe(true)
|
|
363
|
+
expect(checkbox.checked).toBe(true)
|
|
364
|
+
|
|
365
|
+
// Click to uncheck
|
|
366
|
+
fireEvent.click(input)
|
|
367
|
+
await checkbox.updateComplete
|
|
368
|
+
|
|
369
|
+
expect(input.checked).toBe(false)
|
|
370
|
+
expect(checkbox.checked).toBe(false)
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
test('does not toggle when disabled', async () => {
|
|
374
|
+
const container = await createCheckbox('id="test" name="test" label="Test" disabled')
|
|
375
|
+
|
|
376
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
377
|
+
await checkbox.updateComplete
|
|
378
|
+
|
|
379
|
+
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
|
|
380
|
+
expect(input.checked).toBe(false)
|
|
381
|
+
expect(input.disabled).toBe(true)
|
|
382
|
+
|
|
383
|
+
// Try to click
|
|
384
|
+
fireEvent.click(input)
|
|
385
|
+
await checkbox.updateComplete
|
|
386
|
+
|
|
387
|
+
// Should remain unchecked
|
|
388
|
+
expect(input.checked).toBe(false)
|
|
389
|
+
expect(checkbox.checked).toBe(false)
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
test('handles focus and blur events', async () => {
|
|
393
|
+
const container = await createCheckbox('id="test" name="test" label="Test"')
|
|
394
|
+
|
|
395
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
396
|
+
await checkbox.updateComplete
|
|
397
|
+
|
|
398
|
+
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
|
|
399
|
+
|
|
400
|
+
// Test focus
|
|
401
|
+
fireEvent.focus(input)
|
|
402
|
+
await checkbox.updateComplete
|
|
403
|
+
|
|
404
|
+
// Test blur
|
|
405
|
+
fireEvent.blur(input)
|
|
406
|
+
await checkbox.updateComplete
|
|
407
|
+
|
|
408
|
+
// These should not throw errors and the element should remain functional
|
|
409
|
+
expect(checkbox).toBeInTheDocument()
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
test('marks as touched when interacted with', async () => {
|
|
413
|
+
const container = await createCheckbox('id="test" name="test" label="Test"')
|
|
414
|
+
|
|
415
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
416
|
+
await checkbox.updateComplete
|
|
417
|
+
|
|
418
|
+
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
|
|
419
|
+
|
|
420
|
+
// Initially not touched
|
|
421
|
+
expect(checkbox.touched).toBe(false)
|
|
422
|
+
|
|
423
|
+
// Click to interact
|
|
424
|
+
fireEvent.click(input)
|
|
425
|
+
await checkbox.updateComplete
|
|
426
|
+
|
|
427
|
+
// Should be marked as touched
|
|
428
|
+
expect(checkbox.touched).toBe(true)
|
|
429
|
+
})
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
describe('Label position and structure', () => {
|
|
433
|
+
test('places label on the right by default', async () => {
|
|
434
|
+
const container = await createCheckbox('id="test" name="test" label="Right Label"')
|
|
435
|
+
|
|
436
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
437
|
+
await checkbox.updateComplete
|
|
438
|
+
|
|
439
|
+
const inputDiv = checkbox.querySelector('.pkt-input-check__input')
|
|
440
|
+
const children = Array.from(inputDiv?.children || [])
|
|
441
|
+
|
|
442
|
+
// Should have input first, then label
|
|
443
|
+
expect(children[0]).toHaveAttribute('type', 'checkbox')
|
|
444
|
+
expect(children[1]).toHaveClass('pkt-input-check__input-label')
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
test('places label on the left when labelPosition is left', async () => {
|
|
448
|
+
const container = await createCheckbox(
|
|
449
|
+
'id="test" name="test" label="Left Label" labelPosition="left"',
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
453
|
+
await checkbox.updateComplete
|
|
454
|
+
|
|
455
|
+
const inputDiv = checkbox.querySelector('.pkt-input-check__input')
|
|
456
|
+
const children = Array.from(inputDiv?.children || [])
|
|
457
|
+
|
|
458
|
+
// Should have label first, then input
|
|
459
|
+
expect(children[0]).toHaveClass('pkt-input-check__input-label')
|
|
460
|
+
expect(children[1]).toHaveAttribute('type', 'checkbox')
|
|
461
|
+
})
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
describe('Error handling', () => {
|
|
465
|
+
test('applies error styling when hasError is true', async () => {
|
|
466
|
+
const container = await createCheckbox('id="test" name="test" label="Test"')
|
|
467
|
+
|
|
468
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
469
|
+
checkbox.hasError = true
|
|
470
|
+
await checkbox.updateComplete
|
|
471
|
+
|
|
472
|
+
const input = checkbox.querySelector('.pkt-input-check__input-checkbox')
|
|
473
|
+
expect(input).toHaveClass('pkt-input-check__input-checkbox--error')
|
|
474
|
+
})
|
|
475
|
+
|
|
476
|
+
test('does not apply error styling when hasError is false', async () => {
|
|
477
|
+
const container = await createCheckbox('id="test" name="test" label="Test"')
|
|
478
|
+
|
|
479
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
480
|
+
await checkbox.updateComplete
|
|
481
|
+
|
|
482
|
+
const input = checkbox.querySelector('.pkt-input-check__input-checkbox')
|
|
483
|
+
expect(input).not.toHaveClass('pkt-input-check__input-checkbox--error')
|
|
484
|
+
})
|
|
485
|
+
})
|
|
486
|
+
|
|
487
|
+
describe('Accessibility', () => {
|
|
488
|
+
test('has no accessibility violations', async () => {
|
|
489
|
+
const container = await createCheckbox(
|
|
490
|
+
'id="accessible-checkbox" name="test" label="Accessible Checkbox"',
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
494
|
+
await checkbox.updateComplete
|
|
495
|
+
|
|
496
|
+
const results = await axe(checkbox)
|
|
497
|
+
expect(results).toHaveNoViolations()
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
test('associates label with input correctly', async () => {
|
|
501
|
+
const container = await createCheckbox(
|
|
502
|
+
'id="test-association" name="test" label="Associated Label"',
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
506
|
+
await checkbox.updateComplete
|
|
507
|
+
|
|
508
|
+
const input = checkbox.querySelector('input[type="checkbox"]')
|
|
509
|
+
const label = checkbox.querySelector('.pkt-input-check__input-label')
|
|
510
|
+
|
|
511
|
+
expect(input?.getAttribute('id')).toBe('test-association-internal')
|
|
512
|
+
expect(label?.getAttribute('for')).toBe('test-association-internal')
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
test('has correct role for switch when isSwitch is true', async () => {
|
|
516
|
+
const container = await createCheckbox('id="test" name="test" label="Test Switch" isSwitch')
|
|
517
|
+
|
|
518
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
519
|
+
await checkbox.updateComplete
|
|
520
|
+
|
|
521
|
+
const input = checkbox.querySelector('input[type="checkbox"]')
|
|
522
|
+
expect(input?.getAttribute('role')).toBe('switch')
|
|
523
|
+
})
|
|
524
|
+
|
|
525
|
+
test('has correct role for checkbox by default', async () => {
|
|
526
|
+
const container = await createCheckbox('id="test" name="test" label="Test Checkbox"')
|
|
527
|
+
|
|
528
|
+
const checkbox = container.querySelector('pkt-checkbox') as PktCheckbox
|
|
529
|
+
await checkbox.updateComplete
|
|
530
|
+
|
|
531
|
+
const input = checkbox.querySelector('input[type="checkbox"]')
|
|
532
|
+
expect(input?.getAttribute('role')).toBe('checkbox')
|
|
533
|
+
})
|
|
534
|
+
})
|
|
535
|
+
})
|
|
@@ -45,6 +45,13 @@ export class PktCheckbox extends PktInputElement {
|
|
|
45
45
|
super.firstUpdated(_changedProperties)
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
protected updated(changedProperties: PropertyValues): void {
|
|
49
|
+
if (changedProperties.has('defaultChecked') && !this.checked) {
|
|
50
|
+
this.checked = this.defaultChecked
|
|
51
|
+
}
|
|
52
|
+
super.updated(changedProperties)
|
|
53
|
+
}
|
|
54
|
+
|
|
48
55
|
render() {
|
|
49
56
|
const inputTileClasses = classMap({
|
|
50
57
|
'pkt-input-check__input': true,
|
|
@@ -103,7 +110,8 @@ export class PktCheckbox extends PktInputElement {
|
|
|
103
110
|
?disabled=${this.disabled}
|
|
104
111
|
name=${this.name + '-internal'}
|
|
105
112
|
${ref(this.inputRef)}
|
|
106
|
-
@change=${this.
|
|
113
|
+
@change=${this.handleChange}
|
|
114
|
+
@click=${this.handleClick}
|
|
107
115
|
@blur=${this.onBlur}
|
|
108
116
|
@focus=${this.onFocus}
|
|
109
117
|
?checked=${this.checked}
|
|
@@ -115,7 +123,42 @@ export class PktCheckbox extends PktInputElement {
|
|
|
115
123
|
`
|
|
116
124
|
}
|
|
117
125
|
|
|
126
|
+
private handleClick(e: Event) {
|
|
127
|
+
// Prevent click on disabled checkbox
|
|
128
|
+
if (this.disabled) {
|
|
129
|
+
e.preventDefault()
|
|
130
|
+
e.stopImmediatePropagation()
|
|
131
|
+
return false
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private handleChange(e: Event) {
|
|
136
|
+
// Don't process change if disabled
|
|
137
|
+
if (this.disabled) {
|
|
138
|
+
e.preventDefault()
|
|
139
|
+
e.stopImmediatePropagation()
|
|
140
|
+
return false
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.toggleChecked(e)
|
|
144
|
+
}
|
|
145
|
+
|
|
118
146
|
private toggleChecked(e: Event) {
|
|
147
|
+
// Don't toggle if disabled
|
|
148
|
+
if (this.disabled) {
|
|
149
|
+
e.preventDefault()
|
|
150
|
+
e.stopImmediatePropagation()
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Also check if the input element itself is disabled
|
|
155
|
+
const target = e.target as HTMLInputElement
|
|
156
|
+
if (target && target.disabled) {
|
|
157
|
+
e.preventDefault()
|
|
158
|
+
e.stopImmediatePropagation()
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
|
|
119
162
|
e.stopImmediatePropagation()
|
|
120
163
|
this.touched = true
|
|
121
164
|
if (this.inputRef.value) {
|