diff --git a/CHANGELOG.md b/CHANGELOG.md index 48a93c95..390a44f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [2.0.4](https://github.com/openmail/system1-cmp/compare/v2.0.3...v2.0.4) (2020-09-17) + +### Styling + +- [x] Downsize fonts and spacing mobile +- [x] Downsize spacing desktop +- [x] Add scroll bar +- [x] Auto-position CMP vertically based on purposes + ## [2.0.3](https://github.com/openmail/system1-cmp/compare/v2.0.2...v2.0.3) (2020-09-15) ### Fix diff --git a/README.md b/README.md index ad63e53d..44e1a84e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ TCF 2.0 Consent Management Platform (CMP) UI tool. We are in the process of vali - - [Installation / Use](#installation--use) - [API](#api) - [Customized API](#customized-api) @@ -64,6 +63,8 @@ API signatures have changed from the CMP TCF 1.1, but we've tried to keep the co boxShadow: 'none', secondaryColor: '#869cc0', featuresColor: '#d0d3d7', + shouldAutoResizeModal: true, // resizes modal on stacks screen to push stacks below fold + maxHeightModal: '40vh', }, ccpaApplies: true, gdprApplies: true, @@ -175,6 +176,7 @@ Set configuration for your CMP during the `init` phase. __tcfapi('init', 2, () => {}, { theme: { maxHeightModal: '50vh', + shouldAutoResizeModal: true, primaryColor: '#0099ff', }, canLog: true, // pixel logging for monitoring and alerting @@ -210,6 +212,8 @@ Override styling choices using the following properties: - `textLinkColor`: '#0099ff' - `secondaryColor`: '#869cc0' - `featuresColor`: '#d0d3d7' +- `maxHeightModal`: '40vh' // 40vh by default; enforce a maxheight as a %,px,vh +- `shouldAutoResizeModal`: true // false by default; auto resize modal on stacks screen to push purposes below fold. Works in conjunction with `maxHeightModal` ## Initialize from URL Param diff --git a/package.json b/package.json index fd205f18..c87e8032 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "system1-cmp", - "version": "2.0.3", + "version": "2.0.4", "description": "System1 Consent Management Platform for TCF 1.1 GDPR Compliance", "scripts": { "clean": "rimraf ./dist", @@ -13,10 +13,11 @@ "deploy": "yarn clean && yarn deploy:original && yarn deploy:s1", "build:original": "cross-env NODE_ENV=production webpack --progress --config config/webpack.config.babel.js", "deploy:original": "yarn build:original && yarn upload:original", - "upload:original": "cross-var s3-deploy './dist/{*.?(js|html),docs/**}' --cwd './dist' --region us-west-2 --bucket s1-layout-cdn/cmp --gzip --cache 1440 --distId E5JQ1CRXXPTKM --invalidate '/cmp/*.js /cmp/* /cmp/docs/*'", + "upload:original": "cross-var s3-deploy './dist/{*.?(js|html),docs/**}' --cwd './dist' --region us-west-2 --bucket s1-layout-cdn/cmp --gzip html,js,css,json --cache 1440 --distId E5JQ1CRXXPTKM --invalidate '/cmp/*.js /cmp/* /cmp/docs/*'", "build:s1": "cross-env NODE_ENV=production webpack --progress --config config/s1.webpack.config.babel.js", "deploy:s1": "yarn build:s1 && yarn upload:s1", - "upload:s1": "cross-var s3-deploy './dist/$npm_package_version/**' --cwd './dist/$npm_package_version' --region us-west-2 --bucket s1-layout-cdn/cmp/$npm_package_version --gzip --preventUpdates --immutable", + "upload:s1": "cross-var s3-deploy './dist/$npm_package_version/**' --cwd './dist/$npm_package_version' --region us-west-2 --bucket s1-layout-cdn/cmp/$npm_package_version --gzip html,js,css,json --preventUpdates --cache 604800 --distId E5JQ1CRXXPTKM --invalidate '/cmp/$npm_package_version/*", + "upload:s1:again": "cross-var s3-deploy './dist/$npm_package_version/**' --cwd './dist/$npm_package_version' --region us-west-2 --bucket s1-layout-cdn/cmp/$npm_package_version --gzip html,js,css,json --cache 604800 --distId E5JQ1CRXXPTKM --invalidate '/cmp/$npm_package_version/*'", "deploy:test": "yarn build:s1 && yarn upload:test", "upload:test": "cross-var s3-deploy './dist/$npm_package_version/**' --cwd './dist/$npm_package_version' --region us-west-2 --bucket s1-layout-cdn/cmp/test --gzip --cache 1440 --distId E5JQ1CRXXPTKM --invalidate '/cmp/test/* /cmp/test/*.html'", "prebuild": "npm run clean && mkdirp dist", @@ -115,6 +116,7 @@ "handlebars-loader": "^1.7.1", "js-beautify": "^1.7.5", "lodash": "^4.17.11", + "lodash.debounce": "^4.0.8", "no-case": "^3.0.3", "preact": "^8.4.2", "preact-compat": "^3.18.4", diff --git a/src/s1/components/app.jsx b/src/s1/components/app.jsx index eb804cf0..ef1f2d88 100644 --- a/src/s1/components/app.jsx +++ b/src/s1/components/app.jsx @@ -13,6 +13,17 @@ export default class App extends Component { state = { store: this.props.store, shouldShowModal: false, + maxHeightModal: 0, + }; + + handleMaxHeightChange = (newMaxHeightModal) => { + const { maxHeightModal } = this.state; + if (typeof maxHeightModal !== 'string') { + // once we switch to %/vh based max-height, keep it + this.setState({ + maxHeightModal: newMaxHeightModal, + }); + } }; updateState(store) { @@ -25,6 +36,13 @@ export default class App extends Component { componentDidMount() { const { store } = this.props; + const { + config: { theme }, + } = store; + const { shouldAutoResizeModal, maxHeightModal } = theme; + if (!shouldAutoResizeModal) { + this.handleMaxHeightChange(maxHeightModal); + } store.subscribe(this.updateState.bind(this)); setTimeout(this.componentDidUpdate.bind(this), 100); // delay reveal on first load } @@ -42,7 +60,7 @@ export default class App extends Component { } render(props, state) { - const { store, shouldShowModal } = state; + const { store, shouldShowModal, maxHeightModal } = state; const { tcModel } = store; const { consentScreen } = tcModel; @@ -50,15 +68,30 @@ export default class App extends Component {
{!consentScreen || (consentScreen === CONSENT_SCREENS.STACKS_LAYER1 && ( - + ))} {consentScreen === CONSENT_SCREENS.PURPOSES_LAYER2 && ( - + )} {consentScreen === CONSENT_SCREENS.VENDORS_LAYER3 && ( - + )}
); diff --git a/src/s1/components/app.less b/src/s1/components/app.less index 29f07a7a..5ed493cb 100644 --- a/src/s1/components/app.less +++ b/src/s1/components/app.less @@ -25,17 +25,42 @@ margin: 0; } - input:not([type]), input[type="text"], input[type="password"] { + input:not([type]), + input[type='text'], + input[type='password'] { box-sizing: border-box; box-shadow: 0 3px 3px rgba(0, 0, 0, 0.05) inset; background-color: #fff; border-radius: 4px; border: 1px solid #c5c5c5; color: #333; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 1em; margin: 0; padding: 0 9px; height: 28px; } + + ::-webkit-scrollbar { + -webkit-appearance: none; + } + + ::-webkit-scrollbar:vertical { + width: 11px; + } + + ::-webkit-scrollbar:horizontal { + height: 11px; + } + + ::-webkit-scrollbar-thumb { + border-radius: 8px; + border: 2px solid white; /* should match background, can't be transparent */ + background-color: rgba(0, 0, 0, 0.5); + } + + ::-webkit-scrollbar-track { + background-color: rgba(0, 0, 0, 0.02); + border-radius: 8px; + } } diff --git a/src/s1/components/banner/banner.less b/src/s1/components/banner/banner.less index 9f99bfcd..8fe874b5 100644 --- a/src/s1/components/banner/banner.less +++ b/src/s1/components/banner/banner.less @@ -10,12 +10,14 @@ right: 0; left: 0; bottom: 0; + padding-top: 25px; + padding-right: 15px; + padding-left: 30px; z-index: 99999; color: @color-textLight; background: white; font-size: 16px; transition: transform 350ms ease-in-out, opacity 350ms linear; - padding-right: 10px; box-shadow: 0 -1px 1px rgba(0, 0, 0, 0.12), 0 -2px 2px rgba(0, 0, 0, 0.12), 0 -4px 4px rgba(0, 0, 0, 0.12), 0 -8px 8px rgba(0, 0, 0, 0.12), 0 -16px 16px rgba(0, 0, 0, 0.12); border-top-left-radius: 25px; @@ -80,8 +82,12 @@ .content { max-height: 60vh; overflow: auto; - padding: 20px 30px; + padding-top: 0px; + padding-left: 0px; + padding-right: 20px; + padding-bottom: 0px; -webkit-overflow-scrolling: touch; + transition: max-height 350ms ease-in-out; @media @smartphone { padding: 0px 5px; @@ -97,21 +103,23 @@ @media @smartphone { font-size: 12px; - line-height: 20px; + line-height: 18px; } .title { font-size: 32px; font-weight: bold; color: @color-text; - padding: 15px 0; + padding: 5px 0; margin-bottom: 10px; line-height: 32px; @media @smartphone { - font-size: 28px; - line-height: 28px; + font-size: 22px; + line-height: 22px; font-weight: bold; + padding: 5px 0; + margin-bottom: 5px; } } } @@ -119,8 +127,9 @@ .consent { flex-basis: 100%; text-align: left; - margin-top: 30px; - margin-bottom: 30px; + margin-top: 20px; + padding-bottom: 20px; + margin-bottom: 10px; font-size: 20px; display: flex; @@ -128,11 +137,13 @@ display: flex; /* primary flex container */ flex-direction: row; /* horizontal alignment of flex items (default value; can be omitted) */ align-items: stretch; /* will apply equal heights to flex items (default value; can be omitted) */ - font-size: 1em; - line-height: 1.2em; + font-size: 12px; + line-height: 12px; text-align: center; - margin-top: 20px; - margin-bottom: 20px; + padding-top: 15px; + padding-bottom: 10px; + margin-top: 0; + margin-bottom: 10px; } a { diff --git a/src/s1/components/banner/bannerStacks.jsx b/src/s1/components/banner/bannerStacks.jsx index 5884d144..74ef5fe8 100644 --- a/src/s1/components/banner/bannerStacks.jsx +++ b/src/s1/components/banner/bannerStacks.jsx @@ -1,4 +1,5 @@ import { h, Component } from 'preact'; +import debounce from 'lodash.debounce'; import style from './banner.less'; import PurposeList from './purposeList'; @@ -11,6 +12,8 @@ class LocalLabel extends Label { static defaultProps = { prefix: 'layer1Stacks', isShowing: false, + maxHeightModal: 0, + onMaxHeightChange: () => {}, }; } @@ -19,6 +22,30 @@ export default class BannerStacks extends Component { super(props); } + state = { + maxHeightModal: this.getMaxHeightModal(), + }; + + componentDidMount() { + if (window) { + window.addEventListener('resize', this.handleResize); + } + this.handleResize(); + } + + componentWillUnmount() { + if (window) { + window.removeEventListener('resize', this.handleResize); + } + } + + getMaxHeightModal() { + if (this.aboveFoldRef && this.aboveFoldRef.clientHeight) { + return this.aboveFoldRef.clientHeight + 5; + } + return 0; + } + handleAcceptAll = () => { const { store } = this.props; const { @@ -65,8 +92,22 @@ export default class BannerStacks extends Component { }); }; - render(props) { - const { isShowing, store } = props; + handleResize = debounce(() => { + const { maxHeightModal } = this.state; + const { onMaxHeightChange } = this.props; + const newMaxHeightModal = this.getMaxHeightModal(); + + if (newMaxHeightModal !== maxHeightModal) { + this.setState({ + maxHeightModal: newMaxHeightModal, + }); + onMaxHeightChange(newMaxHeightModal); + } + }, 200); + + render(props, state) { + const { maxHeightModal } = state; + const { isShowing, maxHeightModal: maxHeightModalGlobal, store } = props; const { config: { theme }, translations, @@ -77,7 +118,7 @@ export default class BannerStacks extends Component { const { isBannerModal, isBannerInline, - maxHeightModal, + // maxHeightModal, primaryColor, primaryTextColor, backgroundColor, @@ -97,67 +138,69 @@ export default class BannerStacks extends Component { return (
(this.bannerRef = el)} class={bannerClasses.join(' ')} style={{ backgroundColor, color: textLightColor, }} + onResize={this.handleResize} >
-
(this.messageRef = el)}> +
-
- - Ads help us run this site - -
-
- - When you visit our site, pre-selected companies may access and use certain information on your - device and about this site to serve relevant ads or personalized content. - -
-
- - - Manage Your Choices - - - - - Continue to site +
(this.aboveFoldRef = el)}> + +
+ + When you visit our site, pre-selected companies may access and use certain information on + your device and about this site to serve relevant ads or personalized content. - +
+
{!displayLayer1 ?

Loading

: } diff --git a/src/s1/components/banner/bannerVendors.jsx b/src/s1/components/banner/bannerVendors.jsx index 86ffb5ff..965c9205 100644 --- a/src/s1/components/banner/bannerVendors.jsx +++ b/src/s1/components/banner/bannerVendors.jsx @@ -11,6 +11,8 @@ class LocalLabel extends Label { static defaultProps = { prefix: 'layer3Vendors', isShowing: false, + maxHeightModal: 0, // starting modalHeight + onMaxHeightChange: () => {}, }; } @@ -19,6 +21,15 @@ export default class BannerVendors extends Component { super(props); } + componentDidMount() { + const { store, onMaxHeightChange } = this.props; + const { theme } = store; + const { maxHeightModal: maxHeightModalTheme } = theme; + setTimeout(() => { + onMaxHeightChange(maxHeightModalTheme); + }, 1); + } + handleBack = () => { const { store } = this.props; const { @@ -65,7 +76,7 @@ export default class BannerVendors extends Component { }; render(props) { - const { isShowing, store } = props; + const { isShowing, store, maxHeightModal } = props; const { config: { theme }, isSaveShowing, @@ -75,7 +86,7 @@ export default class BannerVendors extends Component { const { isBannerModal, isBannerInline, - maxHeightModal, + // maxHeightModal, primaryColor, primaryTextColor, backgroundColor, diff --git a/src/s1/components/banner/purposeList.less b/src/s1/components/banner/purposeList.less index 0b3a0d60..4891ca69 100644 --- a/src/s1/components/banner/purposeList.less +++ b/src/s1/components/banner/purposeList.less @@ -1,7 +1,7 @@ @import '../../../style/variables.less'; .purposeList { - margin: 0; + margin-top: 20px; padding: 0; display: flex; flex-wrap: wrap; @@ -43,6 +43,10 @@ .itemPurpose { padding: 0; + + @media @smartphone { + font-size: 12px; + } } .itemInteractive { @@ -67,6 +71,7 @@ .itemInteractiveAnchor { display: flex; + margin-right: 5px; span { flex: 1; @@ -99,7 +104,7 @@ fill: @color-linkColor; @media @smartphone { - top: -2px; + top: -1px; } } @@ -110,7 +115,7 @@ .privacyPolicy { svg { - top: -2px; + top: -1px; } } @@ -142,6 +147,7 @@ ul { margin: 0; + padding-left: 20px; } p { diff --git a/src/s1/lib/config.js b/src/s1/lib/config.js index 65581d0f..e5b2a1be 100644 --- a/src/s1/lib/config.js +++ b/src/s1/lib/config.js @@ -15,7 +15,8 @@ export const theme = { boxShadow: 'none', secondaryColor: '#869cc0', featuresColor: '#d0d3d7', - maxHeightModal: '50vh', + shouldAutoResizeModal: false, + maxHeightModal: '40vh', }; export const config = { diff --git a/src/s1/reference/tcf-2.0.html b/src/s1/reference/tcf-2.0.html index 4058f4ce..88b03599 100644 --- a/src/s1/reference/tcf-2.0.html +++ b/src/s1/reference/tcf-2.0.html @@ -128,6 +128,10 @@

TCFString

>System1 CMP Github +
  • + Autoresize: + +
  • @@ -143,6 +147,7 @@

    TCFString

    const domTcfString = document.querySelector('#domTcfString'); const selectLanguage = document.querySelector('#selectLanguage'); const selectVendors = document.querySelector('#selectVendors'); + const domShouldAutoResize = document.querySelector('#domShouldAutoResize'); const setupQaTools = function (store) { const LANGUAGES = store.LANGUAGES; @@ -161,9 +166,14 @@

    TCFString

    domTcfString.value = (store.tcData && store.tcData.tcString) || 'N/A'; }; + const shouldAutoResize = function () { + return window.location.search.indexOf('resize') >= 0 ? true : false; + }; + const config = { theme: { - maxHeightModal: '60vh', + shouldAutoResizeModal: shouldAutoResize(), + maxHeightModal: '40vh', }, business: 'reference', gdprApplies: true, @@ -177,7 +187,7 @@

    TCFString

    // polyfillSrc: './polyfills.js', publisherCountryCode: 'US', language: 'en', // default - narrowedVendors: [1, 2, 3, 4, 5, 6], + // narrowedVendors: [1, 2, 3, 4, 5, 6], }; __tcfapi('addEventListener', 2, function (tcData, success) { @@ -221,6 +231,11 @@

    TCFString

    selectLanguage.value ); }); + + domShouldAutoResize.checked = shouldAutoResize(); + domShouldAutoResize.addEventListener('change', function () { + window.location.search = domShouldAutoResize.checked ? '?resize=true' : ''; + }); diff --git a/yarn.lock b/yarn.lock index aba508e9..11c7be53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6523,6 +6523,11 @@ lodash.camelcase@^4.3.0: resolved "https://system1.jfrog.io/system1/api/npm/npm-virtual/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://system1.jfrog.io/system1/api/npm/npm-virtual/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://system1.jfrog.io/system1/api/npm/npm-virtual/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"