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

Add infrastructure for consent banner and link #3191

Merged
merged 61 commits into from
May 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
ee22baa
parent 9149f2f211cfea68bfa9f5c770fec3a79d31744b
eastandwestwind Apr 25, 2023
06e46cb
fixing some conflicts from rebase
eastandwestwind May 8, 2023
d4bef32
Refactor ConsentBannerButton into a Preact FunctionComponent function
NevilleS May 8, 2023
7655725
Move ConsentBannerButton to a new "components" folder
NevilleS May 8, 2023
88e12ef
adds duplicate banner demo page that allows for config to be injected…
eastandwestwind May 8, 2023
19ffcbd
cypress tests are working for consent banner
eastandwestwind May 9, 2023
69f672a
regenerate package-lock, add back turbo output cache
eastandwestwind May 9, 2023
cfa8110
fix eslint configs
eastandwestwind May 9, 2023
92c7e85
remove public/lib file, rename css vars
eastandwestwind May 9, 2023
71ee51e
restructure some components to functional, removes link component
eastandwestwind May 9, 2023
55eccc0
inject accept/reject events from fides parent for the banner
eastandwestwind May 9, 2023
bf59c50
Add note about aliasing react imports
NevilleS May 9, 2023
39b1618
Update tsconfig to include tsx
allisonking May 9, 2023
af0282e
Swap href instead of using onClick for anchor component
allisonking May 9, 2023
e5723b5
Export debugLog as a function instead of default
allisonking May 9, 2023
a815fd2
clarify diff between banner demo pages
eastandwestwind May 9, 2023
878e00c
Update clients/fides-js/src/lib/cookie.ts
eastandwestwind May 9, 2023
5f64996
Fix accept all and reject all logic
allisonking May 9, 2023
1acf4ae
Run prettier
allisonking May 9, 2023
b6f8e26
Run prettier on privacy center
allisonking May 9, 2023
db0ce36
Run prettier on PC README
allisonking May 9, 2023
902ae80
refactor to inject legacy consent, consent experience, geolocation, a…
eastandwestwind May 10, 2023
675eff9
banner props should default properly
eastandwestwind May 10, 2023
9c26448
move eslint config, update banner to support banner title
eastandwestwind May 10, 2023
fe992d7
remove unneeded npm dev dependency in fides-js
eastandwestwind May 10, 2023
5ac8512
re-enable some eslint rules
eastandwestwind May 10, 2023
482b7ec
Get cypress to run again
allisonking May 10, 2023
1962cd4
Update cypress test to use win.fidesConfig
allisonking May 10, 2023
d33ffab
Allow visitConsentDemo to hang out with its friends
allisonking May 10, 2023
333adaa
Merge branch 'main' into banner-infra
allisonking May 10, 2023
83d2271
Format
allisonking May 10, 2023
6cb4b2e
Update package-lock.json
allisonking May 10, 2023
fb407ed
remove unneeded tslint config
eastandwestwind May 10, 2023
4e2866d
remove passing consent defaults into init, fix circular deps
eastandwestwind May 10, 2023
d8f7377
Patch package-lock for turbo in different architectures
allisonking May 10, 2023
428b183
Autoformat
allisonking May 10, 2023
f3755b2
fix jest in fides-js
eastandwestwind May 10, 2023
21b7883
Only render ConsentBanner after mount
allisonking May 11, 2023
5fd0574
do not render banner by default
eastandwestwind May 11, 2023
7be8b35
config.options is required by package
eastandwestwind May 11, 2023
48d4a21
Merge branch 'main' of github.com:ethyca/fides into banner-infra
eastandwestwind May 12, 2023
c9ca6da
add to changelog
eastandwestwind May 12, 2023
7fa2e94
remove unneeded deps
eastandwestwind May 12, 2023
f0cac32
remove unneeded todo, update privacy center readme
eastandwestwind May 12, 2023
d66c260
rename isDisabled to isOverlayDisabled
eastandwestwind May 12, 2023
2bca847
small renaming, refactor todos into callback func
eastandwestwind May 12, 2023
fc31dc4
Refactor consent.tsx to render App
allisonking May 11, 2023
409d3f3
cleanup after cherry-picking to add new App component
eastandwestwind May 12, 2023
8d1627b
prettier
eastandwestwind May 12, 2023
d461a27
add logic to prevent retrieving geolocation under certain conditions,…
eastandwestwind May 12, 2023
bd35fe9
add check for DOM loaded before adding listener
eastandwestwind May 12, 2023
1b50997
Make the CHANGELOG entry more exciting!
NevilleS May 12, 2023
15160ee
Unbundle the FidesConfig and pass explicit props to initOverlay
NevilleS May 12, 2023
0f8769e
Rename App -> Overlay
NevilleS May 12, 2023
adfee4e
Add a prop for manage preferences link label
NevilleS May 12, 2023
bfb7461
Fix gitignore
NevilleS May 12, 2023
077db9a
Add tests for makeConsentDefaults
NevilleS May 13, 2023
bdf9ab5
Add tests for setConsentCookie
NevilleS May 13, 2023
6d06ab2
Remove TODO
NevilleS May 13, 2023
590c826
Format
NevilleS May 13, 2023
6920461
Merge branch 'main' of github.com:ethyca/fides into banner-infra
NevilleS May 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ The types of changes are:
### Added

