Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GPC indication on fides-js overlay #3673

Merged
merged 13 commits into from
Jun 28, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The types of changes are:

### Added
- Empty state for when there are no relevant privacy notices in the privacy center [#3640](https://github.com/ethyca/fides/pull/3640)
- GPC indicators in fides-js banner and modal [#3673](https://github.com/ethyca/fides/pull/3673)
- Set `sslmode` to `prefer` if connecting to Redshift via ssh [#3685](https://github.com/ethyca/fides/pull/3685)

### Fixed
Expand Down
10 changes: 2 additions & 8 deletions clients/fides-js/src/components/CloseButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,10 @@ const CloseButton = ({
className="fides-close-button"
onClick={onClick}
>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none">
<path
d="M7.99999 7.05732L11.3 3.75732L12.2427 4.69999L8.94266 7.99999L12.2427 11.3L11.3 12.2427L7.99999 8.94266L4.69999 12.2427L3.75732 11.3L7.05732 7.99999L3.75732 4.69999L4.69999 3.75732L7.99999 7.05732Z"
fill="#2D3748"
d="m8 7.057 3.3-3.3.943.943-3.3 3.3 3.3 3.3-.943.943-3.3-3.3-3.3 3.3-.943-.943 3.3-3.3-3.3-3.3.943-.943 3.3 3.3Z"
/>
</svg>
</button>
Expand Down
52 changes: 34 additions & 18 deletions clients/fides-js/src/components/ConsentBanner.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { h, FunctionComponent, VNode } from "preact";
import { getConsentContext } from "../lib/consent-context";
import { ExperienceConfig } from "../lib/consent-types";
import CloseButton from "./CloseButton";
import { GpcBadge } from "./GpcBadge";

interface BannerProps {
experience: ExperienceConfig;
Expand All @@ -14,26 +16,40 @@ const ConsentBanner: FunctionComponent<BannerProps> = ({
buttonGroup,
onClose,
bannerIsOpen,
}) => (
<div
id="fides-banner-container"
className={`fides-banner fides-banner-bottom ${
bannerIsOpen ? "" : "fides-banner-hidden"
} `}
>
<div id="fides-banner">
<div id="fides-banner-inner">
<CloseButton ariaLabel="Close banner" onClick={onClose} />
<div id="fides-banner-title" className="fides-banner-title">
{experience.title}
}) => {
const showGpcBadge = getConsentContext().globalPrivacyControl;
return (
<div
id="fides-banner-container"
className={`fides-banner fides-banner-bottom ${
bannerIsOpen ? "" : "fides-banner-hidden"
} `}
>
<div id="fides-banner">
<div id="fides-banner-inner">
<CloseButton ariaLabel="Close banner" onClick={onClose} />
<div id="fides-banner-heading">
<div id="fides-banner-title" className="fides-banner-title">
{experience.title}
</div>
{showGpcBadge ? (
<GpcBadge
label="Global Privacy Control Signal"
status="detected"
/>
) : null}
</div>
<div
id="fides-banner-description"
className="fides-banner-description"
>
{experience.description}
</div>
{buttonGroup}
</div>
<div id="fides-banner-description" className="fides-banner-description">
{experience.description}
</div>
{buttonGroup}
</div>
</div>
</div>
);
);
};

export default ConsentBanner;
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 @@ -3,6 +3,7 @@ import { Attributes } from "../lib/a11y-dialog";
import { PrivacyNotice, ExperienceConfig } from "../lib/consent-types";
import NoticeToggles from "./NoticeToggles";
import CloseButton from "./CloseButton";
import GpcInfo from "./GpcInfo";

type NoticeKeys = Array<PrivacyNotice["notice_key"]>;

Expand Down Expand Up @@ -51,6 +52,7 @@ const ConsentModal = ({
>
{experience.description}
</p>
<GpcInfo />
<div className="fides-modal-notices">
<NoticeToggles
notices={notices}
Expand Down
36 changes: 36 additions & 0 deletions clients/fides-js/src/components/GpcBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { h } from "preact";
import { GpcStatus, PrivacyNotice } from "../lib/consent-types";
import { getConsentContext } from "../lib/consent-context";
import { getGpcStatusFromNotice } from "../lib/consent-utils";

export const GpcBadge = ({
label,
status,
}: {
label: string;
status: string;
}) => (
<span className="fides-gpc-label">
{label}{" "}
<span className={`fides-gpc-badge fides-gpc-badge-${status}`}>
{status}
</span>
</span>
);

export const GpcBadgeForNotice = ({
value,
notice,
}: {
value: boolean;
notice: PrivacyNotice;
}) => {
const consentContext = getConsentContext();
const status = getGpcStatusFromNotice({ value, notice, consentContext });

if (status === GpcStatus.NONE) {
return null;
}

return <GpcBadge label="Global Privacy Control" status={status.valueOf()} />;
};
29 changes: 29 additions & 0 deletions clients/fides-js/src/components/GpcInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { h } from "preact";
import WarningIcon from "./WarningIcon";
import { getConsentContext } from "../lib/consent-context";

const GpcInfo = () => {
const context = getConsentContext();

if (!context.globalPrivacyControl) {
return null;
}

return (
<div className="fides-gpc-banner">
<div className="fides-gpc-warning">
<WarningIcon />
</div>
<div>
<p className="fides-gpc-header">Global Privacy Control detected</p>
<p>
Your global privacy control preference has been honored. You have been
automatically opted out of data uses cases which adhere to global
privacy control.
</p>
</div>
</div>
);
};

export default GpcInfo;
3 changes: 3 additions & 0 deletions clients/fides-js/src/components/NoticeToggles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ConsentMechanism, PrivacyNotice } from "../lib/consent-types";
import Toggle from "./Toggle";
import Divider from "./Divider";
import { useDisclosure } from "../lib/hooks";
import { GpcBadgeForNotice } from "./GpcBadge";

const NoticeToggle = ({
notice,
Expand Down Expand Up @@ -46,7 +47,9 @@ const NoticeToggle = ({
className="fides-notice-toggle-trigger"
>
{notice.name}
<GpcBadgeForNotice notice={notice} value={checked} />
</span>

<Toggle
name={notice.name || ""}
id={notice.notice_key}
Expand Down
14 changes: 14 additions & 0 deletions clients/fides-js/src/components/WarningIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { h } from "preact";

const WarningIcon = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
fill="currentColor"
>
<path d="M9 12.05a.68.68 0 0 0-.68.7c0 .39.32.7.68.7.39 0 .68-.31.68-.7a.66.66 0 0 0-.68-.7Zm0-1.18c.26 0 .44-.2.44-.46V6.19c0-.26-.2-.47-.44-.47a.49.49 0 0 0-.47.47v4.22c0 .25.21.46.47.46Zm7.27 2.27-5.85-9.9c-.3-.5-.83-.8-1.42-.8-.6 0-1.12.3-1.42.8l-5.86 9.9c-.3.5-.3 1.1-.01 1.6.3.51.83.82 1.43.82h11.72c.6 0 1.13-.3 1.43-.82.29-.5.28-1.1-.02-1.6Zm-.82 1.1c-.1.25-.33.38-.62.38H3.14a.7.7 0 0 1-.61-.35.64.64 0 0 1 0-.65l5.86-9.9A.7.7 0 0 1 9 3.37a.7.7 0 0 1 .61.35l5.86 9.9c.1.2.12.44-.02.63Z" />
</svg>
);

export default WarningIcon;
64 changes: 62 additions & 2 deletions clients/fides-js/src/components/fides.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
--fides-overlay-font-color: #4a5568;
--fides-overlay-font-color-dark: #2d3748;
--fides-overlay-hover-color: #edf2f7;
--fides-overlay-gpc-applied-background-color: #38a169;
--fides-overlay-gpc-applied-text-color: white;
--fides-overlay-gpc-overridden-background-color: #e53e3e;
--fides-overlay-gpc-overridden-text-color: white;
/* Buttons */
--fides-overlay-primary-button-background-color: var(
--fides-overlay-primary-color
Expand Down Expand Up @@ -143,6 +147,12 @@ div#fides-banner-container.fides-banner-top.fides-banner-hidden {
}
}

div#fides-banner-heading {
display: flex;
margin-right: 0.8em;
align-items: center;
}

div#fides-banner-title {
font-size: var(--fides-overlay-font-size-title);
font-weight: 600;
Expand Down Expand Up @@ -286,8 +296,8 @@ div#fides-modal .fides-modal-button-group {

.fides-close-button {
position: absolute;
top: 1em;
right: 1em;
top: 0.5em;
right: 0.2em;
cursor: pointer;
background: none;
border: none;
Expand Down Expand Up @@ -423,10 +433,15 @@ div#fides-modal .fides-modal-button-group {
padding: 0.5em;
display: flex;
justify-content: space-between;
min-height: 40px;
align-items: center;
}

.fides-notice-toggle .fides-notice-toggle-trigger {
width: 100%;
display: flex;
justify-content: space-between;
margin-right: 0.5em;
}

.fides-notice-toggle .fides-notice-toggle-title:hover {
Expand All @@ -441,3 +456,48 @@ div#fides-modal .fides-modal-button-group {
.fides-notice-toggle-expanded {
background-color: var(--fides-overlay-row-hover-color);
}

/* GPC */
.fides-gpc-banner {
border: 1px solid var(--fides-overlay-primary-color);
border-radius: var(--fides-overlay-component-border-radius);
display: flex;
padding: 1.1em;
margin-bottom: 1em;
}

.fides-gpc-banner p {
margin: 0;
}

.fides-gpc-warning {
color: var(--fides-overlay-primary-color);
margin-right: 0.5em;
}

.fides-gpc-header {
font-weight: 700;
}

.fides-gpc-label {
font-weight: 600;
font-size: 0.9em;
}

.fides-gpc-badge {
text-transform: uppercase;
padding: 0 4px;
font-weight: 700;
border-radius: var(--fides-overlay-button-border-radius);
}

.fides-gpc-badge-applied,
.fides-gpc-badge-detected {
background: var(--fides-overlay-gpc-applied-background-color);
color: var(--fides-overlay-gpc-applied-text-color);
}

.fides-gpc-badge-overridden {
background: var(--fides-overlay-gpc-overridden-background-color);
color: var(--fides-overlay-gpc-overridden-text-color);
}
34 changes: 25 additions & 9 deletions clients/fides-js/src/fides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import {
UserGeolocation,
ConsentMethod,
SaveConsentPreference,
ConsentMechanism,
} from "./lib/consent-types";
import {
constructFidesRegionString,
Expand All @@ -79,6 +80,7 @@ import { fetchExperience } from "./services/fides/api";
import { getGeolocation } from "./services/external/geolocation";
import { OverlayProps } from "./components/Overlay";
import { updateConsentPreferences } from "./lib/preferences";
import { resolveConsentValue } from "./lib/consent-value";

export type Fides = {
consent: CookieKeyConsent;
Expand Down Expand Up @@ -131,26 +133,40 @@ const automaticallyApplyGPCPreferences = (
fidesApiUrl: string,
effectiveExperience?: PrivacyExperience | null
) => {
if (!effectiveExperience) {
if (!effectiveExperience || !effectiveExperience.privacy_notices) {
return;
}

if (!getConsentContext().globalPrivacyControl) {
const context = getConsentContext();
if (!context.globalPrivacyControl) {
return;
}

const consentPreferencesToSave: Array<SaveConsentPreference> = [];
allisonking marked this conversation as resolved.
Show resolved Hide resolved
effectiveExperience.privacy_notices?.forEach((notice) => {
if (notice.has_gpc_flag && !notice.current_preference) {
consentPreferencesToSave.push(
new SaveConsentPreference(
let gpcApplied = false;
const consentPreferencesToSave = effectiveExperience.privacy_notices.map(
(notice) => {
if (
notice.has_gpc_flag &&
!notice.current_preference &&
notice.consent_mechanism !== ConsentMechanism.NOTICE_ONLY
) {
gpcApplied = true;
return new SaveConsentPreference(
notice,
transformConsentToFidesUserPreference(false, notice.consent_mechanism)
);
}
return new SaveConsentPreference(
notice,
transformConsentToFidesUserPreference(
resolveConsentValue(notice, context),
notice.consent_mechanism
)
);
}
});
if (consentPreferencesToSave.length > 0) {
);

if (gpcApplied) {
updateConsentPreferences({
consentPreferencesToSave,
experienceId: effectiveExperience.id,
Expand Down
9 changes: 9 additions & 0 deletions clients/fides-js/src/lib/consent-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,15 @@ export enum RequestOrigin {
api = "api",
}

export enum GpcStatus {
/** GPC is not relevant for the consent option. */
allisonking marked this conversation as resolved.
Show resolved Hide resolved
NONE = "none",
/** GPC is enabled and consent matches the configured default. */
APPLIED = "applied",
/** GPC is enabled but consent has been set to override the configured default. */
OVERRIDDEN = "overridden",
}

// ------------------LEGACY TYPES BELOW -------------------

export type ConditionalValue = {
Expand Down
Loading