├── .nvmrc ├── .gitattributes ├── .storybook ├── addons.js ├── config.js ├── webpack.config.js └── preview-head.html ├── .gitignore ├── test └── setupTests.ts ├── src ├── styled.d.ts ├── consent-manager │ ├── font-styles.tsx │ ├── categories.ts │ ├── cancel-dialog.tsx │ ├── buttons.tsx │ ├── banner.tsx │ ├── dialog.tsx │ ├── container.tsx │ ├── index.tsx │ └── preference-dialog.tsx ├── index.ts ├── __tests__ │ ├── index.test.ts │ └── consent-manager-builder │ │ ├── fetch-destinations.test.ts │ │ ├── preferences.test.ts │ │ ├── analytics.test.ts │ │ └── index.todo.js ├── consent-manager-builder │ ├── fetch-destinations.ts │ ├── preferences.ts │ ├── analytics.ts │ └── index.tsx ├── standalone.tsx └── types.ts ├── .editorconfig ├── tsconfig.json ├── .github └── workflows │ └── nodeJS.yml ├── GUIDESTYLES.md ├── .eslintrc ├── stories ├── 4-custom-consent.stories.tsx ├── 1-standalone.stories.tsx ├── components │ ├── destination-tile.tsx │ ├── CookieView.tsx │ └── common-react.tsx ├── 1.1-standalone-custom.stories.tsx ├── ImplyConsentOnInteraction.tsx ├── 3-tool-based.stories.tsx ├── 2-category-based.stories.tsx ├── standalone.html ├── standalone-custom.html ├── 0.1-consent-manager-close-interaction.stories.tsx ├── 0.2-consent-manager-custom-cookie-name.stories.tsx ├── 8-consent-manager-actions-block.stories.tsx ├── 0.3-consent-manager-custom-cookie-attributes.stories.tsx ├── 9-consent-manager-as-modal.stories.tsx ├── 0-consent-manager.stories.tsx ├── 7-default-destination-behavior.stories.tsx ├── 5-custom-categories.stories.tsx └── 6-ccpa-gdpr-example.stories.tsx ├── LICENSE ├── webpack.config.js ├── package.json └── CHANGELOG.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 16 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-options/register' 2 | import '@storybook/addon-storysource/register' 3 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from '@storybook/react' 2 | 3 | configure(require.context('../stories', true, /\.stories\.tsx$/), module) 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /commonjs/ 3 | /esm/ 4 | /standalone/ 5 | /build 6 | /storybook-static 7 | /types 8 | 9 | # IDE 10 | .vscode/ 11 | -------------------------------------------------------------------------------- /test/setupTests.ts: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme' 2 | import Adapter from 'enzyme-adapter-react-16' 3 | 4 | configure({ adapter: new Adapter() }) 5 | -------------------------------------------------------------------------------- /src/styled.d.ts: -------------------------------------------------------------------------------- 1 | import { CSSProp } from 'styled-components' 2 | 3 | declare module 'react' { 4 | interface HTMLAttributes extends DOMAttributes { 5 | css?: CSSProp 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /src/consent-manager/font-styles.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | 3 | export default css` 4 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, 5 | sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; 6 | -moz-osx-font-smoothing: grayscale; 7 | -webkit-font-smoothing: antialiased; 8 | font-smoothing: antialiased; 9 | color: #435a6f; 10 | font-size: 16px; 11 | font-weight: 400; 12 | line-height: 22px; 13 | letter-spacing: -0.05px; 14 | ` 15 | -------------------------------------------------------------------------------- /src/consent-manager/categories.ts: -------------------------------------------------------------------------------- 1 | export const MARKETING_AND_ANALYTICS_CATEGORIES = [ 2 | 'A/B Testing', 3 | 'Analytics', 4 | 'Attribution', 5 | 'Email', 6 | 'Enrichment', 7 | 'Heatmaps & Recordings', 8 | 'Raw Data', 9 | 'Realtime Dashboards', 10 | 'Referrals', 11 | 'Surveys', 12 | 'Video' 13 | ] 14 | 15 | export const ADVERTISING_CATEGORIES = ['Advertising', 'Tag Managers'] 16 | 17 | export const FUNCTIONAL_CATEGORIES = [ 18 | 'CRM', 19 | 'Customer Success', 20 | 'Deep Linking', 21 | 'Helpdesk', 22 | 'Livechat', 23 | 'Performance Monitoring', 24 | 'Personalization', 25 | 'SMS & Push Notifications', 26 | 'Security & Fraud' 27 | ] 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "build", 4 | "module": "esnext", 5 | "target": "es5", 6 | "lib": ["es6", "dom", "es2016", "es2017"], 7 | "jsx": "react", 8 | "allowJs": true, 9 | "moduleResolution": "node", 10 | "forceConsistentCasingInFileNames": true, 11 | "allowSyntheticDefaultImports": true, 12 | "noImplicitReturns": true, 13 | "noImplicitThis": true, 14 | "noImplicitAny": false, 15 | "strictNullChecks": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "esModuleInterop": true, 19 | "declaration": true, 20 | "declarationDir": "types" 21 | }, 22 | "include": ["src"], 23 | "exclude": ["node_modules", "build"] 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/nodeJS.yml: -------------------------------------------------------------------------------- 1 | name: Consent Manager 2 | 3 | on: 4 | push: 5 | branches: ['master'] 6 | pull_request: 7 | branches: ['master'] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [14.x] 16 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | cache: 'yarn' 25 | - run: yarn install --frozen-lockfile 26 | - run: yarn build 27 | - run: yarn lint 28 | - run: yarn test 29 | - run: yarn size-limit 30 | -------------------------------------------------------------------------------- /GUIDESTYLES.md: -------------------------------------------------------------------------------- 1 | # Elements inside Consent Manager 2 | 3 | This is the guide with the list ids from the different components inside Consent-Manager, you can use the id to change the styles with css files inside your project. 4 | 5 | ## Banner 6 | 7 | - segmentio_fragmentBanner 8 | - segmentio_pContent 9 | - segmentio_pSubContent 10 | - segmentio_subContentBtn 11 | - segmentio_actionBlock 12 | - segmentio_allowBtn 13 | - segmentio_denyBtn 14 | - segmentio_closeButton 15 | 16 | ## Cancel Dialog 17 | 18 | - segmentio_backDialogBtn 19 | - segmentio_cancelDialogBtn 20 | 21 | ## Dialog 22 | 23 | - segmentio_overlayDialog 24 | - segmentio_rootDialog 25 | - segmentio_headerDialog 26 | - segmentio_headerCancelBtn 27 | - segmentio_contentDialog 28 | - segmentio_buttonsDialog 29 | 30 | ## Preference Dialog 31 | 32 | - segmentio_prefBtnCancel 33 | - segmentio_prefBtnSubmit 34 | - segmentio_prefTableScroll 35 | - segmentio_prefTable 36 | - segmentio_prefThead 37 | - segmentio_prefTbody 38 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "project": "./tsconfig.json" 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:@typescript-eslint/recommended", 9 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 10 | "plugin:@typescript-eslint/eslint-recommended", 11 | "prettier", 12 | "prettier/@typescript-eslint" 13 | ], 14 | "plugins": ["react", "@typescript-eslint", "prettier"], 15 | "env": { 16 | "browser": true, 17 | "jest": true 18 | }, 19 | "rules": { 20 | "@typescript-eslint/no-unused-vars": "off", 21 | "@typescript-eslint/no-floating-promises": "error", 22 | "@typescript-eslint/explicit-function-return-type": "off", 23 | "@typescript-eslint/ban-ts-ignore": "warn", 24 | "no-global-assign": "off", 25 | "@typescript-eslint/unbound-method": "warn" 26 | }, 27 | "settings": { 28 | "react": { 29 | "pragma": "React", 30 | "version": "detect" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /stories/4-custom-consent.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Pane, Button } from 'evergreen-ui' 3 | import { storiesOf } from '@storybook/react' 4 | import ConsentManager from '../src/consent-manager' 5 | import * as common from './components/common-react' 6 | import { openConsentManager } from '../src' 7 | import CookieView from './components/CookieView' 8 | 9 | const initialPreferences = { 10 | advertising: false, 11 | marketingAndAnalytics: true, 12 | functional: true 13 | } 14 | 15 | const Custom = () => { 16 | return ( 17 | 18 | true} 22 | {...common} 23 | /> 24 | 25 | 28 | 29 | 30 | 31 | ) 32 | } 33 | 34 | storiesOf('Advanced Use Cases', module).add(`Partial consent`, () => ) 35 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const pkg = require('../package.json') 3 | 4 | module.exports = { 5 | mode: 'development', 6 | devtool: 'source-map', 7 | resolve: { 8 | extensions: ['.tsx', '.ts', '.js'] 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.stories\.tsx?$/, 14 | loaders: [ 15 | { 16 | loader: require.resolve('@storybook/source-loader'), 17 | options: { 18 | parser: 'typescript', 19 | prettierConfig: { 20 | printWidth: 100, 21 | singleQuote: false 22 | } 23 | } 24 | } 25 | ], 26 | enforce: 'pre' 27 | }, 28 | { 29 | test: /\.tsx?$/, 30 | exclude: /node_modules/, 31 | loader: 'ts-loader' 32 | } 33 | ] 34 | }, 35 | plugins: [ 36 | new webpack.DefinePlugin({ 37 | 'process.env': { 38 | NODE_ENV: JSON.stringify('production'), 39 | VERSION: JSON.stringify(pkg.version) 40 | } 41 | }) 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /stories/1-standalone.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import { Pane } from 'evergreen-ui' 4 | import SyntaxHighlighter from 'react-syntax-highlighter' 5 | import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs' 6 | 7 | // @ts-ignore 8 | import contents from 'raw-loader!./standalone.html' 9 | import CookieView from './components/CookieView' 10 | 11 | const StandaloneConsentManagerExample = () => { 12 | return ( 13 | <> 14 | 15 | 22 | 23 | {contents} 24 | 25 | 26 | 27 | 28 | 29 | ) 30 | } 31 | 32 | storiesOf('Standalone / Tag', module).add(`Basic`, () => ) 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018, Segment.io, Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /stories/components/destination-tile.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Li, Text, Link, Checkbox, Card } from 'evergreen-ui' 4 | 5 | export default function Destination({ destination, preferences, setPreferences }) { 6 | return ( 7 |
  • 8 | 9 | 12 | {destination.name} 13 | 14 | } 15 | checked={Boolean(preferences[destination.id])} 16 | onChange={() => 17 | setPreferences({ 18 | [destination.id]: !preferences[destination.id] 19 | }) 20 | } 21 | /> 22 | 23 | {destination.description} 24 | 25 | 26 |
  • 27 | ) 28 | } 29 | 30 | Destination.propTypes = { 31 | destination: PropTypes.object.isRequired, 32 | preferences: PropTypes.object.isRequired, 33 | setPreferences: PropTypes.func.isRequired 34 | } 35 | -------------------------------------------------------------------------------- /stories/1.1-standalone-custom.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | import { Pane } from 'evergreen-ui' 4 | import SyntaxHighlighter from 'react-syntax-highlighter' 5 | import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs' 6 | 7 | // @ts-ignore 8 | import contents from 'raw-loader!./standalone-custom.html' 9 | import CookieView from './components/CookieView' 10 | 11 | const StandaloneConsentManagerExample = () => { 12 | return ( 13 | <> 14 | 15 | 22 | 23 | {contents} 24 | 25 | 26 | 27 | 28 | ) 29 | } 30 | 31 | storiesOf('Standalone / Javascript', module).add(`with Customization`, () => ( 32 | 33 | )) 34 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const pkg = require('./package.json') 4 | 5 | module.exports = { 6 | mode: 'production', 7 | devtool: 'source-map', 8 | entry: './src/standalone.tsx', 9 | output: { 10 | path: path.join(__dirname, 'standalone'), 11 | filename: 'consent-manager.js', 12 | library: 'consentManager' 13 | }, 14 | resolve: { 15 | alias: { 16 | react: 'preact/compat', 17 | 'react-dom': 'preact/compat' 18 | }, 19 | extensions: ['.tsx', '.ts', '.js'] 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.tsx?$/, 25 | exclude: /node_modules/, 26 | loader: 'ts-loader' 27 | } 28 | ] 29 | }, 30 | plugins: [ 31 | new webpack.DefinePlugin({ 32 | 'process.env': { 33 | NODE_ENV: JSON.stringify('production'), 34 | VERSION: JSON.stringify(pkg.version) 35 | } 36 | }), 37 | new webpack.BannerPlugin( 38 | ` 39 | Consent Manager v${pkg.version} 40 | https://github.com/segmentio/consent-manager 41 | Released under the MIT license 42 | Copyright © 2021, Segment.io, Inc 43 | `.trim() 44 | ) 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /stories/components/CookieView.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { Pane, Heading, Button } from 'evergreen-ui' 3 | import cookies from 'js-cookie' 4 | import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs' 5 | import SyntaxHighlighter from 'react-syntax-highlighter' 6 | 7 | const CookieView = () => { 8 | const [cookieVal, updateCookieVal] = useState(cookies.getJSON()) 9 | 10 | useEffect(() => { 11 | const clear = setInterval(() => { 12 | updateCookieVal(cookies.getJSON()) 13 | }, 1000) 14 | return () => clearInterval(clear) 15 | }) 16 | 17 | return ( 18 | 19 | Cookies: 20 | 21 | {JSON.stringify(cookieVal, null, 2)} 22 | 23 | 24 | 35 | 36 | ) 37 | } 38 | 39 | export default CookieView 40 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import CMB from './consent-manager-builder' 2 | import CM from './consent-manager' 3 | 4 | export { openDialog as openConsentManager } from './consent-manager/container' 5 | export { 6 | loadPreferences, 7 | savePreferences, 8 | onPreferencesSaved 9 | } from './consent-manager-builder/preferences' 10 | 11 | export const ConsentManagerBuilder = CMB 12 | export const ConsentManager = CM 13 | 14 | type Nav = Navigator & { 15 | msDoNotTrack?: Navigator['doNotTrack'] 16 | } 17 | 18 | export function doNotTrack(): boolean | null { 19 | 20 | if (typeof window !== 'undefined' && (window.navigator || navigator)) { 21 | const nav = navigator as Nav 22 | 23 | let doNotTrackValue = nav.doNotTrack || window.doNotTrack || nav.msDoNotTrack 24 | 25 | // Normalise Firefox < 32 26 | // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/doNotTrack 27 | if (doNotTrackValue === 'yes') { 28 | doNotTrackValue = '1' 29 | } else if (doNotTrackValue === 'no') { 30 | doNotTrackValue = '0' 31 | } 32 | 33 | if (doNotTrackValue === '1') { 34 | return true 35 | } 36 | if (doNotTrackValue === '0') { 37 | return false 38 | } 39 | } 40 | 41 | return null 42 | } 43 | -------------------------------------------------------------------------------- /src/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { doNotTrack } from '../' 2 | 3 | describe('doNotTrack', () => { 4 | beforeEach(() => { 5 | navigator = {} as Navigator 6 | window = {} as Window & typeof globalThis 7 | }) 8 | 9 | test('doNotTrack() supports standard API', () => { 10 | // @ts-ignore 11 | navigator.doNotTrack = '1' 12 | expect(doNotTrack()).toBe(true) 13 | 14 | // @ts-ignore 15 | navigator.doNotTrack = '0' 16 | expect(doNotTrack()).toBe(false) 17 | 18 | // @ts-ignore 19 | navigator.doNotTrack = 'unspecified' 20 | expect(doNotTrack()).toBe(null) 21 | }) 22 | 23 | test('doNotTrack() supports window', () => { 24 | // @ts-ignore 25 | navigator.doNotTrack = undefined 26 | 27 | // @ts-ignore 28 | window.doNotTrack = '1' 29 | expect(doNotTrack()).toBe(true) 30 | 31 | // @ts-ignore 32 | window.doNotTrack = '0' 33 | expect(doNotTrack()).toBe(false) 34 | 35 | // @ts-ignore 36 | window.doNotTrack = 'unspecified' 37 | expect(doNotTrack()).toBeNull() 38 | }) 39 | 40 | test('doNotTrack() support yes/no', () => { 41 | // @ts-ignore 42 | navigator.doNotTrack = 'yes' 43 | expect(doNotTrack()).toBe(true) 44 | 45 | // @ts-ignore 46 | navigator.doNotTrack = 'no' 47 | expect(doNotTrack()).toBe(false) 48 | }) 49 | 50 | test('doNotTrack() supports ms prefix', () => { 51 | // @ts-ignore 52 | navigator.doNotTrack = undefined 53 | // @ts-ignore 54 | window.doNotTrack = undefined 55 | 56 | // @ts-ignore 57 | navigator.msDoNotTrack = '1' 58 | expect(doNotTrack()).toBe(true) 59 | 60 | // @ts-ignore 61 | navigator.msDoNotTrack = '0' 62 | expect(doNotTrack()).toBe(false) 63 | 64 | // @ts-ignore 65 | navigator.msDoNotTrack = 'unspecified' 66 | expect(doNotTrack()).toBeNull() 67 | }) 68 | }) 69 | -------------------------------------------------------------------------------- /src/consent-manager-builder/fetch-destinations.ts: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-fetch' 2 | import flatten from 'lodash/flatten' 3 | import sortedUniqBy from 'lodash/sortedUniqBy' 4 | import sortBy from 'lodash/sortBy' 5 | import { Destination } from '../types' 6 | 7 | async function fetchDestinationForWriteKey( 8 | cdnHost: string, 9 | writeKey: string 10 | ): Promise { 11 | const res = await fetch(`https://${cdnHost}/v1/projects/${writeKey}/integrations`) 12 | 13 | if (!res.ok) { 14 | throw new Error( 15 | `Failed to fetch integrations for write key ${writeKey}: HTTP ${res.status} ${res.statusText}` 16 | ) 17 | } 18 | 19 | const destinations = await res.json() 20 | 21 | // Rename creationName to id to abstract the weird data model 22 | for (const destination of destinations) { 23 | // Because of the legacy Fullstory integration the creationName for this integration is the `name` 24 | if (destination.name === 'Fullstory') { 25 | destination.id = destination.name 26 | } else { 27 | destination.id = destination.creationName 28 | } 29 | delete destination.creationName 30 | } 31 | 32 | return destinations 33 | } 34 | 35 | export default async function fetchDestinations( 36 | cdnHost: string, 37 | writeKeys: string[] 38 | ): Promise { 39 | const destinationsRequests: Promise[] = [] 40 | for (const writeKey of writeKeys) { 41 | destinationsRequests.push(fetchDestinationForWriteKey(cdnHost, writeKey)) 42 | } 43 | 44 | let destinations = flatten(await Promise.all(destinationsRequests)) 45 | // Remove the dummy Repeater destination 46 | destinations = destinations.filter(d => d.id !== 'Repeater') 47 | destinations = sortBy(destinations, ['id']) 48 | destinations = sortedUniqBy(destinations, 'id') 49 | return destinations 50 | } 51 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 57 | -------------------------------------------------------------------------------- /src/consent-manager/cancel-dialog.tsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react' 2 | import Dialog from './dialog' 3 | import { DefaultButton, RedButton } from './buttons' 4 | import { PreferenceDialogTemplate } from '../types' 5 | 6 | interface Props { 7 | innerRef: (node: HTMLElement) => void 8 | onBack: () => void 9 | onConfirm: () => void 10 | title: React.ReactNode 11 | content: React.ReactNode 12 | preferencesDialogTemplate?: PreferenceDialogTemplate 13 | } 14 | 15 | export default class CancelDialog extends PureComponent { 16 | static displayName = 'CancelDialog' 17 | 18 | render() { 19 | const { innerRef, onBack, title, content, preferencesDialogTemplate } = this.props 20 | 21 | const buttons = ( 22 |
    23 | 24 | {preferencesDialogTemplate?.cancelDialogButtons!.backValue} 25 | 26 | 27 | {preferencesDialogTemplate?.cancelDialogButtons!.cancelValue} 28 | 29 |
    30 | ) 31 | 32 | return ( 33 | 40 | {content} 41 | 42 | ) 43 | } 44 | 45 | componentDidMount() { 46 | document.body.addEventListener('keydown', this.handleEsc, false) 47 | } 48 | 49 | componentWillUnmount() { 50 | document.body.removeEventListener('keydown', this.handleEsc, false) 51 | } 52 | 53 | handleSubmit = e => { 54 | const { onConfirm } = this.props 55 | 56 | e.preventDefault() 57 | onConfirm() 58 | } 59 | 60 | handleEsc = (e: KeyboardEvent) => { 61 | const { onConfirm } = this.props 62 | 63 | // Esc key 64 | if (e.keyCode === 27) { 65 | onConfirm() 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /stories/components/common-react.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const bannerContent = ( 4 | 5 | We use cookies (and other similar technologies) to collect data to improve your experience on 6 | our site. By using our website, you’re agreeing to the collection of data as described in our{' '} 7 | 12 | Website Data Collection Policy 13 | 14 | . 15 | 16 | ) 17 | export const bannerSubContent = 'You can manage your preferences here!' 18 | export const preferencesDialogTitle = 'Website Data Collection Preferences' 19 | export const preferencesDialogContent = ( 20 |
    21 |

    22 | Segment uses data collected by cookies and JavaScript libraries to improve your browsing 23 | experience, analyze site traffic, deliver personalized advertisements, and increase the 24 | overall performance of our site. 25 |

    26 |

    27 | By using our website, you’re agreeing to our{' '} 28 | 33 | Website Data Collection Policy 34 | 35 | . 36 |

    37 |

    38 | The table below outlines how we use this data by category. To opt out of a category of data 39 | collection, select “No” and save your preferences. 40 |

    41 |
    42 | ) 43 | export const cancelDialogTitle = 'Are you sure you want to cancel?' 44 | export const cancelDialogContent = ( 45 |
    46 | Your preferences have not been saved. By continuing to use our website, you’re agreeing to our{' '} 47 | 52 | Website Data Collection Policy 53 | 54 | . 55 |
    56 | ) 57 | -------------------------------------------------------------------------------- /stories/ImplyConsentOnInteraction.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import cookies from 'js-cookie' 3 | import { Pane, Heading, Button, Paragraph } from 'evergreen-ui' 4 | import { ConsentManager, openConsentManager } from '../src' 5 | import { 6 | bannerContent, 7 | bannerSubContent, 8 | preferencesDialogContent, 9 | preferencesDialogTitle, 10 | cancelDialogContent, 11 | cancelDialogTitle 12 | } from './components/common-react' 13 | 14 | export const ImplyConsentOnInteraction = () => { 15 | return ( 16 | 17 | 28 | 29 | 30 | Your website content 31 | 32 | Clicking anywhere on this page will cause the Consent Manager to imply consent. 33 | 34 | 35 | 36 |