Skip to content

ENG-3648: Fix FidesJS banner resurfacing on non-applicable notices#8137

Open
gilluminate wants to merge 8 commits intomainfrom
gill/ENG-3648/fidesjs-resurface-non-applicable
Open

ENG-3648: Fix FidesJS banner resurfacing on non-applicable notices#8137
gilluminate wants to merge 8 commits intomainfrom
gill/ENG-3648/fidesjs-resurface-non-applicable

Conversation

@gilluminate
Copy link
Copy Markdown
Contributor

@gilluminate gilluminate commented May 7, 2026

Ticket ENG-3648

Description Of Changes

When a notice key was previously stored in the user's fides_consent cookie under non_applicable_notice_keys (e.g. left over from a prior region) and that same notice later becomes applicable in the current experience, the banner correctly resurfaces once — but dismissing it had no effect, so the banner kept resurfacing on every page load until the user explicitly saved preferences.

Root cause: handleDismiss in NoticeOverlay short-circuited whenever the saved cookie had any consent values set. In the bug scenario the saved cookie carried decisions for the prior region's notices ({essential: true, marketing: true, ...}) but had no entry for the newly-applicable notice. The guard treated "some consent set" as "user has decided everything," skipped handleUpdatePreferences, and the new notice was never written to the consent map.

Fix: tighten the guard so it only skips when every currently-applicable notice already has a recorded preference. If any served notice is missing from the saved consent map, dismiss now records defaults for that notice while preserving existing true decisions (via getEnabledNoticeKeys).

Behavior now matches the ticket's expectation:

  • Banner shows once for a newly-applicable notice
  • Dismissing writes the missing notice keys into consent with their default values
  • Subsequent reloads keep the banner dismissed

Code Changes

  • clients/fides-js/src/components/notices/NoticeOverlay.tsx — replace the consentCookieObjHasSomeConsentSet guard in handleDismiss with a check that runs handleUpdatePreferences whenever any currently-applicable notice has no recorded preference. Drop the now-unused import.
  • clients/privacy-center/cypress/e2e/fides-js/banner-overlay-dismissal.cy.ts — add an E2E regression that pre-seeds the bug scenario (prior consent for one notice, advertising in non_applicable_notice_keys, served applicably), dismisses, and asserts the cookie's consent map gains advertising with consentMethod: dismiss, then reloads and asserts the banner stays hidden.

Steps to Confirm

⚠️ Use two separate browsers for this test (e.g. Chrome + Firefox, or Chrome + Safari). Browser A runs Admin UI; Browser B runs the privacy-center demo. The Admin UI experience-preview re-initializes fides.js with fidesClearCookie: true on every form change, and because the cookie is scoped to Domain=localhost (no port), running both apps in the same browser means editing the experience wipes the manual cookie you're testing with.

  1. Start the full stack and the privacy-center dev server:
    nox -s "dev"
    cd clients/privacy-center && npm run dev
  2. In Browser A, open Admin UI and configure (or pick) a Privacy Experience that will be served to your browser:
    • Component: Banner and Modal
    • Locations: include the location matching your browser/IP (e.g. US)
    • Privacy Notices: include only one notice (e.g. Advertising). Do not include any other notices — any extra served notice that isn't accounted for in the seeded cookie below will legitimately trigger a resurface and mask the fix.
    • Dismissable: enabled
    • Note the notice_key of the served notice (e.g. advertising).
  3. In Browser B, open the fides-js demo: http://localhost:3001/fides-js-demo.html
  4. Open DevTools → Application → Cookies → set fides_consent to the following (URL-encoded), replacing advertising with the notice_key from step 2. The cookie has prior decisions for unrelated notices and lists advertising as previously non-applicable:
    {"consent":{"essential":true,"marketing":false},"identity":{"fides_user_device_id":"9e129592-2acc-4a86-bb7d-1e500c4a76ef"},"fides_meta":{"version":"0.9.0","createdAt":"2026-03-18T22:11:37.070Z","updatedAt":"2026-03-18T22:11:39.476Z","consentMethod":"accept"},"tcf_consent":{},"non_applicable_notice_keys":["advertising"]}
  5. Reload the page. Expected: banner appears (because advertising is now applicable and has no entry in consent).
  6. Click the X to dismiss the banner.
  7. Inspect the cookie in DevTools — consent should now include advertising: false and consentMethod should be dismiss.
  8. Reload. Expected: banner does not resurface.
  9. To confirm the previous (broken) behavior: stash this PR's change, rebuild fides-js (cd clients/fides-js && npm run build), repeat steps 4–8. After dismissing, the cookie's consent will be unchanged (no advertising entry) and the banner resurfaces on every reload.

