diff --git a/.circleci/config.yml b/.circleci/config.yml index 52dd1ffa..5abd33a9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ jobs: build: docker: - image: circleci/node:10 - - image: qlikcore/engine:12.292.0 + - image: qlikcore/engine:12.300.0 command: -S AcceptEULA=yes - image: browserless/chrome @@ -32,8 +32,12 @@ jobs: - run: name: Component tests - command: + command: | npm run test:comp + if [ ! -z "$COVERALLS_REPO_TOKEN" ]; then + echo "Uploading coverage results to coveralls.io..." + cat coverage/lcov.info | npx coveralls + fi - run: name: E2E tests @@ -51,7 +55,7 @@ jobs: path: test/e2e/__artifacts__ - run: - name: Sync documentation to S3 bucket + name: Sync dist to S3 bucket command: | if [ "${CIRCLE_BRANCH}" == "master" ] && [ "${TARGET_USER}" != "" ]; then sudo apt-get -y -qq install awscli diff --git a/README.md b/README.md index f8ebc492..07be6304 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ![catwalk](./src/assets/catwalk.svg) +[![Coverage Status](https://coveralls.io/repos/github/qlik-oss/catwalk/badge.svg)](https://coveralls.io/github/qlik-oss/catwalk) + Gain data model insights quickly during its development and validation phases. This tool is useful when you want to explore your data model for whatever reason; maybe you are creating a complex load script, maybe you want to investigate associations. ![screenshot](./screenshot.png) diff --git a/docker-compose.yml b/docker-compose.yml index f950a243..533bedad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.3" services: qix-engine: - image: qlikcore/engine:12.292.0 + image: qlikcore/engine:12.300.0 restart: always command: | -S AcceptEULA=${ACCEPT_EULA} diff --git a/package-lock.json b/package-lock.json index 83634ab9..4e309ea7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,9 +27,9 @@ } }, "@after-work.js/cli": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@after-work.js/cli/-/cli-5.1.1.tgz", - "integrity": "sha512-RYY+Q9SUwEYCHew9KyFPVplzDQAibaIn0ZoTpANuV3dcponC5F1QKnvGiKiLOI2mZglb2YLSq2OavmOKgAiOPQ==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@after-work.js/cli/-/cli-5.1.3.tgz", + "integrity": "sha512-Acdqm98Zs8AwWzOFtS9WPaWqd8yRntPEU8tgpz4HVFzbliE0sjjtUA2+HTjgBBQHSuQvtzdQkEalSkC+AYFCSw==", "dev": true, "requires": { "@after-work.js/chai-plugin-screenshot": "5.1.1", @@ -39,34 +39,34 @@ "chai-subset": "1.6.0", "import-cwd": "2.1.0", "sinon": "7.1.1", - "sinon-chai": "3.2.0", + "sinon-chai": "3.3.0", "source-map-support": "0.5.9", - "yargs": "12.0.2" + "yargs": "12.0.5" } }, "@after-work.js/node": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@after-work.js/node/-/node-5.1.1.tgz", - "integrity": "sha512-+t0JyVotQ9jNEbQeXA+asHolr5tTbNSdZdCkacYZYfE9NgadvdQZKReP9Hdx+YCPny+re/z5nrS1wKQi56gRVg==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@after-work.js/node/-/node-5.1.3.tgz", + "integrity": "sha512-ko1S6YFrbOse5RSkZM7sqa+MGiYnChvLT1Xg7GdOLXsqRhGtfHvFE8sBf0FbEe/2CZrdb3iE069nBqzRRzehqQ==", "dev": true, "requires": { "@after-work.js/register": "5.1.1", "@after-work.js/transform": "5.1.1", "@after-work.js/utils": "5.1.1", "chokidar": "2.0.4", - "globby": "8.0.1", + "globby": "8.0.2", "import-cwd": "2.1.0", "mocha": "5.2.0", "nyc": "13.1.0" } }, "@after-work.js/puppeteer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@after-work.js/puppeteer/-/puppeteer-5.1.1.tgz", - "integrity": "sha512-SH0qfGnWP+fMBqFFPUnkVwzTcXeibO9kuuOXyGnEmM1C6pYpMvvwLlV3cH/xQJKf+pxm0z5DAz+x3nNLvfbHyQ==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@after-work.js/puppeteer/-/puppeteer-5.1.3.tgz", + "integrity": "sha512-yPP6vll3aX64/pnIrJ1Do/6P02r5bfRih0SpsB7bdWk3Jdp664Ubzfv8bD9LIJYGoxWLb3SbeapQnowuS0QBng==", "dev": true, "requires": { - "@after-work.js/node": "5.1.1", + "@after-work.js/node": "5.1.3", "@after-work.js/utils": "5.1.1", "chrome-launcher": "0.10.5", "import-cwd": "2.1.0", @@ -90,24 +90,24 @@ } }, "@after-work.js/serve": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@after-work.js/serve/-/serve-5.1.1.tgz", - "integrity": "sha512-GnCyz3euX7XESwJ7OStwNsgRHyQt6le9o4mNCrzaC1V/kS2tpqjEFxpvlYm3C0sIU/Vl8nRRkCIM8/GZe4WfRA==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@after-work.js/serve/-/serve-5.1.3.tgz", + "integrity": "sha512-dojJ5KrNxUrlwbggmJSehwz0Ze0852p3H1uEM5JHlnvMq2dJIIwpb2EXtohYIkF6f1unkvCC+ISwb0XnfJIm1Q==", "dev": true, "requires": { - "@after-work.js/server": "5.1.1" + "@after-work.js/server": "5.1.3" } }, "@after-work.js/server": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@after-work.js/server/-/server-5.1.1.tgz", - "integrity": "sha512-P4Ja517Df5OwuYYf/3VW61+J49oDEQLvkHHM7ROxFARTeS4pqLg1rdb4xfkho2JaGPIPQLC8MMVCeE+zqjRizQ==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@after-work.js/server/-/server-5.1.3.tgz", + "integrity": "sha512-rF0HHRqIiMqt76Qddip5Lb8PTS7cBL3FDgFPS63sdlPnkOvt81xy/njrsKgsrAKy7utEZ1VPItC/3SDvp1FWVw==", "dev": true, "requires": { "@after-work.js/transform-middleware": "5.1.1", "@after-work.js/utils": "5.1.1", "import-cwd": "2.1.0", - "koa": "2.6.1", + "koa": "2.6.2", "koa-favicon": "2.0.1", "koa-rewrite": "3.0.1", "koa-static": "5.0.0", @@ -480,9 +480,9 @@ } }, "@babel/plugin-proposal-class-properties": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.2.1.tgz", - "integrity": "sha512-/4FKFChkQ2Jgb8lBDsvFX496YTi7UWTetVgS8oJUpX1e/DlaoeEK57At27ug8Hu2zI2g8bzkJ+8k9qrHZRPGPA==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.2.3.tgz", + "integrity": "sha512-FVuQngLoN2iDrpW7LmhPZ2sO4DJxf35FOcwidwB9Ru9tMvI5URthnkVHuG14IStV+TzkMTyLMoOUlSTtrdVwqw==", "dev": true, "requires": { "@babel/helper-create-class-features-plugin": "7.2.3", @@ -945,9 +945,9 @@ } }, "@babel/preset-env": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.2.0.tgz", - "integrity": "sha512-haGR38j5vOGVeBatrQPr3l0xHbs14505DcM57cbJy48kgMFvvHHoYEhHuRV+7vi559yyAUAVbTWzbK/B/pzJng==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.2.3.tgz", + "integrity": "sha512-AuHzW7a9rbv5WXmvGaPX7wADxFkZIqKlbBh1dmZUQp4iwiPpkE/Qnrji6SC4UQCQzvWY/cpHET29eUhXS9cLPw==", "dev": true, "requires": { "@babel/helper-module-imports": "7.0.0", @@ -1085,6 +1085,12 @@ } } }, + "@iarna/toml": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.1.tgz", + "integrity": "sha512-I2EjI9TbEFJNLziNPFfpo64PNanOaK17iL2kTW/jGlGOa4bvHw4VEied83kOEB7NJjXf1KfvmsQ2aEjy3xjiGg==", + "dev": true + }, "@jimp/bmp": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.5.4.tgz", @@ -1407,6 +1413,56 @@ "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", "dev": true }, + "@parcel/fs": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-1.11.0.tgz", + "integrity": "sha512-86RyEqULbbVoeo8OLcv+LQ1Vq2PKBAvWTU9fCgALxuCTbbs5Ppcvll4Vr+Ko1AnmMzja/k++SzNAwJfeQXVlpA==", + "dev": true, + "requires": { + "@parcel/utils": "1.11.0", + "mkdirp": "0.5.1", + "rimraf": "2.6.3" + } + }, + "@parcel/logger": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-1.11.0.tgz", + "integrity": "sha512-lIRfDg+junbFUUeU0QtHX00gKCgEsYHZydFKwrJ8dc0D+WE2SYT1FcVCgpPAfKYgtg0QQMns8E9vzT9UjH92PQ==", + "dev": true, + "requires": { + "@parcel/workers": "1.11.0", + "chalk": "2.4.2", + "grapheme-breaker": "0.3.2", + "ora": "2.1.0", + "strip-ansi": "4.0.0" + } + }, + "@parcel/utils": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-1.11.0.tgz", + "integrity": "sha512-cA3p4jTlaMeOtAKR/6AadanOPvKeg8VwgnHhOyfi0yClD0TZS/hi9xu12w4EzA/8NtHu0g6o4RDfcNjqN8l1AQ==", + "dev": true + }, + "@parcel/watcher": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-1.11.0.tgz", + "integrity": "sha512-1ySF0sH06jyhpaErW1UWC7BNgkAl6PJyBjuu2cLTW1o71J2iQqgGt95cbuqmfmjI3l0xYN+nauDFqHERaj7Z8A==", + "dev": true, + "requires": { + "@parcel/utils": "1.11.0", + "chokidar": "2.0.4" + } + }, + "@parcel/workers": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-1.11.0.tgz", + "integrity": "sha512-USSjRAAQYsZFlv43FUPdD+jEGML5/8oLF0rUzPQTtK4q9kvaXr49F5ZplyLz5lox78cLZ0TxN2bIDQ1xhOkulQ==", + "dev": true, + "requires": { + "@parcel/utils": "1.11.0", + "physical-cpu-count": "2.0.0" + } + }, "@sinonjs/commons": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.3.0.tgz", @@ -1467,6 +1523,12 @@ "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==", "dev": true }, + "abab": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1489,12 +1551,28 @@ "integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==", "dev": true }, + "acorn-globals": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.0.tgz", + "integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==", + "dev": true, + "requires": { + "acorn": "6.0.5", + "acorn-walk": "6.1.1" + } + }, "acorn-jsx": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", "dev": true }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "dev": true + }, "agent-base": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", @@ -1906,6 +1984,12 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "dev": true + }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -1958,6 +2042,15 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, "asn1.js": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", @@ -1995,6 +2088,12 @@ } } }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -2031,6 +2130,12 @@ "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -2038,9 +2143,9 @@ "dev": true }, "autoprefixer": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.2.tgz", - "integrity": "sha512-tYQYJvZvqlJCzF+BLC//uAcdT/Yy4ik9bwZRXr/EehUJ/bjjpTthsWTy8dpowdoIE1sLCDf1ch4Eb2cOSzZC9w==", + "version": "9.4.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.5.tgz", + "integrity": "sha512-M602C0ZxzFpJKqD4V6eq2j+K5CkzlhekCrcQupJmAOrPEZjWJyj/wSeo6qRSNoN6M3/9mtLPQqTTrABfReytQg==", "dev": true, "requires": { "browserslist": "4.4.1", @@ -2051,6 +2156,18 @@ "postcss-value-parser": "3.3.1" } }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, "axobject-query": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", @@ -2202,6 +2319,15 @@ "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", "dev": true }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, "binary-extensions": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", @@ -2271,6 +2397,12 @@ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "dev": true }, + "browser-process-hrtime": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", + "dev": true + }, "browser-resolve": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", @@ -2467,24 +2599,24 @@ } }, "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", "dev": true, "requires": { - "callsites": "0.2.0" + "caller-callsite": "2.0.0" } }, "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz", + "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==", "dev": true }, "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", "dev": true }, "camelcase-keys": { @@ -2496,6 +2628,14 @@ "camelcase": "4.1.0", "map-obj": "2.0.0", "quick-lru": "1.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + } } }, "caniuse-api": { @@ -2522,6 +2662,12 @@ "integrity": "sha512-n2w1gPQSsYyorSVYqPMqbSaz1w7o9ZC8VhOEGI9T5MfGDzp7sbopQxG6GaQmYsaq13Xfx/mkxJUWC1Dz3oZfzw==", "dev": true }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, "ccount": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.3.tgz", @@ -3013,6 +3159,15 @@ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, "command-exists": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.8.tgz", @@ -3152,6 +3307,18 @@ "is-directory": "0.3.1", "js-yaml": "3.12.1", "parse-json": "4.0.0" + }, + "dependencies": { + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "2.0.0", + "resolve-from": "3.0.0" + } + } } }, "create-ecdh": { @@ -3413,6 +3580,21 @@ } } }, + "cssom": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", + "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", + "dev": true + }, + "cssstyle": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.1.1.tgz", + "integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==", + "dev": true, + "requires": { + "cssom": "0.3.4" + } + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -3428,6 +3610,26 @@ "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=", "dev": true }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, + "requires": { + "abab": "2.0.0", + "whatwg-mimetype": "2.3.0", + "whatwg-url": "7.0.0" + } + }, "date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", @@ -3454,13 +3656,10 @@ } }, "decamelize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", - "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", - "dev": true, - "requires": { - "xregexp": "4.0.0" - } + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true }, "decamelize-keys": { "version": "1.1.0", @@ -3472,12 +3671,6 @@ "map-obj": "1.0.1" }, "dependencies": { - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, "map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", @@ -3519,9 +3712,9 @@ "dev": true }, "deepmerge": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.1.0.tgz", - "integrity": "sha512-/TnecbwXEdycfbsM2++O3eGiatEFHjjNciHEwJclM+T5Kd94qD1AP+2elP/Mq0L5b9VZJao5znR01Mz6eX8Seg==" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==" }, "defaults": { "version": "1.0.3", @@ -3607,6 +3800,12 @@ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", "dev": true }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -3653,11 +3852,12 @@ } }, "dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", "dev": true, "requires": { + "arrify": "1.0.1", "path-type": "3.0.0" } }, @@ -3713,6 +3913,15 @@ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", "dev": true }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, + "requires": { + "webidl-conversions": "4.0.2" + } + }, "domhandler": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", @@ -3762,6 +3971,16 @@ "readable-stream": "2.3.6" } }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" + } + }, "editorconfig": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.2.tgz", @@ -3915,9 +4134,9 @@ "dev": true }, "escodegen": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", - "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", + "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", "dev": true, "requires": { "esprima": "3.1.3", @@ -3936,9 +4155,9 @@ } }, "eslint": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.10.0.tgz", - "integrity": "sha512-HpqzC+BHULKlnPwWae9MaVZ5AXJKpkxCVXQHrFaRw3hbDj26V/9ArYM4Rr/SQ8pi6qUPLXSSXC4RBJlyq2Z2OQ==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.12.0.tgz", + "integrity": "sha512-LntwyPxtOHrsJdcSwyQKVtHofPHdv+4+mFwEe91r2V13vqpM8yLr7b1sW+Oo/yheOPkWYsYlYJCkzlFAt8KV7g==", "dev": true, "requires": { "@babel/code-frame": "7.0.0", @@ -3958,6 +4177,7 @@ "glob": "7.1.3", "globals": "11.10.0", "ignore": "4.0.6", + "import-fresh": "3.0.0", "imurmurhash": "0.1.4", "inquirer": "6.2.1", "js-yaml": "3.12.1", @@ -3972,7 +4192,6 @@ "pluralize": "7.0.0", "progress": "2.0.3", "regexpp": "2.0.1", - "require-uncached": "1.0.3", "semver": "5.6.0", "strip-ansi": "4.0.0", "strip-json-comments": "2.0.1", @@ -4252,16 +4471,29 @@ } }, "eslint-plugin-react": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz", - "integrity": "sha512-cVVyMadRyW7qsIUh3FHp3u6QHNhOgVrLQYdQEB1bPWBsgbNCHdFAeNMquBMCcZJu59eNthX053L70l7gRt4SCw==", + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.12.3.tgz", + "integrity": "sha512-WTIA3cS8OzkPeCi4KWuPmjR33lgG9r9Y/7RmnLTRw08MZKgAfnK/n3BO4X0S67MPkVLazdfCNT/XWqcDu4BLTA==", "dev": true, "requires": { "array-includes": "3.0.3", "doctrine": "2.1.0", "has": "1.0.3", "jsx-ast-utils": "2.0.1", - "prop-types": "15.6.2" + "object.fromentries": "2.0.0", + "prop-types": "15.6.2", + "resolve": "1.9.0" + }, + "dependencies": { + "resolve": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz", + "integrity": "sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==", + "dev": true, + "requires": { + "path-parse": "1.0.6" + } + } } }, "eslint-restricted-globals": { @@ -4482,6 +4714,12 @@ "yauzl": "2.4.1" } }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, "falafel": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.1.0.tgz", @@ -4979,6 +5217,23 @@ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", "dev": true }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.7", + "mime-types": "2.1.21" + } + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -5529,15 +5784,6 @@ } } }, - "fswatcher-child": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fswatcher-child/-/fswatcher-child-1.1.1.tgz", - "integrity": "sha512-FVDjVhR71TkJ+ud6vnRwCHvCgK9drGRdimWcTLqw8iN88uL5tTX+/xrwigJdcuQGrWYo3TRw9gRzk9xqR0UPPQ==", - "dev": true, - "requires": { - "chokidar": "2.0.4" - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -5598,6 +5844,15 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + } + }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -5677,13 +5932,13 @@ "dev": true }, "globby": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.1.tgz", - "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", + "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", "dev": true, "requires": { "array-union": "1.0.2", - "dir-glob": "2.2.2", + "dir-glob": "2.0.0", "fast-glob": "2.2.6", "glob": "7.1.3", "ignore": "3.3.10", @@ -5736,6 +5991,22 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "6.7.0", + "har-schema": "2.0.0" + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -5908,6 +6179,15 @@ "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", "dev": true }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dev": true, + "requires": { + "whatwg-encoding": "1.0.5" + } + }, "html-tags": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", @@ -6056,12 +6336,6 @@ "source-map": "0.5.7" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, "esprima": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", @@ -6430,6 +6704,17 @@ "toidentifier": "1.0.0" } }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.16.0" + } + }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -6494,23 +6779,20 @@ } }, "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", + "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", "dev": true, "requires": { - "caller-path": "2.0.0", - "resolve-from": "3.0.0" + "parent-module": "1.0.0", + "resolve-from": "4.0.0" }, "dependencies": { - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "dev": true, - "requires": { - "caller-callsite": "2.0.0" - } + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true } } }, @@ -6929,6 +7211,12 @@ "has-symbols": "1.0.0" } }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, "is-url": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", @@ -6980,6 +7268,12 @@ "isarray": "1.0.0" } }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, "istanbul-lib-coverage": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", @@ -7136,6 +7430,57 @@ "esprima": "4.0.1" } }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdom": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-13.0.0.tgz", + "integrity": "sha512-Kmq4ASMNkgpY+YufE322EnIKoiz0UWY2DRkKlU7d5YrIW4xiVRhWFrZV1fr6w/ZNxQ50wGAH5gGRzydgnmkkvw==", + "dev": true, + "requires": { + "abab": "2.0.0", + "acorn": "6.0.5", + "acorn-globals": "4.3.0", + "array-equal": "1.0.0", + "cssom": "0.3.4", + "cssstyle": "1.1.1", + "data-urls": "1.1.0", + "domexception": "1.0.1", + "escodegen": "1.11.0", + "html-encoding-sniffer": "1.0.2", + "nwsapi": "2.0.9", + "parse5": "5.1.0", + "pn": "1.1.0", + "request": "2.88.0", + "request-promise-native": "1.0.5", + "saxes": "3.1.6", + "symbol-tree": "3.2.2", + "tough-cookie": "2.5.0", + "w3c-hr-time": "1.0.1", + "w3c-xmlserializer": "1.0.1", + "webidl-conversions": "4.0.2", + "whatwg-encoding": "1.0.5", + "whatwg-mimetype": "2.3.0", + "whatwg-url": "7.0.0", + "ws": "6.1.2", + "xml-name-validator": "3.0.0" + }, + "dependencies": { + "ws": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.2.tgz", + "integrity": "sha512-rfUqzvz0WxmSXtJpPMX2EeASXabOrSMk1ruMOV3JBTBjo4ac2lDjGGsbQSyxj8Odhw5fBib8ZKEjDNvgouNKYw==", + "dev": true, + "requires": { + "async-limiter": "1.0.0" + } + } + } + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -7148,6 +7493,12 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -7160,6 +7511,12 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, "json5": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", @@ -7177,6 +7534,18 @@ } } }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, "jsx-ast-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", @@ -7214,9 +7583,9 @@ "dev": true }, "koa": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.6.1.tgz", - "integrity": "sha512-n9R5Eex4y0drUeqFTeCIeXyz8wjr2AxBo2Cq8LvmiXbJl4yDA5KIrecMPkhnmgACZnPXMRyCLbJoyLmpM9aFAw==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.6.2.tgz", + "integrity": "sha512-KdnBFhTgh9ysMMoYe4J4fLvaKjT7mF3nRYV8MjxLzx6qywFNeptqi4xevyUltg1fZl2CFJ+HeLXuCGx07Yvl/A==", "dev": true, "requires": { "accepts": "1.3.5", @@ -7490,6 +7859,12 @@ "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -7689,6 +8064,12 @@ "yargs-parser": "10.1.0" }, "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -7741,6 +8122,15 @@ "find-up": "2.1.0", "read-pkg": "3.0.0" } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "4.1.0" + } } } }, @@ -8228,6 +8618,12 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, + "nwsapi": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.9.tgz", + "integrity": "sha512-nlWFSCTYQcHk/6A9FFnfhKc14c3aFhfdNBXgo8Qgi9QTBu/qg3Ww+Uiz9wMzXd1T8GFxPc2QIHB6Qtf2XFryFQ==", + "dev": true + }, "nyc": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-13.1.0.tgz", @@ -9367,6 +9763,12 @@ } } }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -9449,6 +9851,18 @@ "has": "1.0.3" } }, + "object.fromentries": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz", + "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==", + "dev": true, + "requires": { + "define-properties": "1.1.3", + "es-abstract": "1.13.0", + "function-bind": "1.1.1", + "has": "1.0.3" + } + }, "object.getownpropertydescriptors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", @@ -9661,9 +10075,9 @@ "dev": true }, "parcel-bundler": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/parcel-bundler/-/parcel-bundler-1.10.3.tgz", - "integrity": "sha512-Lj31fr5o2AZFbazghL/MrubzvJEXLwx24rd3MiR3lncmqCXbd5q0hgl1kpV6X+vRaN9/cSDR8G0lotmgl5OyZg==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/parcel-bundler/-/parcel-bundler-1.11.0.tgz", + "integrity": "sha512-H0w/Obx76vWiG+UtofznfcHZJBmd6JA5iCn7zrGBINyVAh+Nt/JLD6QDROghHLXfJkO4XyczsB+fO+nPbXlFfA==", "dev": true, "requires": { "@babel/code-frame": "7.0.0", @@ -9673,11 +10087,17 @@ "@babel/plugin-transform-flow-strip-types": "7.2.3", "@babel/plugin-transform-modules-commonjs": "7.2.0", "@babel/plugin-transform-react-jsx": "7.2.0", - "@babel/preset-env": "7.2.0", + "@babel/preset-env": "7.2.3", "@babel/runtime": "7.2.0", "@babel/template": "7.2.2", "@babel/traverse": "7.2.3", "@babel/types": "7.2.2", + "@iarna/toml": "2.2.1", + "@parcel/fs": "1.11.0", + "@parcel/logger": "1.11.0", + "@parcel/utils": "1.11.0", + "@parcel/watcher": "1.11.0", + "@parcel/workers": "1.11.0", "ansi-to-html": "0.6.9", "babylon-walk": "1.0.2", "browserslist": "4.4.1", @@ -9692,9 +10112,7 @@ "dotenv-expand": "4.2.0", "fast-glob": "2.2.6", "filesize": "3.6.1", - "fswatcher-child": "1.1.1", "get-port": "3.2.0", - "grapheme-breaker": "0.3.2", "htmlnano": "0.1.10", "is-glob": "4.0.0", "is-url": "1.2.4", @@ -9705,9 +10123,7 @@ "node-forge": "0.7.6", "node-libs-browser": "2.2.0", "opn": "5.4.0", - "ora": "2.1.0", - "physical-cpu-count": "2.0.0", - "postcss": "6.0.23", + "postcss": "7.0.13", "postcss-value-parser": "3.3.1", "posthtml": "0.11.3", "posthtml-parser": "0.4.1", @@ -9717,10 +10133,7 @@ "serialize-to-js": "1.2.2", "serve-static": "1.13.2", "source-map": "0.6.1", - "strip-ansi": "4.0.0", "terser": "3.14.1", - "toml": "2.3.5", - "tomlify-j0.4": "3.0.0", "v8-compile-cache": "2.0.2", "ws": "5.2.2" }, @@ -10029,17 +10442,6 @@ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "requires": { - "chalk": "2.4.2", - "source-map": "0.6.1", - "supports-color": "5.5.0" - } - }, "resolve": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz", @@ -10060,6 +10462,15 @@ "svgo": "1.1.1" } }, + "parent-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.0.tgz", + "integrity": "sha512-8Mf5juOMmiE4FcmzYc4IaiS9L3+9paz2KOiXzkRviCP6aDmN49Hz6EMWz0lGNp9pX80GvvAuLADtyGfW/Em3TA==", + "dev": true, + "requires": { + "callsites": "3.0.0" + } + }, "parse-asn1": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.3.tgz", @@ -10148,6 +10559,12 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "dev": true + }, "parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", @@ -10253,6 +10670,12 @@ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", "dev": true }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, "phin": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", @@ -10319,6 +10742,12 @@ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true + }, "pngjs": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.3.3.tgz", @@ -11395,6 +11824,12 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "dev": true + }, "public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -11470,6 +11905,12 @@ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", "dev": true }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, "query-string": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", @@ -11517,6 +11958,15 @@ } } }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "dev": true, + "requires": { + "performance-now": "2.1.0" + } + }, "rafl": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/rafl/-/rafl-1.2.2.tgz", @@ -11596,9 +12046,9 @@ } }, "react-cookie-consent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/react-cookie-consent/-/react-cookie-consent-2.0.0.tgz", - "integrity": "sha512-yGL7KGpUNz2u3Itq5e5G1xRskazfKunHFMwGqikH5fN0QY2XUTGEAh/r3B7z6/WwuAO0otTl+e2aMavUYAJT1A==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/react-cookie-consent/-/react-cookie-consent-2.0.1.tgz", + "integrity": "sha512-c2sPPHBW409D6Iq89eueaq6JfqyBJLLjEtMT+RiAoHSCPHzilU8f+S52E6/83Dr2gtrk0fGkkVeqgktCQDmHng==", "requires": { "js-cookie": "2.2.0" } @@ -11615,11 +12065,11 @@ } }, "react-floater": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.6.2.tgz", - "integrity": "sha512-fUSQ1L74g05FPOLZ2dNpRdZHoVESFeyn3YAKjHkpprQb3NVspFYQsTpRyGLXNaEDn7IGRq0H+aKUg8yhX3kJRw==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.6.1.tgz", + "integrity": "sha512-3VNFo7hsxQE5m79lYp1KI2/j2X5QVxlGQrVr8ALDCSjn7M2+sw+1z94kyice/Q0s3dFaDYMuFXOZOugQvUCfNA==", "requires": { - "deepmerge": "3.1.0", + "deepmerge": "2.2.1", "exenv": "1.2.2", "is-lite": "0.2.2", "popper.js": "1.14.6", @@ -11652,6 +12102,25 @@ "scroll-doc": "0.2.1", "scrollparent": "2.0.1", "tree-changes": "0.4.0" + }, + "dependencies": { + "deepmerge": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.1.0.tgz", + "integrity": "sha512-/TnecbwXEdycfbsM2++O3eGiatEFHjjNciHEwJclM+T5Kd94qD1AP+2elP/Mq0L5b9VZJao5znR01Mz6eX8Seg==" + }, + "react-floater": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.6.2.tgz", + "integrity": "sha512-fUSQ1L74g05FPOLZ2dNpRdZHoVESFeyn3YAKjHkpprQb3NVspFYQsTpRyGLXNaEDn7IGRq0H+aKUg8yhX3kJRw==", + "requires": { + "deepmerge": "3.1.0", + "exenv": "1.2.2", + "is-lite": "0.2.2", + "popper.js": "1.14.6", + "react-proptype-conditional-require": "1.0.4" + } + } } }, "react-lifecycles-compat": { @@ -11685,27 +12154,15 @@ } }, "react-test-renderer": { - "version": "16.6.3", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.6.3.tgz", - "integrity": "sha512-B5bCer+qymrQz/wN03lT0LppbZUDRq6AMfzMKrovzkGzfO81a9T+PWQW6MzkWknbwODQH/qpJno/yFQLX5IWrQ==", + "version": "16.7.0-alpha.2", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.7.0-alpha.2.tgz", + "integrity": "sha512-taA9MrHMi7hEM/cKgvcvht+9cszhPirCaSP99yxkVQ2JwQxYSltGYq2gFf/UQBqGJMgmgEghN62rxziaL1EK+A==", "dev": true, "requires": { "object-assign": "4.1.1", "prop-types": "15.6.2", "react-is": "16.7.0", - "scheduler": "0.11.3" - }, - "dependencies": { - "scheduler": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.11.3.tgz", - "integrity": "sha512-i9X9VRRVZDd3xZw10NY5Z2cVMbdYg6gqFecfj79USv1CFN+YrJ3gIPRKf1qlY+Sxly4djoKdfx1T+m9dnRB8kQ==", - "dev": true, - "requires": { - "loose-envify": "1.4.0", - "object-assign": "4.1.1" - } - } + "scheduler": "0.12.0" } }, "react-use-promise": { @@ -12280,6 +12737,72 @@ "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", "dev": true }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.8.0", + "caseless": "0.12.0", + "combined-stream": "1.0.7", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.3.3", + "har-validator": "5.1.3", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.21", + "oauth-sign": "0.9.0", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.4.3", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "1.1.31", + "punycode": "1.4.1" + } + } + } + }, + "request-promise-core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", + "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", + "dev": true, + "requires": { + "lodash": "4.17.11" + } + }, + "request-promise-native": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", + "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", + "dev": true, + "requires": { + "request-promise-core": "1.1.1", + "stealthy-require": "1.1.1", + "tough-cookie": "2.5.0" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -12298,24 +12821,6 @@ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "0.1.0", - "resolve-from": "1.0.1" - }, - "dependencies": { - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - } - } - }, "resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", @@ -12469,6 +12974,15 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "saxes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.6.tgz", + "integrity": "sha512-LAYs+lChg1v5uKNzPtsgTxSS5hLo8aIhSMCJt1WMpefAxm3D1RTpMwSpb6ebdL31cubiLTnhokVktBW+cv9Y9w==", + "dev": true, + "requires": { + "xmlchars": "1.3.1" + } + }, "scheduler": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.12.0.tgz", @@ -12690,9 +13204,9 @@ } }, "sinon-chai": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.2.0.tgz", - "integrity": "sha512-Z72B4a0l0IQe5uWi9yzcqX/Ml6K9e1Hp03NmkjJnRG3gDsKTX7KvLFZsVUmCaz0eqeXLLK089mwTsP1P1W+DUQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.3.0.tgz", + "integrity": "sha512-r2JhDY7gbbmh5z3Q62pNbrjxZdOAjpsqW/8yxAZRSqLZqowmfGZPGUZPFf3UX36NLis0cv8VEM5IJh9HgkSOAA==", "dev": true }, "slash": { @@ -12923,6 +13437,23 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "sshpk": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", + "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==", + "dev": true, + "requires": { + "asn1": "0.2.4", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.2", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" + } + }, "stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -12947,7 +13478,7 @@ "integrity": "sha512-6flshd3F1Gwm+Ksxq463LtFd1liC77N/PX1FVVc3OzL3hAmo2fwHFbuArkcfi7s9rTNsLEhcRmXGFZhlgy40uw==", "dev": true, "requires": { - "escodegen": "1.9.1" + "escodegen": "1.11.0" } }, "static-extend": { @@ -12991,6 +13522,27 @@ "shallow-copy": "0.0.1", "static-eval": "2.0.0", "through2": "2.0.5" + }, + "dependencies": { + "escodegen": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", + "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", + "dev": true, + "requires": { + "esprima": "3.1.3", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "optionator": "0.8.2", + "source-map": "0.6.1" + } + }, + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + } } }, "statuses": { @@ -12999,6 +13551,12 @@ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, "stream-browserify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", @@ -13128,7 +13686,7 @@ "integrity": "sha512-kIuX0/9/I2mZeHz6EoFt7UpLt7Mz+ic9/PmFwKMdq4BkQHikg3FkcgAElLdAmaI8Au1JEUOS996ZFE+mwXytmA==", "dev": true, "requires": { - "autoprefixer": "9.4.2", + "autoprefixer": "9.4.5", "balanced-match": "1.0.0", "chalk": "2.4.2", "cosmiconfig": "5.0.7", @@ -13137,7 +13695,7 @@ "file-entry-cache": "2.0.0", "get-stdin": "6.0.0", "global-modules": "1.0.0", - "globby": "8.0.1", + "globby": "8.0.2", "globjoin": "0.1.4", "html-tags": "2.0.0", "ignore": "5.0.4", @@ -13591,6 +14149,12 @@ "util.promisify": "1.0.0" } }, + "symbol-tree": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", + "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", + "dev": true + }, "table": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/table/-/table-5.2.1.tgz", @@ -13782,17 +14346,24 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", "dev": true }, - "toml": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.5.tgz", - "integrity": "sha512-ulY/Z2yPWKl/3JvGJvnEe7mXqVt2+TtDoRxJNgTAwO+3lwXefeCHS697NN0KRy6q7U/b1MnSnj/UGF/4U0U2WQ==", - "dev": true + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "1.1.31", + "punycode": "2.1.1" + } }, - "tomlify-j0.4": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tomlify-j0.4/-/tomlify-j0.4-3.0.0.tgz", - "integrity": "sha512-2Ulkc8T7mXJ2l0W476YC/A209PR38Nw8PuaCNtk9uI3t1zzFdGQeWYGQvmj2PZkVvRC/Yoi4xQKMRnWc/N29tQ==", - "dev": true + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "dev": true, + "requires": { + "punycode": "2.1.1" + } }, "tree-changes": { "version": "0.4.0", @@ -13845,6 +14416,21 @@ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -14172,6 +14758,12 @@ "object.getownpropertydescriptors": "2.0.3" } }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, "v8-compile-cache": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", @@ -14200,6 +14792,17 @@ "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", "dev": true }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + } + }, "vfile": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", @@ -14242,6 +14845,26 @@ "indexof": "0.0.1" } }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "dev": true, + "requires": { + "browser-process-hrtime": "0.1.3" + } + }, + "w3c-xmlserializer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.0.1.tgz", + "integrity": "sha512-XZGI1OH/OLQr/NaJhhPmzhngwcAnZDLytsvXnRmlYeRkmbb0I7sqFFA22erq4WQR0sUu17ZSQOAV9mFwCqKRNg==", + "dev": true, + "requires": { + "domexception": "1.0.1", + "webidl-conversions": "4.0.2", + "xml-name-validator": "3.0.0" + } + }, "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -14251,6 +14874,38 @@ "defaults": "1.0.3" } }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "dev": true, + "requires": { + "lodash.sortby": "4.7.0", + "tr46": "1.0.1", + "webidl-conversions": "4.0.2" + } + }, "whet.extend": { "version": "0.9.9", "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", @@ -14367,6 +15022,12 @@ "xtend": "4.0.1" } }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, "xml-parse-from-string": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", @@ -14389,10 +15050,10 @@ "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", "dev": true }, - "xregexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", - "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "xmlchars": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-1.3.1.tgz", + "integrity": "sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw==", "dev": true }, "xtend": { @@ -14414,13 +15075,13 @@ "dev": true }, "yargs": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", - "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", "dev": true, "requires": { "cliui": "4.1.0", - "decamelize": "2.0.0", + "decamelize": "1.2.0", "find-up": "3.0.0", "get-caller-file": "1.0.3", "os-locale": "3.1.0", @@ -14430,16 +15091,17 @@ "string-width": "2.1.1", "which-module": "2.0.0", "y18n": "4.0.0", - "yargs-parser": "10.1.0" + "yargs-parser": "11.1.1" } }, "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", "dev": true, "requires": { - "camelcase": "4.1.0" + "camelcase": "5.0.0", + "decamelize": "1.2.0" } }, "yauzl": { diff --git a/package.json b/package.json index aaee1e2b..2c0a1e19 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,9 @@ "build": "cross-env NODE_ENV=production parcel build src/index.html", "start": "parcel src/index.html --open", "test:unit": "aw --glob test/unit/*.spec.js --src src/logic/*.js --coverage", - "test:comp": "aw -c ./test/comp/aw.config.js", + "test:comp": "aw -c ./test/comp/aw.config.js && nyc report -t ./coverage/.nyc_output -r lcov -r text", "test:e2e": "aw puppeteer -c ./test/e2e/aw.config.js", + "coverage:html": "nyc report -t ./coverage/.nyc_output -r html", "lint": "eslint --ext .js,.jsx . && stylelint src/.", "lint:fix": "eslint --ext .js,.jsx --fix . && stylelint --fix src/." }, @@ -21,7 +22,7 @@ "enigma.js": "2.4.0", "prop-types": "15.6.2", "react": "16.7.0-alpha.2", - "react-cookie-consent": "2.0.0", + "react-cookie-consent": "2.0.1", "react-dom": "16.7.0-alpha.2", "react-ga": "2.5.6", "react-joyride": "2.0.2", @@ -29,33 +30,36 @@ "react-svg-inline": "2.1.1", "react-use-promise": "0.0.0-alpha.1", "react-virtualized": "9.21.0", + "react-floater": "0.6.1", "react-contextmenu": "2.10.0" }, "devDependencies": { - "@after-work.js/cli": "5.1.1", - "@after-work.js/node": "5.1.1", - "@after-work.js/puppeteer": "5.1.1", - "@after-work.js/serve": "5.1.1", + "@after-work.js/cli": "5.1.3", + "@after-work.js/node": "5.1.3", + "@after-work.js/puppeteer": "5.1.3", + "@after-work.js/serve": "5.1.3", "@babel/core": "7.2.2", - "@babel/plugin-proposal-class-properties": "7.2.1", + "@babel/plugin-proposal-class-properties": "7.2.3", "@babel/plugin-transform-async-to-generator": "7.2.0", "@babel/plugin-transform-react-jsx": "7.2.0", "@babel/plugin-transform-runtime": "7.2.0", - "@babel/preset-env": "7.2.0", + "@babel/preset-env": "7.2.3", "@babel/preset-react": "7.0.0", - "autoprefixer": "9.4.2", + "autoprefixer": "9.4.5", "babel-eslint": "10.0.1", "babel-plugin-istanbul": "5.1.0", "cross-env": "5.2.0", - "eslint": "5.10.0", + "eslint": "5.12.0", "eslint-config-airbnb": "17.1.0", "eslint-plugin-import": "2.14.0", "eslint-plugin-jsx-a11y": "6.1.2", - "eslint-plugin-react": "7.11.1", - "parcel-bundler": "1.10.3", + "jsdom": "13.0.0", "parcel-plugin-inlinesvg": "0.1.0", + "raf": "3.4.1", + "react-test-renderer": "16.7.0-alpha.2", + "eslint-plugin-react": "7.12.3", + "parcel-bundler": "1.11.0", "postcss-nested": "4.1.1", - "react-test-renderer": "16.6.3", "stylelint": "9.9.0", "stylelint-config-standard": "18.2.0", "stylelint-order": "2.0.0" diff --git a/src/assets/more-horizontal-outline.svg b/src/assets/more-horizontal-outline.svg new file mode 100644 index 00000000..4d8d1e06 --- /dev/null +++ b/src/assets/more-horizontal-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/app.jsx b/src/components/app.jsx index 4ee33612..a5541182 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -10,6 +10,7 @@ import Splash from './splash'; import Cubes from './cubes'; import Guide from './guide'; +import { useReloadInProgress } from '../enigma/reload-in-progress-interceptor'; import './app.pcss'; export const AppContext = React.createContext(null); @@ -31,8 +32,17 @@ export default function App() { if (!app) return; session.close(); }, [app]); + const reloadInProgress = useReloadInProgress(app); + + let reloadSplasher = null; + if (reloadInProgress) { + reloadSplasher =
Reload in progress
; + } if (!appLayout) { + if (reloadInProgress) { + return reloadSplasher; + } return ( + {reloadSplasher} ); } diff --git a/src/components/app.pcss b/src/components/app.pcss index 48dd834a..e941229b 100644 --- a/src/components/app.pcss +++ b/src/components/app.pcss @@ -10,3 +10,29 @@ height: 100vh; position: relative; } + +.reload-splasher { + background-color: rgba(128, 128, 128, 0.5); + bottom: 0; + color: white; + display: flex; + left: 0; + pointer-events: all; + position: absolute; + right: 0; + top: 0; + z-index: 1000; +} + +.reload-label { + background-color: lightgrey; + border: 2px solid orange; + border-radius: 0.75rem; + box-shadow: 0 0 2em 0 #999; + font-size: 4rem; + margin: auto; + padding: 1rem 2rem; + position: relative; + text-align: center; + text-shadow: 0 0 1rem #777; +} diff --git a/src/components/cubes.jsx b/src/components/cubes.jsx index 16e1097d..8f433b5f 100644 --- a/src/components/cubes.jsx +++ b/src/components/cubes.jsx @@ -50,7 +50,8 @@ export function Cubes({ app }) { removeCube(cube.id)} /> - )); + + )); return (
diff --git a/src/components/field.jsx b/src/components/field.jsx index 92392cbb..eece5f76 100644 --- a/src/components/field.jsx +++ b/src/components/field.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import SVGInline from 'react-svg-inline'; import close from '../assets/close-outline.svg'; - +import { getSelectionBarTooltip } from './tooltip'; import './field.pcss'; const clearButton = { className: 'clear-selection', svg: close }; @@ -40,7 +40,6 @@ export default function Field({ ? ( { onClearSelection(event, field); }} /> ) : null; - return (
@@ -48,7 +47,7 @@ export default function Field({
{field}
-
+
{' '} {fieldCounts(layout.qListObject.qDimensionInfo, fieldData)}
diff --git a/src/components/filterbox.jsx b/src/components/filterbox.jsx index b177ba43..ac60f261 100644 --- a/src/components/filterbox.jsx +++ b/src/components/filterbox.jsx @@ -1,7 +1,7 @@ import ReactDOM from 'react-dom'; import React, { useRef, useState } from 'react'; import PropTypes from 'prop-types'; -import Column from 'react-virtualized/dist/es/Table/Column'; +import { Column } from 'react-virtualized'; import useClickOutside from './use/click-outside'; import VirtualTable from './virtual-table'; @@ -82,6 +82,14 @@ function rowRenderer({
); } +rowRenderer.propTypes = { + defaultProps: PropTypes.object.isRequired, + rowData: PropTypes.object.isRequired, + style: PropTypes.object.isRequired, + columns: PropTypes.object.isRequired, + className: PropTypes.string.isRequired, + key: PropTypes.string.isRequired, +}; function noRowsRenderer() { return
No values
; diff --git a/src/components/hypercube-table.jsx b/src/components/hypercube-table.jsx index 270b8f41..40055dfd 100644 --- a/src/components/hypercube-table.jsx +++ b/src/components/hypercube-table.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Column from 'react-virtualized/dist/es/Table/Column'; +import { Column } from 'react-virtualized'; import VirtualTable from './virtual-table'; @@ -56,6 +56,14 @@ function rowRenderer({
); } +rowRenderer.propTypes = { + defaultProps: PropTypes.object.isRequired, + rowData: PropTypes.object.isRequired, + style: PropTypes.object.isRequired, + columns: PropTypes.object.isRequired, + className: PropTypes.string.isRequired, + key: PropTypes.string.isRequired, +}; function headerRowRenderer({ className, columns, style, diff --git a/src/components/model.jsx b/src/components/model.jsx index 01ecd6f7..5c7ea165 100644 --- a/src/components/model.jsx +++ b/src/components/model.jsx @@ -1,13 +1,17 @@ import React, { useState, useEffect, useMemo } from 'react'; import PropTypes from 'prop-types'; import usePromise from 'react-use-promise'; - +import ReactFloater from 'react-floater'; import ScrollArea from './scroll-area'; import TableField from './table-field'; import logic from '../logic/logic'; import atplay from '../logic/atplay'; +import { getExtraInfoForField, getAssosicationTooltip, getTableTooltip } from './tooltip'; + import './model.pcss'; +import './tooltip.pcss'; + function findAttribute(event, attrName) { let el = event.target; @@ -29,6 +33,14 @@ export default function Model({ app, appLayout }) { const [openBoxes, setOpenBoxes] = useState({}); const [queryModel, setQueryModel] = useState(null); const [atPlayModel, setAtPlayModel] = useState(null); + const [currentDetailsView, setCurrentDetailsView] = useState(null); // The currently open extra info dialog + + let boxIdCounter = 0; + + function getExtraInfoForTableField(tableName, fieldName) { + const field = queryModel.getTableField(tableName, fieldName); + return getExtraInfoForField(field); + } useEffect(() => { if (!tablesAndKeys) return; @@ -40,13 +52,40 @@ export default function Model({ app, appLayout }) { setAtPlayModel(newAtPlayModel); }, [tablesAndKeys]); - const toggleField = (evt) => { + + function showFieldDetails(table, field, boxId) { + setCurrentDetailsView({ boxId, content: getExtraInfoForTableField(table, field) }); + } + const onClick = (evt) => { if (evt.ctrlKey || evt.metaKey) { return; } + + // The following attribute is set if the extra-info icon is clicked + const extraInfoIcon = findAttribute(evt, 'data-extra-info-icon'); + + // The following attribute indicates what div to align the extra info popup with + const boxId = findAttribute(evt, 'data-boxid'); + + // The following attribute is set if the table header is clicked + const dataTableHeader = findAttribute(evt, 'data-tableheader'); + + // The field the click is on const field = findAttribute(evt, 'fieldz'); + + // The table the click is on const table = findAttribute(evt, 'tablez'); - if (field) { + + // Depending on where the click was do different things: + if (extraInfoIcon) { + // Show field details + showFieldDetails(table, field, boxId); + } else if (dataTableHeader) { + // Re-sort the main data model based on the clicked table + const newQueryModel = new logic.QueryModel(tablesAndKeys, table); + setQueryModel(newQueryModel); + } else if (field) { + // Open or close a field if (openBoxes[field]) { delete openBoxes[field]; } else { @@ -54,9 +93,6 @@ export default function Model({ app, appLayout }) { } const newAtPlayModel = new atplay.AtPlayModel(queryModel, openBoxes); setAtPlayModel(newAtPlayModel); - } else if (table) { - const newQueryModel = new logic.QueryModel(tablesAndKeys, table); - setQueryModel(newQueryModel); } }; @@ -72,6 +108,13 @@ export default function Model({ app, appLayout }) { ); } + const extraInfoContainer = currentDetailsView ? ( +
+ { if (event === 'close') { setCurrentDetailsView(null); } }} /> +
+ ) : null; + + let odd = false; const assocationsHighlighted = Object.keys(openBoxes).length > 1; const gridz = queryModel.resultTableList.map((tableName) => { let columnClasses = 'column'; @@ -80,10 +123,14 @@ export default function Model({ app, appLayout }) { } else if (assocationsHighlighted) { columnClasses += ' notTableAtPlay'; } + if (odd) { + columnClasses += ' odd'; + } + odd = !odd; return ( -
-
+
+
{tableName}
{queryModel.tables[tableName].qNoOfRows}
@@ -95,6 +142,7 @@ export default function Model({ app, appLayout }) { cellContainerStyle.height = '24em'; } + const x = queryModel.grid[fieldName][tableName]; if (x && !x.isEmpty) { let classes = 'vertcell keycell'; @@ -137,21 +185,24 @@ export default function Model({ app, appLayout }) { }; } - + boxIdCounter += 1; + const currentboxid = boxIdCounter; return (
- + + {x.subsetRatioText ? (
{x.subsetRatioText}
) : null} {x.hasAssociationToLeft ? ( -
+
@@ -159,7 +210,7 @@ export default function Model({ app, appLayout }) {
) : null} {x.hasAssociationToRight ? ( -
+
@@ -190,7 +241,7 @@ export default function Model({ app, appLayout }) { return null; } return ( -
+
- {queryModel.tables[tableName].qFields.map((field) => { - const isFilterboxOpen = openBoxes[field.qName]; - if (field.qKeyType === 'NOT_KEY') { + {queryModel.tables[tableName].qFields.map((fieldData) => { + const isFilterboxOpen = openBoxes[fieldData.qName]; + if (fieldData.qKeyType === 'NOT_KEY') { let classes = 'vertcell'; - if (atPlayModel.keysAtPlay[field.qName]) { + if (atPlayModel.keysAtPlay[fieldData.qName]) { classes += ' keyAtPlay'; } else if (assocationsHighlighted) { classes += ' notKeyAtPlay'; @@ -217,14 +268,17 @@ export default function Model({ app, appLayout }) { cellContainerStyle.height = '30em'; } + boxIdCounter += 1; + const currentboxid = boxIdCounter; return (
- +
); } @@ -236,18 +290,21 @@ export default function Model({ app, appLayout }) { }); return ( - -
-
- {gridz} + + +
+
+ {gridz} +
-
- + + {extraInfoContainer} + ); } diff --git a/src/components/model.pcss b/src/components/model.pcss index 3557ba2c..6f6f1807 100644 --- a/src/components/model.pcss +++ b/src/components/model.pcss @@ -2,6 +2,7 @@ --table-background: #f2f2f2; --grid-background: #f2f2f2; --even-column-background: #f6f6f6; + --odd-column-background: #e8e8e8; --association-border: #999; background-color: var(--grid-background); @@ -60,7 +61,6 @@ right: -1em; top: 2.5em; width: 1em; - z-index: 0; } .association-to-right-a { @@ -91,7 +91,6 @@ left: 0.5em; position: absolute; top: -1em; - z-index: 0; } .subsetratio { @@ -119,7 +118,6 @@ position: absolute; top: 2.5em; width: 1em; - z-index: 0; } .association-to-left-a { @@ -151,7 +149,6 @@ position: absolute; right: 0.5em; top: -1em; - z-index: 0; } .association-to-left:nth-child(0) { @@ -164,7 +161,6 @@ position: absolute; right: -3.5em; top: 3.5em; - z-index: 3; } .betweener.insideTable { @@ -189,6 +185,12 @@ padding: 3.5em; } + .column.odd { + background: linear-gradient(to bottom, var(--odd-column-background) 0%, var(--table-background) 100%); + background-repeat: no-repeat; + background-size: auto 30rem; + } + &:nth-child(even) { background-color: var(--even-column-background); diff --git a/src/components/scroll-area.jsx b/src/components/scroll-area.jsx index 9bddb142..fa82b0a2 100644 --- a/src/components/scroll-area.jsx +++ b/src/components/scroll-area.jsx @@ -59,4 +59,6 @@ export default function ScrollArea({ ScrollArea.propTypes = { width: PropTypes.string.isRequired, height: PropTypes.string.isRequired, + className: PropTypes.string.isRequired, + children: PropTypes.object.isRequired, }; diff --git a/src/components/splash.pcss b/src/components/splash.pcss index 7711d595..385f2c95 100644 --- a/src/components/splash.pcss +++ b/src/components/splash.pcss @@ -39,7 +39,7 @@ } .icon { - background-image: url("data:image/svg+xml;utf8,QVF"); + background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2057.53%2057.53%27%3E%3Cdefs%3E%3Cstyle%3E.b%7Bfill%3Anone%3Bstroke%3A%23fff%3Bstroke-miterlimit%3A10%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ccircle%20cx%3D%2728.76%27%20cy%3D%2728.76%27%20r%3D%2728.76%27%20fill%3D%27%23009845%27%2F%3E%3Cpath%20class%3D%27b%27%20d%3D%27M35.08%2011.74c.08%201.48.1%203%20.26%204.44a1.75%201.75%200%200%200%201.9%201.56c1.44.06%202.88.09%204.32.13%27%2F%3E%3Cpath%20class%3D%27b%27%20d%3D%27M15.14%2040.78v2.64a3.13%203.13%200%200%200%203.48%203.48h22.32a3.06%203.06%200%200%200%203.36-3.36V17.86a3.88%203.88%200%200%200-1.21-2.88L37.81%209.7a3.81%203.81%200%200%200-2.88-1.09H18.49a3.18%203.18%200%200%200-3.36%203.36v13.44%27%2F%3E%3Ctext%20transform%3D%27translate%2811.21%2037.06%29%27%20font-size%3D%2714%27%20fill%3D%27%23fff%27%20font-family%3D%27Source%20Sans%20Pro%27%20font-weight%3D%27700%27%3EQ%3Ctspan%20x%3D%279.25%27%20y%3D%270%27%3EVF%3C%2Ftspan%3E%3C%2Ftext%3E%3C%2Fsvg%3E"); background-repeat: no-repeat; background-size: contain; display: inline-block; diff --git a/src/components/table-field.jsx b/src/components/table-field.jsx index f1bb8dee..467adf5c 100644 --- a/src/components/table-field.jsx +++ b/src/components/table-field.jsx @@ -1,35 +1,22 @@ import React from 'react'; import PropTypes from 'prop-types'; +import SVGInline from 'react-svg-inline'; +import moreHorizontalOutline from '../assets/more-horizontal-outline.svg'; import Field from './field'; import Filterbox from './filterbox'; import useModel from './use/model'; import useLayout from './use/layout'; +import { getTooltipForField, getTooltipForSyntheticField } from './tooltip'; import './table-field.pcss'; - -function firstFewValues(layout) { - const rowToText = row => `${row[0].qText || ''}`; - - const selected = layout.qListObject.qDataPages[0].qMatrix.filter(row => row[0].qState === 'S' || row[0].qState === 'O'); - const excluded = layout.qListObject.qDataPages[0].qMatrix.filter(row => row[0].qState === 'X'); - - let result = ''; - if (selected.length > 0) { - result += `Example values:\n${selected.map(rowToText).join(', ')}\n`; - } - if (excluded.length > 0) { - result += `\nExample of excluded values:\n${excluded.map(rowToText).join(', ')}`; - } - - return result; -} +import './tooltip.pcss'; function TableFieldWithoutState({ model, layout, field, fieldData, showFilterbox, }) { let classes = `table-field ${fieldData.qKeyType}`; - if (!layout) { + if (!layout || !layout.qListObject || !layout.qListObject.qDataPages || !layout.qListObject.qDataPages[0]) { return (
{field} @@ -97,18 +76,20 @@ function TableFieldWithoutState({ border: `2px solid ${fieldData.backgroundColor}`, }; - const filterBox = showFilterbox ? : null; + const filterBox = showFilterbox ? : null; return (
+
+
+ +
+
{ filterBox }
@@ -163,7 +144,8 @@ export default function TableField({ }) { const model = useModel(app, createDefinition(field)); const layout = useLayout(model); - + const fieldDataToModify = fieldData; + fieldDataToModify.layout = layout; return TableFieldWithoutState({ model, layout, field, fieldData, showFilterbox, }); diff --git a/src/components/table-field.pcss b/src/components/table-field.pcss index 798fffce..7a0b738d 100644 --- a/src/components/table-field.pcss +++ b/src/components/table-field.pcss @@ -11,7 +11,6 @@ min-width: 22em; padding: 1em; position: relative; - z-index: 1; &:hover { box-shadow: 0 0 2em -0.5em #999; @@ -41,6 +40,15 @@ } } + .extra-information { + border: 1px saddlebrown; + height: 1.5rem; + position: absolute; + right: 0.5rem; + top: 0.5rem; + width: 1.5rem; + } + .details { box-sizing: border-box; display: flex; diff --git a/src/components/tooltip.jsx b/src/components/tooltip.jsx new file mode 100644 index 00000000..f990108f --- /dev/null +++ b/src/components/tooltip.jsx @@ -0,0 +1,235 @@ +import React from 'react'; +/* eslint-disable react/jsx-one-expression-per-line */ + +export function getTooltipForSubsetRatio(field) { + if (!!field.qnPresentDistinctValues && !!field.qnTotalDistinctValues && field.qnPresentDistinctValues < field.qnTotalDistinctValues) { + return ( +
+

+ {`${field.subsetRatioText} subset ratio`} +

+

+ {field.qnPresentDistinctValues} + {' out of '} + {field.qnTotalDistinctValues} + {' '} + {field.qName} + {' values are present in the '} + {field.srcTable.qName} + {' table. '} +

+

+ {'The remaining '} + {field.qnTotalDistinctValues - field.qnPresentDistinctValues} + {' values only exist in other tables ('} + {field.otherTables.join('')} + {').'} +

+

+ {'This means that there are '} + rows in associated tables that do not have any matching rows in this table. +

+

Note that when interacting with a field - all values are shown - not only the ones that are present in the underlying table.

+
+ ); + } + return
; +} + +export function getExtraInfoForField(fieldData) { + const { layout } = fieldData; + if (!layout) { + return null; + } + + const nullCount = fieldData.qnRows - fieldData.qnNonNulls; + const rowWithValueCount = fieldData.qnNonNulls; + const uniqueValueCount = fieldData.qnPresentDistinctValues; + const allNonNullValuesAreUnique = (fieldData.qnPresentDistinctValues === fieldData.qnNonNulls); + const avgDup = rowWithValueCount > uniqueValueCount ? `~ ${(rowWithValueCount / uniqueValueCount).toFixed(3)}` : '1'; + + function keyDescription() { + switch (fieldData.qKeyType) { + case 'PERFECT_KEY': + return ( + +

Perfect key

+ Each row in the {fieldData.srcTable.qName} table is uniquely identified by its {fieldData.qName} value. +

All {fieldData.qName} values in the entire data model are present in the {fieldData.srcTable.qName} table.

+
+ ); + case 'PRIMARY_KEY': + return ( + +

Primary key

+ Each row in the {fieldData.srcTable.qName} table is uniquely identified by its {fieldData.qName} value. +
+ ); + case 'ANY_KEY': + if (allNonNullValuesAreUnique) { + return ( + +

Foreign key - Contains null rows

+

All present values are unique but there are {nullCount} rows with nulls.

+

A single {fieldData.qName} value identifies at most one row in the {fieldData.srcTable.qName} table.

+
+ ); + } + if (fieldData.qHasNull) { + return ( + +

Foreign Key - Many rows per value + null rows

+

Values are repeated on several rows plus there are {nullCount} rows with nulls.

+

A single {fieldData.qName} value may identify several rows in the {fieldData.srcTable.qName} table.

+
+ ); + } + return ( + +

Foreign key - Many rows per value

+

Values are repeated on several rows. All rows have values.

+

A single {fieldData.qName} value may identify several rows in the {fieldData.srcTable.qName} table.

+ {/* Average row count per value is {avgDup} times. No nulls. */} +
+ ); + default: + return null; + } + } + + return ( +
+
+

Field {fieldData.qName}
in table {fieldData.srcTable.qName}

+ + + + + + + + + + + + + + + + {(fieldData.qnTotalDistinctValues > fieldData.qnPresentDistinctValues) + ? ( + + + + + + + + + + + ) + : ( + + + + + ) + } + + + + + +
Total Rows{fieldData.qnRows}
Rows with value{fieldData.qnNonNulls}
Rows with null{nullCount}
Unique values (in this table){fieldData.qnPresentDistinctValues}
Unique values (in all tables){fieldData.qnTotalDistinctValues}
Unique values{fieldData.qnPresentDistinctValues}
Rows per unique value{avgDup}
+ {keyDescription()} + {getTooltipForSubsetRatio(fieldData)} +
+
+
+ ); +} + + +function reduceWithAnd(items, maxItems) { + let result = ''; + if (items.length > maxItems) { + for (let i = 0; i < maxItems; i += 1) { + result += `${items[i]}, `; + } + result += '...'; + } else { + for (let i = 0; i < items.length - 1; i += 1) { + result += `${items[i]}, `; + } + result = `${result} and ${items[items.length - 1]}`; + } + return result; +} + +export function getTooltipForSyntheticField(fieldData) { + if (fieldData.qOriginalFields.length > 0) { + return `Synthetic field\n\nReplaces the fields ${reduceWithAnd(fieldData.qOriginalFields)} with one association field since tables cannot be associated with multiple fields. The original fields have been moved to a synthetic link table.`; + } + if (fieldData.srcTable.qIsSynthetic) { + return 'Synthetic field\n\nLinks the generated association field to the original field values that have been moved into this synthetic link table'; + } + return 'Synthetic field'; +} + + +function firstFewValues(layout) { + const rowToText = row => `${row[0].qText || ''}`; + + const selected = layout.qListObject.qDataPages[0].qMatrix.filter(row => row[0].qState === 'S' || row[0].qState === 'O'); + const excluded = layout.qListObject.qDataPages[0].qMatrix.filter(row => row[0].qState === 'X'); + + let result = ''; + if (selected.length > 0) { + result += `Example values:\n${selected.map(rowToText).join(', ')}\n`; + } + if (excluded.length > 0) { + result += `\nExample of excluded values:\n${excluded.map(rowToText).join(', ')}`; + } + + return result; +} + +export function getTooltipForField(fieldData, layout) { + let descriptions = ''; + + if (fieldData.qHasDuplicates) { + descriptions += 'Duplicate values'; + } else { + descriptions += 'Unique values'; + } + + if (fieldData.qHasNull) { + descriptions += ', has nulls'; + } else { + descriptions += ', no nulls.'; + } + return `Field ${fieldData.qName}\n\n${fieldData.qnTotalDistinctValues} unique values (of which only ${fieldData.qnPresentDistinctValues} are present in the ${fieldData.srcTable.qName} table).\n\n${descriptions}\n\n${firstFewValues(layout)}`; +} + +export function getSelectionBarTooltip(fieldData, layout) { + const total = layout.qListObject.qDimensionInfo.qCardinal; + const states = layout.qListObject.qDimensionInfo.qStateCounts; + + let subsetRatioInfo = ''; + if (fieldData.qnPresentDistinctValues < fieldData.qnTotalDistinctValues) { + subsetRatioInfo = `(of which only ${fieldData.qnPresentDistinctValues} are present in the ${fieldData.srcTable.qName} table)`; + } + + return `${states.qSelected} selected, ${states.qOption + + states.qAlternative} possible, ${ + states.qExcluded} excluded, ${total} values in total ${subsetRatioInfo}.`; +} + +export function getAssosicationTooltip(fieldName) { + return `Association between tables that share the field ${fieldName}. An association allows a selection in one table to infer what field values are possible in the associated table(s).`; +} + +export function getTableTooltip(table) { + return `Table ${table.qName}\n\nRow count: ${table.qNoOfRows}\n\nFields: ${reduceWithAnd(table.qFields.map(item => item.qName), 5)}`; +} diff --git a/src/components/tooltip.pcss b/src/components/tooltip.pcss new file mode 100644 index 00000000..45acf7dd --- /dev/null +++ b/src/components/tooltip.pcss @@ -0,0 +1,8 @@ +.tooltip { + font-size: 1.5rem; + max-width: 30rem; +} + +.tooltip th { + text-align: left; +} diff --git a/src/components/use/layout.jsx b/src/components/use/layout.jsx index 4c638fc9..e0db3ec7 100644 --- a/src/components/use/layout.jsx +++ b/src/components/use/layout.jsx @@ -10,13 +10,17 @@ export default function useLayout(model) { let isKilled = false; const modelChanged = async () => { - const newLayout = model.getAppLayout - ? await model.getAppLayout() - : await model.getLayout(); - if (!isKilled) { - debounce(() => { - setLayout(newLayout); - }); + try { + const newLayout = model.getAppLayout + ? await model.getAppLayout() + : await model.getLayout(); + if (!isKilled) { + debounce(() => { + setLayout(newLayout); + }); + } + } catch (err) { + setLayout(null); } }; diff --git a/src/components/virtual-table.jsx b/src/components/virtual-table.jsx index 06069bc6..ce95fddc 100644 --- a/src/components/virtual-table.jsx +++ b/src/components/virtual-table.jsx @@ -4,7 +4,7 @@ import React, { import { Table, AutoSizer, InfiniteLoader, } from 'react-virtualized'; -import 'react-virtualized/styles.css'; +import './virtual-table.pcss'; import PropTypes from 'prop-types'; // only needs to be imported once function extractNode(layout, defPath) { diff --git a/src/components/virtual-table.pcss b/src/components/virtual-table.pcss new file mode 100644 index 00000000..a311c8e5 --- /dev/null +++ b/src/components/virtual-table.pcss @@ -0,0 +1,73 @@ +.ReactVirtualized__Table__headerRow { + align-items: center; + -webkit-box-align: center; + -webkit-box-direction: normal; + -webkit-box-orient: horizontal; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + -ms-flex-direction: row; + flex-direction: row; + font-weight: 700; + text-transform: uppercase; +} + +.ReactVirtualized__Table__row { + align-items: center; + -webkit-box-align: center; + -webkit-box-direction: normal; + -webkit-box-orient: horizontal; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + -ms-flex-direction: row; + flex-direction: row; +} + +.ReactVirtualized__Table__headerTruncatedText { + display: inline-block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.ReactVirtualized__Table__headerColumn, +.ReactVirtualized__Table__rowColumn { + margin-right: 10px; + min-width: 0; +} + +.ReactVirtualized__Table__rowColumn { + text-overflow: ellipsis; + white-space: nowrap; +} + +.ReactVirtualized__Table__headerColumn:first-of-type, +.ReactVirtualized__Table__rowColumn:first-of-type { + margin-left: 10px; +} + +.ReactVirtualized__Table__sortableHeaderColumn { + cursor: pointer; +} + +.ReactVirtualized__Table__sortableHeaderIconContainer { + align-items: center; + -webkit-box-align: center; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; +} + +.ReactVirtualized__Table__sortableHeaderIcon { + -webkit-box-flex: 0; + fill: currentColor; + -ms-flex: 0 0 24px; + flex: 0 0 24px; + height: 1em; + width: 1em; +} diff --git a/src/enigma/config.js b/src/enigma/config.js index 7493df21..7c4b760a 100644 --- a/src/enigma/config.js +++ b/src/enigma/config.js @@ -3,6 +3,7 @@ import schema from 'enigma.js/schemas/12.170.2.json'; import listCache from './list-cache'; import layoutCache from './layout-cache'; import getDoc from './get-doc'; +import { reloadInProgressInterceptor } from './reload-in-progress-interceptor'; const ERR_ABORTED = 15; @@ -11,7 +12,7 @@ const config = { url: new URLSearchParams(document.location.search).get('engine_url') || `ws://localhost:9076/app/${+new Date()}`, createSocket: url => new WebSocket(url), mixins: [listCache, ...layoutCache, getDoc], - responseInterceptors: [{ + responseInterceptors: [reloadInProgressInterceptor, { onRejected(session, request, error) { if (error.code === ERR_ABORTED) { return request.retry(); diff --git a/src/enigma/reload-in-progress-interceptor.js b/src/enigma/reload-in-progress-interceptor.js new file mode 100644 index 00000000..69486ace --- /dev/null +++ b/src/enigma/reload-in-progress-interceptor.js @@ -0,0 +1,87 @@ +import { useState, useEffect } from 'react'; + +export const ERR_RELOAD_IN_PROGRESS = 11000; +export const WAIT_FOR_RELOAD_IN_PROGRESS_DELAY = 1000; +const RETRY_INTERVAL = 500; +let wasInReloadOnStartup; +let reloadInProgress; +let setReloadInProgress; + +function sleep(time) { + return new Promise(resolve => setTimeout(resolve, time)); +} + +export function useReloadInProgress(app) { + [reloadInProgress, setReloadInProgress] = useState(wasInReloadOnStartup); + useEffect(() => { + if (!app) return null; + + const modelChanged = async () => { + try { + await app.getAppLayout(); + if (reloadInProgress) { + setReloadInProgress(false); + } + // When a change is triggered by a reload the getAppLayout sometimes resolve quick enough to not throw a reload in progress error, so call it + // once more to increase the likelyhood a bit. If this too fails any interaction will immediately pop up the reload in progress dialog + await sleep(WAIT_FOR_RELOAD_IN_PROGRESS_DELAY); + await app.getAppLayout(); + } catch (err) { + if (err.code === ERR_RELOAD_IN_PROGRESS) { + if (!reloadInProgress) { + setReloadInProgress(true); + } + } + } + }; + + app.on('changed', modelChanged); + modelChanged(); + + return () => { + app.removeListener('changed', modelChanged); + }; + }, [app && app.id]); + + return reloadInProgress; +} + +function retryUsingTimeouts(request) { + return new Promise(((resolve/* , reject */) => { + async function retry() { + try { + const res = await request.retry(); + resolve(res); + } catch (err) { + if (err.code === ERR_RELOAD_IN_PROGRESS) { + setTimeout(() => { + retry(); + }, RETRY_INTERVAL); + } + } + } + + setTimeout(() => { + retry(); + }, RETRY_INTERVAL); + })); +} +export const reloadInProgressInterceptor = { + onRejected(session, request, error) { + if (error.code === ERR_RELOAD_IN_PROGRESS) { + if (!reloadInProgress) { + if (setReloadInProgress) { + setReloadInProgress(true); + } else { + wasInReloadOnStartup = true; + } + } + if (request.method === 'GetActiveDoc' + || request.method === 'OpenDoc' + || request.method === 'CreateSessionObject') { + return retryUsingTimeouts(request); + } + } + throw error; + }, +}; diff --git a/src/index.pcss b/src/index.pcss index 18258c9b..0c238e91 100644 --- a/src/index.pcss +++ b/src/index.pcss @@ -216,6 +216,7 @@ body { font-family: 'Source Sans Pro', sans-serif; font-size: 8px; margin: 0; + overflow: hidden; padding: 0; } diff --git a/src/logic/logic.js b/src/logic/logic.js index 06909292..8500b84e 100644 --- a/src/logic/logic.js +++ b/src/logic/logic.js @@ -39,13 +39,13 @@ function assocSymbol(field) { } } -function toSubsetRatioText(qSubsetratio) { - if (qSubsetratio === 1) { +function toSubsetRatioText(qSubsetRatio) { + if (qSubsetRatio === 1) { return ''; - } if (qSubsetratio === 0) { + } if (qSubsetRatio === 0) { return '0%'; } - const subsetRatioText = `${Math.round(qSubsetratio * 100)}%`; + const subsetRatioText = `${Math.round(qSubsetRatio * 100)}%`; if (subsetRatioText === '100%') { return '>99%'; } if (subsetRatioText === '0%') { @@ -55,8 +55,9 @@ function toSubsetRatioText(qSubsetratio) { } function toSubsetRatioTitle(field) { + const subsetratioText = toSubsetRatioText(field.qSubsetRatio); if (!!field.qnPresentDistinctValues && !!field.qnTotalDistinctValues && field.qnPresentDistinctValues < field.qnTotalDistinctValues) { - return `${field.qnPresentDistinctValues} out of ${field.qnTotalDistinctValues} values are present in this table.`; + return `${subsetratioText} subset ratio\n\n${field.qnPresentDistinctValues} out of ${field.qnTotalDistinctValues} ${field.qName} values are present in the ${field.srcTable.qName} table.\nThe remaining ${field.qnTotalDistinctValues - field.qnPresentDistinctValues} values only exist in other tables (${field.otherTables.join('')}) \n\nNote that when interacting with a field - all values are shown - not only the ones that are present in the underlying table.`; } return ''; } @@ -95,11 +96,13 @@ class QueryModel { const fields = {}; const tables = {}; const tablesNamesOfFieldMapMap = {}; - tablesAndKeys.qtr.forEach((table) => { + tablesAndKeys.qtr.forEach((originalTable) => { + const table = JSON.parse(JSON.stringify(originalTable)); // Clone so we can append information to the structure safely tables[table.qName] = table; table.qFields.sort((a, b) => a.qName.localeCompare(b.qName)); table.qFields.forEach((_field) => { - const field = Object.assign({}, _field); + const field = _field; + field.srcTable = table; fields[field.qName] = fields[field.qName] || field; grid[field.qName] = grid[field.qName] || {}; grid[field.qName][table.qName] = field; @@ -128,22 +131,27 @@ class QueryModel { this.analyzeTable(actualStartTable, tablesAlreadyAnalyzed, ''); actualStartTable = biggestTableNotAnalyzed(this.originalTableNamesSortedBySize, tablesAlreadyAnalyzed); } - this.fillInGridInfo(grid); } + getTableField(tableName, fieldName) { + return this.grid[fieldName][tableName]; + } fillInGridInfo(gridIn) { // Fill in blanks + const grid = gridIn; for (let f = 0; f < this.resultFieldList.length; f += 1) { for (let t = 0; t < this.resultTableList.length; t += 1) { - let cell = grid[this.resultFieldList[f]][this.resultTableList[t]]; + const tableName = this.resultTableList[t]; + const fieldName = this.resultFieldList[f]; + let cell = grid[fieldName][tableName]; if (!cell) { cell = { isKey: false, qKeyType: 'NOT_KEY', isEmpty: true, betweenKeys: true, insideTable: true, }; // The default betweenKeys will be overriden - grid[this.resultFieldList[f]][this.resultTableList[t]] = cell; + grid[fieldName][tableName] = cell; } else { cell.betweenKeys = true; cell.isKey = true; @@ -151,6 +159,8 @@ class QueryModel { cell.insideTable = true; cell.backgroundColor = colorOfField(cell); cell.assocSymbol = assocSymbol(cell); + cell.tables = this.tablesOfField(fieldName); + cell.otherTables = this.otherTablesOfField(tableName, fieldName); cell.subsetRatioText = toSubsetRatioText(cell.qSubsetRatio); cell.subsetRatioTitle = toSubsetRatioTitle(cell); } @@ -365,6 +375,10 @@ class QueryModel { return Object.keys(this.tablesNamesOfFieldMapMap[fieldName]); } + otherTablesOfField(tableName, fieldName) { + return Object.keys(this.tablesNamesOfFieldMapMap[fieldName]).filter(name => name !== tableName); + } + fieldsOfTable(tableName) { const keys = this.tables[tableName].qFields.filter(field => field.qKeyType !== 'NOT_KEY'); keys.sort((a, b) => b.qnPresentDistinctValues - a.qnPresentDistinctValues); diff --git a/test/comp/__snapshots__/error-reporting.spec.jsx.snap b/test/comp/__snapshots__/error-reporting.spec.jsx.snap new file mode 100644 index 00000000..260bc690 --- /dev/null +++ b/test/comp/__snapshots__/error-reporting.spec.jsx.snap @@ -0,0 +1,70 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`'should display cookie if not set' 1`] = ` +
+
+ This website uses cookies to ensure you get the best experience on our website. + + + Learn more + +
+ +
+`; + +exports[`'should return ´null´ if GA not set' 1`] = `null`; diff --git a/test/comp/__snapshots__/splash.spec.jsx.snap b/test/comp/__snapshots__/splash.spec.jsx.snap new file mode 100644 index 00000000..08c1f4a2 --- /dev/null +++ b/test/comp/__snapshots__/splash.spec.jsx.snap @@ -0,0 +1,134 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`'should display message if empty docList array' 1`] = ` +
+
+ ", + } + } + /> +

+ Taking your Qlik data models to stage +

+
+

+ WebSocket connected but no open app, in addition the app list was empty. +

+

+ Please make sure there are apps accessible on this engine and reload the page, or connect to another one: +

+
+ + +
+
+
+
+`; + +exports[`'should render docList correctly' 1`] = ` +
+
+ ", + } + } + /> +

+ Taking your Qlik data models to stage +

+
+

+ WebSocket connected, but no open app. Choose one below: +

+
    +
  • + + + + Consumer_Sales + + + ( + See through the eyes of a consumer goods company. Analyze sales data by sales rep, by region, by product, etc. + ) + +
  • +
  • + + + + FDA - Drug Cases + + + ( + This Dashboard is based on the 2015 Q4 data. + ) + +
  • +
  • + + + + New_QVNCycles_with_measures_dimensions_visualizations_ + + + ( + An app with some measures and dimensions and master visualizations. +Texten finns också på skånska. + ) + +
  • +
+
+
+
+`; diff --git a/test/comp/aw.config.js b/test/comp/aw.config.js index bfe615a5..dd3d29e5 100644 --- a/test/comp/aw.config.js +++ b/test/comp/aw.config.js @@ -1,8 +1,42 @@ +const { JSDOM } = require('jsdom'); + +const template = ` + + + + + + +`; + +function copyProps(src, target) { + const props = Object.getOwnPropertyNames(src) + .filter(prop => typeof target[prop] === 'undefined') + .reduce((result, prop) => ({ + ...result, + [prop]: Object.getOwnPropertyDescriptor(src, prop), + }), {}); + Object.defineProperties(target, props); +} + +const jsdom = new JSDOM(template); +const { window } = jsdom; +global.window = window; +global.document = window.document; +global.navigator = { + userAgent: 'node.js', +}; +global.window.cancelAnimationFrame = () => {}; +global.window.requestAnimationFrame = () => {}; +copyProps(window, global); + + module.exports = { glob: 'test/comp/**/*.spec.jsx', - // coverage: true, + coverage: true, src: ['src/components/**/*.jsx'], mocks: [ ['**/*.{svg,css,scss,pcss}'], ], + exit: true, }; diff --git a/test/comp/error-reporting.spec.jsx b/test/comp/error-reporting.spec.jsx new file mode 100644 index 00000000..956cf9c0 --- /dev/null +++ b/test/comp/error-reporting.spec.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import ReactGA from 'react-ga'; +import renderer from 'react-test-renderer'; + +import UseErrorReporting from '../../src/components/error-reporting'; + +describe('error-reporting', () => { + it('should return ´null´ if GA not set', () => { + const tree = renderer.create().toJSON(); + + expect(tree).toMatchSnapshot(); + global.window.close(); + }); + + it('should display cookie if not set', () => { + const sandbox = sinon.createSandbox(); + sandbox.stub(ReactGA, 'initialize'); + sandbox.stub(ReactGA, 'pageview'); + sandbox.stub(window, 'location').value({ pathname: 'some value' }); + sandbox.stub(process, 'env').value({ GA: 'some value' }); + + const tree = renderer.create().toJSON(); + + expect(tree).toMatchSnapshot(); + sandbox.restore(); + }); +}); diff --git a/test/unit/logic.spec.js b/test/unit/logic.spec.js index d3bec481..2e89c81c 100644 --- a/test/unit/logic.spec.js +++ b/test/unit/logic.spec.js @@ -4,6 +4,7 @@ const { data } = require('./sample-data'); describe('QueryModel', () => { it('should return a valid object for a field', () => { const x = new logic.QueryModel(data); + expect(x).to.be.an('object'); expect(x.fields).to.include.all.keys('Fiscal Year', 'Phone'); });