Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Support retention durations #6

Merged
merged 7 commits into from
Dec 15, 2023
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/example/package.json
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
},
"dependencies": {
"@coinbase/cookie-banner": "1.0.1",
"@coinbase/cookie-manager": "^1.0.0",
"@coinbase/cookie-manager": "1.1.0",
"next": "14.0.0",
"react": "^18",
"react-dom": "^18"
6 changes: 4 additions & 2 deletions apps/example/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -9,11 +9,12 @@ import { cookieManagerConfig } from '../utils/cookieManagerConfig';

export default function App({ Component, pageProps }: AppProps) {
const [isMounted, setIsMounted] = useState(false);
const trackingPreference = useRef<TrackingPreference | undefined>();

useEffect(() => {
setIsMounted(true);
}, []);

const trackingPreference = useRef<TrackingPreference | undefined>();
const setTrackingPreference = useCallback((newPreference: TrackingPreference) => {
const priorConsent = trackingPreference.current?.consent;
trackingPreference.current = newPreference;
@@ -38,14 +39,15 @@ export default function App({ Component, pageProps }: AppProps) {
window.location.reload();
}
}, []);

if (!isMounted) return null;

return (
<Provider
onError={console.error}
projectName="test"
locale="en"
region={Region.EU}
region={Region.DEFAULT}
config={cookieManagerConfig}
log={console.log}
onPreferenceChange={setTrackingPreference}
13 changes: 13 additions & 0 deletions apps/example/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import { CookieBanner } from '@coinbase/cookie-banner';
import { useCookie } from '@coinbase/cookie-manager';
import { Inter } from 'next/font/google';
import { useEffect } from 'react';

import useTranslations from '@/hooks/useTranslations';

const inter = Inter({ subsets: ['latin'] });

export default function Home() {
const [, setIpCountryCookie] = useCookie('ip_country');
const [, setLocaleCookie] = useCookie('locale');
const [, setRFMCookie] = useCookie('rfm');
const [trackingPreference] = useCookie('cm_eu_preference');

useEffect(() => {
setIpCountryCookie('US');
setRFMCookie('locale');
setLocaleCookie('en');
}, [setIpCountryCookie, setLocaleCookie, setRFMCookie, trackingPreference]);

return (
<main className={`flex min-h-screen bg-black justify-end flex-col ${inter.className}`}>
<CookieBanner
8 changes: 7 additions & 1 deletion apps/example/utils/cookieManagerConfig.ts
Original file line number Diff line number Diff line change
@@ -9,15 +9,21 @@ export const cookieManagerConfig = {
{
id: 'ip_country',
type: TrackerType.COOKIE,
expiry: new Date('2024-09-29T00:00:00.000Z'),
Sneh1999 marked this conversation as resolved.
Show resolved Hide resolved
},
{
id: 'locale',
type: TrackerType.COOKIE,
},
],
},
{
id: TrackingCategory.PERFORMANCE,
trackers: [
{
id: 'cb-rfm',
id: 'rfm',
type: TrackerType.COOKIE,
sessionCookie: true,
},
],
},
2 changes: 1 addition & 1 deletion packages/cookie-banner/README.md
Original file line number Diff line number Diff line change
@@ -201,4 +201,4 @@ The returned record should contain all the keys mentioned in this example. All t

## License

Licensed under the Apache License. See [LICENSE](./LICENSE) for more information.
Licensed under the Apache License. See [LICENSE](./LICENSE.md) for more information.
3 changes: 2 additions & 1 deletion packages/cookie-banner/package.json
Original file line number Diff line number Diff line change
@@ -18,7 +18,8 @@
"react": "^18.1.0",
"react-dom": "^18.1.0",
"rimraf": "^5.0.5",
"typescript": "^5.2.2"
"typescript": "^5.2.2",
"wrap-ansi": "^7.0.0"
},
"peerDependencies": {
"react": "^18.2.0",
6 changes: 5 additions & 1 deletion packages/cookie-manager/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog

## 1.0.0 (12/7/2023 PST)
## 1.1.0 (12/14/2023)

Support retention durations for Cookies in the config and category level

## 1.0.0 (12/7/2023)

#### 🚀 Updates

42 changes: 32 additions & 10 deletions packages/cookie-manager/README.md
Original file line number Diff line number Diff line change
@@ -4,8 +4,8 @@

# Contents

- [Introduction](#Introduction)
- [Installation](#installation)
- [Introduction](#Introduction)
- [Methods](#methods)
- [Provider](#provider)
- [useCookie](#usecookie)
@@ -20,6 +20,18 @@
- [isOptOut](#isoptout)
- [License](#license)

## Installation

Install the package as follows:

```shell
yarn add @coinbase/cookie-manager

npm install @coinbase/cookie-manager

pnpm install @coinbase/cookie-manager
```

## Introduction

`@coinbase/cookie-manager` helps manage the following first party client side cookie categories:
@@ -61,6 +73,7 @@ export default {
{
id: 'locale',
type: TrackerType.COOKIE,
expiry: new Date('2024-09-29T00:00:00.000Z'),
},
],
},
@@ -70,6 +83,7 @@ export default {
{
id: 'some_cookie',
type: TrackerType.COOKIE,
sessionCookie: true,
},
],
},
@@ -131,18 +145,26 @@ You can also specify regex for a given cookie as follows:

Any id with `-regex` at the end should contain a `regex` which will be used to match different cookies.

In this example: `id_ac7a5c3da45e3612b44543a702e42b01` will also be allowed
In this example: `id_ac7a5c3da45e3612b44543a702e42b01` will also be allowed.

## Installation

Install the package as follows:
Each cookie by default has an expiry of 1 year but you can also override this by specifying an expiry for the cookie within the config:

```shell
yarn add @coinbase/cookie-manager
```
{
id: 'locale',
type: TrackerType.COOKIE,
expiry: new Date('2024-09-29T00:00:00.000Z')
}
```

npm install @coinbase/cookie-manager
If you want a cookie to only be valid for a session, it can optionally be marked as a session cookie as follows:

pnpm install @coinbase/cookie-manager
```
{
id: 'some_cookie',
type: TrackerType.COOKIE,
sessionCookie: true
}
```

## Methods
@@ -476,4 +498,4 @@ const SomeComponent = () => {

## License

Licensed under the Apache License. See [LICENSE](./LICENSE) for more information.
Licensed under the Apache License. See [LICENSE](./LICENSE.md) for more information.
8 changes: 6 additions & 2 deletions packages/cookie-manager/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@coinbase/cookie-manager",
"version": "1.0.0",
"version": "1.1.0",
"description": "Coinbase Cookie Manager",
"main": "dist/index.js",
"license": "Apache-2.0",
@@ -10,6 +10,7 @@
"test": "jest"
},
"devDependencies": {
"@testing-library/react": "^14.1.1",
"@types/jest": "^29.5.8",
"@types/js-cookie": "^3.0.5",
"@types/node": "^20.8.9",
@@ -21,9 +22,12 @@
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typescript": "^5.2.2",
"@testing-library/react": "^14.1.1"
"wrap-ansi": "^7.0.0",
"wrap-ansi-cjs": "7.0.0"
},
"dependencies": {
"@coinbase/cookie-banner": "1.0.1",
"@coinbase/cookie-manager": "1.1.0",
"js-cookie": "^3.0.5"
}
}
11 changes: 10 additions & 1 deletion packages/cookie-manager/src/CookieContext.tsx
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@ import { getDomainWithoutSubdomain, getHostname } from './utils/getDomain';
import getTrackerInfo from './utils/getTrackerInfo';
import hasConsent from './utils/hasConsent';
import isMaxKBSize from './utils/isMaxKBSize';
import setExpiryForCookie from './utils/setExpiryForCookie';
import setGTMVariables from './utils/setGTMVariables';

type CookieCache = Record<string, any>;
@@ -161,11 +162,19 @@ const setCookieFunction = ({
if (isMaxKBSize(encodeURIComponent(stringValue) + cookieName, cookieSize)) {
onError(new Error(`${cookieName} value exceeds ${cookieSize}KB`));
} else {
const newOptions = options ? { ...options } : undefined;
let newOptions = options ? { ...options } : undefined;

if (newOptions?.size) {
delete newOptions.size;
}
const expiration = setExpiryForCookie(cookieName, config) as number | Date | undefined;
if (expiration) {
if (newOptions) {
newOptions = { ...newOptions, expires: expiration };
} else {
newOptions = { expires: expiration };
}
}
Cookies.set(cookieName, stringValue, newOptions);
}
}
8 changes: 8 additions & 0 deletions packages/cookie-manager/src/constants.ts
Original file line number Diff line number Diff line change
@@ -33,3 +33,11 @@ export const TRACKER_CATEGORIES: Array<TrackingCategory> = [
TrackingCategory.PERFORMANCE,
TrackingCategory.TARGETING,
];

export const CATEGORY_EXPIRATION_DAYS: Record<TrackingCategory, number> = {
Sneh1999 marked this conversation as resolved.
Show resolved Hide resolved
[TrackingCategory.NECESSARY]: 365,
[TrackingCategory.FUNCTIONAL]: 365,
[TrackingCategory.PERFORMANCE]: 365,
Sneh1999 marked this conversation as resolved.
Show resolved Hide resolved
[TrackingCategory.TARGETING]: 365,
[TrackingCategory.DELETE_IF_SEEN]: 0,
};
6 changes: 6 additions & 0 deletions packages/cookie-manager/src/examples/config.ts
Original file line number Diff line number Diff line change
@@ -10,6 +10,11 @@ export default {
id: 'locale',
type: TrackerType.COOKIE,
},
{
id: 'test',
type: TrackerType.COOKIE,
expiry: new Date('2038-01-01T00:00:00.000Z'),
},
],
},
{
@@ -36,6 +41,7 @@ export default {
id: 'mode',
// Used to remember if the user dismissed the Advanced mode NUX modal
type: TrackerType.COOKIE,
sessionCookie: true,
},
],
},
2 changes: 2 additions & 0 deletions packages/cookie-manager/src/types.ts
Original file line number Diff line number Diff line change
@@ -41,7 +41,9 @@ export enum TrackerType {
export type Tracker = {
id: string;
type: TrackerType;
sessionCookie?: boolean;
Sneh1999 marked this conversation as resolved.
Show resolved Hide resolved
regex?: string;
expiry?: Date;
};

export type ConfigCategoryInfo = {
19 changes: 19 additions & 0 deletions packages/cookie-manager/src/utils/setExpiryForCookie.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import config from '../examples/config';
import setExpiryForCookie from './setExpiryForCookie';

describe('setExpiryForCookie', () => {
it('returns undefined when no cookie config is found', () => {
expect(setExpiryForCookie('cookieName', config)).toEqual(undefined);
});
it('returns expiry when cookie config has an expiry', () => {
expect(setExpiryForCookie('test', config)).toEqual(new Date('2038-01-01T00:00:00.000Z'));
});
it('returns expiry when cookie is in a category with an expiry', () => {
const expiration = new Date();
expiration.setDate(expiration.getDate() + 365);
expect(setExpiryForCookie('some_cookie', config)).toEqual(expiration);
});
it('returns undefined when cookie is a session cookie', () => {
expect(setExpiryForCookie('mode', config)).toEqual(undefined);
});
});
39 changes: 39 additions & 0 deletions packages/cookie-manager/src/utils/setExpiryForCookie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { CATEGORY_EXPIRATION_DAYS } from '../constants';
import { Config } from '../types';
import getTrackerCategory from './getTrackerCategory';
import trackerMatches from './trackerMatches';

const setExpiryForCookie = (cookieName: string, config: Config) => {
const trackingCategory = getTrackerCategory(cookieName, config);
if (!trackingCategory) {
return undefined;
}

// if cookie has an expiry, set the expiry to that
const cookieConfig = trackingCategory.trackers.find((tracker) =>
trackerMatches(tracker, cookieName)
);

if (!cookieConfig) {
return undefined;
}

// if its a session cookie, do not set an expiry
if (cookieConfig && cookieConfig.sessionCookie) {
return undefined;
}

if (cookieConfig && cookieConfig.expiry) {
return cookieConfig.expiry;
}
// if cookie is in a category with an expiry, set the expiry to that
const trackingCategoryExpiration = CATEGORY_EXPIRATION_DAYS[trackingCategory.id];
if (!trackingCategoryExpiration) {
return undefined;
}
const expiration = new Date();
expiration.setDate(expiration.getDate() + trackingCategoryExpiration);
return expiration;
};

export default setExpiryForCookie;
3 changes: 1 addition & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -6692,8 +6692,7 @@ which@^2.0.1:
dependencies:
isexe "^2.0.0"

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
name wrap-ansi-cjs
[email protected], "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==

Unchanged files with check annotations Beta

Check warning on line 38 in packages/cookie-banner/src/components/CookieBanner.test.tsx

GitHub Actions / build (18.x)

Unexpected console statement