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

UI tabs for fides-js #3782

Merged
merged 14 commits into from
Jul 17, 2023
Merged
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ The types of changes are:

## [Unreleased](https://github.com/ethyca/fides/compare/2.16.0...main)

### Added

- Tab component for `fides-js` [#3782](https://github.com/ethyca/fides/pull/3782)
### Developer Experience

- Changed where db-dependent routers were imported to avoid dependency issues [#3741](https://github.com/ethyca/fides/pull/3741)
Expand Down
28 changes: 20 additions & 8 deletions clients/fides-js/src/components/ConsentModal.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { h, VNode } from "preact";
import { Attributes } from "../lib/a11y-dialog";
import { PrivacyNotice, ExperienceConfig } from "../lib/consent-types";
import {
PrivacyNotice,
ExperienceConfig,
FidesOptions,
} from "../lib/consent-types";
import NoticeToggles from "./NoticeToggles";
import CloseButton from "./CloseButton";
import GpcInfo from "./GpcInfo";
import TcfTabs from "./TcfTabs";

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

Expand All @@ -14,6 +19,7 @@ const ConsentModal = ({
enabledNoticeKeys,
onChange,
buttonGroup,
options,
}: {
attributes: Attributes;
experience: ExperienceConfig;
Expand All @@ -22,8 +28,10 @@ const ConsentModal = ({
onClose: () => void;
onChange: (enabledNoticeKeys: NoticeKeys) => void;
buttonGroup: VNode;
options: FidesOptions;
}) => {
const { container, overlay, dialog, title, closeButton } = attributes;
const showTcf = options.tcfEnabled;

return (
// @ts-ignore A11yDialog ref obj type isn't quite the same
Expand Down Expand Up @@ -53,13 +61,17 @@ const ConsentModal = ({
{experience.description}
</p>
<GpcInfo />
<div className="fides-modal-notices">
<NoticeToggles
notices={notices}
enabledNoticeKeys={enabledNoticeKeys}
onChange={onChange}
/>
</div>
{showTcf ? (
<TcfTabs />
) : (
<div className="fides-modal-notices">
<NoticeToggles
notices={notices}
enabledNoticeKeys={enabledNoticeKeys}
onChange={onChange}
/>
</div>
)}
{buttonGroup}
{experience.privacy_policy_link_label &&
experience.privacy_policy_url ? (
Expand Down
1 change: 1 addition & 0 deletions clients/fides-js/src/components/Overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ const Overlay: FunctionComponent<OverlayProps> = ({
}}
/>
}
options={options}
/>
</div>
);
Expand Down
77 changes: 77 additions & 0 deletions clients/fides-js/src/components/TcfTabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { h } from "preact";
import { useRef, useState } from "preact/hooks";

const TCF_TABS = [
{ name: "Purposes", content: "one" },
{ name: "Features", content: "two" },
{ name: "Vendors", content: "three" },
];

const KEY_ARROW_RIGHT = "ArrowRight";
const KEY_ARROW_LEFT = "ArrowLeft";

const TcfTabs = () => {
const [activeTabIndex, setActiveTabIndex] = useState(0);
const inputRefs = [
useRef<HTMLButtonElement>(null),
useRef<HTMLButtonElement>(null),
useRef<HTMLButtonElement>(null),
];
const handleKeyDown = (event: KeyboardEvent) => {
let newActiveTab;
if (event.code === KEY_ARROW_RIGHT) {
event.preventDefault();
newActiveTab =
activeTabIndex === TCF_TABS.length - 1 ? 0 : activeTabIndex + 1;
}
if (event.code === KEY_ARROW_LEFT) {
event.preventDefault();
newActiveTab =
activeTabIndex === 0 ? TCF_TABS.length - 1 : activeTabIndex - 1;
}
if (newActiveTab != null) {
setActiveTabIndex(newActiveTab);
inputRefs[newActiveTab].current?.focus();
}
};
return (
<div className="fides-tabs">
<ul role="tablist" className="fides-tab-list">
{TCF_TABS.map(({ name }, idx) => (
<li role="presentation" key={name}>
<button
id={`fides-tab-${name}`}
aria-selected={idx === activeTabIndex}
onClick={() => {
setActiveTabIndex(idx);
}}
role="tab"
type="button"
className="fides-tab-button"
tabIndex={idx === activeTabIndex ? undefined : -1}
onKeyDown={handleKeyDown}
ref={inputRefs[idx]}
>
{name}
</button>
</li>
))}
</ul>
<div className="tabpanel-container">
{TCF_TABS.map(({ name, content }, idx) => (
<section
role="tabpanel"
id={`fides-panel-${name}`}
aria-labelledby={`fides-tab-${name}`}
tabIndex={-1}
hidden={idx !== activeTabIndex}
key={name}
>
{content}
</section>
))}
</div>
</div>
);
};
export default TcfTabs;
36 changes: 36 additions & 0 deletions clients/fides-js/src/components/fides.css
Original file line number Diff line number Diff line change
Expand Up @@ -504,3 +504,39 @@ div#fides-modal .fides-modal-button-group {
background: var(--fides-overlay-gpc-overridden-background-color);
color: var(--fides-overlay-gpc-overridden-text-color);
}

.fides-tab-list {
padding: 0;
display: flex;
list-style-type: none;
}

.fides-tab-list > li {
width: 100%;
}

.fides-tab-button {
background: none;
border-width: 0 0 1px 0;
border-bottom: 1px solid var(--fides-overlay-row-divider-color);
color: var(--fides-overlay-body-font-color);
font-weight: 500;
padding: 0.6em 1.2em;
cursor: pointer;
width: 100%;
}

.fides-tab-button[aria-selected="true"] {
color: var(--fides-overlay-primary-active-color);
border-bottom-width: 2px;
border-color: var(--fides-overlay-primary-active-color);
font-weight: 600;
}

.fides-tab-button::focus-visible {
outline: 1px auto Highlight;
outline: 1px auto -webkit-focus-ring-color;
}
.fides-tab-button:focus:not(:focus-visible) {
outline: 0;
}
1 change: 1 addition & 0 deletions clients/fides-js/src/fides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ _Fides = {
modalLinkId: null,
privacyCenterUrl: "",
fidesApiUrl: "",
tcfEnabled: false,
},
fides_meta: {},
identity: {},
Expand Down
3 changes: 3 additions & 0 deletions clients/fides-js/src/lib/consent-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export type FidesOptions = {

// URL for the Fides API, used to fetch and save consent preferences. Required.
fidesApiUrl: string;

// Whether we should show the TCF modal
tcfEnabled: boolean;
};

export class SaveConsentPreference {
Expand Down
16 changes: 8 additions & 8 deletions clients/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions clients/privacy-center/app/server-environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export interface PrivacyCenterSettings {
OVERLAY_PARENT_ID: string | null; // (optional) ID of the parent DOM element where the overlay should be inserted
MODAL_LINK_ID: string | null; // (optional) ID of the DOM element that should trigger the consent modal
PRIVACY_CENTER_URL: string; // e.g. http://localhost:3000
TCF_ENABLED: boolean; // whether we should render the TCF modal
allisonking marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand All @@ -62,6 +63,7 @@ export type PrivacyCenterClientSettings = Pick<
| "OVERLAY_PARENT_ID"
| "MODAL_LINK_ID"
| "PRIVACY_CENTER_URL"
| "TCF_ENABLED"
>;

export type Styles = string;
Expand Down Expand Up @@ -274,6 +276,9 @@ export const loadPrivacyCenterEnvironment =
PRIVACY_CENTER_URL:
process.env.FIDES_PRIVACY_CENTER__PRIVACY_CENTER_URL ||
"http://localhost:3000",
TCF_ENABLED: process.env.FIDES_PRIVACY_CENTER__TCF_ENABLED
? process.env.FIDES_PRIVACY_CENTER__TCF_ENABLED === "true"
: false,
};

// Load configuration file (if it exists)
Expand All @@ -292,6 +297,7 @@ export const loadPrivacyCenterEnvironment =
OVERLAY_PARENT_ID: settings.OVERLAY_PARENT_ID,
MODAL_LINK_ID: settings.MODAL_LINK_ID,
PRIVACY_CENTER_URL: settings.PRIVACY_CENTER_URL,
TCF_ENABLED: settings.TCF_ENABLED,
};

// For backwards-compatibility, override FIDES_API_URL with the value from the config file if present
Expand Down
1 change: 1 addition & 0 deletions clients/privacy-center/pages/api/fides-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export default async function handler(
modalLinkId: environment.settings.MODAL_LINK_ID,
privacyCenterUrl: environment.settings.PRIVACY_CENTER_URL,
fidesApiUrl: environment.settings.FIDES_API_URL,
tcfEnabled: environment.settings.TCF_ENABLED,
},
geolocation,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
modalLinkId: null,
privacyCenterUrl: "http://localhost:3000",
fidesApiUrl: "http://localhost:8080/api/v1",
tcfEnabled: false,
},
};
window.Fides.init(fidesConfig);
Expand Down