@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.
@@ -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
+ })