Skip to content

Commit

Permalink
❇️ Refactor cookie consent and allow multiple cookie policies to be set
Browse files Browse the repository at this point in the history
#2376 (#2396)

* ✨ Allow to set multiple cookie policy #2376

* ♻️ Refactor cookiepolicy #2376

* 🐛 Fix 500 in other paGES #2376

* ✨ Backward compatability #2376

* ♻️ Refactor vookiepolicy query #2396

* Fix news iframe query #2376

* 🔨 Script to migrate cookiepolicy #2376

* ♻️ Simplify cookiepolicy query #2376
  • Loading branch information
padms authored Sep 23, 2024
1 parent f6f6c75 commit 8f94897
Show file tree
Hide file tree
Showing 22 changed files with 146 additions and 98 deletions.
9 changes: 9 additions & 0 deletions sanityv3/migrations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Use this folder to create scripts for content migration.

## Steps:

1. Inside sanityv3 folder run `pnpx sanity migration create "Issue <ISSUE_NO>"` to create a migration script folder. You can choose the appropriate template or start with a basic one.
2. Open the `index.ts` file which is just created and modify the script.
3. Login to sanity from the cli using SSO `pnpm sanity login --sso <sso_slug>`
4. Run `pnpm sanity migration run issue-<ISSUE_NO> --project=<PROJECT_ID> --dataset=<DATASET>` for dry run, and verify the patches.
5. Run `pnpm sanity migration run issue-<ISSUE_NO> --project=<PROJECT_ID> --dataset=<DATASET> --no-dry-run` to migrate.
12 changes: 12 additions & 0 deletions sanityv3/migrations/issue-2376/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineMigration, set } from 'sanity/migrate'

export default defineMigration({
title: 'Issue 2376',
documentTypes: ['page', 'event', 'news', 'magazine'],
migrate: {
string(node, path, context) {
if (['none', 'marketing', 'statistics'].includes(node) && path[path.length - 1] === 'cookiePolicy')
return set([node])
},
},
})
21 changes: 2 additions & 19 deletions sanityv3/schemas/objects/basicIframe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import blocksToText from '../../helpers/blocksToText'
import CompactBlockEditor from '../components/CompactBlockEditor'
import { configureTitleBlockContent } from '../editors'
import type { ColorSelectorValue } from '../components/ColorSelector'
import { cookiePolicy } from './iframe/sharedIframeFields'

const titleContentType = configureTitleBlockContent()

Expand Down Expand Up @@ -73,25 +74,7 @@ export default {
return (parent?.title || parent?.frameTitle) && value === undefined ? 'Required' : true
}),
},
{
name: 'cookiePolicy',
type: 'string',
title: 'Cookie policy',
description: 'Select which cookie policy applies to this iframe.',
fieldset: 'iframe',

options: {
list: [
{ title: 'None', value: 'none' },
{ title: 'Marketing', value: 'marketing' },
{ title: 'Statistics', value: 'statistics' },
],
layout: 'dropdown',
},
initialValue: 'none',
validation: (Rule: Rule) => Rule.required(),
},

