From 8efffd5e99fd176095a63a881169e0fdd7673d3b Mon Sep 17 00:00:00 2001 From: Jay Hodgson Date: Tue, 25 Jun 2024 15:25:13 -0700 Subject: [PATCH 1/2] store cookie prefs in a cookie for cross-site support --- .../CookiesNotification.stories.tsx | 7 +++-- .../CookiesNotification.test.tsx | 31 ++++++++++--------- .../CookiesNotification.tsx | 7 +++-- .../src/utils/hooks/useCookiePreferences.ts | 25 ++++++++++----- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/packages/synapse-react-client/src/components/CookiesNotification/CookiesNotification.stories.tsx b/packages/synapse-react-client/src/components/CookiesNotification/CookiesNotification.stories.tsx index 007c5d5346..e8a54303ba 100644 --- a/packages/synapse-react-client/src/components/CookiesNotification/CookiesNotification.stories.tsx +++ b/packages/synapse-react-client/src/components/CookiesNotification/CookiesNotification.stories.tsx @@ -1,13 +1,16 @@ import { Meta, StoryObj } from '@storybook/react' import React from 'react' import CookiesNotification from './CookiesNotification' -import { COOKIES_AGREEMENT_LOCALSTORAGE_KEY } from '../../utils/hooks/useCookiePreferences' +import UniversalCookies from 'universal-cookie' +import { COOKIES_AGREEMENT_COOKIE_KEY } from '../../utils/hooks/useCookiePreferences' + +const cookies = new UniversalCookies() const meta = { title: 'UI/CookiesNotification', component: CookiesNotification, render: () => { - localStorage.removeItem(COOKIES_AGREEMENT_LOCALSTORAGE_KEY) + cookies.remove(COOKIES_AGREEMENT_COOKIE_KEY) return ( { diff --git a/packages/synapse-react-client/src/components/CookiesNotification/CookiesNotification.test.tsx b/packages/synapse-react-client/src/components/CookiesNotification/CookiesNotification.test.tsx index d74e4a642e..a126a7d053 100644 --- a/packages/synapse-react-client/src/components/CookiesNotification/CookiesNotification.test.tsx +++ b/packages/synapse-react-client/src/components/CookiesNotification/CookiesNotification.test.tsx @@ -4,11 +4,13 @@ import React from 'react' import { createWrapper } from '../../testutils/TestingLibraryUtils' import { SynapseContextType } from '../../utils/context/SynapseContext' import CookiesNotification, { alertConfig } from './CookiesNotification' +import UniversalCookies from 'universal-cookie' import { - COOKIES_AGREEMENT_LOCALSTORAGE_KEY, + COOKIES_AGREEMENT_COOKIE_KEY, CookiePreference, } from '../../utils/hooks/useCookiePreferences' +const cookies = new UniversalCookies() const mockOnCloseFn = jest.fn() function renderComponent(wrapperProps?: SynapseContextType) { const component = render(, { @@ -23,8 +25,7 @@ describe('CookiesNotification', () => { jest.clearAllMocks() }) afterEach(() => { - localStorage.getItem(COOKIES_AGREEMENT_LOCALSTORAGE_KEY) && - localStorage.removeItem(COOKIES_AGREEMENT_LOCALSTORAGE_KEY) + cookies.remove(COOKIES_AGREEMENT_COOKIE_KEY) }) it('displays alert and allows user to accept all cookies', async () => { @@ -40,11 +41,10 @@ describe('CookiesNotification', () => { await user.click(acceptButton) expect(alert).not.toBeInTheDocument() - const localStorageValue = localStorage.getItem( - COOKIES_AGREEMENT_LOCALSTORAGE_KEY, - ) - expect(localStorageValue).toBeDefined() - const cookiePreference = JSON.parse(localStorageValue!) as CookiePreference + const cookiePreference = cookies.get( + COOKIES_AGREEMENT_COOKIE_KEY, + ) as CookiePreference + expect(cookiePreference).toBeDefined() expect(cookiePreference.analyticsAllowed).toBe(true) expect(cookiePreference.functionalAllowed).toBe(true) }) @@ -62,11 +62,10 @@ describe('CookiesNotification', () => { await user.click(disableAllButton) expect(alert).not.toBeInTheDocument() - const localStorageValue = localStorage.getItem( - COOKIES_AGREEMENT_LOCALSTORAGE_KEY, - ) - expect(localStorageValue).toBeDefined() - const cookiePreference = JSON.parse(localStorageValue!) as CookiePreference + const cookiePreference = cookies.get( + COOKIES_AGREEMENT_COOKIE_KEY, + ) as CookiePreference + expect(cookiePreference).toBeDefined() expect(cookiePreference.analyticsAllowed).toBe(false) expect(cookiePreference.functionalAllowed).toBe(false) }) @@ -92,7 +91,11 @@ describe('CookiesNotification', () => { }) it('does not display the alert when cookies have been accepted', () => { - localStorage.setItem(COOKIES_AGREEMENT_LOCALSTORAGE_KEY, 'true') + const cookiePrefence: CookiePreference = { + analyticsAllowed: true, + functionalAllowed: true, + } + cookies.set(COOKIES_AGREEMENT_COOKIE_KEY, cookiePrefence) const { alert } = renderComponent() expect(alert).not.toBeInTheDocument() }) diff --git a/packages/synapse-react-client/src/components/CookiesNotification/CookiesNotification.tsx b/packages/synapse-react-client/src/components/CookiesNotification/CookiesNotification.tsx index 5b989f462c..fd1f3bee64 100644 --- a/packages/synapse-react-client/src/components/CookiesNotification/CookiesNotification.tsx +++ b/packages/synapse-react-client/src/components/CookiesNotification/CookiesNotification.tsx @@ -3,13 +3,16 @@ import FullWidthAlert from '../FullWidthAlert' import { Link, Typography } from '@mui/material' import CookiePreferencesDialog from './CookiePreferencesDialog' import { - COOKIES_AGREEMENT_LOCALSTORAGE_KEY, + COOKIES_AGREEMENT_COOKIE_KEY, CookiePreference, allowAll, allowNone, useCookiePreferences, } from '../../utils/hooks/useCookiePreferences' import { PRIVACY_POLICY_LINK } from '../../utils/SynapseConstants' +import UniversalCookies from 'universal-cookie' + +const cookies = new UniversalCookies() export const alertConfig = { title: 'Our site uses cookies.', @@ -37,7 +40,7 @@ const CookiesNotification = (props: CookieNotificationProps) => { const [, setCookiePreferences] = useCookiePreferences() const [notificationDismissed, setNotificationDismissed] = useState( - localStorage.getItem(COOKIES_AGREEMENT_LOCALSTORAGE_KEY) !== null, + !!cookies.get(COOKIES_AGREEMENT_COOKIE_KEY), ) const [isCookiePrefsDialogVisible, setIsCookiePrefsDialogVisible] = diff --git a/packages/synapse-react-client/src/utils/hooks/useCookiePreferences.ts b/packages/synapse-react-client/src/utils/hooks/useCookiePreferences.ts index 9c2fc1a1d7..677bfdc3c0 100644 --- a/packages/synapse-react-client/src/utils/hooks/useCookiePreferences.ts +++ b/packages/synapse-react-client/src/utils/hooks/useCookiePreferences.ts @@ -1,5 +1,8 @@ import { useCallback } from 'react' import { atom, useAtom } from 'jotai' +import UniversalCookies from 'universal-cookie' + +const cookies = new UniversalCookies() export type CookiePreference = { functionalAllowed: boolean @@ -16,10 +19,12 @@ export const allowNone: CookiePreference = { } export const getCurrentCookiePreferences = () => { - const prefs = localStorage.getItem(COOKIES_AGREEMENT_LOCALSTORAGE_KEY) + const prefs = cookies.get(COOKIES_AGREEMENT_COOKIE_KEY, { + doNotParse: true, + }) as string | undefined let cookiePreference = allowNone try { - if (prefs != null) { + if (prefs != undefined) { cookiePreference = JSON.parse(prefs) as CookiePreference } } catch (err) { @@ -27,10 +32,9 @@ export const getCurrentCookiePreferences = () => { `Failed to parse CookiePreference from value, falling back to allow none. value=${prefs}`, ) } - return cookiePreference } -export const COOKIES_AGREEMENT_LOCALSTORAGE_KEY = +export const COOKIES_AGREEMENT_COOKIE_KEY = 'org.sagebionetworks.security.cookies.portal.preference' const cookiePreferencesAtom = atom( @@ -50,10 +54,15 @@ export const useCookiePreferences = (): [ localStorage.clear() sessionStorage.clear() } - localStorage.setItem( - COOKIES_AGREEMENT_LOCALSTORAGE_KEY, - JSON.stringify(prefs), - ) + + const current = new Date() + const nextYear = new Date() + nextYear.setFullYear(current.getFullYear() + 1) + cookies.set(COOKIES_AGREEMENT_COOKIE_KEY, prefs, { + path: '/', + expires: nextYear, + }) + setCookiePreferencesAtomValue(prefs) }, [setCookiePreferencesAtomValue], From 25ad288e35368231a727a4dfd5623565d9f45d50 Mon Sep 17 00:00:00 2001 From: Jay Hodgson Date: Tue, 25 Jun 2024 17:54:46 -0700 Subject: [PATCH 2/2] and bump SRC --- packages/synapse-react-client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/synapse-react-client/package.json b/packages/synapse-react-client/package.json index 605e50c82e..71849b69f4 100644 --- a/packages/synapse-react-client/package.json +++ b/packages/synapse-react-client/package.json @@ -1,6 +1,6 @@ { "name": "synapse-react-client", - "version": "3.2.26", + "version": "3.2.27", "private": false, "main": "./dist/index.js", "module": "./dist/index.mjs",