Pre-Merge Checklist

  • Issue requirements met
  • All CI pipelines succeeded
  • CHANGELOG.md updated
    • Add a db-migration This indicates that a change includes a database migration label to the entry if your change includes a DB migration
    • Add a high-risk This issue suggests changes that have a high-probability of breaking existing code label to the entry if your change includes a high-risk change (i.e. potential for performance impact or unexpected regression) that should be flagged
    • Updates unreleased work already in Changelog, no new entry necessary
  • UX feedback:
    • All UX related changes have been reviewed by a designer
    • No UX review needed
  • Followup issues:
    • Followup issues created
    • No followup issues
  • Database migrations:
    • Ensure that your downrev is up to date with the latest revision on main
    • Ensure that your downgrade() migration is correct and works
      • If a downgrade migration is not possible for this change, please call this out in the PR description!
    • No migrations
  • Documentation:
    • Documentation complete, PR opened in fidesdocs
    • If there are any new client scopes created as part of the pull request, remember to update public-facing documentation that references our scope registry
    • No documentation updates required

shouldResurfaceBanner treated a notice as missing prior consent
whenever it was absent from savedConsent, even if the notice was
already recorded as non-applicable in the saved cookie. When such a
notice later became applicable in a different experience, the banner
resurfaced on every page load after dismissal until the user actually
saved preferences.

Treat notices listed in cookie.non_applicable_notice_keys as having a
known prior decision, alongside notices with explicit consent values.
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
fides-plus-nightly Ignored Ignored Preview May 8, 2026 6:30pm
fides-privacy-center Ignored Ignored May 8, 2026 6:30pm

Request Review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

Title Lines Statements Branches Functions
admin-ui Coverage: 8%
6.61% (3042/46014) 5.93% (1572/26490) 4.62% (630/13613)
fides-js Coverage: 78%
79.51% (2019/2539) 66.24% (1248/1884) 73.31% (349/476)
privacy-center Coverage: 85%
82.53% (364/441) 79.74% (189/237) 74.07% (60/81)

shouldResurfaceBanner reads cookie.non_applicable_notice_keys, but
updateCookieFromExperience overwrote that field with the current
experience's list before the resurface check ran. When a notice that
was non-applicable in a prior region became applicable in the current
one, the saved record was erased and the banner kept resurfacing
despite the previous fix.

Union the saved list with the current experience's non-applicable
list so the resurface check sees both. Update the existing test to
match and add a deduplication case.
handleDismiss skipped handleUpdatePreferences whenever the saved
cookie had any consent values set. When a notice that was previously
non-applicable became applicable in a new region, the saved cookie
already had values for the prior notices but no entry for the new
one, so dismiss became a no-op and the banner kept resurfacing on
every reload until the user explicitly saved preferences.

Tighten the guard: only skip dismiss-update when every currently-
applicable notice already has a recorded preference. If any served
notice is missing from the saved consent map, dismiss now records
defaults for it (preserving existing decisions).
selectBestNoticeTranslation,
} from "../../lib/i18n";
import { useI18n } from "../../lib/i18n/i18n-context";
import { getOverridesByType } from "../../lib/initialize";
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated lint error (from a prior PR) because this was no longer used

Pre-seeds a cookie with prior consent for an unrelated notice and
"advertising" listed in non_applicable_notice_keys. With a current
experience that serves "advertising" applicably, the banner should
resurface once, dismissing should write advertising into the consent
map, and subsequent reloads should keep the banner hidden.
@gilluminate gilluminate marked this pull request as ready for review May 8, 2026 18:35
@gilluminate gilluminate requested a review from a team as a code owner May 8, 2026 18:35
@gilluminate gilluminate requested review from lucanovera and removed request for a team May 8, 2026 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant