feat(scripts): filter script manager gql response based on cookie consent#2651
feat(scripts): filter script manager gql response based on cookie consent#2651matthewvolk wants to merge 1 commit intocanaryfrom
Conversation
🦋 Changeset detectedLatest commit: 15ae436 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
core/lib/consent-manager/cookies.ts
Outdated
| const enabledCategories = entries | ||
| .filter(([, value]) => value) | ||
| // eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
| .map(([key]) => mapping[key as keyof ConsentState]); |
There was a problem hiding this comment.
Might want to add a .filter and validate the key as ConsentNamesSchema so we don't have to do type assertions.
core/app/[locale]/layout.tsx
Outdated
| const fetchRootLayoutMetadata = cache(async () => { | ||
| const consentCookie = (await consentCookieServer()).get(); | ||
| const consentCategories = getConsentCategoriesFromCookie(consentCookie); | ||
|
|
||
| return await client.fetch({ | ||
| document: RootLayoutMetadataQuery, | ||
| variables: { consentCategories }, | ||
| fetchOptions: { next: { revalidate } }, | ||
| }); | ||
| }); |
There was a problem hiding this comment.
We need to rethink how we pass consentCategories to this query (or maybe create a new query just for scripts).
The problem with this implementation is that you're reading from cookies inside the cached function. If we want to enable composable caching at some other point, consentCategories needs to be a param passed to fetchRootLayoutMetadata (which is simple enough).
However, to read from cookies in layout.tsx without wrapping it in a Streamable, will make this entire layout dynamic, and we want to keep it as static as possible.
I believe we need to figure out a way to stream in what scripts are loaded per selected consent, and render those scripts after the request has streamed in.
There was a problem hiding this comment.
Great catch! Thanks for that, it slipped my mind. Just pushed up a fix, though I think it'd be worthwhile to talk through it a bit more. There's something smelly about my two separate streamableHeaderScripts and streamableFooterScripts fn's.
There was a problem hiding this comment.
I see what you say!
What if we just have one streamableScripts and then <Stream /> the script components.
<Stream fallback={null} value={streamableScripts}>
{(scripts) => <ScriptManagerScripts scripts={scripts. headerScripts} strategy="afterInteractive" />}
</Stream>
...
<Stream fallback={null} value={streamableScripts}>
{(scripts) => <ScriptManagerScripts scripts={scripts.footerScripts} strategy="lazyOnload" />}
</Stream>I have no clue how this conflicts with the strategy tho.
There was a problem hiding this comment.
I guess it is not recommended to Stream afterInteractive scripts
There was a problem hiding this comment.
Follow up question, what scripts are loaded in the header vs footer? Are header scripts essential and should always load? Or is it user choice? My thinking is that maybe header scripts are essential and we always load them in the initial layout request, while scripts requiring consent are loaded in the footer and Streamed in.
bcc570c to
06fd666
Compare
06fd666 to
c14fd2d
Compare
c14fd2d to
15ae436
Compare
Warning
Depends on #2650
What/Why?
Filters BigCommerce scripts based on cookie consent. Reads consent from
c15t-consenton the server, maps C15T consent categories (which cannot be changed, unfortunately) to BigCommerce Script Consent Categories as follows:measurement→ BigCommerceANALYTICSnecessary→ BigCommerceESSENTIALfunctionality→ BigCommerceFUNCTIONALexperience→ BigCommerceFUNCTIONAL(BigCommerce does not have a fifth category, C15T does)marketing→ BigCommerceTARGETINGThese categories are passed to the GraphQL query filter, which does not fetch scripts from BC until the user consents.
Testing
Demo of the testing above:
https://github.com/user-attachments/assets/be44cb40-9077-40a0-9775-0b5170de68f8
Migration
core/lib/consent-manager/action.ts- Server action for consent updatescore/lib/consent-manager/cookies.ts- Addsexperience, exportsConsentStateSchema, addsgetConsentCategoriesFromCookiemappingcore/lib/consent-manager/handlers.ts-setConsentis async; uses server action with revalidationcore/app/[locale]/layout.tsx- Reads cookie, maps categories, passes to GraphQLcore/components/scripts/fragment.ts- AddsconsentCategoriesfilter to header/footer queriessetConsentis async; update callers to await itexperienceconsent type; UI components already support itmeasurement→ANALYTICSnecessary→ESSENTIALfunctionality,experience→FUNCTIONALmarketing→TARGETINGUNKNOWNESSENTIALonlyrevalidateTag('layout')so layout data updates after consent changesRootLayoutMetadataQuerynow requires$consentCategoriesc15t-consentand derives categories before the query