feat(track): gate TrackShopify on Shopify analytics consent - #654
Open
titouanmathis wants to merge 1 commit into
Open
titouanmathis wants to merge 1 commit into
titouanmathis wants to merge 1 commit into
Conversation
`dispatch()` now calls `window.Shopify.customerPrivacy.analyticsProcessingAllowed()` before every publish and publishes only when it returns `true`. `window.Shopify` is read fresh on each dispatch, so a consent change applies to the next event without a remount. Shopify's Web Pixels Manager gates App Pixels at load time, but it hands every published event to custom pixels, which are expected to apply their own consent logic. That gate is now the component's default instead of something each storefront repeats before each event. A dropped event is not queued and is never replayed. Each drop reports a diagnostic so a missing event is diagnosable: `track.shopify-consent-denied` when processing is refused, `track.shopify-privacy-unavailable` when the Customer Privacy API is absent — which is legitimate early in a page's life, since Shopify loads it asynchronously through `loadFeatures()`. The event-name check runs before the consent check. A declaration without an `event` name is wrong for every visitor, so it stays visible instead of being masked by a consent drop. The TrackShopify tests move out of `Track.spec.ts` into their own spec. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LMSCm41fm3g7chxD728vAu
titouanmathis
force-pushed
the
feat/v2-track-shopify-consent
branch
from
September 16, 2026 16:29
e749ab5 to
0f4e148
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 2.x #654 +/- ##
=========================================
Coverage 86.32% 86.32%
Complexity 145 145
=========================================
Files 20 20
Lines 746 746
Branches 88 88
=========================================
Hits 644 644
Misses 95 95
Partials 7 7
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
TrackShopify.dispatch()evaluateswindow.Shopify?.customerPrivacy?.analyticsProcessingAllowed?.()before every publish and publishes only when it returnstrue.window.Shopifyis read fresh on each dispatch, so a consent change applies to the next event without a remount, and a Customer Privacy API that appears after mount is picked up on the next event.A dropped event is not queued and is never replayed once consent is granted. The
declare globalWindow.Shopifytype gains an optionalcustomerPrivacy.analyticsProcessingAllowed.Why the gate is needed
Shopify's Web Pixels Manager gates App Pixels at load time: it loads a pixel only when the visitor has granted every consent that pixel declares as required. It does not gate Custom Pixels, which receive every published event and are expected to implement their own consent logic inside the sandbox. Shopify's Analytics API best practices say to check whether analytics processing is allowed and publish only when it is true.
So the platform covers part of the case and the component covers the rest. Before this change, every storefront had to repeat the same check before each event, which made declarative HTML insufficient for consent-aware tracking.
Why a drop is diagnosed rather than silent
The Customer Privacy API is loaded asynchronously through
loadFeatures(), sowindow.Shopify.customerPrivacyis legitimately undefined early in a page's life. An event that vanishes in that window reads as a bug rather than a privacy decision. Each drop now reports a keyed diagnostic, and the two reasons are distinguished:track.shopify-consent-deniedanalyticsProcessingAllowed()returnedfalsetrack.shopify-privacy-unavailablewindow.Shopify.customerPrivacy.analyticsProcessingAllowedis not a functionThese follow the naming of the existing
track.shopify-unavailableandtrack.missing-event-namekeys.Order of the checks
dispatch()checks the analytics provider, then the event name, then consent. The first two catch a broken environment and a broken declaration, which are wrong for every visitor. A consent drop follows one visitor's choice. Checking consent first would hide an authoring mistake behind a legitimate drop, so an author debugging a missing event sees the actionable diagnostic. The reasoning is in a code comment.Trackis unchangedTrackpushes towindow.dataLayer, an array append that transmits nothing by itself. The tag manager or CMP reading that array decides what leaves the browser, so consent there belongs at the tag level. No opt-out option was added — that stays out until someone needs it.Tests
The
TrackShopifytests move out ofTrack.spec.tsintopackages/tests/Track/TrackShopify.spec.ts, and cover every acceptance criterion through the component's public surface:track.shopify-consent-denied;track.shopify-privacy-unavailable;customerPrivacyobject withoutanalyticsProcessingAllowedbehaves the same;eventname is reported even while consent is denied, which pins the check order;thisstays bound towindow.Shopify.analyticswhen publishing;window.dataLayeris never touched.Verified with the change reverted: six of the new tests fail without the gate.
npm run test(979 tests, 87 files),npm run lintandnpm run manifest:checkall pass.Docs
reference/items/Track/index.md,js-api.md,anatomy.mdandexamples.mddocument the consent-safe default, what is checked and when, that a denied or absent API means no publish, and the diagnostic codes. A short paragraph explains how this relates to Shopify custom pixels and whyTrackis not gated the same way.Closes #653
🤖 Generated with Claude Code
https://claude.ai/code/session_01LMSCm41fm3g7chxD728vAu