diff --git a/CHANGELOG.md b/CHANGELOG.md index 45404dbb7a..fd493a2759 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,12 +24,13 @@ 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) - Fixed hover/focus issues with the v2 tables [#4730](https://github.com/ethyca/fides/pull/4730) - Disable editing of data use declaration name and type after creation [#4731](https://github.com/ethyca/fides/pull/4731) - +- 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) @@ -78,13 +79,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/admin-ui/src/features/common/table/v2/FidesCell.tsx b/clients/admin-ui/src/features/common/table/v2/FidesCell.tsx index 146a918458..7dfd307544 100644 --- a/clients/admin-ui/src/features/common/table/v2/FidesCell.tsx +++ b/clients/admin-ui/src/features/common/table/v2/FidesCell.tsx @@ -67,8 +67,9 @@ export const FidesCell = ({ (isFirstRowOfGroupedRows && hasOneSubRow) ? "1px" : "0px", - borderLeftWidth: "1px", - borderLeftColor: "gray.200", + }} + _last={{ + borderRightWidth: 0, }} height="inherit" onClick={ 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 c2fe8b59c1..e67baedc26 100644 --- a/clients/admin-ui/src/features/common/table/v2/FidesTable.tsx +++ b/clients/admin-ui/src/features/common/table/v2/FidesTable.tsx @@ -147,7 +147,7 @@ const TableBody = ({ ))} {tableInstance.getRowModel().rows.length === 0 && emptyTableNotice && ( - + {emptyTableNotice} @@ -205,8 +205,10 @@ export const FidesTableV2 = ({ ({ {headerGroup.headers.map((header) => (
{ 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"); + }); + }); + }); }); }); });