cookiePolicy,
{
name: 'aspectRatio',
type: 'string',
Expand Down
2 changes: 1 addition & 1 deletion sanityv3/schemas/objects/iframe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export type IFrame = {
aspectRatio: string
height?: number
background?: ColorSelectorValue
cookiePolicy: 'none' | 'marketing' | 'statistics'
cookiePolicy: string[]
}

export default {
Expand Down
17 changes: 11 additions & 6 deletions sanityv3/schemas/objects/iframe/sharedIframeFields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,26 @@ export const url = {

export const cookiePolicy = {
name: 'cookiePolicy',
type: 'string',
type: 'array',
title: 'Cookie policy',
description: 'Select which cookie policy applies to this iframe.',
fieldset: 'iframe',

of: [{ type: 'string' }],
options: {
list: [
{ title: 'None', value: 'none' },
{ title: 'Marketing', value: 'marketing' },
{ title: 'Preferences', value: 'preferences' },
{ title: 'Statistics', value: 'statistics' },
{ title: 'Marketing', value: 'marketing' },
],
layout: 'dropdown',
},
initialValue: 'none',
validation: (Rule: Rule) => Rule.required(),
initialValue: ['none'],
validation: (Rule: Rule) =>
Rule.custom((value: any) => {
if (value === undefined || value?.length === 0) return 'Required'
else if (value.length > 1 && value.includes('none')) return `Cannot select ${value.toString()} together`
return true
}),
}

export const aspectRatio = {
Expand Down
17 changes: 17 additions & 0 deletions sanityv3/schemas/textSnippets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ const snippets: textSnippet = {
defaultValue: 'statistics',
group: groups.cookie,
},
cookie_type_preferences: {
title: 'Type preferences',
defaultValue: 'preferences',
group: groups.cookie,
},
cookie_consent_header: {
title: 'Consent header',
defaultValue: 'Accept Cookies',
Expand All @@ -202,6 +207,18 @@ const snippets: textSnippet = {
'Want the full picture? We’d love to share this content with you, but first you must accept {typeOfCookie} cookies by enabling them in our cookie settings.',
group: groups.cookie,
},
cookie_consent_two: {
title: 'Information text - for two types of cookies',
defaultValue:
'Want the full picture? We’d love to share this content with you, but first you must accept {type1} and {type2} cookies by enabling them in our cookie settings.',
group: groups.cookie,
},
cookie_consent_many: {
title: 'Information text - for all types of cookies ',
defaultValue:
'Want the full picture? We’d love to share this content with you, but first you must accept {type1}, {type2} and {type3} cookies by enabling them in our cookie settings.',
group: groups.cookie,
},
contact_form_name: {
title: 'Name',
defaultValue: 'Name *',
Expand Down
11 changes: 10 additions & 1 deletion web/common/helpers/checkCookieConsent.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export const checkCookieConsent = (cookiePolicy: string): boolean => {
import { CookieType } from '../../types'

const checkSingleCookieConsent = (cookiePolicy: CookieType): boolean => {
if (cookiePolicy === 'none') return true

if (typeof window !== 'undefined') {
Expand All @@ -11,3 +13,10 @@ export const checkCookieConsent = (cookiePolicy: string): boolean => {

return false
}

export const checkCookieConsent = (cookiePolicies: CookieType[]): boolean => {
if (cookiePolicies.length === 1 && cookiePolicies[0] === 'none') return true
return cookiePolicies
.map((it) => checkSingleCookieConsent(it))
.reduce((accumulator, currentValue) => accumulator && currentValue, true)
}
24 changes: 5 additions & 19 deletions web/lib/hooks/useConsent.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import { ConsentType } from './useConsentState'

const getConsentState = (consentType: ConsentType): boolean => {
// Prevents SSR issues
if (typeof window !== 'undefined') {
switch (consentType) {
case 'statistics':
return window.Cookiebot?.consent.statistics
case 'marketing':
return window.Cookiebot?.consent.marketing
default:
return false
}
}
return false
}
import { checkCookieConsent } from '../../common/helpers/checkCookieConsent'
import { CookieType } from '../../types'

/**
* Returns true if the consent is given for the given consentType.
* @param consentType Can be either marketing or statistics
* @returns
*/
export default function useConsent(consentType: ConsentType): boolean | undefined {
const [consent, setConsent] = useState<boolean>(getConsentState(consentType))
export default function useConsent(consentType: CookieType[]): boolean | undefined {
const [consent, setConsent] = useState<boolean>(checkCookieConsent(consentType))
const router = useRouter()
useEffect(() => {
setConsent(getConsentState(consentType))
setConsent(checkCookieConsent(consentType))
}, [consentType, router.asPath])
return consent
}
12 changes: 4 additions & 8 deletions web/lib/hooks/useConsentState.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { Flags } from '../../common/helpers/datasetHelpers'
import { CookieType } from '../../types'
import { checkCookieConsent } from '../../common/helpers/checkCookieConsent'

//COOKIEBOT
declare global {
Expand All @@ -10,19 +12,13 @@ declare global {
}
}

export type ConsentType = 'marketing' | 'statistics'

function useConsentState(consentType: ConsentType, callback: () => void, cleanup?: () => void) {
function useConsentState(consentType: CookieType[], callback: () => void, cleanup?: () => void) {
const [consent, changeConsent] = useState<boolean>(false)
const router = useRouter()

useEffect(() => {
const manageCookies = () => {
if (consentType === 'marketing') {
changeConsent(window?.Cookiebot.consent.marketing)
} else if (consentType === 'statistics') {
changeConsent(window?.Cookiebot.consent.statistics)
}
changeConsent(checkCookieConsent(consentType))
}
window?.addEventListener('CookiebotOnAccept', manageCookies)
window?.addEventListener('CookiebotOnDecline', manageCookies)
Expand Down
2 changes: 1 addition & 1 deletion web/lib/hooks/useVideojsAnalytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type EventData = {

// Video Analytics Hook
const useVideojsAnalytics = (player: Player | null, src: string, title?: string, autoPlay?: boolean): void => {
const allowAnalytics = useConsent('statistics') || false
const allowAnalytics = useConsent(['statistics']) || false

const pushEventToDataLayer = useCallback(
(eventType: EventType, player: Player) => {
Expand Down
2 changes: 1 addition & 1 deletion web/lib/queries/common/eventContentFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const eventContentFields = /* groq */ `
title,
frameTitle,
url,
"cookiePolicy": coalesce(cookiePolicy, 'none'),
cookiePolicy,
"designOptions": {
"aspectRatio": coalesce(aspectRatio, '16:9'),
height,
Expand Down
3 changes: 2 additions & 1 deletion web/lib/queries/common/newsSubqueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import downloadableImageFields from './actions/downloadableImageFields'
import linkSelectorFields from './actions/linkSelectorFields'
import markDefs from './blockEditorMarks'

export const iframeForNewsQuery = /* groq */ `defined(iframe) {
export const iframeForNewsQuery = /* groq */ ` iframe{
title,
frameTitle,
url,
"designOptions": {
"aspectRatio": coalesce(aspectRatio, '16:9'),
height,
},
cookiePolicy
}`

export const contentForNewsQuery = /* groq */ `content[] {
Expand Down
2 changes: 1 addition & 1 deletion web/lib/queries/common/pageContentFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ _type == "keyNumbers" =>{
${linkSelectorFields},
},
url,
"cookiePolicy": coalesce(cookiePolicy, 'none'),
cookiePolicy,
"designOptions": {
"aspectRatio": coalesce(aspectRatio, '16:9'),
${background},
Expand Down
2 changes: 1 addition & 1 deletion web/lib/queries/gridContentFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ frameTitle,
${linkSelectorFields},
},
url,
"cookiePolicy": coalesce(cookiePolicy, 'none'),
cookiePolicy,
"designOptions": {
"aspectRatio": coalesce(aspectRatio, '16:9'),
${background},
Expand Down
4 changes: 3 additions & 1 deletion web/lib/queries/iframeCarouselFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ export const iframeCarouselFields = /* groq */ `
"id": _key,
"type": _type,
title,
items[]{..., "action": action[0]{
items[]{...,
cookiePolicy,
"action": action[0]{
${linkSelectorFields},
},},
"designOptions": {
Expand Down
2 changes: 1 addition & 1 deletion web/pageComponents/shared/iframe/BasicIFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const Container = styled.div`
`

const BasicIFrame = ({ data, ...rest }: { data: IFrameData }) => {
const { title, frameTitle, url, cookiePolicy = 'none', designOptions } = data || {}
const { title, frameTitle, url, cookiePolicy = ['none'], designOptions } = data || {}
if (!url) return null

const { height, aspectRatio, background } = designOptions
Expand Down
46 changes: 29 additions & 17 deletions web/pageComponents/shared/iframe/IFrame.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { HTMLAttributes, useContext } from 'react'
import { HTMLAttributes, useContext, useState } from 'react'
import { PreviewContext } from '../../../lib/contexts/PreviewContext'
import styled from 'styled-components'
import RequestConsentContainer from './RequestConsentContainer'
import useConsentState from '../../../lib/hooks/useConsentState'
import { CookieType } from '../../../types'
import useConsent from '../../../lib/hooks/useConsent'

const IFrameContainer = styled.div<{ aspectRatioPadding: string }>`
position: relative;
Expand Down Expand Up @@ -31,7 +34,7 @@ const calculatePadding = (aspectRatio: string): string => {
type IFrameProps = {
frameTitle: string
url: string
cookiePolicy: string
cookiePolicy: CookieType[]
height?: number
aspectRatio: string
hasSectionTitle: boolean
Expand All @@ -43,13 +46,24 @@ const IFrame = ({
hasSectionTitle = true,
frameTitle,
url,
cookiePolicy = 'none',
cookiePolicy = ['none'],
aspectRatio,
height,
className = '',
describedBy,
}: IFrameProps) => {
const { isPreview } = useContext(PreviewContext)
const [consented, setConsented] = useState(useConsent(cookiePolicy))

useConsentState(
cookiePolicy,
() => {
setConsented(true)
},
() => {
setConsented(false)
},
)

if (!url) return null

Expand All @@ -65,22 +79,20 @@ const IFrame = ({

return (
<>
<div className={`cookieconsent-optin-${cookiePolicy} ${className}`}>
<IFrameContainer aria-describedby={describedBy} aspectRatioPadding={containerPadding}>
<StyledIFrame
allowFullScreen
src={url}
title={frameTitle}
loading="lazy"
data-cookieconsent={cookiePolicy}
></StyledIFrame>
</IFrameContainer>
</div>
{cookiePolicy !== 'none' && (
<div className={`cookieconsent-optout-${cookiePolicy}`}>
<RequestConsentContainer hasSectionTitle={hasSectionTitle} cookiePolicy={cookiePolicy} />
{consented && (
<div className={className}>
<IFrameContainer aria-describedby={describedBy} aspectRatioPadding={containerPadding}>
<StyledIFrame
allowFullScreen
src={url}
title={frameTitle}
loading="lazy"
data-cookieconsent={cookiePolicy}
></StyledIFrame>
</IFrameContainer>
</div>
)}
{!consented && <RequestConsentContainer hasSectionTitle={hasSectionTitle} cookiePolicy={cookiePolicy} />}
</>
)
}
Expand Down
Loading

0 comments on commit 8f94897

Please sign in to comment.