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

Update FidesJS fides_embed option to support embedding both banner & modal components #4782

Merged
merged 11 commits into from
Apr 11, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ The types of changes are:
- Changed the Stripe integration for `Cards` to delete instead of update due to possible issues of a past expiration date [#4768](https://github.com/ethyca/fides/pull/4768)
- Changed display of Data Uses, Categories and Subjects to user friendly names in the Data map report [#4774](https://github.com/ethyca/fides/pull/4774)
- Update active disabled Fides.js toggle color to light grey [#4778](https://github.com/ethyca/fides/pull/4778)
- Update FidesJS fides_embed option to support embedding both banner & modal components [#4782](https://github.com/ethyca/fides/pull/4782)

### Fixed
- Fixed select dropdowns being cut off by edges of modal forms [#4757](https://github.com/ethyca/fides/pull/4757)
Expand Down
2 changes: 2 additions & 0 deletions clients/fides-js/docs/interfaces/FidesOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ ___
When `true`, require FidesJS to "embed" it's UI into a specific `<div>` on
the page, instead of as an overlay over the `<body>` itself. This is useful
for creating a dedicated page to manage consent preferences on your site.
Both the consent modal and the banner will be embedded into the container.
To only embed the consent modal, set `fides_disable_banner` to `true`.

To use the `fides_embed` option, ensure that a DOM element with
`id="fides-embed-container"` exists on the page, which FidesJS will then
Expand Down
19 changes: 13 additions & 6 deletions clients/fides-js/src/components/ConsentBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface BannerProps {
onVendorPageClick?: () => void;
renderButtonGroup: (props: ButtonGroupProps) => VNode;
className?: string;
isEmbedded: boolean;
}

const ConsentBanner: FunctionComponent<BannerProps> = ({
Expand All @@ -37,6 +38,7 @@ const ConsentBanner: FunctionComponent<BannerProps> = ({
onVendorPageClick,
renderButtonGroup,
className,
isEmbedded,
}) => {
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);

Expand Down Expand Up @@ -71,13 +73,18 @@ const ConsentBanner: FunctionComponent<BannerProps> = ({
? i18n.t("exp.banner_description")
: i18n.t("exp.description");

const containerClassName = [
"fides-banner",
"fides-banner-bottom",
!bannerIsOpen && "fides-banner-hidden",
isEmbedded && "fides-embedded",
className,
]
.filter((c) => typeof c === "string")
.join(" ");

return (
<div
id="fides-banner-container"
className={`fides-banner fides-banner-bottom
${bannerIsOpen ? "" : "fides-banner-hidden"}
${className || ""}`}
>
<div id="fides-banner-container" className={containerClassName}>
<div id="fides-banner">
<div id="fides-banner-inner">
<CloseButton
Expand Down
8 changes: 4 additions & 4 deletions clients/fides-js/src/components/ConsentModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { VNode, h } from "preact";
import { ComponentChildren, VNode, h } from "preact";
import { HTMLAttributes } from "preact/compat";

import { Attributes } from "../lib/a11y-dialog";
Expand All @@ -9,17 +9,17 @@ import ConsentContent from "./ConsentContent";

const ConsentModal = ({
attributes,
children,
dismissable,
i18n,
renderModalFooter,
renderModalContent,
}: {
attributes: Attributes;
children: ComponentChildren;
dismissable: boolean | undefined;
i18n: I18n;
onVendorPageClick?: () => void;
renderModalFooter: () => VNode;
renderModalContent: () => VNode;
}) => {
const { container, overlay, dialog, title, closeButton } = attributes;

Expand Down Expand Up @@ -48,7 +48,7 @@ const ConsentModal = ({
i18n={i18n}
renderModalFooter={renderModalFooter}
>
{renderModalContent()}
{children}
</ConsentContent>
</div>
</div>
Expand Down
73 changes: 42 additions & 31 deletions clients/fides-js/src/components/Overlay.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-nested-ternary */
import { h, FunctionComponent, VNode } from "preact";
import {
useEffect,
Expand Down Expand Up @@ -32,11 +33,13 @@ import { FIDES_OVERLAY_WRAPPER } from "../lib/consent-constants";

interface RenderBannerProps {
isOpen: boolean;
isEmbedded: boolean;
onClose: () => void;
onSave: () => void;
onManagePreferencesClick: () => void;
}
interface RenderModalFooter {

interface RenderModalFooterProps {
onClose: () => void;
isMobile: boolean;
}
Expand All @@ -51,7 +54,7 @@ interface Props {
onDismiss: () => void;
renderBanner: (props: RenderBannerProps) => VNode | null;
renderModalContent: () => VNode;
renderModalFooter: (props: RenderModalFooter) => VNode;
renderModalFooter: (props: RenderModalFooterProps) => VNode;
onVendorPageClick?: () => void;
isUiBlocking: boolean;
}
Expand All @@ -73,7 +76,18 @@ const Overlay: FunctionComponent<Props> = ({
const delayBannerMilliseconds = 100;
const delayModalLinkMilliseconds = 200;
const hasMounted = useHasMounted();
const [bannerIsOpen, setBannerIsOpen] = useState(false);

const showBanner = useMemo(
() =>
!options.fidesDisableBanner &&
experience.experience_config?.component !== ComponentType.MODAL &&
shouldResurfaceConsent(experience, cookie, savedConsent),
[cookie, savedConsent, experience, options]
);

const [bannerIsOpen, setBannerIsOpen] = useState(
options.fidesEmbed ? showBanner : false
);
const modalLinkRef = useRef<HTMLElement | null>(null);

useEffect(() => {
Expand Down Expand Up @@ -108,12 +122,14 @@ const Overlay: FunctionComponent<Props> = ({
});

const handleOpenModal = useCallback(() => {
if (instance) {
if (options.fidesEmbed) {
setBannerIsOpen(false);
} else if (instance) {
setBannerIsOpen(false);
instance.show();
onOpen();
}
}, [instance, onOpen]);
}, [instance, onOpen, options]);

const handleCloseModalAfterSave = useCallback(() => {
if (instance && !options.fidesEmbed) {
Expand All @@ -123,20 +139,12 @@ const Overlay: FunctionComponent<Props> = ({
}, [instance, dispatchCloseEvent, options.fidesEmbed]);

useEffect(() => {
if (options.fidesEmbed) {
if (options.fidesEmbed && !bannerIsOpen) {
onOpen();
}
}, [options, onOpen]);

const showBanner = useMemo(
() =>
!options.fidesDisableBanner &&
experience.experience_config?.component !== ComponentType.MODAL &&
shouldResurfaceConsent(experience, cookie, savedConsent) &&
!options.fidesEmbed,
[cookie, savedConsent, experience, options]
);
}, [options, onOpen, bannerIsOpen]);

// The delay is needed for the banner CSS animation
useEffect(() => {
const delayBanner = setTimeout(() => {
if (showBanner) {
Expand Down Expand Up @@ -200,6 +208,7 @@ const Overlay: FunctionComponent<Props> = ({
{showBanner
? renderBanner({
isOpen: bannerIsOpen,
isEmbedded: options.fidesEmbed,
onClose: () => {
setBannerIsOpen(false);
},
Expand All @@ -210,19 +219,20 @@ const Overlay: FunctionComponent<Props> = ({
})
: null}
{options.fidesEmbed ? (
<ConsentContent
titleProps={attributes.title}
className="fides-embed"
i18n={i18n}
renderModalFooter={() =>
renderModalFooter({
onClose: handleCloseModalAfterSave,
isMobile: false,
})
}
>
{renderModalContent()}
</ConsentContent>
bannerIsOpen ? null : (
<ConsentContent
titleProps={attributes.title}
i18n={i18n}
renderModalFooter={() =>
renderModalFooter({
onClose: handleCloseModalAfterSave,
isMobile: false,
})
}
>
{renderModalContent()}
</ConsentContent>
)
) : (
<ConsentModal
attributes={attributes}
Expand All @@ -235,8 +245,9 @@ const Overlay: FunctionComponent<Props> = ({
isMobile: false,
})
}
renderModalContent={renderModalContent}
/>
>
{renderModalContent()}
</ConsentModal>
)}
</div>
);
Expand Down
28 changes: 23 additions & 5 deletions clients/fides-js/src/components/fides.css
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,14 @@
}

div#fides-overlay {
font-family: var(--fides-overlay-font-family);
font-size: var(--fides-overlay-font-size-body);
z-index: 1000;
position: fixed;
}

.fides-banner,
.fides-modal-container {
font-family: var(--fides-overlay-font-family);
font-size: var(--fides-overlay-font-size-body);
white-space: pre-line;

/* CSS reset values, adapted from https://www.joshwcomeau.com/css/custom-css-reset/ */
Expand All @@ -93,7 +97,7 @@ div#fides-overlay {
display: inline;
}

div#fides-banner-container {
div#fides-banner-container:not(.fides-embedded) {
position: fixed;
z-index: 1;
width: 100%;
Expand Down Expand Up @@ -121,6 +125,10 @@ div#fides-banner {
border-top: 1px solid var(--fides-overlay-primary-color);
}

.fides-embedded div#fides-banner {
border: none;
}

div#fides-banner-inner {
width: 100%;
}
Expand All @@ -130,9 +138,16 @@ div#fides-banner-container.fides-banner-bottom {
left: 0;
}

div#fides-banner-container.fides-banner-hidden {
visibility: hidden;
}

div#fides-banner-container.fides-banner-hidden.fides-embedded {
display: none;
}

div#fides-banner-container.fides-banner-bottom.fides-banner-hidden {
transform: translateY(150%);
visibility: hidden;
}

div#fides-banner-container.fides-banner-top {
Expand All @@ -142,7 +157,6 @@ div#fides-banner-container.fides-banner-top {

div#fides-banner-container.fides-banner-top.fides-banner-hidden {
transform: translateY(-150%);
visibility: hidden;
}

div#fides-banner-inner div#fides-button-group {
Expand Down Expand Up @@ -464,6 +478,10 @@ div#fides-banner .fides-close-button {
background: var(--fides-overlay-hover-color);
}

.fides-embedded .fides-close-button {
display: none !important;
}

.fides-modal-notices {
margin-bottom: 16px;
}
Expand Down
9 changes: 8 additions & 1 deletion clients/fides-js/src/components/notices/NoticeOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,13 @@ const NoticeOverlay: FunctionComponent<OverlayProps> = ({
isUiBlocking={!isDismissable}
onOpen={dispatchOpenOverlayEvent}
onDismiss={handleDismiss}
renderBanner={({ isOpen, onClose, onSave, onManagePreferencesClick }) => (
renderBanner={({
isEmbedded,
isOpen,
onClose,
onSave,
onManagePreferencesClick,
}) => (
<ConsentBanner
bannerIsOpen={isOpen}
dismissable={isDismissable}
Expand All @@ -263,6 +269,7 @@ const NoticeOverlay: FunctionComponent<OverlayProps> = ({
handleDismiss();
}}
i18n={i18n}
isEmbedded={isEmbedded}
renderButtonGroup={({ isMobile }) => (
<NoticeConsentButtons
experience={experience}
Expand Down
11 changes: 9 additions & 2 deletions clients/fides-js/src/components/tcf/TcfOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ const TcfOverlay: FunctionComponent<OverlayProps> = ({
return bestTranslation?.privacy_experience_config_history_id;
}
return undefined;
}, [experience, i18n, currentLocale]);
}, [experience, i18n]);

const { servedNotice } = useConsentServed({
privacyExperienceConfigHistoryId,
Expand Down Expand Up @@ -336,7 +336,13 @@ const TcfOverlay: FunctionComponent<OverlayProps> = ({
isUiBlocking={!isDismissable}
onOpen={dispatchOpenOverlayEvent}
onDismiss={handleDismiss}
renderBanner={({ isOpen, onClose, onSave, onManagePreferencesClick }) => {
renderBanner={({
isEmbedded,
isOpen,
onClose,
onSave,
onManagePreferencesClick,
}) => {
const goToVendorTab = () => {
onManagePreferencesClick();
setActiveTabIndex(2);
Expand All @@ -346,6 +352,7 @@ const TcfOverlay: FunctionComponent<OverlayProps> = ({
i18n={i18n}
dismissable={isDismissable}
bannerIsOpen={isOpen}
isEmbedded={isEmbedded}
onOpen={dispatchOpenBannerEvent}
onClose={() => {
onClose();
Expand Down
4 changes: 3 additions & 1 deletion clients/fides-js/src/docs/fides-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ export interface FidesOptions {
* When `true`, require FidesJS to "embed" it's UI into a specific `<div>` on
* the page, instead of as an overlay over the `<body>` itself. This is useful
* for creating a dedicated page to manage consent preferences on your site.
* Both the consent modal and the banner will be embedded into the container.
* To only embed the consent modal, set `fides_disable_banner` to `true`.
*
* To use the `fides_embed` option, ensure that a DOM element with
* `id="fides-embed-container"` exists on the page, which FidesJS will then
* use as the parent element to render within.
*
*
* NOTE: If you're using a JavaScript framework (e.g. React), ensure that you
* do not re-render the parent `<div>` element, as this could remove the
* FidesJS UI fully from the page!
Expand Down
Loading
Loading