@oslokommune/punkt-react 13.22.0 → 14.0.0
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 +44 -0
- package/dist/index.d.ts +78 -15
- package/dist/punkt-react.es.js +5090 -3965
- package/dist/punkt-react.umd.js +749 -448
- package/package.json +5 -5
- package/src/components/header/Header.test.tsx +33 -27
- package/src/components/header/Header.tsx +23 -342
- package/src/components/header/HeaderService.test.tsx +515 -0
- package/src/components/header/HeaderService.tsx +525 -0
- package/src/components/header/types.ts +90 -0
- package/src/components/headerUserMenu/UserMenu.test.tsx +75 -0
- package/src/components/headerUserMenu/UserMenu.tsx +173 -0
- package/src/components/index.ts +1 -0
- package/src/components/interfaces.ts +1 -0
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { vi } from 'vitest'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { render, screen, fireEvent } from '@testing-library/react'
|
|
5
|
+
|
|
6
|
+
//mock element width to control mobile/desktop behavior
|
|
7
|
+
let mockedWidth = 1280
|
|
8
|
+
|
|
9
|
+
vi.mock('../../hooks/useElementWidth', async () => {
|
|
10
|
+
return {
|
|
11
|
+
useElementWidth: () => mockedWidth,
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
import { PktHeaderService } from './HeaderService'
|
|
16
|
+
|
|
17
|
+
describe('PktHeaderService', () => {
|
|
18
|
+
// jsdom does not implement scrollTo; our scroll lock hook uses it
|
|
19
|
+
beforeAll(() => {
|
|
20
|
+
window.scrollTo = vi.fn()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
mockedWidth = 1280
|
|
25
|
+
vi.clearAllMocks()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('renders service name and link when provided', () => {
|
|
29
|
+
const { container } = render(<PktHeaderService serviceName="My Service" serviceLink="#" />)
|
|
30
|
+
const host = container.querySelector('.pkt-header-service__service-link') as HTMLElement | null
|
|
31
|
+
expect(host).toBeTruthy()
|
|
32
|
+
expect(screen.getByText('My Service')).toBeInTheDocument()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('applies compact class when compact=true', () => {
|
|
36
|
+
const { container } = render(<PktHeaderService serviceName="Svc" compact />)
|
|
37
|
+
const header = container.querySelector('.pkt-header-service')
|
|
38
|
+
expect(header).toHaveClass('pkt-header-service--compact')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('shows children inline on desktop, menu button on mobile', () => {
|
|
42
|
+
// Desktop
|
|
43
|
+
mockedWidth = 1440
|
|
44
|
+
const { rerender } = render(
|
|
45
|
+
<PktHeaderService serviceName="Svc">
|
|
46
|
+
<a className="pkt-header-service__slot-link" href="#">
|
|
47
|
+
Link
|
|
48
|
+
</a>
|
|
49
|
+
</PktHeaderService>,
|
|
50
|
+
)
|
|
51
|
+
expect(screen.getByText('Link')).toBeInTheDocument()
|
|
52
|
+
|
|
53
|
+
// Mobile
|
|
54
|
+
mockedWidth = 500
|
|
55
|
+
rerender(
|
|
56
|
+
<PktHeaderService serviceName="Svc">
|
|
57
|
+
<a className="pkt-header-service__slot-link" href="#">
|
|
58
|
+
Link
|
|
59
|
+
</a>
|
|
60
|
+
</PktHeaderService>,
|
|
61
|
+
)
|
|
62
|
+
expect(screen.getByRole('button', { name: 'Åpne meny' })).toBeInTheDocument()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('renders desktop search input when showSearch is true (desktop)', () => {
|
|
66
|
+
mockedWidth = 1400
|
|
67
|
+
render(<PktHeaderService serviceName="Svc" showSearch={true} />)
|
|
68
|
+
const search = screen.getByRole('searchbox', { name: 'Søk' })
|
|
69
|
+
expect(search).toBeInTheDocument()
|
|
70
|
+
expect(search).toHaveAttribute('id', 'header-service-search')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('renders mobile search toggle and opens search field when clicked (mobile)', () => {
|
|
74
|
+
mockedWidth = 500
|
|
75
|
+
render(<PktHeaderService serviceName="Svc" showSearch={true} />)
|
|
76
|
+
const btn = screen.getByRole('button', { name: 'Åpne søkefelt' })
|
|
77
|
+
fireEvent.click(btn)
|
|
78
|
+
// After opening, the mobile menu should show the search input
|
|
79
|
+
expect(screen.getByRole('searchbox', { name: 'Søk' })).toBeInTheDocument()
|
|
80
|
+
expect(screen.getByRole('searchbox')).toHaveAttribute('id', 'mobile-search-menu')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('renders user button on desktop and opens user menu on click', () => {
|
|
84
|
+
mockedWidth = 1400
|
|
85
|
+
render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel' }} />)
|
|
86
|
+
// Button shows both fullname and shortname (both 'Aksel' when no shortname provided)
|
|
87
|
+
const userBtn = screen.getByRole('button', { name: /Aksel/ })
|
|
88
|
+
expect(userBtn).toBeInTheDocument()
|
|
89
|
+
fireEvent.click(userBtn)
|
|
90
|
+
// User menu should be mounted
|
|
91
|
+
const menu = screen.getByRole('navigation')
|
|
92
|
+
expect(menu).toHaveClass('pkt-user-menu')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// New tests for hideLogo prop
|
|
96
|
+
describe('hideLogo prop', () => {
|
|
97
|
+
it('shows oslo logo by default (hideLogo=false)', () => {
|
|
98
|
+
const { container } = render(<PktHeaderService serviceName="Svc" />)
|
|
99
|
+
const logos = container.querySelectorAll('.pkt-header-service__logo')
|
|
100
|
+
expect(logos.length).toBe(1)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('hides oslo logo when hideLogo=true', () => {
|
|
104
|
+
const { container } = render(<PktHeaderService serviceName="Svc" hideLogo={true} />)
|
|
105
|
+
const logos = container.querySelectorAll('.pkt-header-service__logo')
|
|
106
|
+
expect(logos.length).toBe(0)
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
// New tests for logoLink prop
|
|
111
|
+
describe('logoLink prop', () => {
|
|
112
|
+
it('renders logo as link when logoLink is a string', () => {
|
|
113
|
+
const { container } = render(<PktHeaderService serviceName="Svc" logoLink="https://oslo.kommune.no" />)
|
|
114
|
+
const logoLink = container.querySelector('.pkt-header-service__logo a')
|
|
115
|
+
expect(logoLink).toBeTruthy()
|
|
116
|
+
expect(logoLink).toHaveAttribute('href', 'https://oslo.kommune.no')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('renders logo without link/button when logoLink is not provided', () => {
|
|
120
|
+
const { container } = render(<PktHeaderService serviceName="Svc" />)
|
|
121
|
+
const logoLink = container.querySelector('.pkt-header-service__logo a')
|
|
122
|
+
const logoButton = container.querySelector('.pkt-header-service__logo button')
|
|
123
|
+
expect(logoLink).toBeNull()
|
|
124
|
+
expect(logoButton).toBeNull()
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// New tests for position and scrollBehavior props
|
|
129
|
+
describe('position and scrollBehavior props', () => {
|
|
130
|
+
it('applies fixed class by default (position="fixed")', () => {
|
|
131
|
+
const { container } = render(<PktHeaderService serviceName="Svc" />)
|
|
132
|
+
const header = container.querySelector('.pkt-header-service')
|
|
133
|
+
expect(header).toHaveClass('pkt-header-service--fixed')
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('does not apply fixed class when position="relative"', () => {
|
|
137
|
+
const { container } = render(<PktHeaderService serviceName="Svc" position="relative" />)
|
|
138
|
+
const header = container.querySelector('.pkt-header-service')
|
|
139
|
+
expect(header).not.toHaveClass('pkt-header-service--fixed')
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('applies scroll-to-hide class by default (scrollBehavior="hide")', () => {
|
|
143
|
+
const { container } = render(<PktHeaderService serviceName="Svc" />)
|
|
144
|
+
const header = container.querySelector('.pkt-header-service')
|
|
145
|
+
expect(header).toHaveClass('pkt-header-service--scroll-to-hide')
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('does not apply scroll-to-hide class when scrollBehavior="none"', () => {
|
|
149
|
+
const { container } = render(<PktHeaderService serviceName="Svc" scrollBehavior="none" />)
|
|
150
|
+
const header = container.querySelector('.pkt-header-service')
|
|
151
|
+
expect(header).not.toHaveClass('pkt-header-service--scroll-to-hide')
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// New tests for user with lastLoggedIn
|
|
156
|
+
describe('user props', () => {
|
|
157
|
+
it('displays user name in button', () => {
|
|
158
|
+
mockedWidth = 1400
|
|
159
|
+
render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel Olsen' }} />)
|
|
160
|
+
const userBtn = screen.getByRole('button', { name: /Aksel Olsen/ })
|
|
161
|
+
expect(userBtn).toBeInTheDocument()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('logs deprecation warning when shortname is provided', () => {
|
|
165
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
166
|
+
mockedWidth = 1400
|
|
167
|
+
render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel', shortname: 'AO' }} />)
|
|
168
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('shortname'))
|
|
169
|
+
consoleSpy.mockRestore()
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('displays formatted lastLoggedIn in user menu', () => {
|
|
173
|
+
mockedWidth = 1400
|
|
174
|
+
render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel', lastLoggedIn: new Date('2025-09-19') }} />)
|
|
175
|
+
const userBtn = screen.getByRole('button', { name: /Aksel/ })
|
|
176
|
+
fireEvent.click(userBtn)
|
|
177
|
+
// Should format the date in Norwegian
|
|
178
|
+
expect(screen.getByText(/Sist pålogget:/)).toBeInTheDocument()
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('displays lastLoggedIn string as-is in user menu', () => {
|
|
182
|
+
mockedWidth = 1400
|
|
183
|
+
render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel', lastLoggedIn: '19. september 2025' }} />)
|
|
184
|
+
const userBtn = screen.getByRole('button', { name: /Aksel/ })
|
|
185
|
+
fireEvent.click(userBtn)
|
|
186
|
+
expect(screen.getByText('19. september 2025')).toBeInTheDocument()
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
// New tests for representing props
|
|
191
|
+
describe('representing props', () => {
|
|
192
|
+
it('displays representing name in user button when representing is provided', () => {
|
|
193
|
+
mockedWidth = 1400
|
|
194
|
+
render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel' }} representing={{ name: 'Oslo Kommune' }} />)
|
|
195
|
+
const userBtn = screen.getByRole('button', { name: /Oslo Kommune/ })
|
|
196
|
+
expect(userBtn).toBeInTheDocument()
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
it('logs deprecation warning when representing.shortname is provided', () => {
|
|
200
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
201
|
+
mockedWidth = 1400
|
|
202
|
+
render(
|
|
203
|
+
<PktHeaderService
|
|
204
|
+
serviceName="Svc"
|
|
205
|
+
user={{ name: 'Aksel' }}
|
|
206
|
+
representing={{ name: 'Oslo Kommune', shortname: 'OK' }}
|
|
207
|
+
/>,
|
|
208
|
+
)
|
|
209
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('shortname'))
|
|
210
|
+
consoleSpy.mockRestore()
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('displays orgNumber in user menu when representing has orgNumber', () => {
|
|
214
|
+
mockedWidth = 1400
|
|
215
|
+
render(
|
|
216
|
+
<PktHeaderService
|
|
217
|
+
serviceName="Svc"
|
|
218
|
+
user={{ name: 'Aksel' }}
|
|
219
|
+
representing={{ name: 'Oslo Kommune', orgNumber: '911738066' }}
|
|
220
|
+
/>,
|
|
221
|
+
)
|
|
222
|
+
const userBtn = screen.getByRole('button', { name: /Oslo Kommune/ })
|
|
223
|
+
fireEvent.click(userBtn)
|
|
224
|
+
expect(screen.getByText('Org.nr. 911738066')).toBeInTheDocument()
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
// New tests for canChangeRepresentation
|
|
229
|
+
describe('canChangeRepresentation prop', () => {
|
|
230
|
+
it('shows change representation button when canChangeRepresentation=true', () => {
|
|
231
|
+
mockedWidth = 1400
|
|
232
|
+
const handleChange = vi.fn()
|
|
233
|
+
render(
|
|
234
|
+
<PktHeaderService
|
|
235
|
+
serviceName="Svc"
|
|
236
|
+
user={{ name: 'Aksel' }}
|
|
237
|
+
representing={{ name: 'Oslo Kommune' }}
|
|
238
|
+
canChangeRepresentation={true}
|
|
239
|
+
changeRepresentation={handleChange}
|
|
240
|
+
/>,
|
|
241
|
+
)
|
|
242
|
+
const userBtn = screen.getByRole('button', { name: /Oslo Kommune/ })
|
|
243
|
+
fireEvent.click(userBtn)
|
|
244
|
+
const changeBtn = screen.getByRole('button', { name: 'Endre organisasjon' })
|
|
245
|
+
expect(changeBtn).toBeInTheDocument()
|
|
246
|
+
fireEvent.click(changeBtn)
|
|
247
|
+
expect(handleChange).toHaveBeenCalledTimes(1)
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
it('does not show change representation button when canChangeRepresentation=false', () => {
|
|
251
|
+
mockedWidth = 1400
|
|
252
|
+
render(
|
|
253
|
+
<PktHeaderService
|
|
254
|
+
serviceName="Svc"
|
|
255
|
+
user={{ name: 'Aksel' }}
|
|
256
|
+
representing={{ name: 'Oslo Kommune' }}
|
|
257
|
+
canChangeRepresentation={false}
|
|
258
|
+
/>,
|
|
259
|
+
)
|
|
260
|
+
const userBtn = screen.getByRole('button', { name: /Oslo Kommune/ })
|
|
261
|
+
fireEvent.click(userBtn)
|
|
262
|
+
expect(screen.queryByRole('button', { name: 'Endre organisasjon' })).not.toBeInTheDocument()
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
// New tests for userMenu prop
|
|
267
|
+
describe('userMenu prop', () => {
|
|
268
|
+
it('renders userMenu items in user menu dropdown', () => {
|
|
269
|
+
mockedWidth = 1400
|
|
270
|
+
const userMenu = [
|
|
271
|
+
{ title: 'Mine bookinger', iconName: 'heart', target: '/bookinger' },
|
|
272
|
+
{ title: 'Innstillinger', iconName: 'cogwheel', target: () => {} },
|
|
273
|
+
]
|
|
274
|
+
render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel' }} userMenu={userMenu} />)
|
|
275
|
+
const userBtn = screen.getByRole('button', { name: /Aksel/ })
|
|
276
|
+
fireEvent.click(userBtn)
|
|
277
|
+
expect(screen.getByText('Mine bookinger')).toBeInTheDocument()
|
|
278
|
+
expect(screen.getByText('Innstillinger')).toBeInTheDocument()
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it('renders userMenu link items as links', () => {
|
|
282
|
+
mockedWidth = 1400
|
|
283
|
+
const userMenu = [{ title: 'Min profil', target: '/profil' }]
|
|
284
|
+
const { container } = render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel' }} userMenu={userMenu} />)
|
|
285
|
+
const userBtn = screen.getByRole('button', { name: /Aksel/ })
|
|
286
|
+
fireEvent.click(userBtn)
|
|
287
|
+
// PktLink renders as an <a> tag with class pkt-link
|
|
288
|
+
const linkElement = container.querySelector('a.pkt-link[href="/profil"]')
|
|
289
|
+
expect(linkElement).toBeTruthy()
|
|
290
|
+
expect(screen.getByText('Min profil')).toBeInTheDocument()
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('calls userMenu button item onClick when clicked', () => {
|
|
294
|
+
mockedWidth = 1400
|
|
295
|
+
const handleClick = vi.fn()
|
|
296
|
+
const userMenu = [{ title: 'Custom Action', target: handleClick }]
|
|
297
|
+
render(<PktHeaderService serviceName="Svc" user={{ name: 'Aksel' }} userMenu={userMenu} />)
|
|
298
|
+
const userBtn = screen.getByRole('button', { name: /Aksel/ })
|
|
299
|
+
fireEvent.click(userBtn)
|
|
300
|
+
const actionBtn = screen.getByRole('button', { name: 'Custom Action' })
|
|
301
|
+
fireEvent.click(actionBtn)
|
|
302
|
+
expect(handleClick).toHaveBeenCalledTimes(1)
|
|
303
|
+
})
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
// New tests for logOut callback and placement
|
|
307
|
+
describe('logOut callback', () => {
|
|
308
|
+
it('shows logout button in header when logOutButtonPlacement="header"', () => {
|
|
309
|
+
mockedWidth = 1400
|
|
310
|
+
const handleLogout = vi.fn()
|
|
311
|
+
render(
|
|
312
|
+
<PktHeaderService
|
|
313
|
+
serviceName="Svc"
|
|
314
|
+
user={{ name: 'Aksel' }}
|
|
315
|
+
logOutButtonPlacement="header"
|
|
316
|
+
logOut={handleLogout}
|
|
317
|
+
/>,
|
|
318
|
+
)
|
|
319
|
+
const logoutBtn = screen.getByRole('button', { name: 'Logg ut' })
|
|
320
|
+
expect(logoutBtn).toBeInTheDocument()
|
|
321
|
+
fireEvent.click(logoutBtn)
|
|
322
|
+
expect(handleLogout).toHaveBeenCalledTimes(1)
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
it('shows logout button in userMenu when logOutButtonPlacement="userMenu"', () => {
|
|
326
|
+
mockedWidth = 1400
|
|
327
|
+
const handleLogout = vi.fn()
|
|
328
|
+
render(
|
|
329
|
+
<PktHeaderService
|
|
330
|
+
serviceName="Svc"
|
|
331
|
+
user={{ name: 'Aksel' }}
|
|
332
|
+
logOutButtonPlacement="userMenu"
|
|
333
|
+
logOut={handleLogout}
|
|
334
|
+
/>,
|
|
335
|
+
)
|
|
336
|
+
const userBtn = screen.getByRole('button', { name: /Aksel/ })
|
|
337
|
+
fireEvent.click(userBtn)
|
|
338
|
+
const logoutBtn = screen.getByRole('button', { name: 'Logg ut' })
|
|
339
|
+
expect(logoutBtn).toBeInTheDocument()
|
|
340
|
+
fireEvent.click(logoutBtn)
|
|
341
|
+
expect(handleLogout).toHaveBeenCalledTimes(1)
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
it('shows logout button in both header and userMenu when logOutButtonPlacement="both"', () => {
|
|
345
|
+
mockedWidth = 1400
|
|
346
|
+
const handleLogout = vi.fn()
|
|
347
|
+
render(
|
|
348
|
+
<PktHeaderService
|
|
349
|
+
serviceName="Svc"
|
|
350
|
+
user={{ name: 'Aksel' }}
|
|
351
|
+
logOutButtonPlacement="both"
|
|
352
|
+
logOut={handleLogout}
|
|
353
|
+
/>,
|
|
354
|
+
)
|
|
355
|
+
// Header logout
|
|
356
|
+
const headerLogoutBtn = screen.getByRole('button', { name: 'Logg ut' })
|
|
357
|
+
expect(headerLogoutBtn).toBeInTheDocument()
|
|
358
|
+
|
|
359
|
+
// Open user menu
|
|
360
|
+
const userBtn = screen.getByRole('button', { name: /Aksel/ })
|
|
361
|
+
fireEvent.click(userBtn)
|
|
362
|
+
// Should have logout in menu too
|
|
363
|
+
const logoutBtns = screen.getAllByRole('button', { name: 'Logg ut' })
|
|
364
|
+
expect(logoutBtns.length).toBe(2)
|
|
365
|
+
})
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// Test for userMenuFooter deprecation warning
|
|
369
|
+
describe('userMenuFooter deprecation', () => {
|
|
370
|
+
it('logs deprecation warning when userMenuFooter is provided', () => {
|
|
371
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
372
|
+
mockedWidth = 1400
|
|
373
|
+
render(
|
|
374
|
+
<PktHeaderService
|
|
375
|
+
serviceName="Svc"
|
|
376
|
+
user={{ name: 'Aksel' }}
|
|
377
|
+
userMenuFooter={[{ title: 'Footer Item', target: '#' }]}
|
|
378
|
+
/>,
|
|
379
|
+
)
|
|
380
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('userMenuFooter'))
|
|
381
|
+
consoleSpy.mockRestore()
|
|
382
|
+
})
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
// Test for userOptions "no longer available" warning
|
|
386
|
+
describe('userOptions warning', () => {
|
|
387
|
+
it('logs warning when userOptions is provided', () => {
|
|
388
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
389
|
+
mockedWidth = 1400
|
|
390
|
+
render(
|
|
391
|
+
<PktHeaderService
|
|
392
|
+
serviceName="Svc"
|
|
393
|
+
user={{ name: 'Aksel' }}
|
|
394
|
+
userOptions={[{ title: 'Option', target: '#' }]}
|
|
395
|
+
/>,
|
|
396
|
+
)
|
|
397
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('userOptions'))
|
|
398
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('no longer available'))
|
|
399
|
+
consoleSpy.mockRestore()
|
|
400
|
+
})
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
// Tests for serviceLink and serviceClick props
|
|
404
|
+
describe('serviceLink and serviceClick props', () => {
|
|
405
|
+
it('renders service name as link when serviceLink is a string', () => {
|
|
406
|
+
const { container } = render(<PktHeaderService serviceName="My Service" serviceLink="https://example.com" />)
|
|
407
|
+
const link = container.querySelector('a.pkt-link.pkt-header-service__service-link')
|
|
408
|
+
expect(link).toBeTruthy()
|
|
409
|
+
expect(link).toHaveAttribute('href', 'https://example.com')
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
it('renders service name as button when serviceClick is provided', () => {
|
|
413
|
+
const handleClick = vi.fn()
|
|
414
|
+
const { container } = render(<PktHeaderService serviceName="My Service" serviceClick={handleClick} />)
|
|
415
|
+
const button = container.querySelector('button.pkt-header-service__service-link')
|
|
416
|
+
expect(button).toBeTruthy()
|
|
417
|
+
fireEvent.click(button!)
|
|
418
|
+
expect(handleClick).toHaveBeenCalledTimes(1)
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
it('renders service name as span when neither serviceLink nor serviceClick is provided', () => {
|
|
422
|
+
const { container } = render(<PktHeaderService serviceName="My Service" />)
|
|
423
|
+
const span = container.querySelector('span.pkt-header-service__service-link')
|
|
424
|
+
expect(span).toBeTruthy()
|
|
425
|
+
expect(span?.tagName).toBe('SPAN')
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
it('prioritizes serviceLink over serviceClick when both are provided', () => {
|
|
429
|
+
const handleClick = vi.fn()
|
|
430
|
+
const { container } = render(
|
|
431
|
+
<PktHeaderService serviceName="My Service" serviceLink="https://example.com" serviceClick={handleClick} />,
|
|
432
|
+
)
|
|
433
|
+
const link = container.querySelector('a.pkt-link.pkt-header-service__service-link')
|
|
434
|
+
const button = container.querySelector('button.pkt-header-service__service-link')
|
|
435
|
+
expect(link).toBeTruthy()
|
|
436
|
+
expect(button).toBeNull()
|
|
437
|
+
})
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
// Tests for logoClick prop
|
|
441
|
+
describe('logoClick prop', () => {
|
|
442
|
+
it('renders logo as button when logoClick is provided', () => {
|
|
443
|
+
const handleClick = vi.fn()
|
|
444
|
+
const { container } = render(<PktHeaderService serviceName="Svc" logoClick={handleClick} />)
|
|
445
|
+
const logoButton = container.querySelector('.pkt-header-service__logo button')
|
|
446
|
+
expect(logoButton).toBeTruthy()
|
|
447
|
+
fireEvent.click(logoButton!)
|
|
448
|
+
expect(handleClick).toHaveBeenCalledTimes(1)
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
it('prioritizes logoLink over logoClick when both are provided', () => {
|
|
452
|
+
const handleClick = vi.fn()
|
|
453
|
+
const { container } = render(
|
|
454
|
+
<PktHeaderService serviceName="Svc" logoLink="https://oslo.kommune.no" logoClick={handleClick} />,
|
|
455
|
+
)
|
|
456
|
+
const logoLink = container.querySelector('.pkt-header-service__logo a')
|
|
457
|
+
const logoButton = container.querySelector('.pkt-header-service__logo button')
|
|
458
|
+
expect(logoLink).toBeTruthy()
|
|
459
|
+
expect(logoButton).toBeNull()
|
|
460
|
+
})
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
// Tests for logout button placement on mobile vs desktop
|
|
464
|
+
describe('logout button placement mobile vs desktop', () => {
|
|
465
|
+
it('shows logout button in user area on desktop when logOutButtonPlacement="header"', () => {
|
|
466
|
+
mockedWidth = 1400
|
|
467
|
+
const handleLogout = vi.fn()
|
|
468
|
+
render(
|
|
469
|
+
<PktHeaderService
|
|
470
|
+
serviceName="Svc"
|
|
471
|
+
user={{ name: 'Aksel' }}
|
|
472
|
+
logOutButtonPlacement="header"
|
|
473
|
+
logOut={handleLogout}
|
|
474
|
+
/>,
|
|
475
|
+
)
|
|
476
|
+
// On desktop, logout button should be in user area
|
|
477
|
+
const logoutBtn = screen.getByRole('button', { name: 'Logg ut' })
|
|
478
|
+
expect(logoutBtn).toBeInTheDocument()
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
it('shows logout button in content area on mobile when logOutButtonPlacement="header"', () => {
|
|
482
|
+
mockedWidth = 500
|
|
483
|
+
const handleLogout = vi.fn()
|
|
484
|
+
const { container } = render(
|
|
485
|
+
<PktHeaderService
|
|
486
|
+
serviceName="Svc"
|
|
487
|
+
user={{ name: 'Aksel' }}
|
|
488
|
+
logOutButtonPlacement="header"
|
|
489
|
+
logOut={handleLogout}
|
|
490
|
+
/>,
|
|
491
|
+
)
|
|
492
|
+
const contentArea = container.querySelector('.pkt-header-service__content')
|
|
493
|
+
const logoutBtn = contentArea?.querySelector('button')
|
|
494
|
+
expect(logoutBtn).toBeTruthy()
|
|
495
|
+
expect(screen.getByRole('button', { name: 'Logg ut' })).toBeInTheDocument()
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
it('does not show logout button in header areas when logOutButtonPlacement="userMenu"', () => {
|
|
499
|
+
mockedWidth = 1400
|
|
500
|
+
const handleLogout = vi.fn()
|
|
501
|
+
render(
|
|
502
|
+
<PktHeaderService
|
|
503
|
+
serviceName="Svc"
|
|
504
|
+
user={{ name: 'Aksel' }}
|
|
505
|
+
logOutButtonPlacement="userMenu"
|
|
506
|
+
logOut={handleLogout}
|
|
507
|
+
/>,
|
|
508
|
+
)
|
|
509
|
+
// When logOutButtonPlacement is userMenu, logout button should not be visible outside the menu
|
|
510
|
+
// Only the user menu button should be visible, not a separate logout button
|
|
511
|
+
const logoutButtons = screen.queryAllByRole('button', { name: 'Logg ut' })
|
|
512
|
+
expect(logoutButtons.length).toBe(0)
|
|
513
|
+
})
|
|
514
|
+
})
|
|
515
|
+
})
|