@oslokommune/punkt-react 16.21.0 → 16.21.1
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 +17 -0
- package/dist/index.d.ts +16 -6
- package/dist/punkt-react.es.js +2767 -14961
- package/dist/punkt-react.umd.js +1 -2387
- package/package.json +3 -3
- package/src/components/consent/Consent.test.tsx +217 -0
- package/src/components/consent/Consent.tsx +277 -18
- package/src/components/consent/strings.ts +125 -0
- package/dist/dialog-polyfill.esm-0dVT8G1o-Cf1Ux-Z9.js +0 -315
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oslokommune/punkt-react",
|
|
3
|
-
"version": "16.21.
|
|
3
|
+
"version": "16.21.1",
|
|
4
4
|
"description": "React komponentbibliotek til Punkt, et designsystem laget av Oslo Origo",
|
|
5
5
|
"homepage": "https://punkt.oslo.kommune.no",
|
|
6
6
|
"author": "Team Designsystem, Oslo Origo",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@lit-labs/ssr-dom-shim": "^1.2.1",
|
|
41
41
|
"@lit/react": "^1.0.7",
|
|
42
|
-
"@oslokommune/punkt-elements": "^16.21.
|
|
42
|
+
"@oslokommune/punkt-elements": "^16.21.1",
|
|
43
43
|
"classnames": "^2.5.1",
|
|
44
44
|
"prettier": "^3.3.3",
|
|
45
45
|
"react-hook-form": "^7.53.0"
|
|
@@ -109,5 +109,5 @@
|
|
|
109
109
|
"url": "https://github.com/oslokommune/punkt/issues"
|
|
110
110
|
},
|
|
111
111
|
"license": "MIT",
|
|
112
|
-
"gitHead": "
|
|
112
|
+
"gitHead": "15799459d01ea67c7770b8603da84069a717a164"
|
|
113
113
|
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
|
4
|
+
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
5
|
+
import { afterEach, beforeEach, vi } from 'vitest'
|
|
6
|
+
|
|
7
|
+
import { PktConsent } from './Consent'
|
|
8
|
+
|
|
9
|
+
expect.extend(toHaveNoViolations)
|
|
10
|
+
|
|
11
|
+
type CapturedHandler = ((consent: { value: string | { items: unknown[] } }) => void) | undefined
|
|
12
|
+
let capturedHandler: CapturedHandler
|
|
13
|
+
|
|
14
|
+
const installCookieBannerStub = ({ hasConsent = false }: { hasConsent?: boolean } = {}) => {
|
|
15
|
+
Object.defineProperty(window, 'cookieBanner', {
|
|
16
|
+
value: {
|
|
17
|
+
cookieConsent: {
|
|
18
|
+
validateConsentCookie: vi.fn().mockResolvedValue(hasConsent),
|
|
19
|
+
getConsentCookie: vi.fn().mockReturnValue('{"items":[]}'),
|
|
20
|
+
},
|
|
21
|
+
openCookieModal: vi.fn(),
|
|
22
|
+
},
|
|
23
|
+
writable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
})
|
|
26
|
+
capturedHandler = undefined
|
|
27
|
+
Object.defineProperty(window, '__cookieEvents', {
|
|
28
|
+
value: {
|
|
29
|
+
on: vi.fn((_event: string, handler: CapturedHandler) => {
|
|
30
|
+
capturedHandler = handler
|
|
31
|
+
}),
|
|
32
|
+
off: vi.fn(),
|
|
33
|
+
},
|
|
34
|
+
writable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const preInjectScriptMarker = () => {
|
|
40
|
+
// The component's loadConsentScript() resolves immediately if the marker
|
|
41
|
+
// script is already in the DOM. Inject it up-front so the test doesn't
|
|
42
|
+
// depend on a network fetch that jsdom can't fulfil.
|
|
43
|
+
if (!document.querySelector('#oslo-consent-script')) {
|
|
44
|
+
const marker = document.createElement('script')
|
|
45
|
+
marker.id = 'oslo-consent-script'
|
|
46
|
+
document.head.appendChild(marker)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe('PktConsent', () => {
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
preInjectScriptMarker()
|
|
53
|
+
installCookieBannerStub()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
afterEach(() => {
|
|
57
|
+
document.querySelector('#oslo-consent-script')?.remove()
|
|
58
|
+
document.querySelector('#oslo-consent-styles')?.remove()
|
|
59
|
+
delete (window as { cookieBanner?: unknown }).cookieBanner
|
|
60
|
+
delete (window as { __cookieEvents?: unknown }).__cookieEvents
|
|
61
|
+
capturedHandler = undefined
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
describe('Trigger rendering', () => {
|
|
65
|
+
test('renders the default button trigger with the bokmål settings label', () => {
|
|
66
|
+
render(<PktConsent />)
|
|
67
|
+
|
|
68
|
+
expect(
|
|
69
|
+
screen.getByRole('button', { name: 'Innstillinger for informasjonskapsler' }),
|
|
70
|
+
).toBeInTheDocument()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('uses the English settings label when i18nLanguage is "en"', () => {
|
|
74
|
+
render(<PktConsent i18nLanguage="en" />)
|
|
75
|
+
|
|
76
|
+
expect(screen.getByRole('button', { name: 'Go to settings' })).toBeInTheDocument()
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('renders a custom triggerText when provided', () => {
|
|
80
|
+
render(<PktConsent triggerText="Endre samtykke" />)
|
|
81
|
+
|
|
82
|
+
expect(screen.getByRole('button', { name: 'Endre samtykke' })).toBeInTheDocument()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('renders a link trigger when triggerType="link"', () => {
|
|
86
|
+
const { container } = render(<PktConsent triggerType="link" triggerText="Innstillinger" />)
|
|
87
|
+
|
|
88
|
+
const link = container.querySelector('a.pkt-link')
|
|
89
|
+
expect(link).toBeInTheDocument()
|
|
90
|
+
expect(link).toHaveAttribute('href', '#')
|
|
91
|
+
expect(link).toHaveTextContent('Innstillinger')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('renders a footer link with chevron icon when triggerType="footerlink"', () => {
|
|
95
|
+
const { container } = render(
|
|
96
|
+
<PktConsent triggerType="footerlink" triggerText="Innstillinger" />,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
const link = container.querySelector('a.pkt-footer__link')
|
|
100
|
+
expect(link).toBeInTheDocument()
|
|
101
|
+
expect(link?.querySelector('.pkt-icon')).toHaveAttribute('name', 'chevron-right')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('renders an icon-only button when triggerType="icon"', () => {
|
|
105
|
+
const { container } = render(<PktConsent triggerType="icon" triggerText="Cookies" />)
|
|
106
|
+
|
|
107
|
+
const button = container.querySelector('button')
|
|
108
|
+
expect(button).toHaveClass('pkt-btn--icon-only')
|
|
109
|
+
expect(button).toHaveClass('pkt-btn--tertiary')
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
describe('Click behaviour', () => {
|
|
114
|
+
test('opens the cookie modal on button click', () => {
|
|
115
|
+
vi.useFakeTimers()
|
|
116
|
+
try {
|
|
117
|
+
render(<PktConsent />)
|
|
118
|
+
fireEvent.click(screen.getByRole('button'))
|
|
119
|
+
vi.runAllTimers()
|
|
120
|
+
expect(window.cookieBanner?.openCookieModal).toHaveBeenCalled()
|
|
121
|
+
} finally {
|
|
122
|
+
vi.useRealTimers()
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test('opens the cookie modal on link click', () => {
|
|
127
|
+
vi.useFakeTimers()
|
|
128
|
+
try {
|
|
129
|
+
const { container } = render(<PktConsent triggerType="link" />)
|
|
130
|
+
const link = container.querySelector('a.pkt-link') as HTMLAnchorElement
|
|
131
|
+
fireEvent.click(link)
|
|
132
|
+
vi.runAllTimers()
|
|
133
|
+
expect(window.cookieBanner?.openCookieModal).toHaveBeenCalled()
|
|
134
|
+
} finally {
|
|
135
|
+
vi.useRealTimers()
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
describe('Side effects', () => {
|
|
141
|
+
test('sets the cookie banner globals from props', async () => {
|
|
142
|
+
render(
|
|
143
|
+
<PktConsent
|
|
144
|
+
devMode
|
|
145
|
+
googleAnalyticsId="GA-123"
|
|
146
|
+
hotjarId="HJ-456"
|
|
147
|
+
cookieDomain=".example.com"
|
|
148
|
+
cookieSecure="true"
|
|
149
|
+
cookieExpiryDays="90"
|
|
150
|
+
/>,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
await waitFor(() => {
|
|
154
|
+
expect(window.cookieBanner_googleAnalyticsId).toBe('GA-123')
|
|
155
|
+
})
|
|
156
|
+
expect(window.cookieBanner_hotjarId).toBe('HJ-456')
|
|
157
|
+
expect(window.cookieBanner_cookieDomain).toBe('.example.com')
|
|
158
|
+
expect(window.cookieBanner_cookieSecure).toBe('true')
|
|
159
|
+
expect(window.cookieBanner_cookieExpiryDays).toBe('90')
|
|
160
|
+
expect(window.cookieBanner_devMode).toBe(true)
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
describe('onToggleConsent', () => {
|
|
165
|
+
test('invokes onToggleConsent with a CustomEvent carrying parsed consent details', async () => {
|
|
166
|
+
installCookieBannerStub({ hasConsent: true })
|
|
167
|
+
const onToggle = vi.fn()
|
|
168
|
+
|
|
169
|
+
render(<PktConsent onToggleConsent={onToggle} />)
|
|
170
|
+
|
|
171
|
+
// First emit comes from the initial consent-cookie read; wait for it,
|
|
172
|
+
// then clear so we can assert specifically on the handler-driven call.
|
|
173
|
+
await waitFor(() => expect(capturedHandler).toBeDefined())
|
|
174
|
+
await waitFor(() => expect(onToggle).toHaveBeenCalled())
|
|
175
|
+
onToggle.mockClear()
|
|
176
|
+
|
|
177
|
+
capturedHandler!({
|
|
178
|
+
value: JSON.stringify({
|
|
179
|
+
items: [
|
|
180
|
+
{ name: 'statistics', consent: true },
|
|
181
|
+
{ name: 'survey', consent: false },
|
|
182
|
+
],
|
|
183
|
+
}),
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
expect(onToggle).toHaveBeenCalledTimes(1)
|
|
187
|
+
const event = onToggle.mock.calls[0][0] as CustomEvent<Record<string, boolean>>
|
|
188
|
+
expect(event).toBeInstanceOf(CustomEvent)
|
|
189
|
+
expect(event.type).toBe('toggle-consent')
|
|
190
|
+
expect(event.detail).toEqual({ statistics: true, survey: false })
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
describe('Accessibility', () => {
|
|
195
|
+
test('button trigger has no axe violations', async () => {
|
|
196
|
+
const { container } = render(<PktConsent triggerText="Cookie settings" />)
|
|
197
|
+
const results = await axe(container)
|
|
198
|
+
expect(results).toHaveNoViolations()
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test('link trigger has no axe violations', async () => {
|
|
202
|
+
const { container } = render(
|
|
203
|
+
<PktConsent triggerType="link" triggerText="Cookie settings" />,
|
|
204
|
+
)
|
|
205
|
+
const results = await axe(container)
|
|
206
|
+
expect(results).toHaveNoViolations()
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
test('footer link trigger has no axe violations', async () => {
|
|
210
|
+
const { container } = render(
|
|
211
|
+
<PktConsent triggerType="footerlink" triggerText="Cookie settings" />,
|
|
212
|
+
)
|
|
213
|
+
const results = await axe(container)
|
|
214
|
+
expect(results).toHaveNoViolations()
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
})
|
|
@@ -1,23 +1,282 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
3
|
+
import {
|
|
4
|
+
forwardRef,
|
|
5
|
+
HTMLAttributes,
|
|
6
|
+
MouseEvent as ReactMouseEvent,
|
|
7
|
+
Ref,
|
|
8
|
+
useEffect,
|
|
9
|
+
useRef,
|
|
10
|
+
} from 'react'
|
|
11
|
+
|
|
12
|
+
import { PktButton } from '../button/Button'
|
|
13
|
+
import { PktIcon } from '../icon/Icon'
|
|
14
|
+
import { consentStrings } from './strings'
|
|
15
|
+
|
|
16
|
+
interface ConsentItem {
|
|
17
|
+
name: string
|
|
18
|
+
consent: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ConsentValue {
|
|
22
|
+
items: ConsentItem[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface CookieConsentEvent {
|
|
26
|
+
value: string | ConsentValue
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type ConsentDetailsMap = Record<string, boolean>
|
|
30
|
+
|
|
31
|
+
type CookieEventHandler = (consent: CookieConsentEvent) => void
|
|
32
|
+
|
|
33
|
+
interface CookieEvents {
|
|
34
|
+
on(eventName: string, handler: CookieEventHandler): void
|
|
35
|
+
off(eventName: string, handler: CookieEventHandler): void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface CookieBannerApi {
|
|
39
|
+
cookieConsent: {
|
|
40
|
+
validateConsentCookie(): Promise<boolean>
|
|
41
|
+
getConsentCookie(): string | ConsentValue
|
|
42
|
+
}
|
|
43
|
+
openCookieModal(): void
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare global {
|
|
47
|
+
interface Window {
|
|
48
|
+
cookieBanner_devMode?: boolean
|
|
49
|
+
cookieBanner_cookieDomain?: string | null
|
|
50
|
+
cookieBanner_cookieSecure?: string | null
|
|
51
|
+
cookieBanner_cookieExpiryDays?: string | null
|
|
52
|
+
cookieBanner_googleAnalyticsId?: string | null
|
|
53
|
+
cookieBanner_hotjarId?: string | null
|
|
54
|
+
cookieBanner?: CookieBannerApi
|
|
55
|
+
__cookieEvents?: CookieEvents
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let consentScriptPromise: Promise<void> | null = null
|
|
60
|
+
|
|
61
|
+
const loadConsentScript = (): Promise<void> => {
|
|
62
|
+
if (consentScriptPromise) return consentScriptPromise
|
|
63
|
+
|
|
64
|
+
consentScriptPromise = new Promise((resolve, reject) => {
|
|
65
|
+
if (document.querySelector('#oslo-consent-script')) {
|
|
66
|
+
resolve()
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
const script = document.createElement('script')
|
|
70
|
+
script.src = 'https://cdn.web.oslo.kommune.no/cb/cb-v1.1.0.js'
|
|
71
|
+
script.id = 'oslo-consent-script'
|
|
72
|
+
script.onload = () => resolve()
|
|
73
|
+
script.onerror = reject
|
|
74
|
+
document.head.appendChild(script)
|
|
75
|
+
|
|
76
|
+
const styles = document.createElement('link')
|
|
77
|
+
styles.href = 'https://cdn.web.oslo.kommune.no/cb/cb-v1.1.0.css'
|
|
78
|
+
styles.type = 'text/css'
|
|
79
|
+
styles.rel = 'stylesheet'
|
|
80
|
+
styles.id = 'oslo-consent-styles'
|
|
81
|
+
document.head.appendChild(styles)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
return consentScriptPromise
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const parseJsonOrPassThrough = <T,>(value: T | string): T => {
|
|
88
|
+
if (typeof value !== 'string') return value
|
|
89
|
+
try {
|
|
90
|
+
return JSON.parse(value) as T
|
|
91
|
+
} catch {
|
|
92
|
+
return value as unknown as T
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const toConsentDetails = (consent: CookieConsentEvent): ConsentDetailsMap => {
|
|
97
|
+
const parsed = parseJsonOrPassThrough<ConsentValue>(consent.value)
|
|
98
|
+
return parsed.items.reduce<ConsentDetailsMap>((acc, item) => {
|
|
99
|
+
acc[item.name] = item.consent
|
|
100
|
+
return acc
|
|
101
|
+
}, {})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type TPktConsentTriggerType = 'button' | 'link' | 'footerlink' | 'icon'
|
|
105
|
+
|
|
106
|
+
export interface IPktConsent extends HTMLAttributes<HTMLElement> {
|
|
107
|
+
devMode?: boolean
|
|
108
|
+
cookieDomain?: string | null
|
|
109
|
+
cookieSecure?: string | null
|
|
110
|
+
cookieExpiryDays?: string | null
|
|
111
|
+
hotjarId?: string | null
|
|
112
|
+
googleAnalyticsId?: string | null
|
|
113
|
+
i18nLanguage?: string
|
|
114
|
+
triggerType?: TPktConsentTriggerType | null
|
|
115
|
+
triggerText?: string | null
|
|
116
|
+
onToggleConsent?: (e: CustomEvent<ConsentDetailsMap>) => void
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const PktConsent = forwardRef<HTMLElement, IPktConsent>(function PktConsent(
|
|
120
|
+
{
|
|
121
|
+
devMode = false,
|
|
122
|
+
cookieDomain = null,
|
|
123
|
+
cookieSecure = null,
|
|
124
|
+
cookieExpiryDays = null,
|
|
125
|
+
hotjarId = null,
|
|
126
|
+
googleAnalyticsId = null,
|
|
127
|
+
i18nLanguage = 'nb',
|
|
128
|
+
triggerType = 'button',
|
|
129
|
+
triggerText,
|
|
130
|
+
onToggleConsent,
|
|
131
|
+
...rest
|
|
20
132
|
},
|
|
21
|
-
|
|
133
|
+
ref,
|
|
134
|
+
) {
|
|
135
|
+
const onToggleRef = useRef(onToggleConsent)
|
|
136
|
+
onToggleRef.current = onToggleConsent
|
|
137
|
+
|
|
138
|
+
const configRef = useRef({
|
|
139
|
+
devMode,
|
|
140
|
+
cookieDomain,
|
|
141
|
+
cookieSecure,
|
|
142
|
+
cookieExpiryDays,
|
|
143
|
+
hotjarId,
|
|
144
|
+
googleAnalyticsId,
|
|
145
|
+
})
|
|
146
|
+
configRef.current = {
|
|
147
|
+
devMode,
|
|
148
|
+
cookieDomain,
|
|
149
|
+
cookieSecure,
|
|
150
|
+
cookieExpiryDays,
|
|
151
|
+
hotjarId,
|
|
152
|
+
googleAnalyticsId,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const triggerInitRef = useRef<() => void>(() => {})
|
|
156
|
+
|
|
157
|
+
const language = (i18nLanguage in consentStrings.i18n
|
|
158
|
+
? i18nLanguage
|
|
159
|
+
: 'nb') as keyof typeof consentStrings.i18n
|
|
160
|
+
const resolvedTriggerText =
|
|
161
|
+
triggerText ?? consentStrings.i18n[language].contentPresentation.buttons.settings
|
|
162
|
+
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
let cancelled = false
|
|
165
|
+
let cookieEventHandler: CookieEventHandler | undefined
|
|
166
|
+
|
|
167
|
+
const config = configRef.current
|
|
168
|
+
// cookieBanner_* identifiers are defined by the external cookie banner script
|
|
169
|
+
/* eslint-disable camelcase */
|
|
170
|
+
window.cookieBanner_googleAnalyticsId = config.googleAnalyticsId
|
|
171
|
+
window.cookieBanner_hotjarId = config.hotjarId
|
|
172
|
+
if (config.cookieDomain) window.cookieBanner_cookieDomain = config.cookieDomain
|
|
173
|
+
if (config.cookieSecure) window.cookieBanner_cookieSecure = config.cookieSecure
|
|
174
|
+
if (config.cookieExpiryDays) window.cookieBanner_cookieExpiryDays = config.cookieExpiryDays
|
|
175
|
+
if (config.devMode) window.cookieBanner_devMode = config.devMode
|
|
176
|
+
/* eslint-enable camelcase */
|
|
177
|
+
|
|
178
|
+
const emitConsents = (consent: CookieConsentEvent) => {
|
|
179
|
+
const handler = onToggleRef.current
|
|
180
|
+
if (!handler) return
|
|
181
|
+
handler(
|
|
182
|
+
new CustomEvent<ConsentDetailsMap>('toggle-consent', {
|
|
183
|
+
detail: toConsentDetails(consent),
|
|
184
|
+
bubbles: true,
|
|
185
|
+
cancelable: false,
|
|
186
|
+
}),
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
triggerInitRef.current = () => {
|
|
191
|
+
window.document.dispatchEvent(
|
|
192
|
+
new Event('CookieBannerReady', { bubbles: true, cancelable: true }),
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
const banner = window.cookieBanner
|
|
196
|
+
const events = window.__cookieEvents
|
|
197
|
+
if (!banner || !events) return
|
|
198
|
+
|
|
199
|
+
banner.cookieConsent.validateConsentCookie().then((hasConsent) => {
|
|
200
|
+
if (cancelled || !hasConsent) return
|
|
201
|
+
|
|
202
|
+
const cookie = banner.cookieConsent.getConsentCookie()
|
|
203
|
+
window.setTimeout(() => {
|
|
204
|
+
if (!cancelled) emitConsents({ value: cookie })
|
|
205
|
+
}, 0)
|
|
206
|
+
|
|
207
|
+
if (cookieEventHandler) {
|
|
208
|
+
events.off('CookieManager.setCookie', cookieEventHandler)
|
|
209
|
+
}
|
|
210
|
+
cookieEventHandler = (consent) => emitConsents(consent)
|
|
211
|
+
events.on('CookieManager.setCookie', cookieEventHandler)
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
loadConsentScript().then(() => {
|
|
216
|
+
if (!cancelled) triggerInitRef.current()
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
return () => {
|
|
220
|
+
cancelled = true
|
|
221
|
+
if (cookieEventHandler) {
|
|
222
|
+
window.__cookieEvents?.off('CookieManager.setCookie', cookieEventHandler)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}, [])
|
|
226
|
+
|
|
227
|
+
const openModal = (e: ReactMouseEvent<HTMLElement>) => {
|
|
228
|
+
e.preventDefault()
|
|
229
|
+
if (!window.cookieBanner?.cookieConsent) {
|
|
230
|
+
triggerInitRef.current()
|
|
231
|
+
}
|
|
232
|
+
setTimeout(() => window.cookieBanner?.openCookieModal())
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (triggerType === 'link') {
|
|
236
|
+
return (
|
|
237
|
+
<a
|
|
238
|
+
{...rest}
|
|
239
|
+
ref={ref as Ref<HTMLAnchorElement>}
|
|
240
|
+
href="#"
|
|
241
|
+
className="pkt-link"
|
|
242
|
+
onClick={openModal}
|
|
243
|
+
>
|
|
244
|
+
{resolvedTriggerText}
|
|
245
|
+
</a>
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
if (triggerType === 'footerlink') {
|
|
249
|
+
return (
|
|
250
|
+
<a
|
|
251
|
+
{...rest}
|
|
252
|
+
ref={ref as Ref<HTMLAnchorElement>}
|
|
253
|
+
href="#"
|
|
254
|
+
className="pkt-footer__link"
|
|
255
|
+
onClick={openModal}
|
|
256
|
+
>
|
|
257
|
+
<PktIcon name="chevron-right" className="pkt-footer__link-icon" />
|
|
258
|
+
{resolvedTriggerText}
|
|
259
|
+
</a>
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
if (triggerType === 'icon') {
|
|
263
|
+
return (
|
|
264
|
+
<PktButton
|
|
265
|
+
ref={ref as Ref<HTMLButtonElement>}
|
|
266
|
+
skin="tertiary"
|
|
267
|
+
variant="icon-only"
|
|
268
|
+
iconName="cookie"
|
|
269
|
+
onClick={openModal}
|
|
270
|
+
>
|
|
271
|
+
{resolvedTriggerText}
|
|
272
|
+
</PktButton>
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
return (
|
|
276
|
+
<PktButton ref={ref as Ref<HTMLButtonElement>} onClick={openModal}>
|
|
277
|
+
{resolvedTriggerText}
|
|
278
|
+
</PktButton>
|
|
279
|
+
)
|
|
280
|
+
})
|
|
22
281
|
|
|
23
282
|
PktConsent.displayName = 'PktConsent'
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export const consentStrings = {
|
|
2
|
+
i18n: {
|
|
3
|
+
nb: {
|
|
4
|
+
contentPresentation: {
|
|
5
|
+
title: 'Oslo kommune bruker informasjonskapsler',
|
|
6
|
+
description: [
|
|
7
|
+
'For at nettstedet skal fungere og være trygt, bruker Oslo kommune informasjonskapsler. Noen er teknisk nødvendige, mens andre sikrer ulik funksjonalitet.',
|
|
8
|
+
'Godtar du alle informasjonskapsler, tillater du også at vi samler inn data om statistikk og brukeradferd. Da hjelper du oss med å lage et bedre nettsted uten at du trenger å dele noe personlig informasjon med oss.',
|
|
9
|
+
],
|
|
10
|
+
buttons: {
|
|
11
|
+
accept: 'Godta alle',
|
|
12
|
+
reject: 'Kun nødvendige',
|
|
13
|
+
settings: 'Innstillinger for informasjonskapsler',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
contentSettings: {
|
|
17
|
+
title: 'Innstillinger for informasjonskapsler',
|
|
18
|
+
description: [
|
|
19
|
+
'Her kan du velge hvilke typer informasjonskapsler du vil tillate. Tillatelsen gjelder i 90 dager. Husk at nødvendige informasjonskapsler ikke kan velges bort.',
|
|
20
|
+
'Du kan når som helst endre innstillingene og finne mer informasjon nederst på nettstedet under «Innstillinger for informasjonskapsler» og «Personvern og informasjonskapsler».',
|
|
21
|
+
],
|
|
22
|
+
buttons: {
|
|
23
|
+
back: 'Tilbake',
|
|
24
|
+
save: 'Lagre innstillinger',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
en: {
|
|
29
|
+
contentPresentation: {
|
|
30
|
+
title: 'Before you visit Oslo kommune ...',
|
|
31
|
+
description: [
|
|
32
|
+
'This website uses cookies to make improvements. In this context, we need your consent to measure the traffic on the website in relation to statistics and feedback.',
|
|
33
|
+
'To read more about what we use cookies for, go to our privacy declaration which you will find at the bottom of our websites.',
|
|
34
|
+
],
|
|
35
|
+
buttons: {
|
|
36
|
+
accept: 'Yes, I accept',
|
|
37
|
+
reject: 'Only necessary',
|
|
38
|
+
settings: 'Go to settings',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
contentSettings: {
|
|
42
|
+
title: 'Her kan du aktivt velge mellom ulike informasjonskapsler',
|
|
43
|
+
description: [
|
|
44
|
+
'For å lese mer om hva vi bruker informasjonskapsler til gå til vår personvernserklering som du finner på våre nettsider',
|
|
45
|
+
],
|
|
46
|
+
buttons: {
|
|
47
|
+
back: 'Back',
|
|
48
|
+
save: 'Save settings',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
cookies: [
|
|
54
|
+
{
|
|
55
|
+
name: 'statistics',
|
|
56
|
+
defaultValue: null,
|
|
57
|
+
i18n: {
|
|
58
|
+
nb: {
|
|
59
|
+
title: 'Statistikk og analyse',
|
|
60
|
+
description:
|
|
61
|
+
'Jeg godtar at Oslo kommune bruker informasjonskapsler til å innhente data om statistikk og brukeradferd.',
|
|
62
|
+
buttons: {
|
|
63
|
+
yes: 'Ja',
|
|
64
|
+
no: 'Nei',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
en: {
|
|
68
|
+
title: 'Statistics and analytics',
|
|
69
|
+
description: 'Select your preference for statistics and analytics',
|
|
70
|
+
buttons: {
|
|
71
|
+
yes: 'Yes',
|
|
72
|
+
no: 'No',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'survey',
|
|
79
|
+
defaultValue: null,
|
|
80
|
+
i18n: {
|
|
81
|
+
nb: {
|
|
82
|
+
title: 'Skriftlige tilbakemeldinger',
|
|
83
|
+
description:
|
|
84
|
+
'Jeg godtar at Oslo kommune bruker informasjonskapsler til å vise responsfelt og registrere tilbakemeldinger.',
|
|
85
|
+
buttons: {
|
|
86
|
+
yes: 'Ja',
|
|
87
|
+
no: 'Nei',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
en: {
|
|
91
|
+
title: 'Survey',
|
|
92
|
+
description: 'Select your preference for survey',
|
|
93
|
+
buttons: {
|
|
94
|
+
yes: 'Yes',
|
|
95
|
+
no: 'No',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'functional',
|
|
102
|
+
defaultValue: true,
|
|
103
|
+
i18n: {
|
|
104
|
+
nb: {
|
|
105
|
+
title: 'Funksjonelle informasjonskapsler',
|
|
106
|
+
description:
|
|
107
|
+
'Jeg godtar at Oslo kommune bruker informasjonskapsler for å gjøre ulike funksjoner på nettstedet mulige (for eksempel skjemaer og barnehagevelger).',
|
|
108
|
+
buttons: {
|
|
109
|
+
yes: 'Ja',
|
|
110
|
+
no: 'Nei',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
en: {
|
|
114
|
+
title: 'Functional cookies',
|
|
115
|
+
description:
|
|
116
|
+
'For the website to function optimally, we recommend that you accept these cookies',
|
|
117
|
+
buttons: {
|
|
118
|
+
yes: 'Yes',
|
|
119
|
+
no: 'No',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
}
|