- Add an automated test to check for `/fides-consent.js` backwards compatibility [#3289](https://github.com/ethyca/fides/pull/3289)
- Add infrastructure for "overlay" consent components (Preact, CSS bundling, etc.) and initial version of consent banner [#3191](https://github.com/ethyca/fides/pull/3191)

## [2.13.0](https://github.com/ethyca/fides/compare/2.12.1...2.13.0)

Expand Down
2 changes: 1 addition & 1 deletion clients/cypress-e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Admin UI will be found at `localhost:3000` and Privacy Center at `localhost:3001
Then, in this folder:

```
turbo run cy:run
npm run cy:run
```

### Environment variables
Expand Down
15 changes: 14 additions & 1 deletion clients/fides-js/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
{
"extends": ["airbnb", "airbnb-typescript/base", "prettier"],
"extends": ["preact", "airbnb", "airbnb-typescript/base", "prettier"],
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"react/jsx-filename-extension": [
2,
{ "extensions": [",.js", ".jsx", ".ts", ".tsx"] }
],
"react/function-component-definition": [
2,
{
"namedComponents": "arrow-function",
"unnamedComponents": "arrow-function"
}
],
"curly": ["error", "all"],
"nonblock-statement-body-position": ["error", "below"],
"import/prefer-default-export": "off",
"import/extensions": "off",
"react/react-in-jsx-scope": "off",
"react/prop-types": "off",
"no-param-reassign": [
"error",
{
Expand Down
102 changes: 101 additions & 1 deletion clients/fides-js/__tests__/lib/cookie.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ import {
CookieKeyConsent,
FidesCookie,
getOrMakeFidesCookie,
makeConsentDefaults,
makeFidesCookie,
saveFidesCookie,
} from "~/lib/cookie";
setConsentCookieAcceptAll,
setConsentCookieRejectAll,
} from "../../src/lib/cookie";
allisonking marked this conversation as resolved.
Show resolved Hide resolved
import type { ConsentConfig } from "../../src/lib/consent-config";
import type { ConsentContext } from "../../src/lib/consent-context";

// Setup mock date
const MOCK_DATE = "2023-01-01T12:00:00.000Z";
Expand Down Expand Up @@ -167,3 +172,98 @@ describe("saveFidesCookie", () => {
expect(mockSetCookie.mock.calls[0][2]).toHaveProperty("domain", expected);
});
});

describe("makeConsentDefaults", () => {
const config: ConsentConfig = {
options: [
{
cookieKeys: ["default_undefined"],
fidesDataUseKey: "provide.service",
},
{
cookieKeys: ["default_true"],
default: true,
fidesDataUseKey: "improve.system",
},
{
cookieKeys: ["default_false"],
default: false,
fidesDataUseKey: "personalize.system",
},
{
cookieKeys: ["default_true_with_gpc_false"],
default: { value: true, globalPrivacyControl: false },
fidesDataUseKey: "advertising.third_party",
},
{
cookieKeys: ["default_false_with_gpc_true"],
default: { value: false, globalPrivacyControl: true },
fidesDataUseKey: "third_party_sharing.payment_processing",
},
],
};

describe("when global privacy control is not present", () => {
const context: ConsentContext = {};

it("returns the default consent values by key", () => {
expect(makeConsentDefaults({ config, context })).toEqual({
default_true: true,
default_false: false,
default_true_with_gpc_false: true,
default_false_with_gpc_true: false,
});
});
});

describe("when global privacy control is set", () => {
const context: ConsentContext = {
globalPrivacyControl: true,
};

it("returns the default consent values by key", () => {
expect(makeConsentDefaults({ config, context })).toEqual({
default_true: true,
default_false: false,
default_true_with_gpc_false: false,
default_false_with_gpc_true: true,
});
});
});
});

describe("setConsentCookie", () => {
afterEach(() => mockSetCookie.mockClear());

const defaults: CookieKeyConsent = {
default_true: true,
default_false: false,
another_true: true,
another_false: false,
};

it("AcceptAll sets all consent preferences to true", () => {
setConsentCookieAcceptAll(defaults);
expect(mockSetCookie.mock.calls).toHaveLength(1);
const cookie = JSON.parse(mockSetCookie.mock.calls[0][1]);
expect(cookie.consent).toEqual({
default_true: true,
default_false: true,
another_true: true,
another_false: true,
});
});

// NOTE: this will need to be updated for notice-only preferences!
it("RejectAll sets all consent preferences to false", () => {
setConsentCookieRejectAll(defaults);
expect(mockSetCookie.mock.calls).toHaveLength(1);
const cookie = JSON.parse(mockSetCookie.mock.calls[0][1]);
expect(cookie.consent).toEqual({
default_true: false,
default_false: false,
another_true: false,
another_false: false,
});
});
});
7 changes: 6 additions & 1 deletion clients/fides-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"directory": "clients/fides-js"
},
"dependencies": {
"preact": "^10.13.2",
"typescript-cookie": "^1.0.6",
"uuid": "^9.0.0"
},
Expand All @@ -37,7 +38,9 @@
"eslint": "^8.36.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.0.0",
"eslint-config-preact": "^1.3.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-react": "^7.32.2",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"js-cookie": "^3.0.5",
Expand All @@ -47,7 +50,9 @@
"rollup-plugin-dts": "^5.3.0",
"rollup-plugin-esbuild": "^5.0.0",
"rollup-plugin-filesize": "^10.0.0",
"rollup-plugin-import-css": "^3.2.1",
"ts-jest": "^29.1.0",
"typescript": "^4.9.5"
"typescript": "^4.9.5",
"typescript-plugin-css-modules": "^5.0.1"
}
}
16 changes: 14 additions & 2 deletions clients/fides-js/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import dts from "rollup-plugin-dts";
import esbuild from "rollup-plugin-esbuild";
import filesize from "rollup-plugin-filesize";
import nodeResolve from "@rollup/plugin-node-resolve";
import css from "rollup-plugin-import-css";

const name = "fides";
const isDev = process.env.NODE_ENV === "development";
Expand All @@ -15,8 +16,11 @@ const GZIP_SIZE_WARN_KB = 15; // log a warning if bundle size exceeds this
export default [
{
input: `src/${name}.ts`,
// DEFER: Add aliases for typical react imports (see https://preactjs.com/guide/v10/getting-started/#aliasing-in-rollup)
// This will be needed if & when we want to leverage other packages written for the React ecosystem
plugins: [
nodeResolve(),
css(),
esbuild({
minify: !isDev,
}),
Expand All @@ -28,6 +32,14 @@ export default [
verbose: true,
hook: "writeBundle",
}),
copy({
// Automatically add the built css to the privacy center's static files for bundling:
targets: [
{ src: `dist/${name}.css`, dest: "../privacy-center/public/lib/" },
],
verbose: true,
eastandwestwind marked this conversation as resolved.
Show resolved Hide resolved
hook: "writeBundle",
}),
filesize({
reporter: [
"boxen", // default reporter, which prints a nice CLI output
Expand Down Expand Up @@ -70,7 +82,7 @@ export default [
},
{
input: `src/${name}.ts`,
plugins: [nodeResolve(), esbuild()],
plugins: [nodeResolve(), css(), esbuild()],
output: [
{
// Compatible with ES module imports. Apps in this repo may be able to share the code.
Expand All @@ -82,7 +94,7 @@ export default [
},
{
input: `src/${name}.ts`,
plugins: [dts()],
plugins: [dts(), css()],
output: [
{
file: `dist/${name}.d.ts`,
Expand Down
102 changes: 102 additions & 0 deletions clients/fides-js/src/components/ConsentBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { h, FunctionComponent } from "preact";
import { useState, useEffect } from "preact/hooks";
import { ButtonType } from "../lib/consent-types";
import ConsentBannerButton from "./ConsentBannerButton";
import "../lib/banner.module.css";
import { useHasMounted } from "../lib/hooks";

interface BannerProps {
onAcceptAll: () => void;
onRejectAll: () => void;
privacyCenterUrl: string;
bannerDescription?: string;
bannerTitle?: string;
confirmationButtonLabel?: string;
rejectButtonLabel?: string;
managePreferencesLabel?: string;
waitBeforeShow?: number;
}

const ConsentBanner: FunctionComponent<BannerProps> = ({
onAcceptAll,
onRejectAll,
privacyCenterUrl,
bannerDescription = "This website processes your data respectfully, so we require your consent to use cookies.",
bannerTitle = "Manage your consent",
confirmationButtonLabel = "Accept All",
managePreferencesLabel = "Manage Preferences",
rejectButtonLabel = "Reject All",
waitBeforeShow = 100,
}) => {
const [isShown, setIsShown] = useState(false);
const hasMounted = useHasMounted();
eastandwestwind marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
const delayBanner = setTimeout(() => {
setIsShown(true);
}, waitBeforeShow);
return () => clearTimeout(delayBanner);
}, [setIsShown, waitBeforeShow]);

const navigateToPrivacyCenter = (): void => {
if (privacyCenterUrl) {
window.location.assign(privacyCenterUrl);
}
};
eastandwestwind marked this conversation as resolved.
Show resolved Hide resolved

if (!hasMounted) {
return null;
}

return (
<div
id="fides-consent-banner"
className={`fides-consent-banner fides-consent-banner-bottom ${
isShown ? "" : "fides-consent-banner-hidden"
} `}
>
<div>
<div
id="fides-consent-banner-title"
className="fides-consent-banner-title"
>
{bannerTitle || ""}
</div>
<div
id="fides-consent-banner-description"
className="fides-consent-banner-description"
>
{bannerDescription || ""}
</div>
</div>
<div
id="fides-consent-banner-buttons"
className="fides-consent-banner-buttons"
>
<ConsentBannerButton
buttonType={ButtonType.TERTIARY}
label={managePreferencesLabel}
onClick={navigateToPrivacyCenter}
/>
<ConsentBannerButton
buttonType={ButtonType.SECONDARY}
label={rejectButtonLabel}
onClick={() => {
onRejectAll();
setIsShown(false);
}}
/>
<ConsentBannerButton
buttonType={ButtonType.PRIMARY}
label={confirmationButtonLabel}
onClick={() => {
onAcceptAll();
setIsShown(false);
}}
/>
</div>
</div>
);
};

export default ConsentBanner;
25 changes: 25 additions & 0 deletions clients/fides-js/src/components/ConsentBannerButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { h, FunctionComponent } from "preact";
import { ButtonType } from "../lib/consent-types";

interface ButtonProps {
buttonType: ButtonType;
label?: string;
onClick?: () => void;
}

const ConsentBannerButton: FunctionComponent<ButtonProps> = ({
buttonType,
label,
onClick,
}) => (
<button
type="button"
id={`fides-consent-banner-button-${buttonType.valueOf()}`}
className={`fides-consent-banner-button fides-consent-banner-button-${buttonType.valueOf()}`}
onClick={onClick}
>
{label || ""}
</button>
);

export default ConsentBannerButton;