UNPKG

klaro

Version:

A simple but powerful consent manager.

279 lines (248 loc) 9.09 kB
import React from 'react'; import ConsentModal from './consent-modal'; import { getPurposes } from '../utils/config'; import Text from './text'; import { asTitle } from '../utils/strings'; export default class ConsentNotice extends React.Component { constructor(props) { super(props); this.state = { modal: props.modal, confirming: false, }; } componentDidUpdate(prevProps) { if (prevProps.modal !== this.props.modal) this.setState({ modal: this.props.modal }); if (this.noticeRef) { this.noticeRef.focus(); } } executeButtonClicked = (setChangedAll, changedAllValue, eventType) => { const { modal } = this.state; let changedServices = 0; if (setChangedAll) changedServices = this.props.manager.changeAll(changedAllValue); const confirmed = this.props.manager.confirmed; this.props.manager.saveAndApplyConsents(eventType); if ( setChangedAll && !confirmed && (modal || this.props.config.mustConsent) ) { const close = () => { this.setState({ confirming: false }); this.props.hide(); }; this.setState({ confirming: true }); if (changedServices === 0) close(); else { setTimeout(close, 800); } } else { this.props.hide(); } }; saveAndHide = () => { this.executeButtonClicked(false, false, 'save'); }; acceptAndHide = () => { this.executeButtonClicked(true, true, 'accept'); }; declineAndHide = () => { this.executeButtonClicked(true, false, 'decline'); }; render() { const { lang, config, show, manager, testing, t } = this.props; const { confirming, modal } = this.state; const { embedded, noticeAsModal, hideLearnMore } = config; // we exclude functional services from this list, as they are always required and // the user cannot decline their use... const purposeOrder = config.purposeOrder || []; const purposes = getPurposes(config) .filter((purpose) => purpose !== 'functional') .sort((a, b) => purposeOrder.indexOf(a) - purposeOrder.indexOf(b)); const purposesTranslations = purposes.map( (purpose) => t(['!', 'purposes', purpose, 'title?']) || asTitle(purpose) ); let purposesText = ''; if (purposesTranslations.length === 1) purposesText = purposesTranslations[0]; else purposesText = [ ...purposesTranslations.slice(0, -2), purposesTranslations.slice(-2).join(' & '), ].join(', '); let ppUrl; // to do: deprecate and remove this if (config.privacyPolicy !== undefined) { if (typeof config.privacyPolicy === 'string') ppUrl = config.privacyPolicy; else if (typeof config.privacyPolicy === 'object') { ppUrl = config.privacyPolicy[lang] || config.privacyPolicy.default; } } else { // this is the modern way ppUrl = t(['!', 'privacyPolicyUrl'], { lang: lang }); if (ppUrl !== undefined) ppUrl = ppUrl.join(''); } const showModal = (e) => { e.preventDefault(); this.setState({ modal: true }); }; const hideModal = () => { if (config.mustConsent && !config.acceptAll) return; if (manager.confirmed && !testing) this.props.hide(); else this.setState({ modal: false }); setTimeout(() => { if (this.noticeRef) { this.noticeRef.focus(); } }, 1); }; let changesText; if (manager.changed) changesText = ( <p className="cn-changes"> {t(['consentNotice', 'changeDescription'])} </p> ); // we only show the notice if it's explicitly demanded (show=True), if // testing mode is enabled (testing=true) or if there's a confirmation // process/animation going on (confirming=true) if (!show && !testing && !confirming) return <div />; const noticeIsVisible = (!config.mustConsent || noticeAsModal) && !manager.confirmed && !config.noNotice; const declineButton = config.hideDeclineAll ? ( '' ) : ( <button className="cm-btn cm-btn-danger cn-decline" type="button" onClick={this.declineAndHide} > {t(['decline'])} </button> ); const acceptButton = config.acceptAll ? ( <button className="cm-btn cm-btn-success" type="button" onClick={this.acceptAndHide} > {t(['ok'])} </button> ) : ( <button className="cm-btn cm-btn-success" type="button" onClick={this.saveAndHide} > {t(['ok'])} </button> ); const learnMoreLink = () => noticeAsModal ? ( <button key="learnMoreLink" className="cm-btn cm-btn-lern-more cm-btn-info" type="button" onClick={showModal} > {t(['consentNotice', 'learnMore'])} </button> ) : ( <a key="learnMoreLink" className="cm-link cn-learn-more" href="#" onClick={showModal} > {t(['consentNotice', 'learnMore'])} </a> ); let ppLink; if (ppUrl !== undefined) ppLink = ( <a key="ppLink" href={ppUrl}> {t(['privacyPolicy', 'name'])} </a> ); if ( modal || (manager.confirmed && !testing) || (!manager.confirmed && config.mustConsent) ) return ( <ConsentModal t={t} lang={lang} config={config} hide={hideModal} confirming={confirming} declineAndHide={this.declineAndHide} saveAndHide={this.saveAndHide} acceptAndHide={this.acceptAndHide} manager={manager} /> ); const notice = ( <div role="dialog" aria-describedby="id-cookie-notice" aria-labelledby="id-cookie-title" id="klaro-cookie-notice" tabIndex="0" autoFocus ref={(div) => { this.noticeRef = div; }} className={`cookie-notice ${ !noticeIsVisible && !testing ? 'cookie-notice-hidden' : '' } ${noticeAsModal ? 'cookie-modal-notice' : ''} ${ embedded ? 'cn-embedded' : '' }`} > <div className="cn-body"> {t(['!', 'consentNotice', 'title']) && config.showNoticeTitle && ( <h2 id="id-cookie-title"> {t(['consentNotice', 'title'])} </h2> )} <p id="id-cookie-notice"> <Text config={config} text={t(['consentNotice', 'description'], { purposes: ( <strong key="strong">{purposesText}</strong> ), privacyPolicy: ppLink, learnMoreLink: learnMoreLink(), })} /> </p> {testing && <p>{t(['consentNotice', 'testing'])}</p>} {changesText} <div className="cn-ok"> {!hideLearnMore && learnMoreLink()} <div className="cn-buttons"> {declineButton} {acceptButton} </div> </div> </div> </div> ); if (!noticeAsModal) return notice; return ( <div id="cookieScreen" className="cookie-modal"> <div className="cm-bg" /> {notice} </div> ); } }