mirror of
https://github.com/Mastermindzh/react-cookie-consent.git
synced 2025-01-20 10:31:03 +01:00
Switched to tsdx and Typescript
Added storybook with examples from readme
This commit is contained in:
parent
c6799ea446
commit
c544f19c41
8
.babelrc
8
.babelrc
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": ["@babel/preset-env"],
|
|
||||||
"plugins": [
|
|
||||||
"@babel/plugin-proposal-object-rest-spread",
|
|
||||||
"@babel/plugin-transform-react-jsx",
|
|
||||||
"@babel/plugin-transform-object-assign"
|
|
||||||
]
|
|
||||||
}
|
|
68
.eslintrc
Normal file
68
.eslintrc
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"jest": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"plugins": ["import"],
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"prettier/@typescript-eslint",
|
||||||
|
"plugin:prettier/recommended",
|
||||||
|
"eslint:recommended",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2020,
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"react/react-in-jsx-scope": "off",
|
||||||
|
"no-console": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"allow": ["debug", "error"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-eval": "error",
|
||||||
|
"import/first": "error",
|
||||||
|
"camelcase": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"ignoreImports": true,
|
||||||
|
"ignoreDestructuring": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"consistent-return": "warn",
|
||||||
|
"constructor-super": "error",
|
||||||
|
"curly": "error",
|
||||||
|
"eol-last": "warn",
|
||||||
|
"eqeqeq": ["error", "smart"],
|
||||||
|
"import/order": 1,
|
||||||
|
"new-parens": "error",
|
||||||
|
"no-debugger": "error",
|
||||||
|
"no-fallthrough": "off",
|
||||||
|
"max-len": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"code": 120
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-shadow": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"hoist": "all"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-trailing-spaces": "warn",
|
||||||
|
"no-underscore-dangle": "error",
|
||||||
|
"no-unsafe-finally": "error",
|
||||||
|
"no-var": "error",
|
||||||
|
"object-shorthand": "error",
|
||||||
|
"one-var": ["error", "never"],
|
||||||
|
"prefer-arrow/prefer-arrow-functions": "off",
|
||||||
|
"prefer-const": "error",
|
||||||
|
"radix": "off",
|
||||||
|
"space-in-parens": ["off", "never"]
|
||||||
|
}
|
||||||
|
}
|
32
.github/workflows/main.yml
vendored
Normal file
32
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
name: CI
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }}
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node: ['10.x', '12.x', '14.x']
|
||||||
|
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Use Node ${{ matrix.node }}
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node }}
|
||||||
|
|
||||||
|
- name: Install deps and build (with cache)
|
||||||
|
uses: bahmutov/npm-install@v1
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: yarn lint
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: yarn test --ci --coverage --maxWorkers=2
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: yarn build
|
12
.github/workflows/size.yml
vendored
Normal file
12
.github/workflows/size.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
name: size
|
||||||
|
on: [pull_request]
|
||||||
|
jobs:
|
||||||
|
size:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
CI_JOB_NUMBER: 1
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- uses: andresz1/size-limit-action@v1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,3 +1,6 @@
|
|||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
.cache
|
||||||
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
build/index.js.LICENSE.txt
|
storybook-static
|
||||||
example/*
|
|
||||||
|
6
.husky/pre-commit
Executable file
6
.husky/pre-commit
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npm test
|
||||||
|
npm run lint
|
||||||
|
npm run build
|
8
.storybook/main.js
Normal file
8
.storybook/main.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module.exports = {
|
||||||
|
stories: ['../stories/**/*.stories.@(ts|tsx|js|jsx)'],
|
||||||
|
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
|
||||||
|
// https://storybook.js.org/docs/react/configure/typescript#mainjs-configuration
|
||||||
|
typescript: {
|
||||||
|
check: true, // type-check stories during Storybook build
|
||||||
|
}
|
||||||
|
};
|
5
.storybook/preview.js
Normal file
5
.storybook/preview.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// https://storybook.js.org/docs/react/writing-stories/parameters#global-parameters
|
||||||
|
export const parameters = {
|
||||||
|
// https://storybook.js.org/docs/react/essentials/actions#automatically-matching-args
|
||||||
|
actions: { argTypesRegex: '^on.*' },
|
||||||
|
};
|
@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [[8.0.0]((https://github.com/Mastermindzh/react-cookie-consent/releases/tag/8.0.0)]
|
||||||
|
|
||||||
|
- Switched to tsdx and Typescript
|
||||||
|
- Added storybook with examples from readme
|
||||||
|
|
||||||
## [[7.6.0]](https://github.com/Mastermindzh/react-cookie-consent/releases/tag/7.6.0]
|
## [[7.6.0]](https://github.com/Mastermindzh/react-cookie-consent/releases/tag/7.6.0]
|
||||||
|
|
||||||
- Added `customButtonWrapperAttributes` prop which allows to add custom attributes to the button wrapper div
|
- Added `customButtonWrapperAttributes` prop which allows to add custom attributes to the button wrapper div
|
||||||
|
78
build/index.d.ts
vendored
78
build/index.d.ts
vendored
@ -1,78 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import Cookies from "js-cookie";
|
|
||||||
|
|
||||||
export interface CookieConsentProps {
|
|
||||||
location?: "top" | "bottom" | "none";
|
|
||||||
sameSite?: "strict" | "lax" | "none";
|
|
||||||
visible?: "hidden" | "show" | "byCookieValue";
|
|
||||||
cookieSecurity?: boolean;
|
|
||||||
style?: object;
|
|
||||||
buttonStyle?: object;
|
|
||||||
declineButtonStyle?: object;
|
|
||||||
contentStyle?: object;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
disableStyles?: boolean;
|
|
||||||
hideOnAccept?: boolean;
|
|
||||||
hideOnDecline?: boolean;
|
|
||||||
onAccept?: (acceptedByScrolling?: boolean) => void;
|
|
||||||
onDecline?: Function;
|
|
||||||
buttonText?: Function | React.ReactNode;
|
|
||||||
declineButtonText?: Function | React.ReactNode;
|
|
||||||
cookieName?: string;
|
|
||||||
cookieValue?: string | boolean | number;
|
|
||||||
declineCookieValue?: string | boolean | number;
|
|
||||||
setDeclineCookie?: boolean;
|
|
||||||
debug?: boolean;
|
|
||||||
expires?: number;
|
|
||||||
containerClasses?: string;
|
|
||||||
contentClasses?: string;
|
|
||||||
buttonWrapperClasses?: string;
|
|
||||||
buttonClasses?: string;
|
|
||||||
declineButtonClasses?: string;
|
|
||||||
buttonId?: string;
|
|
||||||
declineButtonId?: string;
|
|
||||||
extraCookieOptions?: object;
|
|
||||||
disableButtonStyles?: boolean;
|
|
||||||
enableDeclineButton?: boolean;
|
|
||||||
flipButtons?: boolean;
|
|
||||||
ButtonComponent?: React.ElementType;
|
|
||||||
overlay?: boolean;
|
|
||||||
overlayClasses?: string;
|
|
||||||
overlayStyle?: object;
|
|
||||||
onOverlayClick?: () => void;
|
|
||||||
acceptOnOverlayClick?: boolean;
|
|
||||||
ariaAcceptLabel?: string;
|
|
||||||
ariaDeclineLabel?: string;
|
|
||||||
acceptOnScroll?: boolean;
|
|
||||||
acceptOnScrollPercentage?: number;
|
|
||||||
customContentAttributes?: object;
|
|
||||||
customContainerAttributes?: object;
|
|
||||||
customButtonProps?: object;
|
|
||||||
customDeclineButtonProps?: object;
|
|
||||||
customButtonWrapperAttributes?: React.HTMLAttributes<HTMLDivElement>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class CookieConsent extends React.Component<CookieConsentProps, {}> {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of the consent cookie
|
|
||||||
* Retrieves the regular value first and if not found the legacy one according
|
|
||||||
* to: https://web.dev/samesite-cookie-recipes/#handling-incompatible-clients
|
|
||||||
* @param {*} name optional name of the cookie
|
|
||||||
*/
|
|
||||||
export function getCookieConsentValue(name?: string): string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the consent cookie
|
|
||||||
* Remove the cookie on browser in order to allow user to change their consent
|
|
||||||
* @param {*} name optional name of the cookie
|
|
||||||
*/
|
|
||||||
export function resetCookieConsentValue(name?: string): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the legacy cookie name by the regular cookie name
|
|
||||||
* @param {string} name of cookie to get
|
|
||||||
*/
|
|
||||||
export function getLegacyCookieName(name: string): string;
|
|
||||||
|
|
||||||
export { Cookies };
|
|
File diff suppressed because one or more lines are too long
1
example
1
example
@ -1 +0,0 @@
|
|||||||
Subproject commit 8f1152c04d7bc70ba098783db2b752379ed7ec0f
|
|
44837
package-lock.json
generated
44837
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
132
package.json
132
package.json
@ -1,74 +1,98 @@
|
|||||||
{
|
{
|
||||||
"name": "react-cookie-consent",
|
"name": "react-cookie-consent",
|
||||||
"author": {
|
|
||||||
"name": "Rick van Lieshout",
|
|
||||||
"email": "info@rickvanlieshout.com"
|
|
||||||
},
|
|
||||||
"version": "7.6.0",
|
"version": "7.6.0",
|
||||||
"description": "A small, simple and customizable cookie consent bar for use in React applications.",
|
"description": "A small, simple and customizable cookie consent bar for use in React applications.",
|
||||||
"main": "build/index.js",
|
|
||||||
"types": "build/index.d.ts",
|
|
||||||
"dependencies": {
|
|
||||||
"js-cookie": "^2.2.1",
|
|
||||||
"prop-types": "^15.7.2"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"build": "webpack",
|
|
||||||
"prettier": "prettier 'src/**/*.{js*,ts*,htm*,md,scss}' --write",
|
|
||||||
"patch": "npm --no-git-tag-version version patch",
|
|
||||||
"minor": "npm --no-git-tag-version version minor",
|
|
||||||
"major": "npm --no-git-tag-version version major",
|
|
||||||
"preversion": "grep \"\\[$npm_package_version\\]\" CHANGELOG.md > /dev/null || ( echo 'You need to add an entry in CHANGELOG.md for this version.' && false )",
|
|
||||||
"release": "npm run build && git add -A && git tag $npm_package_version && git commit -m \"release $npm_package_version\" && git push && git push --tags && npm publish",
|
|
||||||
"release-patch": "npm run patch && npm run release",
|
|
||||||
"release-minor": "npm run minor && npm run release",
|
|
||||||
"release-major": "npm run major && npm run release"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/Mastermindzh/react-cookie-consent.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
"cookie",
|
"cookie",
|
||||||
"consent",
|
"consent",
|
||||||
"cookiebar"
|
"cookiebar"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"homepage": "https://github.com/Mastermindzh/react-cookie-consent#readme",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/Mastermindzh/react-cookie-consent/issues"
|
"url": "https://github.com/Mastermindzh/react-cookie-consent/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/Mastermindzh/react-cookie-consent#readme",
|
"repository": {
|
||||||
"devDependencies": {
|
"type": "git",
|
||||||
"@babel/cli": "^7.17.6",
|
"url": "git+https://github.com/Mastermindzh/react-cookie-consent.git"
|
||||||
"@babel/core": "^7.17.8",
|
|
||||||
"@babel/plugin-proposal-object-rest-spread": "^7.17.3",
|
|
||||||
"@babel/plugin-transform-object-assign": "^7.16.7",
|
|
||||||
"@babel/plugin-transform-react-jsx": "^7.17.3",
|
|
||||||
"@babel/preset-env": "^7.16.11",
|
|
||||||
"@mastermindzh/prettier-config": "^1.0.0",
|
|
||||||
"@types/js-cookie": "^2.2.6",
|
|
||||||
"babel-loader": "^8.2.4",
|
|
||||||
"copy-webpack-plugin": "^6.2.1",
|
|
||||||
"husky": "^4.3.0",
|
|
||||||
"lint-staged": "^10.5.1",
|
|
||||||
"prettier": "^2.6.2",
|
|
||||||
"react": "^18.0.0",
|
|
||||||
"webpack": "^5.71.0",
|
|
||||||
"webpack-cli": "^4.9.2"
|
|
||||||
},
|
},
|
||||||
"prettier": "@mastermindzh/prettier-config",
|
"license": "MIT",
|
||||||
"husky": {
|
"author": {
|
||||||
"hooks": {
|
"name": "Rick van Lieshout",
|
||||||
"pre-commit": "lint-staged --allow-empty"
|
"email": "info@rickvanlieshout.com"
|
||||||
}
|
},
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"module": "dist/react-cookie-consent.esm.js",
|
||||||
|
"typings": "dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"analyze": "size-limit --why",
|
||||||
|
"build": "tsdx build",
|
||||||
|
"build-storybook": "build-storybook",
|
||||||
|
"lint": "tsdx lint",
|
||||||
|
"major": "npm --no-git-tag-version version major",
|
||||||
|
"minor": "npm --no-git-tag-version version minor",
|
||||||
|
"organize": "npx format-package -w && npx sort-package-json",
|
||||||
|
"patch": "npm --no-git-tag-version version patch",
|
||||||
|
"prepare": "tsdx build",
|
||||||
|
"prettier": "prettier 'src/**/*.{js*,ts*,htm*,md,scss}' --write",
|
||||||
|
"postinstall": "husky install",
|
||||||
|
"publish": "npx np",
|
||||||
|
"size": "size-limit",
|
||||||
|
"start": "tsdx watch",
|
||||||
|
"storybook": "start-storybook -p 6006",
|
||||||
|
"test": "tsdx test --passWithNoTests",
|
||||||
|
"preversion": "grep \"\\[$npm_package_version\\]\" CHANGELOG.md > /dev/null || ( echo 'You need to add an entry in CHANGELOG.md for this version.' && false )"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js*,ts*,htm*,md,scss}": [
|
"*.{js*,ts*,htm*,md,scss}": [
|
||||||
"prettier --write"
|
"prettier --write"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"prettier": "@mastermindzh/prettier-config",
|
||||||
|
"dependencies": {
|
||||||
|
"js-cookie": "^2.2.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@emotion/react": "^11.9.3",
|
||||||
|
"@emotion/styled": "^11.9.3",
|
||||||
|
"@mastermindzh/prettier-config": "^1.0.0",
|
||||||
|
"@mui/material": "^5.9.2",
|
||||||
|
"@size-limit/preset-small-lib": "^7.0.8",
|
||||||
|
"@storybook/addon-essentials": "^6.5.9",
|
||||||
|
"@storybook/addons": "^6.5.9",
|
||||||
|
"@storybook/react": "^6.5.9",
|
||||||
|
"@types/js-cookie": "^2.2.6",
|
||||||
|
"@types/react": "^18.0.15",
|
||||||
|
"@types/react-dom": "^18.0.6",
|
||||||
|
"babel-loader": "^8.2.5",
|
||||||
|
"husky": "^8.0.1",
|
||||||
|
"prettier": "^2.6.2",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-is": "^18.2.0",
|
||||||
|
"size-limit": "^7.0.8",
|
||||||
|
"tsdx": "^0.14.1",
|
||||||
|
"tslib": "^2.4.0",
|
||||||
|
"typescript": "^3.9.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"size-limit": [
|
||||||
|
{
|
||||||
|
"path": "dist/react-cookie-consent.cjs.production.min.js",
|
||||||
|
"limit": "10 KB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "dist/react-cookie-consent.esm.js",
|
||||||
|
"limit": "10 KB"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
115
src/CookieConsent.props.tsx
Normal file
115
src/CookieConsent.props.tsx
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import React, { FunctionComponent, ReactNode } from "react";
|
||||||
|
import {
|
||||||
|
defaultCookieConsentName,
|
||||||
|
POSITION_OPTIONS,
|
||||||
|
SAME_SITE_OPTIONS,
|
||||||
|
VISIBILITY_OPTIONS,
|
||||||
|
} from "./models/constants";
|
||||||
|
|
||||||
|
export interface CookieConsentProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
style: React.CSSProperties;
|
||||||
|
buttonStyle: React.CSSProperties;
|
||||||
|
declineButtonStyle: React.CSSProperties;
|
||||||
|
contentStyle: React.CSSProperties;
|
||||||
|
disableStyles: boolean;
|
||||||
|
hideOnAccept: boolean;
|
||||||
|
hideOnDecline: boolean;
|
||||||
|
onAccept: (acceptedByScrolling: boolean) => void;
|
||||||
|
onDecline: () => void;
|
||||||
|
buttonText: string | ReactNode | Function;
|
||||||
|
declineButtonText: string | ReactNode | Function;
|
||||||
|
cookieName: string;
|
||||||
|
cookieValue: string | object;
|
||||||
|
declineCookieValue: string | object;
|
||||||
|
setDeclineCookie: boolean;
|
||||||
|
debug: boolean;
|
||||||
|
expires: number;
|
||||||
|
containerClasses: string;
|
||||||
|
contentClasses: string;
|
||||||
|
buttonClasses: string;
|
||||||
|
buttonWrapperClasses: string;
|
||||||
|
declineButtonClasses: string;
|
||||||
|
buttonId: string;
|
||||||
|
declineButtonId: string;
|
||||||
|
overlayClasses: string;
|
||||||
|
ariaAcceptLabel: string;
|
||||||
|
ariaDeclineLabel: string;
|
||||||
|
disableButtonStyles: boolean;
|
||||||
|
enableDeclineButton: boolean;
|
||||||
|
flipButtons: boolean;
|
||||||
|
cookieSecurity?: boolean;
|
||||||
|
overlay: boolean;
|
||||||
|
acceptOnOverlayClick: boolean;
|
||||||
|
acceptOnScroll: boolean;
|
||||||
|
acceptOnScrollPercentage: number;
|
||||||
|
ButtonComponent: any;
|
||||||
|
extraCookieOptions: Object;
|
||||||
|
overlayStyle: Object;
|
||||||
|
customContentAttributes: Object;
|
||||||
|
customContainerAttributes: Object;
|
||||||
|
customButtonProps: Object;
|
||||||
|
customDeclineButtonProps: Object;
|
||||||
|
customButtonWrapperAttributes: Object;
|
||||||
|
onOverlayClick: () => void;
|
||||||
|
// these should be enums
|
||||||
|
location: string;
|
||||||
|
visible: string;
|
||||||
|
sameSite: "strict" | "Strict" | "lax" | "Lax" | "none" | "None" | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DefaultButtonComponent: FunctionComponent<{ children: ReactNode; [x: string]: any }> = ({
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
return <button {...props}>{children}</button>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const defaultCookieConsentProps: CookieConsentProps = {
|
||||||
|
disableStyles: false,
|
||||||
|
hideOnAccept: true,
|
||||||
|
hideOnDecline: true,
|
||||||
|
location: POSITION_OPTIONS.BOTTOM,
|
||||||
|
visible: VISIBILITY_OPTIONS.BY_COOKIE_VALUE,
|
||||||
|
onAccept: (_acceptedByScrolling) => {},
|
||||||
|
onDecline: () => {},
|
||||||
|
cookieName: defaultCookieConsentName,
|
||||||
|
cookieValue: "true",
|
||||||
|
declineCookieValue: "false",
|
||||||
|
setDeclineCookie: true,
|
||||||
|
buttonText: "I understand",
|
||||||
|
declineButtonText: "I decline",
|
||||||
|
debug: false,
|
||||||
|
expires: 365,
|
||||||
|
containerClasses: "CookieConsent",
|
||||||
|
contentClasses: "",
|
||||||
|
buttonClasses: "",
|
||||||
|
buttonWrapperClasses: "",
|
||||||
|
declineButtonClasses: "",
|
||||||
|
buttonId: "rcc-confirm-button",
|
||||||
|
declineButtonId: "rcc-decline-button",
|
||||||
|
extraCookieOptions: {},
|
||||||
|
disableButtonStyles: false,
|
||||||
|
enableDeclineButton: false,
|
||||||
|
flipButtons: false,
|
||||||
|
sameSite: SAME_SITE_OPTIONS.LAX,
|
||||||
|
ButtonComponent: DefaultButtonComponent,
|
||||||
|
overlay: false,
|
||||||
|
overlayClasses: "",
|
||||||
|
onOverlayClick: () => {},
|
||||||
|
acceptOnOverlayClick: false,
|
||||||
|
ariaAcceptLabel: "Accept cookies",
|
||||||
|
ariaDeclineLabel: "Decline cookies",
|
||||||
|
acceptOnScroll: false,
|
||||||
|
acceptOnScrollPercentage: 25,
|
||||||
|
customContentAttributes: {},
|
||||||
|
customContainerAttributes: {},
|
||||||
|
customButtonProps: {},
|
||||||
|
customDeclineButtonProps: {},
|
||||||
|
customButtonWrapperAttributes: {},
|
||||||
|
style: {},
|
||||||
|
buttonStyle: {},
|
||||||
|
declineButtonStyle: {},
|
||||||
|
contentStyle: {},
|
||||||
|
overlayStyle: {},
|
||||||
|
};
|
59
src/CookieConsent.state.ts
Normal file
59
src/CookieConsent.state.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
export interface CookieConsentState {
|
||||||
|
visible: boolean;
|
||||||
|
style: React.CSSProperties;
|
||||||
|
buttonStyle: React.CSSProperties;
|
||||||
|
declineButtonStyle: React.CSSProperties;
|
||||||
|
contentStyle: React.CSSProperties;
|
||||||
|
overlayStyle: React.CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultState: CookieConsentState = {
|
||||||
|
visible: false,
|
||||||
|
style: {
|
||||||
|
alignItems: "baseline",
|
||||||
|
background: "#353535",
|
||||||
|
color: "white",
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
left: "0",
|
||||||
|
position: "fixed",
|
||||||
|
width: "100%",
|
||||||
|
zIndex: "999",
|
||||||
|
},
|
||||||
|
buttonStyle: {
|
||||||
|
background: "#ffd42d",
|
||||||
|
border: "0",
|
||||||
|
borderRadius: "0px",
|
||||||
|
boxShadow: "none",
|
||||||
|
color: "black",
|
||||||
|
cursor: "pointer",
|
||||||
|
flex: "0 0 auto",
|
||||||
|
padding: "5px 10px",
|
||||||
|
margin: "15px",
|
||||||
|
},
|
||||||
|
declineButtonStyle: {
|
||||||
|
background: "#c12a2a",
|
||||||
|
border: "0",
|
||||||
|
borderRadius: "0px",
|
||||||
|
boxShadow: "none",
|
||||||
|
color: "#e5e5e5",
|
||||||
|
cursor: "pointer",
|
||||||
|
flex: "0 0 auto",
|
||||||
|
padding: "5px 10px",
|
||||||
|
margin: "15px",
|
||||||
|
},
|
||||||
|
contentStyle: {
|
||||||
|
flex: "1 0 300px",
|
||||||
|
margin: "15px",
|
||||||
|
},
|
||||||
|
overlayStyle: {
|
||||||
|
position: "fixed",
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
zIndex: "999",
|
||||||
|
backgroundColor: "rgba(0,0,0,0.3)",
|
||||||
|
},
|
||||||
|
};
|
@ -1,129 +1,15 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
|
import React, { Component, CSSProperties } from "react";
|
||||||
|
import { ConditionalWrapper } from "./components/ConditionalWrapper";
|
||||||
|
import { CookieConsentProps, defaultCookieConsentProps } from "./CookieConsent.props";
|
||||||
|
import { CookieConsentState, defaultState } from "./CookieConsent.state";
|
||||||
|
import { POSITION_OPTIONS, SAME_SITE_OPTIONS, VISIBILITY_OPTIONS } from "./models/constants";
|
||||||
|
import { getCookieConsentValue, getLegacyCookieName } from "./utilities";
|
||||||
|
|
||||||
export const OPTIONS = {
|
export class CookieConsent extends Component<CookieConsentProps, CookieConsentState> {
|
||||||
TOP: "top",
|
public static defaultProps = defaultCookieConsentProps;
|
||||||
BOTTOM: "bottom",
|
|
||||||
NONE: "none",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SAME_SITE_OPTIONS = {
|
state: CookieConsentState = defaultState;
|
||||||
STRICT: "strict",
|
|
||||||
LAX: "lax",
|
|
||||||
NONE: "none",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const VISIBLE_OPTIONS = {
|
|
||||||
HIDDEN: "hidden",
|
|
||||||
SHOW: "show",
|
|
||||||
BY_COOKIE_VALUE: "byCookieValue",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of the consent cookie
|
|
||||||
* Retrieves the regular value first and if not found the legacy one according
|
|
||||||
* to: https://web.dev/samesite-cookie-recipes/#handling-incompatible-clients
|
|
||||||
* @param {*} name optional name of the cookie
|
|
||||||
*/
|
|
||||||
export const getCookieConsentValue = (name = defaultCookieConsentName) => {
|
|
||||||
const cookieValue = Cookies.get(name);
|
|
||||||
|
|
||||||
// if the cookieValue is undefined check for the legacy cookie
|
|
||||||
if (cookieValue === undefined) {
|
|
||||||
return Cookies.get(getLegacyCookieName(name));
|
|
||||||
}
|
|
||||||
return cookieValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the consent cookie
|
|
||||||
* Remove the cookie on browser in order to allow user to change their consent
|
|
||||||
* @param {*} name optional name of the cookie
|
|
||||||
*/
|
|
||||||
export const resetCookieConsentValue = (name = defaultCookieConsentName) => {
|
|
||||||
Cookies.remove(name);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the legacy cookie name by the regular cookie name
|
|
||||||
* @param {string} name of cookie to get
|
|
||||||
*/
|
|
||||||
const getLegacyCookieName = (name) => {
|
|
||||||
return `${name}-legacy`;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default name of the cookie which is set by CookieConsent
|
|
||||||
*/
|
|
||||||
const defaultCookieConsentName = "CookieConsent";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A function to wrap elements with a "wrapper" on a condition
|
|
||||||
* @param {object} wrappingOptions
|
|
||||||
* condition == boolean condition, when to wrap
|
|
||||||
* wrapper == style to wrap. e.g <div>{children}</div>
|
|
||||||
* children == react passes whatever is between tags as children. Don't supply this yourself.
|
|
||||||
*/
|
|
||||||
const ConditionalWrapper = ({ condition, wrapper, children }) => {
|
|
||||||
return condition ? wrapper(children) : children;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CookieConsent extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
visible: false,
|
|
||||||
style: {
|
|
||||||
alignItems: "baseline",
|
|
||||||
background: "#353535",
|
|
||||||
color: "white",
|
|
||||||
display: "flex",
|
|
||||||
flexWrap: "wrap",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
left: "0",
|
|
||||||
position: "fixed",
|
|
||||||
width: "100%",
|
|
||||||
zIndex: "999",
|
|
||||||
},
|
|
||||||
buttonStyle: {
|
|
||||||
background: "#ffd42d",
|
|
||||||
border: "0",
|
|
||||||
borderRadius: "0px",
|
|
||||||
boxShadow: "none",
|
|
||||||
color: "black",
|
|
||||||
cursor: "pointer",
|
|
||||||
flex: "0 0 auto",
|
|
||||||
padding: "5px 10px",
|
|
||||||
margin: "15px",
|
|
||||||
},
|
|
||||||
declineButtonStyle: {
|
|
||||||
background: "#c12a2a",
|
|
||||||
border: "0",
|
|
||||||
borderRadius: "0px",
|
|
||||||
boxShadow: "none",
|
|
||||||
color: "#e5e5e5",
|
|
||||||
cursor: "pointer",
|
|
||||||
flex: "0 0 auto",
|
|
||||||
padding: "5px 10px",
|
|
||||||
margin: "15px",
|
|
||||||
},
|
|
||||||
contentStyle: {
|
|
||||||
flex: "1 0 300px",
|
|
||||||
margin: "15px",
|
|
||||||
},
|
|
||||||
overlayStyle: {
|
|
||||||
position: "fixed",
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
zIndex: "999",
|
|
||||||
backgroundColor: "rgba(0,0,0,0.3)",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { debug } = this.props;
|
const { debug } = this.props;
|
||||||
@ -193,17 +79,18 @@ class CookieConsent extends Component {
|
|||||||
* Sets two cookies to handle incompatible browsers, more details:
|
* Sets two cookies to handle incompatible browsers, more details:
|
||||||
* https://web.dev/samesite-cookie-recipes/#handling-incompatible-clients
|
* https://web.dev/samesite-cookie-recipes/#handling-incompatible-clients
|
||||||
*/
|
*/
|
||||||
setCookie(cookieName, cookieValue) {
|
setCookie(cookieName: string, cookieValue: string | object) {
|
||||||
const { extraCookieOptions, expires, sameSite } = this.props;
|
const { extraCookieOptions, expires, sameSite } = this.props;
|
||||||
let { cookieSecurity } = this.props;
|
let { cookieSecurity } = this.props;
|
||||||
|
|
||||||
if (cookieSecurity === undefined) {
|
if (cookieSecurity === undefined) {
|
||||||
cookieSecurity = location ? location.protocol === "https:" : true;
|
cookieSecurity = window.location ? window.location.protocol === "https:" : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cookieOptions = { expires, ...extraCookieOptions, sameSite, secure: cookieSecurity };
|
const cookieOptions = { expires, ...extraCookieOptions, sameSite, secure: cookieSecurity };
|
||||||
|
|
||||||
// Fallback for older browsers where can not set SameSite=None, SEE: https://web.dev/samesite-cookie-recipes/#handling-incompatible-clients
|
// Fallback for older browsers where can not set SameSite=None,
|
||||||
|
// SEE: https://web.dev/samesite-cookie-recipes/#handling-incompatible-clients
|
||||||
if (sameSite === SAME_SITE_OPTIONS.NONE) {
|
if (sameSite === SAME_SITE_OPTIONS.NONE) {
|
||||||
Cookies.set(getLegacyCookieName(cookieName), cookieValue, cookieOptions);
|
Cookies.set(getLegacyCookieName(cookieName), cookieValue, cookieOptions);
|
||||||
}
|
}
|
||||||
@ -229,10 +116,10 @@ class CookieConsent extends Component {
|
|||||||
const { acceptOnScrollPercentage } = this.props;
|
const { acceptOnScrollPercentage } = this.props;
|
||||||
|
|
||||||
// (top / height) - height * 100
|
// (top / height) - height * 100
|
||||||
const rootNode = document.documentElement,
|
const rootNode = document.documentElement;
|
||||||
body = document.body,
|
const body = document.body;
|
||||||
top = "scrollTop",
|
const top = "scrollTop";
|
||||||
height = "scrollHeight";
|
const height = "scrollHeight";
|
||||||
|
|
||||||
const percentage =
|
const percentage =
|
||||||
((rootNode[top] || body[top]) /
|
((rootNode[top] || body[top]) /
|
||||||
@ -254,9 +141,9 @@ class CookieConsent extends Component {
|
|||||||
render() {
|
render() {
|
||||||
// If the bar shouldn't be visible don't render anything.
|
// If the bar shouldn't be visible don't render anything.
|
||||||
switch (this.props.visible) {
|
switch (this.props.visible) {
|
||||||
case VISIBLE_OPTIONS.HIDDEN:
|
case VISIBILITY_OPTIONS.HIDDEN:
|
||||||
return null;
|
return null;
|
||||||
case VISIBLE_OPTIONS.BY_COOKIE_VALUE:
|
case VISIBILITY_OPTIONS.BY_COOKIE_VALUE:
|
||||||
if (!this.state.visible) {
|
if (!this.state.visible) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -297,11 +184,11 @@ class CookieConsent extends Component {
|
|||||||
customButtonWrapperAttributes,
|
customButtonWrapperAttributes,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let myStyle = {};
|
let myStyle: CSSProperties = {};
|
||||||
let myButtonStyle = {};
|
let myButtonStyle: CSSProperties = {};
|
||||||
let myDeclineButtonStyle = {};
|
let myDeclineButtonStyle: CSSProperties = {};
|
||||||
let myContentStyle = {};
|
let myContentStyle: CSSProperties = {};
|
||||||
let myOverlayStyle = {};
|
let myOverlayStyle: CSSProperties = {};
|
||||||
|
|
||||||
if (disableStyles) {
|
if (disableStyles) {
|
||||||
// if styles are disabled use the provided styles (or none)
|
// if styles are disabled use the provided styles (or none)
|
||||||
@ -331,11 +218,11 @@ class CookieConsent extends Component {
|
|||||||
|
|
||||||
// syntactic sugar to enable user to easily select top / bottom
|
// syntactic sugar to enable user to easily select top / bottom
|
||||||
switch (location) {
|
switch (location) {
|
||||||
case OPTIONS.TOP:
|
case POSITION_OPTIONS.TOP:
|
||||||
myStyle.top = "0";
|
myStyle.top = "0";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPTIONS.BOTTOM:
|
case POSITION_OPTIONS.BOTTOM:
|
||||||
myStyle.bottom = "0";
|
myStyle.bottom = "0";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -411,100 +298,4 @@ class CookieConsent extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CookieConsent.propTypes = {
|
|
||||||
location: PropTypes.oneOf(Object.keys(OPTIONS).map((key) => OPTIONS[key])),
|
|
||||||
visible: PropTypes.oneOf(Object.keys(VISIBLE_OPTIONS).map((key) => VISIBLE_OPTIONS[key])),
|
|
||||||
sameSite: PropTypes.oneOf(Object.keys(SAME_SITE_OPTIONS).map((key) => SAME_SITE_OPTIONS[key])),
|
|
||||||
style: PropTypes.object,
|
|
||||||
buttonStyle: PropTypes.object,
|
|
||||||
declineButtonStyle: PropTypes.object,
|
|
||||||
contentStyle: PropTypes.object,
|
|
||||||
children: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
|
||||||
disableStyles: PropTypes.bool,
|
|
||||||
hideOnAccept: PropTypes.bool,
|
|
||||||
hideOnDecline: PropTypes.bool,
|
|
||||||
onAccept: PropTypes.func,
|
|
||||||
onDecline: PropTypes.func,
|
|
||||||
buttonText: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.element]),
|
|
||||||
declineButtonText: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.element]),
|
|
||||||
cookieName: PropTypes.string,
|
|
||||||
cookieValue: PropTypes.oneOfType([PropTypes.string, PropTypes.bool, PropTypes.number]),
|
|
||||||
declineCookieValue: PropTypes.oneOfType([PropTypes.string, PropTypes.bool, PropTypes.number]),
|
|
||||||
setDeclineCookie: PropTypes.bool,
|
|
||||||
debug: PropTypes.bool,
|
|
||||||
expires: PropTypes.number,
|
|
||||||
containerClasses: PropTypes.string,
|
|
||||||
contentClasses: PropTypes.string,
|
|
||||||
buttonClasses: PropTypes.string,
|
|
||||||
buttonWrapperClasses: PropTypes.string,
|
|
||||||
declineButtonClasses: PropTypes.string,
|
|
||||||
buttonId: PropTypes.string,
|
|
||||||
declineButtonId: PropTypes.string,
|
|
||||||
extraCookieOptions: PropTypes.object,
|
|
||||||
disableButtonStyles: PropTypes.bool,
|
|
||||||
enableDeclineButton: PropTypes.bool,
|
|
||||||
flipButtons: PropTypes.bool,
|
|
||||||
ButtonComponent: PropTypes.elementType,
|
|
||||||
cookieSecurity: PropTypes.bool,
|
|
||||||
overlay: PropTypes.bool,
|
|
||||||
overlayClasses: PropTypes.string,
|
|
||||||
overlayStyle: PropTypes.object,
|
|
||||||
onOverlayClick: PropTypes.func,
|
|
||||||
acceptOnOverlayClick: PropTypes.bool,
|
|
||||||
ariaAcceptLabel: PropTypes.string,
|
|
||||||
ariaDeclineLabel: PropTypes.string,
|
|
||||||
acceptOnScroll: PropTypes.bool,
|
|
||||||
acceptOnScrollPercentage: PropTypes.number,
|
|
||||||
customContentAttributes: PropTypes.object,
|
|
||||||
customContainerAttributes: PropTypes.object,
|
|
||||||
customButtonProps: PropTypes.object,
|
|
||||||
customDeclineButtonProps: PropTypes.object,
|
|
||||||
customButtonWrapperAttributes: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
CookieConsent.defaultProps = {
|
|
||||||
disableStyles: false,
|
|
||||||
hideOnAccept: true,
|
|
||||||
hideOnDecline: true,
|
|
||||||
location: OPTIONS.BOTTOM,
|
|
||||||
visible: VISIBLE_OPTIONS.BY_COOKIE_VALUE,
|
|
||||||
onAccept: () => {},
|
|
||||||
onDecline: () => {},
|
|
||||||
cookieName: defaultCookieConsentName,
|
|
||||||
cookieValue: true,
|
|
||||||
declineCookieValue: false,
|
|
||||||
setDeclineCookie: true,
|
|
||||||
buttonText: "I understand",
|
|
||||||
declineButtonText: "I decline",
|
|
||||||
debug: false,
|
|
||||||
expires: 365,
|
|
||||||
containerClasses: "CookieConsent",
|
|
||||||
contentClasses: "",
|
|
||||||
buttonClasses: "",
|
|
||||||
buttonWrapperClasses: "",
|
|
||||||
declineButtonClasses: "",
|
|
||||||
buttonId: "rcc-confirm-button",
|
|
||||||
declineButtonId: "rcc-decline-button",
|
|
||||||
extraCookieOptions: {},
|
|
||||||
disableButtonStyles: false,
|
|
||||||
enableDeclineButton: false,
|
|
||||||
flipButtons: false,
|
|
||||||
sameSite: SAME_SITE_OPTIONS.LAX,
|
|
||||||
ButtonComponent: ({ children, ...props }) => <button {...props}>{children}</button>,
|
|
||||||
overlay: false,
|
|
||||||
overlayClasses: "",
|
|
||||||
onOverlayClick: () => {},
|
|
||||||
acceptOnOverlayClick: false,
|
|
||||||
ariaAcceptLabel: "Accept cookies",
|
|
||||||
ariaDeclineLabel: "Decline cookies",
|
|
||||||
acceptOnScroll: false,
|
|
||||||
acceptOnScrollPercentage: 25,
|
|
||||||
customContentAttributes: {},
|
|
||||||
customContainerAttributes: {},
|
|
||||||
customButtonProps: {},
|
|
||||||
customDeclineButtonProps: {},
|
|
||||||
customButtonWrapperAttributes: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CookieConsent;
|
export default CookieConsent;
|
||||||
export { Cookies };
|
|
18
src/components/ConditionalWrapper.tsx
Normal file
18
src/components/ConditionalWrapper.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { FunctionComponent, ReactNode } from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
condition: boolean;
|
||||||
|
wrapper: (_: ReactNode) => any;
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function to wrap elements with a "wrapper" on a condition
|
||||||
|
* @param {object} wrappingOptions
|
||||||
|
* condition == boolean condition, when to wrap
|
||||||
|
* wrapper == style to wrap. e.g <div>{children}</div>
|
||||||
|
* children == react passes whatever is between tags as children. Don't supply this yourself.
|
||||||
|
*/
|
||||||
|
export const ConditionalWrapper: FunctionComponent<Props> = ({ condition, wrapper, children }) => {
|
||||||
|
return condition ? wrapper(children) : children;
|
||||||
|
};
|
78
src/index.d.ts
vendored
78
src/index.d.ts
vendored
@ -1,78 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import Cookies from "js-cookie";
|
|
||||||
|
|
||||||
export interface CookieConsentProps {
|
|
||||||
location?: "top" | "bottom" | "none";
|
|
||||||
sameSite?: "strict" | "lax" | "none";
|
|
||||||
visible?: "hidden" | "show" | "byCookieValue";
|
|
||||||
cookieSecurity?: boolean;
|
|
||||||
style?: object;
|
|
||||||
buttonStyle?: object;
|
|
||||||
declineButtonStyle?: object;
|
|
||||||
contentStyle?: object;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
disableStyles?: boolean;
|
|
||||||
hideOnAccept?: boolean;
|
|
||||||
hideOnDecline?: boolean;
|
|
||||||
onAccept?: (acceptedByScrolling?: boolean) => void;
|
|
||||||
onDecline?: Function;
|
|
||||||
buttonText?: Function | React.ReactNode;
|
|
||||||
declineButtonText?: Function | React.ReactNode;
|
|
||||||
cookieName?: string;
|
|
||||||
cookieValue?: string | boolean | number;
|
|
||||||
declineCookieValue?: string | boolean | number;
|
|
||||||
setDeclineCookie?: boolean;
|
|
||||||
debug?: boolean;
|
|
||||||
expires?: number;
|
|
||||||
containerClasses?: string;
|
|
||||||
contentClasses?: string;
|
|
||||||
buttonWrapperClasses?: string;
|
|
||||||
buttonClasses?: string;
|
|
||||||
declineButtonClasses?: string;
|
|
||||||
buttonId?: string;
|
|
||||||
declineButtonId?: string;
|
|
||||||
extraCookieOptions?: object;
|
|
||||||
disableButtonStyles?: boolean;
|
|
||||||
enableDeclineButton?: boolean;
|
|
||||||
flipButtons?: boolean;
|
|
||||||
ButtonComponent?: React.ElementType;
|
|
||||||
overlay?: boolean;
|
|
||||||
overlayClasses?: string;
|
|
||||||
overlayStyle?: object;
|
|
||||||
onOverlayClick?: () => void;
|
|
||||||
acceptOnOverlayClick?: boolean;
|
|
||||||
ariaAcceptLabel?: string;
|
|
||||||
ariaDeclineLabel?: string;
|
|
||||||
acceptOnScroll?: boolean;
|
|
||||||
acceptOnScrollPercentage?: number;
|
|
||||||
customContentAttributes?: object;
|
|
||||||
customContainerAttributes?: object;
|
|
||||||
customButtonProps?: object;
|
|
||||||
customDeclineButtonProps?: object;
|
|
||||||
customButtonWrapperAttributes?: React.HTMLAttributes<HTMLDivElement>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class CookieConsent extends React.Component<CookieConsentProps, {}> {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of the consent cookie
|
|
||||||
* Retrieves the regular value first and if not found the legacy one according
|
|
||||||
* to: https://web.dev/samesite-cookie-recipes/#handling-incompatible-clients
|
|
||||||
* @param {*} name optional name of the cookie
|
|
||||||
*/
|
|
||||||
export function getCookieConsentValue(name?: string): string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the consent cookie
|
|
||||||
* Remove the cookie on browser in order to allow user to change their consent
|
|
||||||
* @param {*} name optional name of the cookie
|
|
||||||
*/
|
|
||||||
export function resetCookieConsentValue(name?: string): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the legacy cookie name by the regular cookie name
|
|
||||||
* @param {string} name of cookie to get
|
|
||||||
*/
|
|
||||||
export function getLegacyCookieName(name: string): string;
|
|
||||||
|
|
||||||
export { Cookies };
|
|
11
src/index.tsx
Normal file
11
src/index.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import Cookies from "js-cookie";
|
||||||
|
import { ConditionalWrapper } from "./components/ConditionalWrapper";
|
||||||
|
import { CookieConsent } from "./CookieConsent";
|
||||||
|
import { POSITION_OPTIONS, VISIBILITY_OPTIONS } from "./models/constants";
|
||||||
|
|
||||||
|
export * from "./models/constants";
|
||||||
|
export * from "./utilities";
|
||||||
|
export { Cookies, CookieConsent, ConditionalWrapper };
|
||||||
|
// backwards compatibility exports
|
||||||
|
export { POSITION_OPTIONS as OPTIONS, VISIBILITY_OPTIONS as VISIBLE_OPTIONS };
|
||||||
|
export default CookieConsent;
|
1
src/models/constants/defaultCookieName.ts
Normal file
1
src/models/constants/defaultCookieName.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const defaultCookieConsentName = "CookieConsent";
|
4
src/models/constants/index.ts
Normal file
4
src/models/constants/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from "./positionOptions";
|
||||||
|
export * from "./sameSiteOptions";
|
||||||
|
export * from "./visibilityOptions";
|
||||||
|
export * from "./defaultCookieName";
|
5
src/models/constants/positionOptions.ts
Normal file
5
src/models/constants/positionOptions.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const POSITION_OPTIONS = {
|
||||||
|
TOP: "top",
|
||||||
|
BOTTOM: "bottom",
|
||||||
|
NONE: "none",
|
||||||
|
};
|
5
src/models/constants/sameSiteOptions.ts
Normal file
5
src/models/constants/sameSiteOptions.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum SAME_SITE_OPTIONS {
|
||||||
|
STRICT = "strict",
|
||||||
|
LAX = "lax",
|
||||||
|
NONE = "none",
|
||||||
|
}
|
5
src/models/constants/visibilityOptions.ts
Normal file
5
src/models/constants/visibilityOptions.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const VISIBILITY_OPTIONS = {
|
||||||
|
HIDDEN: "hidden",
|
||||||
|
SHOW: "show",
|
||||||
|
BY_COOKIE_VALUE: "byCookieValue",
|
||||||
|
};
|
35
src/utilities.ts
Normal file
35
src/utilities.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import Cookies from "js-cookie";
|
||||||
|
import { defaultCookieConsentName } from "./models/constants";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the consent cookie
|
||||||
|
* Retrieves the regular value first and if not found the legacy one according
|
||||||
|
* to: https://web.dev/samesite-cookie-recipes/#handling-incompatible-clients
|
||||||
|
* @param {*} name optional name of the cookie
|
||||||
|
*/
|
||||||
|
export const getCookieConsentValue = (name = defaultCookieConsentName) => {
|
||||||
|
const cookieValue = Cookies.get(name);
|
||||||
|
|
||||||
|
// if the cookieValue is undefined check for the legacy cookie
|
||||||
|
if (cookieValue === undefined) {
|
||||||
|
return Cookies.get(getLegacyCookieName(name));
|
||||||
|
}
|
||||||
|
return cookieValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the consent cookie
|
||||||
|
* Remove the cookie on browser in order to allow user to change their consent
|
||||||
|
* @param {*} name optional name of the cookie
|
||||||
|
*/
|
||||||
|
export const resetCookieConsentValue = (name = defaultCookieConsentName) => {
|
||||||
|
Cookies.remove(name);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the legacy cookie name by the regular cookie name
|
||||||
|
* @param {string} name of cookie to get
|
||||||
|
*/
|
||||||
|
export const getLegacyCookieName = (name: string) => {
|
||||||
|
return `${name}-legacy`;
|
||||||
|
};
|
11
stories/defaults/intro.tsx
Normal file
11
stories/defaults/intro.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { FunctionComponent } from "react";
|
||||||
|
|
||||||
|
type Props = {};
|
||||||
|
|
||||||
|
export const Intro: FunctionComponent<Props> = () => {
|
||||||
|
return (
|
||||||
|
<h1>
|
||||||
|
<p>Debug is turned on for all stories so that the bar always shows up</p>
|
||||||
|
</h1>
|
||||||
|
);
|
||||||
|
};
|
3
stories/defaults/storyProps.ts
Normal file
3
stories/defaults/storyProps.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { CookieConsentProps, defaultCookieConsentProps } from "../../src/CookieConsent.props";
|
||||||
|
|
||||||
|
export const defaultStoryProps: CookieConsentProps = { ...defaultCookieConsentProps, debug: true };
|
13
stories/defaults/template.tsx
Normal file
13
stories/defaults/template.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Story } from "@storybook/react";
|
||||||
|
import CookieConsent from "../../src";
|
||||||
|
import { Intro } from "./intro";
|
||||||
|
|
||||||
|
export const DefaultTemplate: Story<any> = (args) => (
|
||||||
|
<>
|
||||||
|
<Intro />
|
||||||
|
<CookieConsent {...args}>
|
||||||
|
This website uses cookies to enhance the user experience.
|
||||||
|
<span style={{ fontSize: "10px" }}>This bit of text is smaller :O</span>
|
||||||
|
</CookieConsent>
|
||||||
|
</>
|
||||||
|
);
|
34
stories/index.stories.tsx
Normal file
34
stories/index.stories.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { Meta } from "@storybook/react";
|
||||||
|
import { CookieConsent } from "../src";
|
||||||
|
import { defaultStoryProps } from "./defaults/storyProps";
|
||||||
|
import { DefaultTemplate } from "./defaults/template";
|
||||||
|
|
||||||
|
const meta: Meta = {
|
||||||
|
title: "CookieConsent",
|
||||||
|
component: CookieConsent,
|
||||||
|
argTypes: {
|
||||||
|
children: {
|
||||||
|
control: {
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
controls: { expanded: true },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
export const Default = DefaultTemplate.bind({});
|
||||||
|
Default.args = defaultStoryProps;
|
||||||
|
|
||||||
|
// stories
|
||||||
|
export * from "./stories/acceptOnScroll.story";
|
||||||
|
export * from "./stories/additionalButtons.story";
|
||||||
|
export * from "./stories/customStyling.story";
|
||||||
|
export * from "./stories/flippedButtons.story";
|
||||||
|
export * from "./stories/muiButtons.story";
|
||||||
|
export * from "./stories/onAccept.story";
|
||||||
|
export * from "./stories/overlay.story";
|
||||||
|
export * from "./stories/rainbows.story";
|
34
stories/stories/acceptOnScroll.story.tsx
Normal file
34
stories/stories/acceptOnScroll.story.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { Story } from "@storybook/react";
|
||||||
|
import CookieConsent from "../../src";
|
||||||
|
import { CookieConsentProps } from "../../src/CookieConsent.props";
|
||||||
|
import { Intro } from "../defaults/intro";
|
||||||
|
import { defaultStoryProps } from "../defaults/storyProps";
|
||||||
|
|
||||||
|
const AcceptOnScrollTemplate: Story<any> = (args) => (
|
||||||
|
<>
|
||||||
|
<Intro />
|
||||||
|
{Array.from(Array(25).keys()).map((_something) => (
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Voluptates ipsa sequi soluta
|
||||||
|
mollitia illum, hic quaerat ipsum sint odit delectus magni neque sunt adipisci culpa harum
|
||||||
|
aut distinctio quisquam ab!
|
||||||
|
</p>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<CookieConsent {...args}>
|
||||||
|
Scroll for {args.acceptOnScrollPercentage}% and the onAccept will trigger
|
||||||
|
</CookieConsent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const AcceptOnScroll = AcceptOnScrollTemplate.bind({});
|
||||||
|
AcceptOnScroll.args = {
|
||||||
|
...defaultStoryProps,
|
||||||
|
onAccept: (acceptedByScrolling) => {
|
||||||
|
alert(`ACCEPTED! By scrolling? ${JSON.stringify(acceptedByScrolling)}`);
|
||||||
|
},
|
||||||
|
acceptOnScroll: true,
|
||||||
|
acceptOnScrollPercentage: 25,
|
||||||
|
} as CookieConsentProps;
|
||||||
|
|
||||||
|
export { AcceptOnScroll };
|
17
stories/stories/additionalButtons.story.tsx
Normal file
17
stories/stories/additionalButtons.story.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Story } from "@storybook/react";
|
||||||
|
import CookieConsent from "../../src";
|
||||||
|
import { Intro } from "../defaults/intro";
|
||||||
|
import { defaultStoryProps } from "../defaults/storyProps";
|
||||||
|
|
||||||
|
const AdditionalButtonsTemplate: Story<any> = (args) => (
|
||||||
|
<>
|
||||||
|
<Intro />
|
||||||
|
<CookieConsent {...args}>
|
||||||
|
<button style={{ color: "gray" }}>I am a custom config button</button>
|
||||||
|
<span style={{ fontSize: "10px" }}>This bit of text is smaller :O</span>
|
||||||
|
</CookieConsent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
const AdditionalButtons = AdditionalButtonsTemplate.bind({});
|
||||||
|
AdditionalButtons.args = defaultStoryProps;
|
||||||
|
export { AdditionalButtons };
|
12
stories/stories/customStyling.story.tsx
Normal file
12
stories/stories/customStyling.story.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { CookieConsentProps } from "../../src/CookieConsent.props";
|
||||||
|
import { defaultStoryProps } from "../defaults/storyProps";
|
||||||
|
import { DefaultTemplate } from "../defaults/template";
|
||||||
|
|
||||||
|
const CustomStyling = DefaultTemplate.bind({});
|
||||||
|
CustomStyling.args = {
|
||||||
|
...defaultStoryProps,
|
||||||
|
style: { background: "red" },
|
||||||
|
buttonStyle: { fontWeight: "bold" },
|
||||||
|
} as CookieConsentProps;
|
||||||
|
|
||||||
|
export { CustomStyling };
|
12
stories/stories/flippedButtons.story.tsx
Normal file
12
stories/stories/flippedButtons.story.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { CookieConsentProps } from "../../src/CookieConsent.props";
|
||||||
|
import { defaultStoryProps } from "../defaults/storyProps";
|
||||||
|
import { DefaultTemplate } from "../defaults/template";
|
||||||
|
|
||||||
|
const FlippedButtons = DefaultTemplate.bind({});
|
||||||
|
FlippedButtons.args = {
|
||||||
|
...defaultStoryProps,
|
||||||
|
flipButtons: true,
|
||||||
|
enableDeclineButton: true,
|
||||||
|
} as CookieConsentProps;
|
||||||
|
|
||||||
|
export { FlippedButtons };
|
14
stories/stories/muiButtons.story.ts
Normal file
14
stories/stories/muiButtons.story.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Button } from "@mui/material";
|
||||||
|
import { CookieConsentProps } from "../../src/CookieConsent.props";
|
||||||
|
import { defaultStoryProps } from "../defaults/storyProps";
|
||||||
|
import { DefaultTemplate } from "../defaults/template";
|
||||||
|
|
||||||
|
const CustomMuiButton = DefaultTemplate.bind({});
|
||||||
|
CustomMuiButton.args = {
|
||||||
|
...defaultStoryProps,
|
||||||
|
disableButtonStyles: true,
|
||||||
|
ButtonComponent: Button,
|
||||||
|
customButtonProps: { variant: "contained", style: { marginRight: "10px" } },
|
||||||
|
} as CookieConsentProps;
|
||||||
|
|
||||||
|
export { CustomMuiButton };
|
13
stories/stories/onAccept.story.tsx
Normal file
13
stories/stories/onAccept.story.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { CookieConsentProps } from "../../src/CookieConsent.props";
|
||||||
|
import { defaultStoryProps } from "../defaults/storyProps";
|
||||||
|
import { DefaultTemplate } from "../defaults/template";
|
||||||
|
|
||||||
|
const CustomOnAccept = DefaultTemplate.bind({});
|
||||||
|
CustomOnAccept.args = {
|
||||||
|
...defaultStoryProps,
|
||||||
|
onAccept: (acceptedByScrolling) => {
|
||||||
|
alert(`ACCEPTED! By scrolling? ${JSON.stringify(acceptedByScrolling)}`);
|
||||||
|
},
|
||||||
|
} as CookieConsentProps;
|
||||||
|
|
||||||
|
export { CustomOnAccept };
|
11
stories/stories/overlay.story.tsx
Normal file
11
stories/stories/overlay.story.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { CookieConsentProps } from "../../src/CookieConsent.props";
|
||||||
|
import { defaultStoryProps } from "../defaults/storyProps";
|
||||||
|
import { DefaultTemplate } from "../defaults/template";
|
||||||
|
|
||||||
|
const Overlay = DefaultTemplate.bind({});
|
||||||
|
Overlay.args = {
|
||||||
|
...defaultStoryProps,
|
||||||
|
overlay: true,
|
||||||
|
} as CookieConsentProps;
|
||||||
|
|
||||||
|
export { Overlay };
|
22
stories/stories/rainbows.story.tsx
Normal file
22
stories/stories/rainbows.story.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { CookieConsentProps } from "../../src/CookieConsent.props";
|
||||||
|
import { defaultStoryProps } from "../defaults/storyProps";
|
||||||
|
import { DefaultTemplate } from "../defaults/template";
|
||||||
|
|
||||||
|
const Rainbows = DefaultTemplate.bind({});
|
||||||
|
Rainbows.args = {
|
||||||
|
...defaultStoryProps,
|
||||||
|
buttonText: "OMG DOUBLE RAINBOW",
|
||||||
|
|
||||||
|
style: {
|
||||||
|
background: "linear-gradient(to right, orange , yellow, green, cyan, blue, violet)",
|
||||||
|
textShadow: "2px 2px black",
|
||||||
|
},
|
||||||
|
buttonStyle: {
|
||||||
|
background: "linear-gradient(to left, orange , yellow, green, cyan, blue, violet)",
|
||||||
|
color: "white",
|
||||||
|
fontWeight: "bolder",
|
||||||
|
textShadow: "2px 2px black",
|
||||||
|
},
|
||||||
|
} as CookieConsentProps;
|
||||||
|
|
||||||
|
export { Rainbows };
|
35
tsconfig.json
Normal file
35
tsconfig.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
// see https://www.typescriptlang.org/tsconfig to better understand tsconfigs
|
||||||
|
"include": ["src", "types"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "esnext",
|
||||||
|
"lib": ["dom", "esnext"],
|
||||||
|
"importHelpers": true,
|
||||||
|
// output .d.ts declaration files for consumers
|
||||||
|
"declaration": true,
|
||||||
|
// output .js.map sourcemap files for consumers
|
||||||
|
"sourceMap": true,
|
||||||
|
// match output dir to input dir. e.g. dist/index instead of dist/src/index
|
||||||
|
"rootDir": "./src",
|
||||||
|
// stricter type-checking for stronger correctness. Recommended by TS
|
||||||
|
"strict": true,
|
||||||
|
// linter checks for common issues
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
// noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
// use Node's module resolution algorithm, instead of the legacy TS one
|
||||||
|
"moduleResolution": "node",
|
||||||
|
// transpile JSX to React.createElement
|
||||||
|
"jsx": "react",
|
||||||
|
// interop between ESM and CJS modules. Recommended by TS
|
||||||
|
"esModuleInterop": true,
|
||||||
|
// significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS
|
||||||
|
"skipLibCheck": true,
|
||||||
|
// error out if import and file system have a casing mismatch. Recommended by TS
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
// `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc`
|
||||||
|
"noEmit": true,
|
||||||
|
}
|
||||||
|
}
|
@ -1,37 +0,0 @@
|
|||||||
var path = require("path");
|
|
||||||
var CopyWebpackPlugin = require("copy-webpack-plugin");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
entry: "./src/index.js",
|
|
||||||
output: {
|
|
||||||
path: path.resolve(__dirname, "build"),
|
|
||||||
filename: "index.js",
|
|
||||||
library: {
|
|
||||||
name: "ReactCookieConsent",
|
|
||||||
type: "umd",
|
|
||||||
},
|
|
||||||
environment: {
|
|
||||||
arrowFunction: false, // the generated runtime-code should not use arrow functions
|
|
||||||
},
|
|
||||||
globalObject: `typeof self !== 'undefined' ? self : this`,
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.js$/,
|
|
||||||
include: path.resolve(__dirname, "src"),
|
|
||||||
exclude: /(node_modules|build)/,
|
|
||||||
use: {
|
|
||||||
loader: "babel-loader",
|
|
||||||
options: {
|
|
||||||
presets: ["@babel/preset-env"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
externals: {
|
|
||||||
react: "commonjs react", // this line is just to use the React dependency of our parent-testing-project instead of using our own React.
|
|
||||||
},
|
|
||||||
plugins: [new CopyWebpackPlugin({ patterns: [{ from: "src/index.d.ts", to: "index.d.ts" }] })],
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user