From 9ce32696ba64f7b916ca2bab97f3ec2df7035dcc Mon Sep 17 00:00:00 2001 From: Catherine Smith Date: Fri, 22 Mar 2024 08:21:33 -0400 Subject: [PATCH 1/2] Sets GPP applicable sections appropriately when user is in a state outside of GPP jurisdiction (#4727) --- CHANGELOG.md | 12 +- clients/fides-js/src/fides-ext-gpp.ts | 4 +- clients/fides-js/src/lib/extensions.ts | 1 + clients/fides-js/src/lib/gpp/us-notices.ts | 8 + .../cypress/e2e/consent-banner-gpp.cy.ts | 426 +++++++++++------- 5 files changed, 284 insertions(+), 167 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c61d72b0eb..c207f7c236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,12 +24,12 @@ The types of changes are: ### Changed - Updated privacy notice & experience forms to hide translation UI when user doesn't have translation feature [#4728](https://github.com/ethyca/fides/pull/4728), [#4734](https://github.com/ethyca/fides/pull/4734) - Custom privacy request fields now support list values [#4686](https://github.com/ethyca/fides/pull/4686) - +- Update when GPP API reports signal status: ready [#4635](https://github.com/ethyca/fides/pull/4635) + ### Fixed - Fixed responsive issues with the buttons on the integration screen [#4729](https://github.com/ethyca/fides/pull/4729) - Disable editing of data use declaration name and type after creation [#4731](https://github.com/ethyca/fides/pull/4731) - -- Initialization issues with ExperienceNotices (#4723)[https://github.com/ethyca/fides/pull/4723] +- Initialization issues with ExperienceNotices [#4723](https://github.com/ethyca/fides/pull/4723) ## [2.32.0](https://github.com/ethyca/fides/compare/2.31.1...2.32.0) @@ -77,13 +77,13 @@ The types of changes are: - Navbar update and new properties page [#4633](https://github.com/ethyca/fides/pull/4633) - Access and erasure support for Oracle Responsys [#4618](https://github.com/ethyca/fides/pull/4618) -### Changed -- Update when GPP API reports signal status: ready [#4635](https://github.com/ethyca/fides/pull/4635) - ### Fixed - Fix issue where "x" button on Fides.js components overwrites saved preferences [#4649](https://github.com/ethyca/fides/pull/4649) - Initialize Fides.consent with default values from experience when saved consent cookie (fides_consent) does not exist [#4665](https://github.com/ethyca/fides/pull/4665) +### Changed +- Sets GPP applicableSections to -1 when a user visits from a state that is not part of the GPP [#4727](https://github.com/ethyca/fides/pull/4727) + ## [2.30.1](https://github.com/ethyca/fides/compare/2.30.0...2.30.1) ### Fixed diff --git a/clients/fides-js/src/fides-ext-gpp.ts b/clients/fides-js/src/fides-ext-gpp.ts index f6a55bf2bb..2d3ad5681f 100644 --- a/clients/fides-js/src/fides-ext-gpp.ts +++ b/clients/fides-js/src/fides-ext-gpp.ts @@ -191,7 +191,9 @@ export const initializeGppCmpApi = () => { cmpApi, experience, }); - cmpApi.setApplicableSections(sectionsChanged.map((s) => s.id)); + if (sectionsChanged.length) { + cmpApi.setApplicableSections(sectionsChanged.map((s) => s.id)); + } } }); diff --git a/clients/fides-js/src/lib/extensions.ts b/clients/fides-js/src/lib/extensions.ts index a07a4b0da0..abc89508d7 100644 --- a/clients/fides-js/src/lib/extensions.ts +++ b/clients/fides-js/src/lib/extensions.ts @@ -12,6 +12,7 @@ export const setupExtensions = async ({ options: FidesOptions; experience: PrivacyExperience | EmptyExperience | undefined; }) => { + // TODO (PROD-1830): pre-bundle this when GPP is enabled instead of relying on experience if (experience?.gpp_settings?.enabled) { try { await import(`${options.fidesJsBaseUrl}/fides-ext-gpp.js`); diff --git a/clients/fides-js/src/lib/gpp/us-notices.ts b/clients/fides-js/src/lib/gpp/us-notices.ts index fee675f5a3..6084f4e56d 100644 --- a/clients/fides-js/src/lib/gpp/us-notices.ts +++ b/clients/fides-js/src/lib/gpp/us-notices.ts @@ -83,6 +83,10 @@ export const setGppNoticesProvidedFromExperience = ({ const gppSection = FIDES_REGION_TO_GPP_SECTION[gppRegion]; if (!gppSection) { + if (experience?.gpp_settings?.us_approach === GPPUSApproach.STATE) { + cmpApi.setApplicableSections([-1]); + return []; + } return []; } @@ -130,6 +134,10 @@ export const setGppOptOutsFromCookieAndExperience = ({ const gppSection = FIDES_REGION_TO_GPP_SECTION[gppRegion]; if (!gppSection) { + if (experience?.gpp_settings?.us_approach === GPPUSApproach.STATE) { + cmpApi.setApplicableSections([-1]); + return []; + } return []; } sectionsChanged.add(gppSection); diff --git a/clients/privacy-center/cypress/e2e/consent-banner-gpp.cy.ts b/clients/privacy-center/cypress/e2e/consent-banner-gpp.cy.ts index a12dd64ba2..fde851116a 100644 --- a/clients/privacy-center/cypress/e2e/consent-banner-gpp.cy.ts +++ b/clients/privacy-center/cypress/e2e/consent-banner-gpp.cy.ts @@ -5,12 +5,41 @@ */ /* eslint-disable no-underscore-dangle */ -import { CONSENT_COOKIE_NAME, FidesCookie, FidesEndpointPaths } from "fides-js"; +import { + CONSENT_COOKIE_NAME, + FidesCookie, + FidesEndpointPaths, + PrivacyExperience, +} from "fides-js"; import { API_URL, TCF_VERSION_HASH } from "../support/constants"; import { mockCookie } from "../support/mocks"; import { stubConfig } from "../support/stubs"; describe("Fides-js GPP extension", () => { + /** + * Visit the fides-js-components-demo page with optional overrides on experience + */ + const visitDemoWithGPP = (props: { + overrideExperience?: (experience: PrivacyExperience) => PrivacyExperience; + }) => { + cy.fixture("consent/experience_gpp.json").then((payload) => { + let experience = payload.items[0]; + if (props.overrideExperience) { + experience = props.overrideExperience(payload.items[0]); + cy.log( + "Using overridden PrivacyExperience data from overrideExperience()", + experience + ); + } + stubConfig({ + options: { + isOverlayEnabled: true, + tcfEnabled: false, + }, + experience, + }); + }); + }; /** * TODO (PROD-1439): remove this workaround by fixing GPP initialization! * @@ -361,197 +390,274 @@ describe("Fides-js GPP extension", () => { }); describe("with TCF disabled and GPP enabled", () => { - beforeEach(() => { - cy.fixture("consent/experience_gpp.json").then((payload) => { - stubConfig({ - options: { - isOverlayEnabled: true, - tcfEnabled: false, - }, - experience: payload.items[0], + describe("when visiting from a state with an applicable section", () => { + beforeEach(() => { + visitDemoWithGPP({}); + cy.waitUntilFidesInitialized().then(() => { + cy.get("@FidesUIShown").should("have.been.calledOnce"); + cy.window().then((win) => { + win.__gpp("addEventListener", cy.stub().as("gppListener")); + }); }); }); - cy.waitUntilFidesInitialized().then(() => { - cy.get("@FidesUIShown").should("have.been.calledOnce"); + it("loads the gpp extension if it is enabled", () => { cy.window().then((win) => { - win.__gpp("addEventListener", cy.stub().as("gppListener")); + win.__gpp("ping", cy.stub().as("gppPing")); + cy.get("@gppPing") + .should("have.been.calledOnce") + .its("lastCall.args") + .then(([data, success]) => { + expect(success).to.eql(true); + expect(data.signalStatus).to.eql("ready"); + }); }); }); - }); - it("loads the gpp extension if it is enabled", () => { - cy.window().then((win) => { - win.__gpp("ping", cy.stub().as("gppPing")); - cy.get("@gppPing") - .should("have.been.calledOnce") + it("can go through the flow of user opting in to data sales and sharing", () => { + cy.get("button").contains("Opt in to all").click(); + cy.waitUntilCookieExists(CONSENT_COOKIE_NAME).then(() => { + cy.getCookie(CONSENT_COOKIE_NAME).then((cookie) => { + const fidesCookie: FidesCookie = JSON.parse( + decodeURIComponent(cookie!.value) + ); + const { consent } = fidesCookie; + expect(consent).to.eql({ data_sales_sharing_gpp_us_state: true }); + }); + }); + + const expected = [ + // First two gppStrings indicate the data_sales_sharing_gpp_us_state notice was served and opted in (default) + { + eventName: "listenerRegistered", + data: true, + gppString: "DBABBg~BUoAAABY.QA", + }, + { + eventName: "cmpDisplayStatus", + data: "hidden", + gppString: "DBABBg~BUoAAABY.QA", + }, + // Second two gppStrings indicate the data_sales_sharing_gpp_us_state notice was served and opted into + { + eventName: "sectionChange", + data: "uscav1", + gppString: "DBABBg~BUoAAABY.QA", + }, + { + eventName: "signalStatus", + data: "ready", + gppString: "DBABBg~BUoAAABY.QA", + }, + ]; + // Check the GPP events + cy.get("@gppListener") + .its("args") + .then( + ( + args: [ + { eventName: string; data: string | boolean; pingData: any }, + boolean + ][] + ) => { + args.forEach(([data, success], idx) => { + expect(success).to.eql(true); + expect(data.eventName).to.eql(expected[idx].eventName); + expect(data.data).to.eql(expected[idx].data); + expect(data.pingData.gppString).to.eql(expected[idx].gppString); + }); + } + ); + cy.get("@gppListener") .its("lastCall.args") - .then(([data, success]) => { - expect(success).to.eql(true); - expect(data.signalStatus).to.eql("ready"); + .then((args) => { + const [data] = args; + expect(data.pingData.applicableSections).to.eql([8]); + // TODO: once locations and regulations are set, this value may change as it is currently hard coded + expect(data.pingData.supportedAPIs).to.eql([ + "8:uscav1", + "10:uscov1", + "12:usctv1", + "11:usutv1", + "9:usvav1", + ]); }); }); - }); - it("can go through the flow of user opting in to data sales and sharing", () => { - cy.get("button").contains("Opt in to all").click(); - cy.waitUntilCookieExists(CONSENT_COOKIE_NAME).then(() => { - cy.getCookie(CONSENT_COOKIE_NAME).then((cookie) => { - const fidesCookie: FidesCookie = JSON.parse( - decodeURIComponent(cookie!.value) - ); - const { consent } = fidesCookie; - expect(consent).to.eql({ data_sales_sharing_gpp_us_state: true }); + it("can go through the flow of user opting out of data sales and sharing", () => { + cy.get("button").contains("Opt out of all").click(); + cy.waitUntilCookieExists(CONSENT_COOKIE_NAME).then(() => { + cy.getCookie(CONSENT_COOKIE_NAME).then((cookie) => { + const fidesCookie: FidesCookie = JSON.parse( + decodeURIComponent(cookie!.value) + ); + const { consent } = fidesCookie; + expect(consent).to.eql({ data_sales_sharing_gpp_us_state: false }); + }); }); + + const expected = [ + // First two gppStrings indicate the data_sales_sharing_gpp_us_state notice was served and opted in (default) + { + eventName: "listenerRegistered", + data: true, + gppString: "DBABBg~BUoAAABY.QA", + }, + { + eventName: "cmpDisplayStatus", + data: "hidden", + gppString: "DBABBg~BUoAAABY.QA", + }, + // Second two gppStrings indicate the data_sales_sharing_gpp_us_state notice was served and opted out + { + eventName: "sectionChange", + data: "uscav1", + gppString: "DBABBg~BUUAAABY.QA", + }, + { + eventName: "signalStatus", + data: "ready", + gppString: "DBABBg~BUUAAABY.QA", + }, + ]; + // Check the GPP events + cy.get("@gppListener") + .its("args") + .then( + ( + args: [ + { eventName: string; data: string | boolean; pingData: any }, + boolean + ][] + ) => { + args.forEach(([data, success], idx) => { + expect(success).to.eql(true); + expect(data.eventName).to.eql(expected[idx].eventName); + expect(data.data).to.eql(expected[idx].data); + expect(data.pingData.gppString).to.eql(expected[idx].gppString); + }); + } + ); }); - const expected = [ - // First two gppStrings indicate the data_sales_sharing_gpp_us_state notice was served and opted in (default) - { - eventName: "listenerRegistered", - data: true, - gppString: "DBABBg~BUoAAABY.QA", - }, - { - eventName: "cmpDisplayStatus", - data: "hidden", - gppString: "DBABBg~BUoAAABY.QA", - }, - // Second two gppStrings indicate the data_sales_sharing_gpp_us_state notice was served and opted into - { - eventName: "sectionChange", - data: "uscav1", - gppString: "DBABBg~BUoAAABY.QA", - }, - { - eventName: "signalStatus", - data: "ready", - gppString: "DBABBg~BUoAAABY.QA", - }, - ]; - // Check the GPP events - cy.get("@gppListener") - .its("args") - .then( - ( - args: [ - { eventName: string; data: string | boolean; pingData: any }, - boolean - ][] - ) => { - args.forEach(([data, success], idx) => { + it("can handle a returning user", () => { + const cookie = mockCookie({ + consent: { data_sales_sharing_gpp_us_state: true }, + }); + cy.setCookie(CONSENT_COOKIE_NAME, JSON.stringify(cookie)); + visitDemoWithGPP({}); + cy.waitUntilFidesInitialized().then(() => { + // TODO(PROD-1439): remove this workaround + workaroundGppInitializationDelayBug(); + + cy.get("@FidesUIShown").should("not.have.been.called"); + + cy.window().then((win) => { + win.__gpp("addEventListener", cy.stub().as("gppListener")); + }); + // Initializes string properly + cy.get("@gppListener") + .its("args") + .then((args) => { + const [data, success] = args[0]; expect(success).to.eql(true); - expect(data.eventName).to.eql(expected[idx].eventName); - expect(data.data).to.eql(expected[idx].data); - expect(data.pingData.gppString).to.eql(expected[idx].gppString); + // Opt in string + expect(data.pingData.applicableSections).to.eql([8]); + expect(data.pingData.gppString).to.eql("DBABBg~BUoAAABY.QA"); }); - } - ); - cy.get("@gppListener") - .its("lastCall.args") - .then((args) => { - const [data] = args; - expect(data.pingData.applicableSections).to.eql([8]); - // TODO: once locations and regulations are set, this value may change as it is currently hard coded - expect(data.pingData.supportedAPIs).to.eql([ - "8:uscav1", - "10:uscov1", - "12:usctv1", - "11:usutv1", - "9:usvav1", - ]); }); + }); }); - - it("can go through the flow of user opting out of data sales and sharing", () => { - cy.get("button").contains("Opt out of all").click(); - cy.waitUntilCookieExists(CONSENT_COOKIE_NAME).then(() => { - cy.getCookie(CONSENT_COOKIE_NAME).then((cookie) => { - const fidesCookie: FidesCookie = JSON.parse( - decodeURIComponent(cookie!.value) - ); - const { consent } = fidesCookie; - expect(consent).to.eql({ data_sales_sharing_gpp_us_state: false }); + describe("when visiting from a state that does not have an applicable section", () => { + beforeEach(() => { + visitDemoWithGPP({ + overrideExperience: (experience: any) => { + /* eslint-disable no-param-reassign */ + experience.region = "us_nc"; + return experience; + }, + }); + cy.waitUntilFidesInitialized().then(() => { + cy.get("@FidesUIShown").should("have.been.calledOnce"); + cy.window().then((win) => { + win.__gpp("addEventListener", cy.stub().as("gppListener")); + }); }); }); - const expected = [ - // First two gppStrings indicate the data_sales_sharing_gpp_us_state notice was served and opted in (default) - { - eventName: "listenerRegistered", - data: true, - gppString: "DBABBg~BUoAAABY.QA", - }, - { - eventName: "cmpDisplayStatus", - data: "hidden", - gppString: "DBABBg~BUoAAABY.QA", - }, - // Second two gppStrings indicate the data_sales_sharing_gpp_us_state notice was served and opted out - { - eventName: "sectionChange", - data: "uscav1", - gppString: "DBABBg~BUUAAABY.QA", - }, - { - eventName: "signalStatus", - data: "ready", - gppString: "DBABBg~BUUAAABY.QA", - }, - ]; - // Check the GPP events - cy.get("@gppListener") - .its("args") - .then( - ( - args: [ - { eventName: string; data: string | boolean; pingData: any }, - boolean - ][] - ) => { - args.forEach(([data, success], idx) => { + it("loads the gpp extension if it is enabled", () => { + cy.window().then((win) => { + win.__gpp("ping", cy.stub().as("gppPing")); + cy.get("@gppPing") + .should("have.been.calledOnce") + .its("lastCall.args") + .then(([data, success]) => { expect(success).to.eql(true); - expect(data.eventName).to.eql(expected[idx].eventName); - expect(data.data).to.eql(expected[idx].data); - expect(data.pingData.gppString).to.eql(expected[idx].gppString); + expect(data.signalStatus).to.eql("ready"); + expect(data.applicableSections).to.eql([-1]); }); - } - ); - }); - - it("can handle a returning user", () => { - const cookie = mockCookie({ - consent: { data_sales_sharing_gpp_us_state: true }, - }); - cy.setCookie(CONSENT_COOKIE_NAME, JSON.stringify(cookie)); - cy.fixture("consent/experience_gpp.json").then((payload) => { - stubConfig({ - options: { - isOverlayEnabled: true, - tcfEnabled: false, - }, - experience: payload.items[0], }); }); - cy.waitUntilFidesInitialized().then(() => { - // TODO(PROD-1439): remove this workaround - workaroundGppInitializationDelayBug(); - cy.get("@FidesUIShown").should("not.have.been.called"); + it("can go through the flow of user opting in to data sales and sharing", () => { + cy.get("button").contains("Opt in to all").click(); - cy.window().then((win) => { - win.__gpp("addEventListener", cy.stub().as("gppListener")); - }); - // Initializes string properly + // Check the GPP events cy.get("@gppListener") .its("args") .then((args) => { - const [data, success] = args[0]; + // this is the "signalStatus" of "ready" event + const [data, success] = args[2]; expect(success).to.eql(true); - // Opt in string - expect(data.pingData.applicableSections).to.eql([8]); - expect(data.pingData.gppString).to.eql("DBABBg~BUoAAABY.QA"); + expect(data.pingData.applicableSections).to.eql([-1]); }); }); + + it("can go through the flow of user opting out of data sales and sharing", () => { + cy.get("button").contains("Opt out of all").click(); + + // Check the GPP events + cy.get("@gppListener") + .its("args") + .then((args) => { + // this is the "signalStatus" of "ready" event + const [data, success] = args[2]; + expect(success).to.eql(true); + expect(data.pingData.applicableSections).to.eql([-1]); + }); + }); + + it("can handle a returning user", () => { + const cookie = mockCookie({ + consent: { data_sales_sharing_gpp_us_state: true }, + }); + cy.setCookie(CONSENT_COOKIE_NAME, JSON.stringify(cookie)); + visitDemoWithGPP({ + overrideExperience: (experience: any) => { + /* eslint-disable no-param-reassign */ + experience.region = "us_nc"; + return experience; + }, + }); + cy.waitUntilFidesInitialized().then(() => { + // TODO(PROD-1439): remove this workaround + workaroundGppInitializationDelayBug(); + + cy.get("@FidesUIShown").should("not.have.been.called"); + cy.window().then((win) => { + win.__gpp("addEventListener", cy.stub().as("gppListener")); + }); + // Initializes string properly + cy.get("@gppListener") + .its("args") + .then((args) => { + const [data, success] = args[0]; + expect(success).to.eql(true); + // Opt in string + expect(data.pingData.applicableSections).to.eql([-1]); + expect(data.pingData.gppString).to.eql("DBAA"); + }); + }); + }); }); }); }); From b06bca27d2c9f8fa0c638c0b223b7ceeceaf5222 Mon Sep 17 00:00:00 2001 From: Lucano Vera Date: Fri, 22 Mar 2024 13:33:52 -0300 Subject: [PATCH 2/2] PROD-1863 Clean up table border lines (#4733) Co-authored-by: Lucano Vera --- CHANGELOG.md | 3 ++- .../src/features/common/table/v2/FidesCell.tsx | 5 +++-- .../src/features/common/table/v2/FidesTable.tsx | 15 +++++++-------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c207f7c236..7b3e7bd26d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,8 @@ The types of changes are: ### Fixed - Fixed responsive issues with the buttons on the integration screen [#4729](https://github.com/ethyca/fides/pull/4729) - Disable editing of data use declaration name and type after creation [#4731](https://github.com/ethyca/fides/pull/4731) -- Initialization issues with ExperienceNotices [#4723](https://github.com/ethyca/fides/pull/4723) +- Cleaned up table borders [#4733](https://github.com/ethyca/fides/pull/4733) +- Initialization issues with ExperienceNotices (#4723)[https://github.com/ethyca/fides/pull/4723] ## [2.32.0](https://github.com/ethyca/fides/compare/2.31.1...2.32.0) diff --git a/clients/admin-ui/src/features/common/table/v2/FidesCell.tsx b/clients/admin-ui/src/features/common/table/v2/FidesCell.tsx index b6247bd3f4..68c8458594 100644 --- a/clients/admin-ui/src/features/common/table/v2/FidesCell.tsx +++ b/clients/admin-ui/src/features/common/table/v2/FidesCell.tsx @@ -63,8 +63,9 @@ export const FidesCell = ({ (isFirstRowOfGroupedRows && hasOneSubRow) ? "1px" : "0px", - borderLeftWidth: "1px", - borderLeftColor: "gray.200", + }} + _last={{ + borderRightWidth: 0, }} height="inherit" style={{ diff --git a/clients/admin-ui/src/features/common/table/v2/FidesTable.tsx b/clients/admin-ui/src/features/common/table/v2/FidesTable.tsx index 9f2d4b8ed9..a037352a3a 100644 --- a/clients/admin-ui/src/features/common/table/v2/FidesTable.tsx +++ b/clients/admin-ui/src/features/common/table/v2/FidesTable.tsx @@ -136,7 +136,7 @@ const TableBody = ({ ))} {tableInstance.getRowModel().rows.length === 0 && emptyTableNotice && ( - + {emptyTableNotice} @@ -194,8 +194,10 @@ export const FidesTableV2 = ({ ({ {headerGroup.headers.map((header) => (