react-cookie-kit
Version:
GDPR / CCPA Easy Cookie, Script, Do-Not-Sell, and Fingerprint Consent Management for Websites.
361 lines (315 loc) • 10.4 kB
JavaScript
import cx from "classnames";
import PropTypes from "prop-types";
import React from "react";
import {
animations,
consentsSources,
consentStatuses,
cookieTypes,
positions,
themes,
} from "xcoobee-cookie-kit-core/src/configs";
import {
clearLocale,
clearCountryCode,
} from "xcoobee-cookie-kit-core/src/LocaleManager";
import cookieConsentsCache from "xcoobee-cookie-kit-core/src/cookieConsentsCache";
import CookieConsentShape from "./lib/CookieConsentShape";
import CookieKitPopup from "./CookieKitPopup";
const COMPLETE = consentStatuses.complete;
const CLOSED = consentStatuses.closed;
const BLOCK = "xb-cookie-kit";
function reset() {
clearLocale();
clearCountryCode();
cookieConsentsCache.clear();
window.location.reload();
}
function ResetButton() {
const className = "xb-cookie-kit__button xb-cookie-kit__reset-button";
return (
<button type="button" className={className} onClick={reset}>Reset</button>
);
}
export default class CookieKit extends React.PureComponent {
static propTypes = {
campaignReference: PropTypes.string,
companyLogo: PropTypes.string,
consentsSource: PropTypes.oneOf(consentsSources).isRequired,
consentStatus: PropTypes.oneOf(Object.values(consentStatuses)).isRequired,
cookieConsents: PropTypes.arrayOf(CookieConsentShape.isRequired).isRequired,
display: PropTypes.bool,
displayDoNotSell: PropTypes.bool,
displayFingerprint: PropTypes.bool,
doNotSellConsent: PropTypes.bool,
expirationTime: PropTypes.number,
fingerprintConsent: PropTypes.bool,
hideBrandTag: PropTypes.bool.isRequired,
hideOnComplete: PropTypes.bool.isRequired,
isAnimated: PropTypes.bool,
loginStatus: PropTypes.bool,
onConsentStatusChange: PropTypes.func.isRequired,
onCookieConsentsChange: PropTypes.func.isRequired,
position: PropTypes.oneOf(positions).isRequired,
privacyUrl: PropTypes.string.isRequired,
requestDataTypes: PropTypes.arrayOf(
PropTypes.oneOf(cookieTypes).isRequired,
).isRequired,
termsUrl: PropTypes.string.isRequired,
testMode: PropTypes.bool,
textMessage: PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape({
"en-us": PropTypes.string,
"de-de": PropTypes.string,
"es-419": PropTypes.string,
"fr-fr": PropTypes.string,
}),
]).isRequired,
theme: PropTypes.oneOf(themes),
};
static defaultProps = {
campaignReference: null,
companyLogo: null,
display: true,
displayDoNotSell: false,
displayFingerprint: false,
doNotSellConsent: false,
expirationTime: 0,
fingerprintConsent: false,
isAnimated: true,
loginStatus: false,
testMode: false,
theme: "popup",
};
constructor(props) {
// console.log("CookieKit#constructor");
super(props);
const isOpen = props.consentsSource === "unknown";
this.state = {
animated: true,
hasClosed: false,
isOpen: false,
isShown: true,
pulsing: false,
transparent: false,
};
this.timers = [];
if (props.isAnimated) {
this.startPulsing();
}
if (!isOpen) {
this.startDismissTimer();
}
}
// componentDidMount() {
// console.log("CookieKit#componentDidMount");
// console.dir(this.props);
// console.dir(this.state);
// }
// componentDidUpdate(prevProps, prevState) {
// console.log("CookieKit#componentDidUpdate");
// if (this.props !== prevProps) {
// console.log("props changed:");
// console.dir(this.props);
// }
// if (this.state !== prevState) {
// console.log("state changed:");
// console.dir(this.state);
// }
// }
componentWillUnmount() {
this.timers.forEach(timer => clearTimeout(timer));
}
handleOpen = () => {
// console.log("CookieKit#handleOpen");
const { transparent } = this.state;
if (transparent) {
return;
}
this.clearTimers();
this.stopPulsing();
this.setState({ isOpen: true });
};
handlePopupClose = () => {
// console.log("CookieKit#handlePopupClose");
const { consentStatus, onConsentStatusChange } = this.props;
if (consentStatus !== COMPLETE) {
onConsentStatusChange(CLOSED);
}
this.clearTimers();
this.startDismissTimer();
this.setState({ hasClosed: true, isOpen: false });
};
handlePopupLogin = () => {
// console.log("CookieKit#handlePopupLogin");
this.clearTimers();
this.setState({ isOpen: false });
// HACK: Because `startPulsing` depends on `props.consentsSource` and it
// could be changed in `onAuthentication` in what seems to be the next
// event loop, we are also delaying the calls to these methods in the next
// event loop. Without this, the pulsing is not started.
setTimeout(() => {
this.startPulsing();
this.startDismissTimer();
}, 1);
};
handlePopupSubmit = (nextConsentSettings, doNotSellConsent, fingerprintConsent) => {
// console.log("CookieKit#handlePopupSubmit");
// console.dir(nextConsentSettings);
const {
hideOnComplete,
onCookieConsentsChange,
} = this.props;
onCookieConsentsChange(nextConsentSettings, doNotSellConsent, fingerprintConsent);
this.clearTimers();
this.setState({ isOpen: false });
if (hideOnComplete) {
this.setState({ pulsing: false, transparent: true });
this.timers.push(setTimeout(() => {
this.setState({ isShown: false });
}, 1000));
} else {
// HACK: Because `startPulsing` depends on `props.consentsSource` and it
// is changed in `onCookieConsentsChange` in what seems to be the next
// event loop, we are also delaying the calls to these methods in the next
// event loop. Without this, the pulsing is not started.
setTimeout(() => {
this.startPulsing();
this.startDismissTimer();
}, 1);
}
};
clearTimers() {
// console.log("CookieKit#clearTimers");
this.timers.forEach(timer => clearTimeout(timer));
this.timers = [];
}
startPulsing() {
// console.log("CookieKit#startPulsing");
const { consentsSource, hideOnComplete } = this.props;
const animation = animations[consentsSource];
if (animation && animation !== "default") {
this.timers.push(setTimeout(() => this.setState({ animated: true, pulsing: true }), 500));
this.timers.push(setTimeout(() => this.stopPulsing(), 4500));
this.timers.push(setTimeout(() => this.setState({ animated: false }), 5000));
if (hideOnComplete) {
this.timers.push(setTimeout(() => {
this.setState({ transparent: true });
}, 5000));
this.timers.push(setTimeout(() => {
this.setState({ isShown: false });
}, 6000));
}
}
}
stopPulsing() {
// console.log("CookieKit#stopPulsing");
this.setState({ pulsing: false });
}
startDismissTimer() {
// console.log("CookieKit#startDismissTimer");
const { expirationTime } = this.props;
const timeOut = expirationTime;
if (timeOut && timeOut > 0) {
this.timers.push(setTimeout(() => {
this.setState({ transparent: true });
}, timeOut * 1000));
this.timers.push(setTimeout(() => {
this.setState({ isShown: false });
}, timeOut * 1000 + 1000));
}
}
render() {
// console.log("CookieKit#render");
const {
campaignReference,
companyLogo,
consentsSource,
cookieConsents,
doNotSellConsent,
display,
displayDoNotSell,
displayFingerprint,
fingerprintConsent,
hideBrandTag,
isAnimated,
loginStatus,
position,
privacyUrl,
requestDataTypes,
termsUrl,
testMode,
textMessage,
theme,
} = this.props;
const { animated, hasClosed, isOpen, isShown, pulsing, transparent } = this.state;
const animation = animated && isAnimated ? animations[consentsSource] : animations.unknown;
const renderPopup = isOpen || (consentsSource === "unknown" && !hasClosed);
const renderResetButton = testMode;
return (
isShown && (
<div
className={
cx(
BLOCK,
position,
{
transparent,
},
{
scroll: isOpen,
},
{
"d-none": !display,
},
)
}
>
{renderPopup
? (
<CookieKitPopup
companyLogo={companyLogo}
cookieConsents={cookieConsents}
displayDoNotSell={displayDoNotSell}
displayFingerprint={displayFingerprint}
doNotSellConsent={doNotSellConsent}
fingerprintConsent={fingerprintConsent}
hideBrandTag={hideBrandTag}
loginStatus={loginStatus}
isConnected={!!campaignReference}
onClose={this.handlePopupClose}
onLogin={this.handlePopupLogin}
onSubmit={this.handlePopupSubmit}
privacyUrl={privacyUrl}
requestDataTypes={requestDataTypes}
termsUrl={termsUrl}
textMessage={textMessage}
theme={theme}
/>
)
: (
<button
type="button"
className={`${BLOCK}__button ${BLOCK}__cookie-button`}
onClick={this.handleOpen}
>
<div
className={
cx(
`${BLOCK}__cookie-icon`,
`${BLOCK}__cookie-icon--${animation}`,
{
[`${BLOCK}__pulsing`]: pulsing,
},
)
}
/>
</button>
)}
{renderResetButton && (<ResetButton />)}
</div>
)
);
}
}