From c336823bd24b3aa075e76bba018715ce4b858df3 Mon Sep 17 00:00:00 2001 From: Anton Synenko <49228987+TonySynenko@users.noreply.github.com> Date: Wed, 31 Jul 2019 12:41:20 +0300 Subject: [PATCH] Add contentful based top banner (#1911) * data fetch && swig && styles * banners actualization script * finish (?) * finish (?) * remove "extends" from eslint settings * remove sensitive info * update hexo index file * banner padding fix * remove Promise.protoype.finally() from hexo index script * Contentful driven banners, parsed dynamically: Hexo index.js && data fetch && swig && styles && banners_actualization script * add cypress test for banners * docs update v1 * Fix toc width so that it doesn't create a horizontal scrollbar on smaller heights screen. * package-lock * just a few tweaks to the README instructions * fixed conflict issue from package.json merge --- .gitignore | 3 + .../integration/contentful_banners_spec.js | 31 ++++++ index.js | 57 +++++++++-- package-lock.json | 99 +++++++++++++++++++ package.json | 5 + readme.md | 19 ++++ source/_data/banners.yml | 5 + themes/cypress/layout/api-toc.swig | 2 +- themes/cypress/layout/layout.swig | 11 ++- .../cypress/layout/partial/after_footer.swig | 1 + themes/cypress/layout/partial/banners.swig | 13 +++ themes/cypress/layout/partial/header.swig | 59 ++++++----- .../cypress/source/css/_partial/banners.scss | 77 +++++++++++++++ themes/cypress/source/css/_partial/base.scss | 5 +- .../cypress/source/css/_partial/header.scss | 42 +++++--- .../cypress/source/css/_partial/sidebar.scss | 5 +- themes/cypress/source/css/_partial/toc.scss | 10 +- themes/cypress/source/js/.eslintrc | 2 +- .../source/js/banners_actualization.js | 51 ++++++++++ 19 files changed, 442 insertions(+), 55 deletions(-) create mode 100644 cypress/integration/contentful_banners_spec.js create mode 100644 source/_data/banners.yml create mode 100644 themes/cypress/layout/partial/banners.swig create mode 100644 themes/cypress/source/css/_partial/banners.scss create mode 100644 themes/cypress/source/js/banners_actualization.js diff --git a/.gitignore b/.gitignore index ad862e27e8..a7b89fa16f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ themes/cypress/source/fonts/vendor/ cypress/screenshots cypress/videos + +# Dynamically parsed data +source/_data/banners.yml diff --git a/cypress/integration/contentful_banners_spec.js b/cypress/integration/contentful_banners_spec.js new file mode 100644 index 0000000000..0cb5f3b857 --- /dev/null +++ b/cypress/integration/contentful_banners_spec.js @@ -0,0 +1,31 @@ +const YAML = require('yamljs') +const emojiStrip = require('emoji-strip') + +const allBannersYaml = 'source/_data/banners.yml' + +describe('Contentful driven banners', () => { + it('displays all current banners with proper info', function () { + cy.readFile(allBannersYaml) + .then((yamlString) => YAML.parse(yamlString.replace(/\\u[\dA-F]{8}/gi, ''))) + .then((banners) => { + if (typeof banners === 'undefined' || !banners || !banners.length) return this.skip() + + cy.visit('/') + + cy.get('#header .top-banners_item') + .each((banner, i) => { + const text = banner.children('.top-banners_item--body').html().trim() + const { startDate, endDate } = banner.data() + const btn = banner.children('.call-to-action') + const btnText = btn.text().trim() + const btnLink = btn.attr('href') + + expect(emojiStrip(text), `Banner #${i + 1} text is proper`).to.eq(banners[i].text.replace(/\r?\n|\r/g, ' ')) + expect(startDate, `Banner #${i + 1} startDate is proper`).to.eq(banners[i].startDate) + expect(endDate, `Banner #${i + 1} endDate is proper`).to.eq(banners[i].endDate) + expect(btnText, `Banner #${i + 1} call-to-action text is proper`).to.eq(banners[i].buttonText.trim()) + expect(btnLink, `Banner #${i + 1} call-to-action link is proper`).to.eq(banners[i].buttonLink) + }) + }) + }) +}) diff --git a/index.js b/index.js index e0c706be96..3ebe692984 100644 --- a/index.js +++ b/index.js @@ -8,6 +8,11 @@ process.on('unhandledRejection', function (reason, p) { const Hexo = require('hexo') const chalk = require('chalk') const minimist = require('minimist') +const Contentful = require('contentful') +const moment = require('moment') +const yaml = require('js-yaml') +const fs = require('fs') +const { documentToHtmlString } = require('@contentful/rich-text-html-renderer') // these are the args like --port const args = minimist(process.argv.slice(2)) @@ -86,17 +91,57 @@ function initHexo () { console.log('in environment %s site url is %s', env, url) hexo.config.url = url - // set this on both our process + hexo + // set this on both our process + Hexo process.env.NODE_ENV = hexo.env.NODE_ENV = env console.log('NODE_ENV is:', chalk.cyan(env)) - return hexo.init() - .then(() => { - return hexo.call(cmd, args) + return new Promise((resolve, reject) => { + const space = hexo.env.GATSBY_CONTENTFUL_SPACE_ID || process.env.GATSBY_CONTENTFUL_SPACE_ID + const accessToken = hexo.env.GATSBY_CONTENTFUL_ACCESS_TOKEN || process.env.GATSBY_CONTENTFUL_ACCESS_TOKEN + + if (typeof space === 'undefined' || typeof accessToken === 'undefined') { + return reject({ + message: 'No Contentful space variables.', + }) + } + + return Contentful.createClient({ space, accessToken }) + .getEntries({ content_type: 'topBanner' }) + .then(({ items }) => { + const data = items.reduce((filtered, option) => { + if (moment(option.fields.endDate).isSameOrAfter(moment())) { + filtered.push({ ...option.fields, text: documentToHtmlString(option.fields.text) }) + } + + return filtered + }, []) + + fs.writeFile( + `${__dirname}/source/_data/banners.yml`, + yaml.safeDump(data), + (error) => { + // log if writeFile ends with error, but don't block hexo init process + if (error) { + console.warn(error) + + return reject(error) + } + + return resolve() + }, + ) + }) + }) + // start Hexo + .then(() => hexo.init().then(() => hexo.call(cmd, args))) + .catch((error) => { + // log error object + console.error(error) + + // but start Hexo anyway + return hexo.init().then(() => hexo.call(cmd, args)) }) - } initHexo() - diff --git a/package-lock.json b/package-lock.json index 2d3fca7575..4f18b9236e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -209,6 +209,20 @@ "minimist": "1.2.0" } }, + "@contentful/rich-text-html-renderer": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@contentful/rich-text-html-renderer/-/rich-text-html-renderer-13.1.0.tgz", + "integrity": "sha512-4E6O7cYO+6v3fQrBM9Bu/X9QXfNmH/DZevypw+UOJZyUfbYDA9KsPdXKa04hjnc3fMlBW0Kss33wj9DsaCap2g==", + "requires": { + "@contentful/rich-text-types": "13.1.0", + "escape-html": "1.0.3" + } + }, + "@contentful/rich-text-types": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@contentful/rich-text-types/-/rich-text-types-13.1.0.tgz", + "integrity": "sha512-/fwELDysqdZdhTW9tLUOtpoBnlaI6WVxtrrmB+lAbYkoC7G9NVaOPwxXqwYidgTl9I1ZJGfU/ZEr8GDyUIcZWg==" + }, "@cypress/commit-info": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@cypress/commit-info/-/commit-info-1.2.2.tgz", @@ -2935,6 +2949,22 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "2.0.3" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + } + } + }, "babel-jest": { "version": "24.8.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.8.0.tgz", @@ -4460,6 +4490,35 @@ } } }, + "contentful": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/contentful/-/contentful-7.8.2.tgz", + "integrity": "sha512-8aaCOxk2FJv5mDZlPh9s6BKj6rkjqGghtVuKXwx/sEYRQZVBsvvQgq+pMnN5XvTE2EHPEfu6C9d5J5cvbWlOjg==", + "requires": { + "axios": "0.19.0", + "contentful-resolve-response": "1.1.4", + "contentful-sdk-core": "6.3.6", + "json-stringify-safe": "5.0.1", + "lodash": "4.17.15" + } + }, + "contentful-resolve-response": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/contentful-resolve-response/-/contentful-resolve-response-1.1.4.tgz", + "integrity": "sha512-oFq6n6zjbiwD9/7mBa8YHPwvPM0B0D4uOgg1n/rVzpQPhCrzeIixNj6fbJAbDiJt05rZqxiY3K1Db7pPRhRaZw==", + "requires": { + "lodash": "4.17.15" + } + }, + "contentful-sdk-core": { + "version": "6.3.6", + "resolved": "https://registry.npmjs.org/contentful-sdk-core/-/contentful-sdk-core-6.3.6.tgz", + "integrity": "sha512-QUHrnzBXzJmLD68apaAQYy0BaTtbfmPwZvPk2hJ3uTzjiR0rlFC7qHxL/aN4rBx8ahHqxJ6EGgmoGmcjBYxe5A==", + "requires": { + "lodash": "4.17.15", + "qs": "6.5.2" + } + }, "convert-source-map": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", @@ -5702,6 +5761,23 @@ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, + "emoji-strip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/emoji-strip/-/emoji-strip-1.0.1.tgz", + "integrity": "sha1-z5OQU1BEEhiIQgrk20NtA6bMRZ0=", + "dev": true, + "requires": { + "emoji-regex": "6.5.1" + }, + "dependencies": { + "emoji-regex": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz", + "integrity": "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==", + "dev": true + } + } + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -6853,6 +6929,29 @@ "integrity": "sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc=", "dev": true }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "font-awesome": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", diff --git a/package.json b/package.json index 03d7771900..42b1a065c0 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "cypress": "3.4.1", "dependency-check": "3.4.1", "deps-ok": "1.4.1", + "emoji-strip": "1.0.1", "eslint": "6.1.0", "eslint-plugin-cypress": "2.6.0", "eslint-plugin-cypress-dev": "2.1.0", @@ -128,7 +129,9 @@ "yamljs": "0.3.0" }, "dependencies": { + "@contentful/rich-text-html-renderer": "13.1.0", "check-more-types": "2.24.0", + "contentful": "7.8.2", "debug": "4.1.1", "docsearch.js": "2.6.3", "font-awesome": "4.7.0", @@ -142,9 +145,11 @@ "hexo-renderer-marked": "0.3.2", "hexo-renderer-scss": "1.2.0", "hexo-server": "0.3.3", + "js-yaml": "3.13.1", "lazy-ass": "1.6.0", "lodash": "4.17.15", "menuspy": "1.3.0", + "moment": "2.24.0", "roboto-fontface": "0.10.0", "scrollingelement": "1.5.2", "yall": "github:malchata/yall.js#1c782f1ab7a0becb1a88fd6f3b260e55d5e213a2" diff --git a/readme.md b/readme.md index 90a860c3fb..c9f75080ca 100644 --- a/readme.md +++ b/readme.md @@ -16,6 +16,25 @@ please see our [Contributing Guideline](/CONTRIBUTING.md). Cypress is [first time OSS contributor friendly](http://www.firsttimersonly.com/). See [these issues](https://github.com/cypress-io/cypress-documentation/labels/first-timers-only) to contribute in a meaningful way. +### Contentful driven data: + +If you need any [Contentful](https://www.contentful.com/) driven data to be parsed before Hexo serve - you need to declare it Circle CI or/and bash. You may need [Contentful](https://www.contentful.com/) environment variables inside your machine or container: + +- `GATSBY_CONTENTFUL_SPACE_ID` + + [Contentful](https://www.contentful.com/) 12 digit key. You can find it (if you have granted access) in [Contentful](https://www.contentful.com/) acc: + + Settings → API keys → Master → Space ID + +- `GATSBY_CONTENTFUL_ACCESS_TOKEN` + + [Contentful](https://www.contentful.com/) 64 digit token. You can find it (if you have granted access) in [Contentful](https://www.contentful.com/) acc: + + Settings → API keys → Master → Content Delivery API - access token + + P.S. If you don't have any of this - Hexo will build & serve as usual, but without any + [Contentful](https://www.contentful.com/) driven data. + ## Deploying See our [Deploy Guideline](DEPLOY.md). diff --git a/source/_data/banners.yml b/source/_data/banners.yml new file mode 100644 index 0000000000..f1fe4fea4d --- /dev/null +++ b/source/_data/banners.yml @@ -0,0 +1,5 @@ +- startDate: '2019-07-17T14:00-05:00' + endDate: '2019-07-31T23:59-05:00' + text: "

\U0001F6A9Learn how to get complete code coverage with Cypress on July 31st at 11AM PDT/2PM EDT with Gleb Bahmutov and Amir Rustamzadeh

" + buttonText: 'Register Now ' + buttonLink: 'https://zoom.us/webinar/register/2115632233252/WN_bbV5ezckSMu9icH0KwuHuA' diff --git a/themes/cypress/layout/api-toc.swig b/themes/cypress/layout/api-toc.swig index 379f7464ab..42b554efc7 100644 --- a/themes/cypress/layout/api-toc.swig +++ b/themes/cypress/layout/api-toc.swig @@ -1,4 +1,4 @@ -
+
{{ partial('partial/sidebar') }} diff --git a/themes/cypress/layout/layout.swig b/themes/cypress/layout/layout.swig index 7c029026ba..2e15b1c094 100644 --- a/themes/cypress/layout/layout.swig +++ b/themes/cypress/layout/layout.swig @@ -3,11 +3,18 @@ {{ partial('partial/head') }} {{ partial('partial/google-tag-manager-noscript')}} +
- {{ partial('partial/header') }} - {{ body }} + {{ partial('partial/header', {}, { cache: true }) }} + +
+ {{ partial('partial/header', { className: 'header-top-hidden' }, { cache: true }) }} + {{ body }} +
+
+ {{ partial('partial/mobile_nav') }} {{ partial('partial/after_footer') }} diff --git a/themes/cypress/layout/partial/after_footer.swig b/themes/cypress/layout/partial/after_footer.swig index 3c2ad9c910..9142ca1b3c 100644 --- a/themes/cypress/layout/partial/after_footer.swig +++ b/themes/cypress/layout/partial/after_footer.swig @@ -1,5 +1,6 @@ +{{ js('js/banners_actualization') }} {{ js('js/global_variable') }} {{ js('js/lang_select') }} {{ js('js/vendor/scrollingelement') }} diff --git a/themes/cypress/layout/partial/banners.swig b/themes/cypress/layout/partial/banners.swig new file mode 100644 index 0000000000..654846d63f --- /dev/null +++ b/themes/cypress/layout/partial/banners.swig @@ -0,0 +1,13 @@ +
+ {% for banner in site.data.banners %} + + {% endfor %} +
diff --git a/themes/cypress/layout/partial/header.swig b/themes/cypress/layout/partial/header.swig index b87f6d6dd0..441a60db53 100644 --- a/themes/cypress/layout/partial/header.swig +++ b/themes/cypress/layout/partial/header.swig @@ -1,44 +1,55 @@ -