@oslokommune/punkt-elements 13.5.5 → 13.5.9
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/package.json +9 -9
- package/src/components/alert/alert.test.ts +3 -2
- package/src/components/button/button.test.ts +6 -5
- package/src/components/card/card.test.ts +5 -4
- package/src/components/combobox/combobox.test.ts +2 -1
- package/src/components/consent/consent.test.ts +22 -21
- package/src/components/heading/heading.test.ts +2 -1
- package/src/components/icon/icon.test.ts +3 -2
- package/src/components/link/link.test.ts +2 -1
- package/src/components/messagebox/messagebox.test.ts +3 -2
- package/src/components/radiobutton/radiobutton.test.ts +3 -2
- package/src/components/select/select.test.ts +3 -2
- package/src/components/tag/tag.test.ts +212 -0
- package/src/components/textarea/textarea.test.ts +289 -0
- package/src/components/textinput/textinput.test.ts +421 -0
|
@@ -0,0 +1,289 @@
|
|
|
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 './textarea'
|
|
7
|
+
|
|
8
|
+
export interface TextareaTestConfig extends BaseTestConfig {
|
|
9
|
+
// From PktTextarea specific properties
|
|
10
|
+
value?: string
|
|
11
|
+
autocomplete?: string
|
|
12
|
+
rows?: number | null
|
|
13
|
+
|
|
14
|
+
// From PktInputElement base class (commonly used ones)
|
|
15
|
+
id?: string
|
|
16
|
+
label?: string
|
|
17
|
+
name?: string
|
|
18
|
+
disabled?: boolean
|
|
19
|
+
readonly?: boolean
|
|
20
|
+
required?: boolean
|
|
21
|
+
placeholder?: string | null
|
|
22
|
+
maxlength?: number | null
|
|
23
|
+
minlength?: number | null
|
|
24
|
+
hasError?: boolean
|
|
25
|
+
errorMessage?: string
|
|
26
|
+
helptext?: string
|
|
27
|
+
fullwidth?: boolean
|
|
28
|
+
counter?: boolean
|
|
29
|
+
inline?: boolean
|
|
30
|
+
ariaLabelledby?: string | null
|
|
31
|
+
ariaDescribedBy?: string | null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Use shared framework
|
|
35
|
+
export const createTextareaTest = async (config: TextareaTestConfig = {}) => {
|
|
36
|
+
const { container, element } = await createElementTest<
|
|
37
|
+
CustomElementFor<'pkt-textarea'>,
|
|
38
|
+
TextareaTestConfig
|
|
39
|
+
>('pkt-textarea', config)
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
container,
|
|
43
|
+
textarea: element,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
expect.extend(toHaveNoViolations)
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
document.body.innerHTML = ''
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
describe('PktTextarea', () => {
|
|
54
|
+
describe('Basic Rendering', () => {
|
|
55
|
+
test('renders without errors', async () => {
|
|
56
|
+
const { textarea } = await createTextareaTest()
|
|
57
|
+
expect(textarea).toBeInTheDocument()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('renders with default properties', async () => {
|
|
61
|
+
const { textarea } = await createTextareaTest()
|
|
62
|
+
expect(textarea.value).toBe('')
|
|
63
|
+
expect(textarea.autocomplete).toBe('off')
|
|
64
|
+
expect(textarea.rows).toBe(null)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('renders textarea element', async () => {
|
|
68
|
+
const { textarea } = await createTextareaTest()
|
|
69
|
+
const textareaElement = textarea.querySelector('textarea')
|
|
70
|
+
expect(textareaElement).toBeInTheDocument()
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
describe('Properties and Attributes', () => {
|
|
75
|
+
test('sets value correctly', async () => {
|
|
76
|
+
const value = 'Test textarea content'
|
|
77
|
+
const { textarea } = await createTextareaTest({ value })
|
|
78
|
+
|
|
79
|
+
expect(textarea.value).toBe(value)
|
|
80
|
+
const textareaElement = textarea.querySelector('textarea') as HTMLTextAreaElement
|
|
81
|
+
expect(textareaElement.value).toBe(value)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('sets rows correctly', async () => {
|
|
85
|
+
const { textarea } = await createTextareaTest({ rows: 5 })
|
|
86
|
+
|
|
87
|
+
expect(textarea.rows).toBe(5)
|
|
88
|
+
const textareaElement = textarea.querySelector('textarea')
|
|
89
|
+
expect(textareaElement?.getAttribute('rows')).toBe('5')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('sets autocomplete correctly', async () => {
|
|
93
|
+
const { textarea } = await createTextareaTest({ autocomplete: 'on' })
|
|
94
|
+
|
|
95
|
+
expect(textarea.autocomplete).toBe('on')
|
|
96
|
+
const textareaElement = textarea.querySelector('textarea')
|
|
97
|
+
expect(textareaElement?.getAttribute('autocomplete')).toBe('on')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('handles disabled state', async () => {
|
|
101
|
+
const { textarea } = await createTextareaTest({ disabled: true })
|
|
102
|
+
|
|
103
|
+
const textareaElement = textarea.querySelector('textarea')
|
|
104
|
+
expect(textareaElement?.hasAttribute('disabled')).toBe(true)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('handles readonly state', async () => {
|
|
108
|
+
const { textarea } = await createTextareaTest({ readonly: true })
|
|
109
|
+
|
|
110
|
+
const textareaElement = textarea.querySelector('textarea')
|
|
111
|
+
expect(textareaElement?.hasAttribute('readonly')).toBe(true)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('handles required state', async () => {
|
|
115
|
+
const { textarea } = await createTextareaTest({ required: true })
|
|
116
|
+
|
|
117
|
+
const textareaElement = textarea.querySelector('textarea')
|
|
118
|
+
expect(textareaElement?.hasAttribute('required')).toBe(false) // Not set as attribute on textarea
|
|
119
|
+
|
|
120
|
+
const inputWrapper = textarea.querySelector('pkt-input-wrapper')
|
|
121
|
+
expect(inputWrapper?.hasAttribute('required')).toBe(true) // But passed to wrapper
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
describe('Input Wrapper Integration', () => {
|
|
126
|
+
test('displays label correctly', async () => {
|
|
127
|
+
const { textarea } = await createTextareaTest({ label: 'Comment' })
|
|
128
|
+
|
|
129
|
+
const inputWrapper = textarea.querySelector('pkt-input-wrapper')
|
|
130
|
+
expect(inputWrapper?.getAttribute('label')).toBe('Comment')
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
test('displays helptext correctly', async () => {
|
|
134
|
+
const { textarea } = await createTextareaTest({ helptext: 'Enter your message' })
|
|
135
|
+
|
|
136
|
+
// helptext is passed as a property, not attribute to input-wrapper
|
|
137
|
+
expect(textarea.helptext).toBe('Enter your message')
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
test('handles error state', async () => {
|
|
141
|
+
const { textarea } = await createTextareaTest({
|
|
142
|
+
hasError: true,
|
|
143
|
+
errorMessage: 'This field is required',
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
expect(textarea.hasError).toBe(true)
|
|
147
|
+
expect(textarea.errorMessage).toBe('This field is required')
|
|
148
|
+
|
|
149
|
+
const inputWrapper = textarea.querySelector('pkt-input-wrapper')
|
|
150
|
+
expect(inputWrapper?.hasAttribute('hasError')).toBe(true)
|
|
151
|
+
|
|
152
|
+
const textareaElement = textarea.querySelector('textarea')
|
|
153
|
+
expect(textareaElement?.getAttribute('aria-invalid')).toBe('true')
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
test('handles fullwidth styling', async () => {
|
|
157
|
+
const { textarea } = await createTextareaTest({ fullwidth: true })
|
|
158
|
+
|
|
159
|
+
const textareaElement = textarea.querySelector('textarea')
|
|
160
|
+
expect(textareaElement?.className).toContain('pkt-input--fullwidth')
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
describe('Character Counter', () => {
|
|
165
|
+
test('shows counter when enabled', async () => {
|
|
166
|
+
const { textarea } = await createTextareaTest({
|
|
167
|
+
counter: true,
|
|
168
|
+
maxlength: 100,
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
const inputWrapper = textarea.querySelector('pkt-input-wrapper')
|
|
172
|
+
expect(inputWrapper?.hasAttribute('counter')).toBe(true)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
test('updates counter on value change', async () => {
|
|
176
|
+
const { textarea } = await createTextareaTest({
|
|
177
|
+
counter: true,
|
|
178
|
+
maxlength: 100,
|
|
179
|
+
value: 'Hello',
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
expect(textarea.counterCurrent).toBe(5)
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
describe('User Interaction', () => {
|
|
187
|
+
test('updates value on user input', async () => {
|
|
188
|
+
const { textarea } = await createTextareaTest()
|
|
189
|
+
const textareaElement = textarea.querySelector('textarea') as HTMLTextAreaElement
|
|
190
|
+
|
|
191
|
+
fireEvent.input(textareaElement, { target: { value: 'New content' } })
|
|
192
|
+
await textarea.updateComplete
|
|
193
|
+
|
|
194
|
+
expect(textarea.value).toBe('New content')
|
|
195
|
+
expect(textarea.touched).toBe(true)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('handles focus and blur events', async () => {
|
|
199
|
+
const { textarea } = await createTextareaTest()
|
|
200
|
+
const textareaElement = textarea.querySelector('textarea') as HTMLTextAreaElement
|
|
201
|
+
|
|
202
|
+
// Focus and input to trigger touched state
|
|
203
|
+
fireEvent.focus(textareaElement)
|
|
204
|
+
fireEvent.input(textareaElement, { target: { value: 'test input' } })
|
|
205
|
+
await textarea.updateComplete
|
|
206
|
+
fireEvent.blur(textareaElement)
|
|
207
|
+
await textarea.updateComplete
|
|
208
|
+
|
|
209
|
+
// Test that input with value change sets touched state
|
|
210
|
+
expect(textarea.touched).toBe(true)
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
describe('Validation', () => {
|
|
215
|
+
test('respects maxlength constraint', async () => {
|
|
216
|
+
const { textarea } = await createTextareaTest({ maxlength: 10 })
|
|
217
|
+
|
|
218
|
+
const textareaElement = textarea.querySelector('textarea')
|
|
219
|
+
expect(textareaElement?.getAttribute('maxlength')).toBe('10')
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
test('respects minlength constraint', async () => {
|
|
223
|
+
const { textarea } = await createTextareaTest({ minlength: 5 })
|
|
224
|
+
|
|
225
|
+
const textareaElement = textarea.querySelector('textarea')
|
|
226
|
+
expect(textareaElement?.getAttribute('minlength')).toBe('5')
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
describe('Accessibility', () => {
|
|
231
|
+
test('passes through accessibility attributes', async () => {
|
|
232
|
+
const { textarea } = await createTextareaTest({
|
|
233
|
+
ariaLabelledby: 'external-label',
|
|
234
|
+
ariaDescribedBy: 'external-description',
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
const textareaElement = textarea.querySelector('textarea')
|
|
238
|
+
expect(textareaElement?.getAttribute('aria-labelledby')).toBe('external-label')
|
|
239
|
+
|
|
240
|
+
// ariaDescribedBy is passed as property to input-wrapper
|
|
241
|
+
expect(textarea.ariaDescribedBy).toBe('external-description')
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
test('textarea is accessible', async () => {
|
|
245
|
+
const { textarea } = await createTextareaTest({
|
|
246
|
+
label: 'Message',
|
|
247
|
+
helptext: 'Enter your message here',
|
|
248
|
+
required: true,
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
const results = await axe(textarea)
|
|
252
|
+
expect(results).toHaveNoViolations()
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
describe('Complex Configuration', () => {
|
|
257
|
+
test('renders with all properties set', async () => {
|
|
258
|
+
const config: TextareaTestConfig = {
|
|
259
|
+
label: 'Feedback',
|
|
260
|
+
value: 'Initial feedback text',
|
|
261
|
+
placeholder: 'Enter your feedback...',
|
|
262
|
+
rows: 6,
|
|
263
|
+
maxlength: 500,
|
|
264
|
+
counter: true,
|
|
265
|
+
required: true,
|
|
266
|
+
helptext: 'Please provide detailed feedback',
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const { textarea } = await createTextareaTest(config)
|
|
270
|
+
|
|
271
|
+
expect(textarea.value).toBe(config.value)
|
|
272
|
+
expect(textarea.rows).toBe(config.rows)
|
|
273
|
+
expect(textarea.maxlength).toBe(config.maxlength)
|
|
274
|
+
expect(textarea.required).toBe(config.required)
|
|
275
|
+
|
|
276
|
+
const inputWrapper = textarea.querySelector('pkt-input-wrapper')
|
|
277
|
+
expect(inputWrapper?.getAttribute('label')).toBe(config.label)
|
|
278
|
+
expect(textarea.helptext).toBe(config.helptext) // Property, not attribute
|
|
279
|
+
expect(inputWrapper?.hasAttribute('counter')).toBe(true)
|
|
280
|
+
|
|
281
|
+
const textareaElement = textarea.querySelector('textarea')
|
|
282
|
+
expect(textareaElement?.getAttribute('placeholder')).toBe(config.placeholder)
|
|
283
|
+
expect(textareaElement?.getAttribute('rows')).toBe(String(config.rows))
|
|
284
|
+
expect(textareaElement?.getAttribute('maxlength')).toBe(String(config.maxlength))
|
|
285
|
+
// required is handled by input-wrapper, not set directly on textarea
|
|
286
|
+
expect(textarea.required).toBe(true)
|
|
287
|
+
})
|
|
288
|
+
})
|
|
289
|
+
})
|