Skip to content

Commit ce1e36c

Browse files
JuligsVmMad
andauthored
refactor(cookie-manager): improve react cookie manager and add more configuration options (#154)
* feat(cookie-manager): add new props to cookie manager * refactor(cookie-manager): update context structure * refactor: wip * refactor(cookie-manager): update Cookie Manager svelte * refactor: simplify code * feat: add callbacks to the skcm configuration --------- Co-authored-by: JCNoguera <88061365+VmMad@users.noreply.github.com>
1 parent cc65d10 commit ce1e36c

25 files changed

Lines changed: 353 additions & 280 deletions

File tree

cookie-manager/core/clientSideOnly.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import { Service } from './types'
22
import { GOOGLE_ANALYTICS_EXPIRATION_DAYS } from './constants'
33

4-
export const loadGoogleAnalytics = (
5-
id: Service['id'],
6-
setServicesInitialized: (bool: boolean) => void
7-
): void => {
4+
export const loadGoogleAnalytics = (id: Service['id']): void => {
85
function gtag(_key: string, _value: unknown, _config?: { cookie_expires: number }) {
96
// eslint-disable-next-line prefer-rest-params
107
window.dataLayer.push(arguments)
@@ -17,7 +14,6 @@ export const loadGoogleAnalytics = (
1714
const script = document.createElement('script')
1815
script.src = `https://www.googletagmanager.com/gtag/js?id=${id}`
1916
document.body.appendChild(script)
20-
setServicesInitialized(true)
2117
}
2218

2319
export const removeGoogleAnalytics = (id: Service['id']): void => {

cookie-manager/core/services.ts

Lines changed: 29 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { deleteCookie, getCookie, setCookie } from './utils'
99
import { loadGoogleAnalytics, removeGoogleAnalytics } from './clientSideOnly'
1010
import { COOKIE_EXPIRATION_DAYS } from './constants'
1111

12-
export function initializeServices(
12+
export function initializeGoogleAnalytics(
1313
servicesInitialized: boolean,
1414
configuredServices: Service[],
1515
setServicesInitialized: (bool: boolean) => void
@@ -21,71 +21,65 @@ export function initializeServices(
2121
const googleAnalytics4Config = configuredServices.find(
2222
({ type }) => type === SupportedService.GoogleAnalytics4
2323
)
24+
2425
if (googleAnalyticsUniversalConfig?.enabled) {
25-
loadGoogleAnalytics(googleAnalyticsUniversalConfig.id, setServicesInitialized)
26+
loadGoogleAnalytics(googleAnalyticsUniversalConfig.id)
27+
setServicesInitialized(true)
2628
} else {
2729
if (googleAnalytics4Config?.enabled) {
28-
loadGoogleAnalytics(googleAnalytics4Config.id, setServicesInitialized)
30+
loadGoogleAnalytics(googleAnalytics4Config.id)
31+
setServicesInitialized(true)
2932
}
3033
}
3134
}
3235
}
3336

34-
interface InitConfiguredServicesArgs {
35-
services: SKCMConfiguration['services']
36-
onConfiguredServicesInitialized: (
37-
configuredServices: Service[],
38-
necessaryCookies: ServiceCookie[]
39-
) => void
40-
}
41-
export function initializeConfiguredServices({
42-
services: {
37+
export function initializeConfiguredServices(services: SKCMConfiguration['services'] = {}): {
38+
configuredServices: Service[]
39+
necessaryCookies: ServiceCookie[]
40+
} {
41+
const {
4342
googleAnalyticsUniversalId,
4443
googleAnalytics4Id,
4544
adCookiesEnabled,
4645
customNecessaryCookies
47-
} = {},
48-
onConfiguredServicesInitialized
49-
}: InitConfiguredServicesArgs): void {
50-
let _configuredServices: Service[] = []
51-
let _necessaryCookies: ServiceCookie[] = []
46+
} = services
47+
48+
let configuredServices: Service[] = []
49+
let necessaryCookies: ServiceCookie[] = []
5250
if (googleAnalyticsUniversalId) {
53-
_configuredServices.push({
51+
configuredServices.push({
5452
type: SupportedService.GoogleAnalyticsUniversal,
5553
id: googleAnalyticsUniversalId,
5654
enabled: getCookie(SKCM_GA_GOOGLE_ANALYTICS_UNIVERSAL_COOKIE?.name) === 'true',
5755
cookies: GoogleOwnCookies.GoogleAnalyticsUniversal
5856
})
59-
_necessaryCookies.push(SKCM_GA_GOOGLE_ANALYTICS_UNIVERSAL_COOKIE)
57+
necessaryCookies.push(SKCM_GA_GOOGLE_ANALYTICS_UNIVERSAL_COOKIE)
6058
}
6159
if (googleAnalytics4Id) {
62-
_configuredServices.push({
60+
configuredServices.push({
6361
type: SupportedService.GoogleAnalytics4,
6462
id: googleAnalytics4Id,
6563
enabled: getCookie(SKCM_GA_GOOGLE_ANALYTICS_4_COOKIE?.name) === 'true',
6664
cookies: GoogleOwnCookies.GoogleAnalytics4
6765
})
68-
_necessaryCookies.push(SKCM_GA_GOOGLE_ANALYTICS_4_COOKIE)
66+
necessaryCookies.push(SKCM_GA_GOOGLE_ANALYTICS_4_COOKIE)
6967
}
7068
if (customNecessaryCookies) {
71-
_necessaryCookies = [..._necessaryCookies, ...customNecessaryCookies]
69+
necessaryCookies = [...necessaryCookies, ...customNecessaryCookies]
7270
}
7371
if (!adCookiesEnabled) {
74-
const filteredCookies = _configuredServices.map((service) => ({
72+
const filteredCookies = configuredServices.map((service) => ({
7573
...service,
7674
cookies: service?.cookies?.filter((cookie) => cookie?.category !== CookieCategory.Advertising)
7775
}))
78-
_configuredServices = filteredCookies
76+
configuredServices = filteredCookies
7977
}
8078

81-
onConfiguredServicesInitialized?.(_configuredServices, _necessaryCookies)
79+
return { configuredServices, necessaryCookies }
8280
}
8381

84-
export function stopCoreServices(
85-
configuredServices: Service[],
86-
removeAdditionalCookiesCb: () => void,
87-
setServicesInitialized: (bool: boolean) => void
88-
): void {
82+
export function stopCoreServices(configuredServices: Service[]): void {
8983
const googleAnalyticsUniversalConfig = configuredServices?.find(
9084
({ type }) => type === SupportedService.GoogleAnalyticsUniversal
9185
)
@@ -94,20 +88,15 @@ export function stopCoreServices(
9488
)
9589
removeGoogleAnalytics(googleAnalytics4Config?.id)
9690
removeGoogleAnalytics(googleAnalyticsUniversalConfig?.id)
97-
removeAdditionalCookiesCb()
98-
99-
setServicesInitialized?.(false)
10091
}
10192

102-
export function setNecessaryCookies(
93+
export function applyCookieManagerNecessaryCookies(
10394
value: 'true' | 'false',
104-
configuredServices: Service[],
105-
necessaryCookies: ServiceCookie[],
106-
setConfiguredServices: (services: Service[]) => void
95+
necessaryCookies: ServiceCookie[]
10796
): void {
108-
const SKCM_NECESSARY_COOKIES: string[] = [
109-
SKCM_GA_GOOGLE_ANALYTICS_UNIVERSAL_COOKIE?.name,
110-
SKCM_GA_GOOGLE_ANALYTICS_4_COOKIE?.name
97+
const SKCM_NECESSARY_COOKIES = [
98+
SKCM_GA_GOOGLE_ANALYTICS_UNIVERSAL_COOKIE.name,
99+
SKCM_GA_GOOGLE_ANALYTICS_4_COOKIE.name
111100
]
112101
// set cookies
113102
const neededCookies =
@@ -117,12 +106,6 @@ export function setNecessaryCookies(
117106
for (let i = 0; i < neededCookies?.length; i++) {
118107
setCookie(neededCookies[i]?.name, value, COOKIE_EXPIRATION_DAYS)
119108
}
120-
// enable services
121-
const _configuredServices = configuredServices?.map((service) => ({
122-
...service,
123-
enabled: value === 'true'
124-
}))
125-
setConfiguredServices(_configuredServices)
126109
}
127110

128111
export function clearAdditionalCookies(necessaryCookies: ServiceCookie[]): void {

cookie-manager/core/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ export type SKCMConfiguration = {
4545
disclaimer?: DisclaimerConfiguration
4646
services?: ServicesConfiguration
4747
theme?: Theme
48+
onAcceptCookies?: () => void
49+
onDeclineCookies?: () => void
4850
}
4951

5052
export type CookieProvider = {

cookie-manager/react/README.md

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,45 @@ export default function Disclaimer(): React.JSX.Element {
7070
}
7171
```
7272

73+
### Run custom logic on Accept / Decline
74+
75+
If you need to initialize third-party services, or to run any custom tracking logic depending on the user choice, pass `onAcceptCookies` or `onDeclineCookies` to your `SKCMConfiguration`.
76+
77+
```tsx
78+
'use client'
79+
80+
import { CookieManager, type SKCMConfiguration } from '@boxfish-studio/react-cookie-manager'
81+
82+
export default function Disclaimer(): React.JSX.Element {
83+
const configuration: SKCMConfiguration = {
84+
disclaimer: {
85+
title: 'This website uses cookies',
86+
body: 'By using this site, you agree with our use of cookies'
87+
},
88+
services: {
89+
customAnalyticsCookies: [
90+
{
91+
name: 'ANALYTICS_' + 'SERVICE_ID',
92+
purpose: 'Analytics - tracks usage for basic website functionality',
93+
expiry: '1 year',
94+
type: 'http',
95+
showDisclaimerIfMissing: true
96+
}
97+
]
98+
},
99+
onAcceptCookies: () => {
100+
// Initialize third-party services here
101+
console.log('User accepted cookies')
102+
},
103+
onDeclineCookies: () => {
104+
console.log('User declined cookies')
105+
}
106+
}
107+
108+
return <CookieManager configuration={configuration} />
109+
}
110+
```
111+
73112
### Control page navigation
74113

75114
Import the `useUpdatePathGA` hook and use it with your router of preference:
@@ -132,14 +171,14 @@ export default function RootLayout({
132171
children: React.ReactNode
133172
}>): React.JSX.Element {
134173
return (
135-
<ContextProviders>
136-
<html lang="en">
137-
<body>
174+
<html lang="en">
175+
<body>
176+
<ContextProviders>
138177
{children}
139178
<Disclaimer />
140-
</body>
141-
</html>
142-
</ContextProviders>
179+
</ContextProviders>
180+
</body>
181+
</html>
143182
)
144183
}
145184
```
@@ -171,14 +210,14 @@ export default function CookieLibraryPage() {
171210

172211
### Using the internal store
173212

174-
You can use the `useCookieManagerContext` hook to know whether the services are running or not.
213+
You can use the `useCookieManager` hook to know whether the services are running or not.
175214

176215
```tsx
177-
import { useCookieManagerContext } from '@boxfish-studio/react-cookie-manager'
216+
import { useCookieManager } from '@boxfish-studio/react-cookie-manager'
178217

179218
export function Component() {
180-
const { servicesInitialized } = useCookieManagerContext()
181-
const isRunning = servicesInitialized.value
219+
const { servicesInitialized } = useCookieManager()
220+
const isRunning = servicesInitialized
182221

183222
// ...
184223
}
@@ -218,6 +257,8 @@ type SKCMConfiguration = {
218257
medium?: string
219258
light?: string
220259
}
260+
onAcceptCookies?: () => void
261+
onDeclineCookies?: () => void
221262
}
222263
```
223264

cookie-manager/react/src/App.tsx

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,58 @@
11
import { CookieManager, CookieLibrary } from '@components'
22
import { SKCMConfiguration } from '@core/types'
33

4-
function App(): React.JSX.Element {
5-
const configuration: SKCMConfiguration = {
6-
disclaimer: {
7-
title: 'Custom Title',
8-
body: 'Custom Body'
9-
},
10-
services: {
11-
googleAnalyticsUniversalId: 'UA-XXXXXXXXX',
12-
googleAnalytics4Id: 'G-XXXXXXX',
13-
adCookiesEnabled: true,
14-
customNecessaryCookies: [
15-
{
16-
name: '1st Cookie Test',
17-
provider: 'Boxfish',
18-
providerUrl: 'boxfish.studio',
19-
purpose: 'Stores the user´s preferences.',
20-
expiry: '2 months',
21-
type: 'HTTP',
22-
showDisclaimerIfMissing: true
23-
},
24-
{
25-
name: 'Cookie 2',
26-
provider: 'Boxfish',
27-
providerUrl: 'boxfish.studio',
28-
purpose: 'Stores the user´s cookies consent state for the current domain',
29-
expiry: '30 days',
30-
type: 'HTTP',
31-
showDisclaimerIfMissing: true
32-
}
33-
]
34-
}
4+
const configuration: SKCMConfiguration = {
5+
disclaimer: {
6+
title: 'Custom Title',
7+
body: 'Custom Body'
8+
},
9+
services: {
10+
googleAnalyticsUniversalId: 'UA-XXXXXXXXX',
11+
googleAnalytics4Id: 'G-XXXXXXX',
12+
adCookiesEnabled: true,
13+
customNecessaryCookies: [
14+
{
15+
name: '1st_Cookie_Test',
16+
provider: 'Boxfish',
17+
providerUrl: 'boxfish.studio',
18+
purpose: 'Stores the user´s preferences.',
19+
expiry: '2 months',
20+
type: 'HTTP',
21+
showDisclaimerIfMissing: true
22+
},
23+
{
24+
name: 'Cookie_2',
25+
provider: 'Boxfish',
26+
providerUrl: 'boxfish.studio',
27+
purpose: 'Stores the user´s cookies consent state for the current domain',
28+
expiry: '30 days',
29+
type: 'HTTP',
30+
showDisclaimerIfMissing: true
31+
}
32+
]
33+
},
34+
onAcceptCookies: () => {
35+
document.cookie = '1st_Cookie_Test=true; path=/; max-age=' + 60 * 60 * 24 * 30 // 30 days
36+
document.cookie = 'Cookie_2=true; path=/; max-age=' + 60 * 60 * 24 * 30 // 30 days
37+
},
38+
onDeclineCookies: () => {
39+
document.cookie = '1st_Cookie_Test=false; path=/; max-age=' + 60 * 60 * 24 * 30 // 30 days
40+
document.cookie = 'Cookie_2=false; path=/; max-age=' + 60 * 60 * 24 * 30 // 30 days
41+
},
42+
theme: {
43+
light: '#fff',
44+
dark: '#131f37',
45+
medium: '#b0bfd9',
46+
primary: '#14cabf'
3547
}
48+
}
49+
50+
function App(): React.JSX.Element {
3651
return (
3752
<>
3853
<CookieManager configuration={configuration} />
3954
<CookieLibrary configuration={configuration} />
4055
</>
4156
)
4257
}
43-
4458
export default App

cookie-manager/react/src/index.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ button:focus-visible {
5656
outline: 4px auto -webkit-focus-ring-color;
5757
}
5858

59-
@media (prefers-color-scheme: light) {
59+
@media (prefers-color-scheme: dark) {
6060
:root {
6161
color: #213547;
6262
background-color: #ffffff;

0 commit comments

Comments
 (0)