Skip to content

Commit

Permalink
Add support for Vendor Count in descriptions (#5210)
Browse files Browse the repository at this point in the history
  • Loading branch information
gilluminate committed Aug 16, 2024
1 parent 667b2dd commit 4fbe90d
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ The types of changes are:
- Add function to return list of StagedResource objs according to list of URNs [#5192](https://github.com/ethyca/fides/pull/5192)
- Add DSR Support for ScyllaDB [#5140](https://github.com/ethyca/fides/pull/5140)
- Added support for nested fields in BigQuery in D&D result views [#5175](https://github.com/ethyca/fides/pull/5175)
- Added support for Vendor Count in Fides-JS overlay descriptions [#5210](https://github.com/ethyca/fides/pull/5210)

### Fixed
- Fixed the OAuth2 configuration for the Snap integration [#5158](https://github.com/ethyca/fides/pull/5158)
Expand Down
2 changes: 2 additions & 0 deletions clients/fides-js/src/components/ConsentModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const ConsentModal = ({
children,
dismissable,
i18n,
onVendorPageClick,
renderModalFooter,
}: {
attributes: Attributes;
Expand Down Expand Up @@ -46,6 +47,7 @@ const ConsentModal = ({
titleProps={title}
i18n={i18n}
renderModalFooter={renderModalFooter}
onVendorPageClick={onVendorPageClick}
>
{children}
</ConsentContent>
Expand Down
42 changes: 27 additions & 15 deletions clients/fides-js/src/components/ExperienceDescription.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/* eslint-disable no-template-curly-in-string */
import { h } from "preact";
import { useContext } from "preact/hooks";

const TEXT_TO_LINK = "vendors page.";
import { VendorButtonContext } from "../lib/tcf/vendor-button-context";

const VENDOR_COUNT_LINK = "${VENDOR_COUNT_LINK}";

const ExperienceDescription = ({
description,
Expand All @@ -11,6 +15,12 @@ const ExperienceDescription = ({
onVendorPageClick?: () => void;
allowHTMLDescription?: boolean | null;
}) => {
let vendorCount = 0;
const context = useContext(VendorButtonContext);
if (context?.vendorCount) {
vendorCount = context.vendorCount;
}

if (!description) {
return null;
}
Expand All @@ -29,22 +39,24 @@ const ExperienceDescription = ({
}

// Swap out reference to "vendors page" with a button that can go to the vendor page
if (
onVendorPageClick &&
(description.endsWith(TEXT_TO_LINK) ||
description.endsWith(`${TEXT_TO_LINK}\n`))
) {
const firstPart = description.split(TEXT_TO_LINK)[0];
if (description.includes(VENDOR_COUNT_LINK)) {
const parts = description.split(VENDOR_COUNT_LINK);
return (
<div>
{firstPart}{" "}
<button
type="button"
className="fides-link-button"
onClick={onVendorPageClick}
>
{TEXT_TO_LINK}
</button>
{parts[0]}
{!onVendorPageClick && vendorCount && (
<span className="fides-vendor-count">{vendorCount}</span>
)}
{onVendorPageClick && vendorCount && (
<button
type="button"
className="fides-link-button fides-vendor-count"
onClick={onVendorPageClick}
>
{vendorCount}
</button>
)}
{parts[1]}
</div>
);
}
Expand Down
9 changes: 9 additions & 0 deletions clients/fides-js/src/components/tcf/TcfOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import type {
TCFVendorLegitimateInterestsRecord,
TCFVendorSave,
} from "../../lib/tcf/types";
import { useVendorButton } from "../../lib/tcf/vendor-button-context";
import { fetchGvlTranslations } from "../../services/api";
import Button from "../Button";
import ConsentBanner from "../ConsentBanner";
Expand Down Expand Up @@ -205,6 +206,8 @@ const TcfOverlay: FunctionComponent<OverlayProps> = ({
cookie,
savedConsent,
}) => {
const { setVendorCount } = useVendorButton();

const initialEnabledIds: EnabledIds = useMemo(() => {
const {
tcf_purpose_consents: consentPurposes = [],
Expand Down Expand Up @@ -252,6 +255,12 @@ const TcfOverlay: FunctionComponent<OverlayProps> = ({
setCurrentLocale(locale);
};

useEffect(() => {
if (experience.vendor_count && setVendorCount) {
setVendorCount(experience.vendor_count);
}
}, [experience, setVendorCount]);

useEffect(() => {
if (!currentLocale && locale && defaultLocale) {
if (locale !== defaultLocale) {
Expand Down
10 changes: 9 additions & 1 deletion clients/fides-js/src/components/tcf/VendorInfoBanner.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { h } from "preact";
import { useMemo } from "preact/hooks";
import { useEffect, useMemo } from "preact/hooks";

import { PrivacyExperience } from "../../lib/consent-types";
import type { I18n } from "../../lib/i18n";
import { useVendorButton } from "../../lib/tcf/vendor-button-context";

const VendorInfo = ({
label,
Expand Down Expand Up @@ -38,6 +39,7 @@ const VendorInfoBanner = ({
i18n: I18n;
goToVendorTab: () => void;
}) => {
const { setVendorCount } = useVendorButton();
const counts = useMemo(() => {
const {
tcf_vendor_consents: consentVendors = [],
Expand All @@ -57,6 +59,12 @@ const VendorInfoBanner = ({
return { total, consent, legint };
}, [experience]);

useEffect(() => {
if (counts.total && setVendorCount) {
setVendorCount(counts.total);
}
}, [counts.total, setVendorCount]);

return (
<div className="fides-background-dark fides-vendor-info-banner">
<VendorInfo
Expand Down
1 change: 1 addition & 0 deletions clients/fides-js/src/lib/consent-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ export type PrivacyExperience = {
gvl?: GVLJson; // NOTE: uses our client-side GVLJson type
meta?: ExperienceMeta;
available_locales?: string[];
vendor_count?: number;
};

/**
Expand Down
4 changes: 2 additions & 2 deletions clients/fides-js/src/lib/i18n/i18n-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface I18nContextProps {
setIsLoading: Dispatch<StateUpdater<boolean>>;
}

const I18nContext = createContext<I18nContextProps>({} as I18nContextProps);
const I18nContext = createContext<I18nContextProps | Record<any, never>>({});

export const I18nProvider: FunctionComponent = ({ children }) => {
const [currentLocale, setCurrentLocale] = useState<string>();
Expand All @@ -41,7 +41,7 @@ export const I18nProvider: FunctionComponent = ({ children }) => {

export const useI18n = () => {
const context = useContext(I18nContext);
if (!context) {
if (!context || Object.keys(context).length === 0) {
throw new Error("useI18n must be used within a I18nProvider");
}
return context;
Expand Down
5 changes: 4 additions & 1 deletion clients/fides-js/src/lib/tcf/renderOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import TcfOverlay from "../../components/tcf/TcfOverlay";
import { OverlayProps } from "../../components/types";
import { I18nProvider } from "../i18n/i18n-context";
import { loadTcfMessagesFromFiles } from "./i18n/tcf-i18n-utils";
import { VendorButtonProvider } from "./vendor-button-context";

export const renderOverlay = (props: OverlayProps, parent: ContainerNode) => {
/**
Expand All @@ -18,7 +19,9 @@ export const renderOverlay = (props: OverlayProps, parent: ContainerNode) => {

render(
<I18nProvider>
<TcfOverlay {...props} />
<VendorButtonProvider>
<TcfOverlay {...props} />
</VendorButtonProvider>
</I18nProvider>,
parent,
);
Expand Down
46 changes: 46 additions & 0 deletions clients/fides-js/src/lib/tcf/vendor-button-context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { createContext, h } from "preact";
import { FC } from "preact/compat";
import {
Dispatch,
StateUpdater,
useContext,
useMemo,
useState,
} from "preact/hooks";

interface VendorButtonContextProps {
vendorCount?: number;
setVendorCount: Dispatch<StateUpdater<number | undefined>>;
}

export const VendorButtonContext = createContext<
VendorButtonContextProps | Record<any, never>
>({});

export const VendorButtonProvider: FC = ({ children }) => {
const [vendorCount, setVendorCount] = useState<number>();

const value: VendorButtonContextProps = useMemo(
() => ({
vendorCount,
setVendorCount,
}),
[vendorCount, setVendorCount],
);

return (
<VendorButtonContext.Provider value={value}>
{children}
</VendorButtonContext.Provider>
);
};

export const useVendorButton = () => {
const context = useContext(VendorButtonContext);
if (!context || Object.keys(context).length === 0) {
throw new Error(
"useVendorButton must be used within a VendorButtonProvider",
);
}
return context;
};
9 changes: 8 additions & 1 deletion clients/privacy-center/cypress/e2e/consent-banner-tcf.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,14 @@ describe("Fides-js TCF", () => {
});
});

it("can open the modal", () => {
it("can open the modal from vendor count", () => {
cy.get("div#fides-banner").within(() => {
cy.get(".fides-vendor-count").first().should("have.text", "2").click();
});
cy.get("#fides-tab-vendors");
});

it("can open the modal from preferences button", () => {
cy.get("div#fides-banner").within(() => {
cy.get("#fides-button-group").within(() => {
cy.get("button").contains("Manage preferences").click();
Expand Down
2 changes: 2 additions & 0 deletions clients/privacy-center/cypress/e2e/consent-i18n.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ const ENGLISH_NOTICES: TestNoticeTranslations[] = [
const ENGLISH_TCF_BANNER: TestTcfBannerTranslations = {
...ENGLISH_BANNER,
...{
banner_description:
"[banner] We, and our 2 vendors, use cookies and similar",
vendors_count: "Vendors",
vendors_consent_count: "Vendors using consent",
vendors_legint_count: "Vendors using legitimate interest",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"language": "en",
"accept_button_label": "Opt in to all",
"acknowledge_button_label": "OK",
"banner_description": "[banner] We use cookies and similar methods to recognize visitors and remember their preferences. We may also use these technologies to gauge the effectiveness of advertising campaigns, target advertisements, and analyze website traffic. Some of these technologies are essential for ensuring the proper functioning of the service or website and cannot be disabled, while others are optional but serve to enhance the user experience in various ways.\n\nWe, in collaboration with our partners, store and/or access information on a user's device, including but not limited to IP addresses, unique identifiers, and browsing data stored in cookies, in order to process personal data. You have the option to manage your preferences by selecting the 'Manage Preferences' option located in the page's footer. To review or object to instances where our partners assert a legitimate interest in utilizing your data, please visit our vendors page.\n",
"banner_description": "[banner] We, and our ${VENDOR_COUNT_LINK} vendors, use cookies and similar methods to recognize visitors and remember their preferences. We may also use these technologies to gauge the effectiveness of advertising campaigns, target advertisements, and analyze website traffic. Some of these technologies are essential for ensuring the proper functioning of the service or website and cannot be disabled, while others are optional but serve to enhance the user experience in various ways.\n\nWe, in collaboration with our partners, store and/or access information on a user's device, including but not limited to IP addresses, unique identifiers, and browsing data stored in cookies, in order to process personal data. You have the option to manage your preferences by selecting the 'Manage Preferences' option located in the page's footer. To review or object to instances where our partners assert a legitimate interest in utilizing your data, please visit our vendors page.\n",
"banner_title": "[banner] Manage your consent preferences",
"description": "We use cookies and similar methods to recognize visitors and remember their preferences. We may also use these technologies to gauge the effectiveness of advertising campaigns, target advertisements, and analyze website traffic. Some of these technologies are essential for ensuring the proper functioning of the service or website and cannot be disabled, while others are optional but serve to enhance the user experience in various ways.\n\nWe, in collaboration with our partners, store and/or access information on a user's device, including but not limited to IP addresses, unique identifiers, and browsing data stored in cookies, in order to process personal data. You have the option to manage your preferences by selecting the 'Manage Preferences' option located in the page's footer. To review or object to instances where our partners assert a legitimate interest in utilizing your data, please visit our vendors page.\n",
"modal_link_label": "Manage my consent preferences",
Expand Down

0 comments on commit 4fbe90d

Please sign in to comment.