diff --git a/package-lock.json b/package-lock.json index f76415ba2..100de84ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1338,14 +1338,14 @@ "dev": true }, "@emotion/babel-utils": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-utils/-/babel-utils-0.6.5.tgz", - "integrity": "sha1-NNeETrUy0Rdcj8cBdb63TQcb++s=", + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@emotion/babel-utils/-/babel-utils-0.6.7.tgz", + "integrity": "sha512-U6iTV9HurrZ0ye8VOTIGcV4tMdDtAIGbfCH58L/NdOhW05pV/KVlNmbXsHp01VMSM+pgmSkGqHSLLE8zdDYJvA==", "dev": true, "requires": { - "@emotion/hash": "^0.6.3", - "@emotion/memoize": "^0.6.3", - "@emotion/serialize": "^0.8.3", + "@emotion/hash": "^0.6.5", + "@emotion/memoize": "^0.6.5", + "@emotion/serialize": "^0.8.5", "convert-source-map": "^1.5.1", "find-root": "^1.1.0", "source-map": "^0.7.2" @@ -1360,54 +1360,54 @@ } }, "@emotion/hash": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.6.3.tgz", - "integrity": "sha1-DnpWBGJvxsbUrEBhovWsgNUCYqQ=", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.6.5.tgz", + "integrity": "sha512-JlZbn5+adseTdDPTUkx/O1/UZbhaGR5fCLLWQDCIJ4eP9fJcVdP/qjlTveEX6mkNoJHWFbZ47wArWQQ0Qk6nMA==", "dev": true }, "@emotion/is-prop-valid": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.6.3.tgz", - "integrity": "sha1-sElW4tZVAn/ojGI0Zp/XBk+wccw=", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.6.5.tgz", + "integrity": "sha512-pfjQHT3FCZkAPxqY+sI6MmMTPMCKGIV3DcC1qhf68XkE9pGtKqb5WNN2vfmLgTMGnNVXnaFyx/VjdDYmGFcRaw==", "dev": true, "requires": { - "@emotion/memoize": "^0.6.3" + "@emotion/memoize": "^0.6.5" } }, "@emotion/memoize": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.3.tgz", - "integrity": "sha1-ZDeaHWr2+NT+i9bv6dnoJOpLItg=", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.5.tgz", + "integrity": "sha512-n1USr7yICA4LFIv7z6kKsXM8rZJxd1btKCBmDewlit+3OJ2j4bDfgXTAxTHYbPkHS/eztHmFWfsbxW2Pu5mDqA==", "dev": true }, "@emotion/serialize": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.8.3.tgz", - "integrity": "sha1-D61Vual/lSPmsf1vtvdLbLQcf4s=", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.8.5.tgz", + "integrity": "sha512-NObUNfK7lqxFpSJWHRwJeeGSZaElEgMn5eR76ift06yx9FdwIfFY47ffapkf2lJUoIUx4ffP55fPSVPMgv0Qgw==", "dev": true, "requires": { - "@emotion/hash": "^0.6.3", - "@emotion/memoize": "^0.6.3", - "@emotion/unitless": "^0.6.3", - "@emotion/utils": "^0.7.1" + "@emotion/hash": "^0.6.5", + "@emotion/memoize": "^0.6.5", + "@emotion/unitless": "^0.6.5", + "@emotion/utils": "^0.7.3" } }, "@emotion/stylis": { - "version": "0.6.10", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.6.10.tgz", - "integrity": "sha1-fTIeY568i6I6zlmQwg6U3Ou4890=", + "version": "0.6.12", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.6.12.tgz", + "integrity": "sha512-yS+t7l5FeYeiIyADyqjFBJvdotpphHb2S3mP4qak5BpV7ODvxuyAVF24IchEslW+A1MWHAhn5SiOW6GZIumiEQ==", "dev": true }, "@emotion/unitless": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.6.3.tgz", - "integrity": "sha1-ZWguaKgnAccO77ONf5QaLAv6kN4=", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.6.5.tgz", + "integrity": "sha512-kAFHWyQTMLcOvse+RfEfJ6uwwOlVFZcUOMX0wuBvEMwnF3Zb/6VQ/xDgbx9fNlp3I6AAmijAtUtIyT2qVldpnA==", "dev": true }, "@emotion/utils": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.7.1.tgz", - "integrity": "sha1-5E5ZbQPJ8WujsSetMzqKByvLWgo=", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.7.3.tgz", + "integrity": "sha512-1bGqG9IYJETOOqQEjWinGx1deLlAW/Fcew4GyrzZx0dJIgAjivZTmwzjrhB+Vhj7TgjMr0aTyj5byHmZezisZQ==", "dev": true }, "@mdx-js/loader": { @@ -1544,9 +1544,9 @@ "dev": true }, "@types/bluebird": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.21.tgz", - "integrity": "sha512-6UNEwyw+6SGMC/WMI0ld0PS4st7Qq51qgguFrFizOSpGvZiqe9iswztFSdZvwJBEhLOy2JaxNE6VC7yMAlbfyQ==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.22.tgz", + "integrity": "sha512-wQamz3CYZCz9QVxhPOF+Odu2jZifcXkGJDff7580sx/TCR6/9Zsgv+iGe2/o4Upcs0RDFLVJuEtYSEFeAqDTSA==", "dev": true }, "@types/body-parser": { @@ -1794,9 +1794,9 @@ } }, "@types/ioredis": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-3.2.12.tgz", - "integrity": "sha512-n1jXNSx4CFcDSmuV1Zchb6YpmcovMgCZtsboqGExlDRR6DWKorS9Dy44ekaM5Txq0CYlZhqJSj86gG0/nW/EBA==", + "version": "3.2.13", + "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-3.2.13.tgz", + "integrity": "sha512-38qPXqV4oEzucMcdl0yhqN9JCGYOjspvfQyOL4T4HpMcD4VIj4RfuumuM2mwTtK12jiHF0oxjyU8EvnLfHD+TA==", "dev": true, "requires": { "@types/bluebird": "*", @@ -1804,9 +1804,9 @@ } }, "@types/jest": { - "version": "23.1.5", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.1.5.tgz", - "integrity": "sha512-GlN74UAcT2i+G4BzVVI/aHip0HDXZaiY11VEjHzAz74+dB3hIeM5lJmnnZx4acxxinK9lT+uEH1Vsa5aWj6w4Q==", + "version": "23.1.6", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.1.6.tgz", + "integrity": "sha512-lBu2tjrfGuj0gARErNmHZJrnWBdRrYk2XqlBY3LRv8Dqxk3w3461uuFMKmwfDDiOa5kzXocUnunCBBacGwF3+A==", "dev": true }, "@types/joi": { @@ -1843,9 +1843,9 @@ "dev": true }, "@types/lodash": { - "version": "4.14.111", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.111.tgz", - "integrity": "sha512-t2FwnkdqdZNYPJHTEF+Zf//j5d2I7UbM2Ng+vqqmUCE2RuiVVINJi9RlVdpKvqPqVItsJa0X+ra/tvmwLzlcgg==", + "version": "4.14.112", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.112.tgz", + "integrity": "sha512-jDD7sendv3V7iwyRXSlECOR8HCtMN2faVA9ngLdHHihSVIwY7nbfsKl2kA6fimUDU1i5l/zgpG3aevwWnN3zCQ==", "dev": true }, "@types/luxon": { @@ -1954,6 +1954,15 @@ "csstype": "^2.2.0" } }, + "@types/react-copy-to-clipboard": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-4.2.5.tgz", + "integrity": "sha512-5LggaWWlMcgehfeDmr4FZmK8MXntzxLYERFdrXk72ye6QwpoZpyYFGgUM+xGVVtIP2WXlor8twZTXK1xckEDYw==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-dom": { "version": "16.0.6", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.0.6.tgz", @@ -2928,9 +2937,9 @@ "dev": true }, "ast-types": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.10.1.tgz", - "integrity": "sha512-UY7+9DPzlJ9VM8eY0b2TUZcZvF+1pO0hzMtAyjBYKhOmnvRlqYNYnWdtsMj0V16CGaMlpL0G1jnLbLo4AyotuQ==", + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.5.tgz", + "integrity": "sha512-oJjo+5e7/vEc2FBK8gUalV0pba4L3VdBIs2EKhOLHLcOd2FgQIVQN9xb0eZ9IjEWyAL7vq6fGJxOvVvdCHNyMw==", "dev": true }, "astral-regex": { @@ -3248,9 +3257,9 @@ } }, "babel-plugin-emotion": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-9.2.5.tgz", - "integrity": "sha1-AEbgO+XBYnb4U4BHb4jJ/L98lTY=", + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-9.2.6.tgz", + "integrity": "sha512-aCRXUPm2pwaUqUtpQ2Gzbn5EeOD2RyUDTQDJl5Yqwg1RLQPs3OvnB6Xt6GUrMomMISxuwFrxuWfBMajHv74UjQ==", "dev": true, "requires": { "@babel/helper-module-imports": "7.0.0-beta.51", @@ -3258,6 +3267,7 @@ "@emotion/hash": "^0.6.2", "@emotion/memoize": "^0.6.1", "@emotion/stylis": "^0.6.10", + "babel-core": "^6.26.3", "babel-plugin-macros": "^2.0.0", "babel-plugin-syntax-jsx": "^6.18.0", "convert-source-map": "^1.5.0", @@ -3287,6 +3297,45 @@ "lodash": "^4.17.5", "to-fast-properties": "^2.0.0" } + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true } } }, @@ -6169,6 +6218,15 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "copy-to-clipboard": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz", + "integrity": "sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw==", + "dev": true, + "requires": { + "toggle-selection": "^1.0.3" + } + }, "copy-webpack-plugin": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.2.tgz", @@ -6434,9 +6492,9 @@ } }, "create-emotion": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/create-emotion/-/create-emotion-9.2.5.tgz", - "integrity": "sha1-XbTwbZNgJeQ70xJFOj7pRuTV5ds=", + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/create-emotion/-/create-emotion-9.2.6.tgz", + "integrity": "sha512-4g46va26lw6DPfKF7HeWY3OI/qoaNSwpvO+li8dMydZfC6f6+ZffwlYHeIyAhGR8Z8C8c0H9J1pJbQRtb9LScw==", "dev": true, "requires": { "@emotion/hash": "^0.6.2", @@ -6449,9 +6507,9 @@ } }, "create-emotion-styled": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/create-emotion-styled/-/create-emotion-styled-9.2.5.tgz", - "integrity": "sha1-P1VTZXECM/DyaDHR7PC473YNSyo=", + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/create-emotion-styled/-/create-emotion-styled-9.2.6.tgz", + "integrity": "sha512-Kk6qWuEiHY9iptLsxzoJztHM237yedoY2Zhmc79HwkRkC+wZ7W8IFt5gP6AAGx6eN44hc3Zwc+WA5N7ku3QJfg==", "dev": true, "requires": { "@emotion/is-prop-valid": "^0.6.1" @@ -7938,6 +7996,16 @@ "tapable": "^1.0.0" } }, + "eslint-scope": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -8029,9 +8097,9 @@ } }, "webpack": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.16.0.tgz", - "integrity": "sha512-oNx9djAd6uAcccyfqN3hyXLNMjZHiRySZmBQ4c8FNmf1SNJGhx7n9TSvHNyXxgToRdH65g/Q97s94Ip9N6F7xg==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.16.1.tgz", + "integrity": "sha512-6jpzObU18y7lXDJz7XCLvzgrqcJ0rZ2jhKvnTivza9gM2GvPW93xxtmEll2GgmdC0zVQAtbHrH/9BtyMjSDZfA==", "dev": true, "requires": { "@webassemblyjs/ast": "1.5.13", @@ -8045,7 +8113,7 @@ "ajv-keywords": "^3.1.0", "chrome-trace-event": "^1.0.0", "enhanced-resolve": "^4.1.0", - "eslint-scope": "^3.7.1", + "eslint-scope": "^4.0.0", "json-parse-better-errors": "^1.0.2", "loader-runner": "^2.3.0", "loader-utils": "^1.1.0", @@ -8061,15 +8129,6 @@ "webpack-sources": "^1.0.1" } }, - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - }, "yargs": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.1.tgz", @@ -8327,19 +8386,19 @@ "dev": true }, "emotion": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/emotion/-/emotion-9.2.5.tgz", - "integrity": "sha1-Zm+xUaacJcdYL/PeBvYPjYSPdKo=", + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/emotion/-/emotion-9.2.6.tgz", + "integrity": "sha512-95/EiWkADklxyy1y1qlJeX5Cepa7WfpJBJSBgbLkDCBzOnP4maluvz52xcV5UaObBTfVnEBq77Go6/bgF7+xaA==", "dev": true, "requires": { - "babel-plugin-emotion": "^9.2.5", - "create-emotion": "^9.2.5" + "babel-plugin-emotion": "^9.2.6", + "create-emotion": "^9.2.6" } }, "emotion-theming": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/emotion-theming/-/emotion-theming-9.2.5.tgz", - "integrity": "sha1-8z8hDxHIDhE2QbNK7J4L+eBMvYk=", + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/emotion-theming/-/emotion-theming-9.2.6.tgz", + "integrity": "sha512-sbZStubPmaDuMhs3+saH4XegnoMgbVtEY2giD1MP+maDinCnJdzf/1Apcip1wo5HMAN7vrjvpmcY13pH34xR6g==", "dev": true, "requires": { "hoist-non-react-statics": "^2.3.1" @@ -8614,9 +8673,9 @@ } }, "esm": { - "version": "3.0.69", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.0.69.tgz", - "integrity": "sha512-ECQbEBAzGE15NJXO+ediu/Zdk7rtxIkPAlFbgoJbLM96lAJEKC0XfmN2PmFHNvh6k6m+wAJUffL3E49KO7xCLg==", + "version": "3.0.71", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.0.71.tgz", + "integrity": "sha512-RAgyznLvtK17ld4IQBp+wEsVp3JAHb9Nnbu5fGevnFpKeKVPuh/WexdqUuanhez5s9NJD2+iEtM8LfFu0jL8Rw==", "dev": true }, "esprima": { @@ -9330,6 +9389,13 @@ "dev": true, "requires": { "intl-pluralrules": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b" + }, + "dependencies": { + "intl-pluralrules": { + "version": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b", + "from": "github:projectfluent/IntlPluralRules#module", + "dev": true + } } }, "fluent-langneg": { @@ -9527,24 +9593,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "bundled": true, + "resolved": "", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": "", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": "", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.4", - "bundled": true, + "resolved": "", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "optional": true, "requires": { @@ -9554,12 +9624,14 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, + "resolved": "", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "brace-expansion": { "version": "1.1.11", - "bundled": true, + "resolved": "", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -9568,34 +9640,40 @@ }, "chownr": { "version": "1.0.1", - "bundled": true, + "resolved": "", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "bundled": true, + "resolved": "", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, "concat-map": { "version": "0.0.1", - "bundled": true, + "resolved": "", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, + "resolved": "", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, "core-util-is": { "version": "1.0.2", - "bundled": true, + "resolved": "", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "2.6.9", - "bundled": true, + "resolved": "", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "optional": true, "requires": { @@ -9604,25 +9682,29 @@ }, "deep-extend": { "version": "0.5.1", - "bundled": true, + "resolved": "", + "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": "", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "bundled": true, + "resolved": "", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "bundled": true, + "resolved": "", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, "requires": { @@ -9631,13 +9713,15 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": "", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": "", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, "requires": { @@ -9653,7 +9737,8 @@ }, "glob": { "version": "7.1.2", - "bundled": true, + "resolved": "", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "optional": true, "requires": { @@ -9667,13 +9752,15 @@ }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": "", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.21", - "bundled": true, + "resolved": "", + "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", "dev": true, "optional": true, "requires": { @@ -9682,7 +9769,8 @@ }, "ignore-walk": { "version": "3.0.1", - "bundled": true, + "resolved": "", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, "requires": { @@ -9691,7 +9779,8 @@ }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": "", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, "requires": { @@ -9701,18 +9790,21 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, + "resolved": "", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "ini": { "version": "1.3.5", - "bundled": true, + "resolved": "", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": "", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { "number-is-nan": "^1.0.0" @@ -9720,13 +9812,15 @@ }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": "", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": "", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -9734,12 +9828,14 @@ }, "minimist": { "version": "0.0.8", - "bundled": true, + "resolved": "", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "minipass": { "version": "2.2.4", - "bundled": true, + "resolved": "", + "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, "requires": { "safe-buffer": "^5.1.1", @@ -9748,7 +9844,8 @@ }, "minizlib": { "version": "1.1.0", - "bundled": true, + "resolved": "", + "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", "dev": true, "optional": true, "requires": { @@ -9757,7 +9854,8 @@ }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": "", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" @@ -9765,13 +9863,15 @@ }, "ms": { "version": "2.0.0", - "bundled": true, + "resolved": "", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true }, "needle": { "version": "2.2.0", - "bundled": true, + "resolved": "", + "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", "dev": true, "optional": true, "requires": { @@ -9782,7 +9882,8 @@ }, "node-pre-gyp": { "version": "0.10.0", - "bundled": true, + "resolved": "", + "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", "dev": true, "optional": true, "requires": { @@ -9800,7 +9901,8 @@ }, "nopt": { "version": "4.0.1", - "bundled": true, + "resolved": "", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, "requires": { @@ -9810,13 +9912,15 @@ }, "npm-bundled": { "version": "1.0.3", - "bundled": true, + "resolved": "", + "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.1.10", - "bundled": true, + "resolved": "", + "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", "dev": true, "optional": true, "requires": { @@ -9826,7 +9930,8 @@ }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": "", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, "requires": { @@ -9838,18 +9943,21 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": "", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": "", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": "", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" @@ -9857,19 +9965,22 @@ }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": "", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": "", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "bundled": true, + "resolved": "", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, "requires": { @@ -9879,19 +9990,22 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": "", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "bundled": true, + "resolved": "", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.7", - "bundled": true, + "resolved": "", + "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "dev": true, "optional": true, "requires": { @@ -9903,7 +10017,8 @@ "dependencies": { "minimist": { "version": "1.2.0", - "bundled": true, + "resolved": "", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true } @@ -9911,7 +10026,8 @@ }, "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": "", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, "requires": { @@ -9926,7 +10042,8 @@ }, "rimraf": { "version": "2.6.2", - "bundled": true, + "resolved": "", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "optional": true, "requires": { @@ -9935,42 +10052,49 @@ }, "safe-buffer": { "version": "5.1.1", - "bundled": true, + "resolved": "", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, "safer-buffer": { "version": "2.1.2", - "bundled": true, + "resolved": "", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "bundled": true, + "resolved": "", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.5.0", - "bundled": true, + "resolved": "", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": "", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": "", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": "", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -9980,7 +10104,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": "", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, "requires": { @@ -9989,7 +10114,8 @@ }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": "", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -9997,13 +10123,15 @@ }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": "", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.1", - "bundled": true, + "resolved": "", + "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", "dev": true, "optional": true, "requires": { @@ -10018,13 +10146,15 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true, + "resolved": "", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.2", - "bundled": true, + "resolved": "", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "optional": true, "requires": { @@ -10033,12 +10163,14 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": "", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "yallist": { "version": "3.0.2", - "bundled": true, + "resolved": "", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", "dev": true } } @@ -11393,11 +11525,6 @@ "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", "dev": true }, - "intl-pluralrules": { - "version": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b", - "from": "intl-pluralrules@github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b", - "dev": true - }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -16368,6 +16495,12 @@ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, + "popper.js": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.3.tgz", + "integrity": "sha1-FDj5jQRqz3tNeM1QK/QYrGTU8JU=", + "dev": true + }, "portfinder": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", @@ -16522,12 +16655,12 @@ } }, "postcss-color-functional-notation": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-1.0.1.tgz", - "integrity": "sha512-8ECwUd75SwcjLMzFdRVRBqjVoHwGeWpLQKCNIQo9T+QeCrUNKI1NJ6rgpni5vo7iZ7MZy21qKqUieMM3D0wHYQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-1.0.2.tgz", + "integrity": "sha512-FxkEr+s/KCrcrTxUhHcDMKGZmnLjUKK7pl2gDjnEoAJaVcbThdDWLhuASu02qdA3Ys7np/BwJgwc72JrURTvJQ==", "dev": true, "requires": { - "postcss": "^6.0.22", + "postcss": "^6.0.23", "postcss-values-parser": "^1.5.0" } }, @@ -17819,6 +17952,15 @@ } } }, + "postcss-nesting": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-6.0.0.tgz", + "integrity": "sha512-Yoglsy6eZbDCbRIXoYSmnIt9ao4xyg07iFwVBd7WyIkDzMSeRxIqUk8xEAdkeJQ7eGfWo6RufrTU7FSUjZ22fg==", + "dev": true, + "requires": { + "postcss": "^6.0.22" + } + }, "postcss-normalize-charset": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", @@ -18088,18 +18230,18 @@ } }, "postcss-preset-env": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-5.2.1.tgz", - "integrity": "sha512-zsGFBKtTyCY5YxOq6Q/WR4oa9/LOKawEWMxJ7UpMtfj1t50OWeGZadRQp/i7DSSWjKus+eJKLIHDxLaobwhidQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-5.2.2.tgz", + "integrity": "sha512-ehhyyssfRLv6N5zysA09lTgL1KgGOt9RMuXEOrB8CV+aH6Ky1fz1OMmEv9YK+JQ6okRIeQTktOZ+STfTZj2ckw==", "dev": true, "requires": { - "autoprefixer": "^8.6.3", + "autoprefixer": "^8.6.5", "browserslist": "^3.2.8", - "caniuse-lite": "^1.0.30000859", + "caniuse-lite": "^1.0.30000865", "cssdb": "^3.1.0", "postcss": "^6.0.23", "postcss-attribute-case-insensitive": "^3.0.1", - "postcss-color-functional-notation": "^1.0.1", + "postcss-color-functional-notation": "^1.0.2", "postcss-color-hex-alpha": "^3.0.0", "postcss-color-mod-function": "^2.4.2", "postcss-color-rebeccapurple": "^3.1.0", @@ -18128,14 +18270,11 @@ "postcss-selector-not": "^3.0.1" }, "dependencies": { - "postcss-nesting": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-6.0.0.tgz", - "integrity": "sha512-Yoglsy6eZbDCbRIXoYSmnIt9ao4xyg07iFwVBd7WyIkDzMSeRxIqUk8xEAdkeJQ7eGfWo6RufrTU7FSUjZ22fg==", - "dev": true, - "requires": { - "postcss": "^6.0.22" - } + "caniuse-lite": { + "version": "1.0.30000865", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000865.tgz", + "integrity": "sha512-vs79o1mOSKRGv/1pSkp4EXgl4ZviWeYReXw60XfacPU64uQWZwJT6vZNmxRF9O+6zu71sJwMxLK5JXxbzuVrLw==", + "dev": true } } }, @@ -19059,6 +19198,16 @@ "integrity": "sha512-9Fv10F6rrzFDIT04aoRsw3dbN6l6onNXbBvCpt4OjXZFGDz/P65laNIujHVTlVjcqErut9d4BL0aplVByyGcJw==", "dev": true }, + "react-copy-to-clipboard": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.1.tgz", + "integrity": "sha512-ELKq31/E3zjFs5rDWNCfFL4NvNFQvGRoJdAKReD/rUPA+xxiLPQmZBZBvy2vgH7V0GE9isIQpT9WXbwIVErYdA==", + "dev": true, + "requires": { + "copy-to-clipboard": "^3", + "prop-types": "^15.5.8" + } + }, "react-dev-utils": { "version": "6.0.0-next.3e165448", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-6.0.0-next.3e165448.tgz", @@ -19256,18 +19405,26 @@ "dev": true }, "react-docgen": { - "version": "3.0.0-beta9", - "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-3.0.0-beta9.tgz", - "integrity": "sha512-3UqwxygAP/eZdDtOKum6vClKWUlceZ7RBVQ3Fe122l1WBYOqHcBzoUZIwN8feaLVo+s2eB/q+NkBfanLgvmt+w==", + "version": "3.0.0-rc.0", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-3.0.0-rc.0.tgz", + "integrity": "sha512-quhNeAo5LeACZ31LDAifq2Knyz2XE+Zm9QLo4EHgw+NifxBzH8FCQ5PyAPORqaAhWMiPOwYoIZ0biZVGqEkYag==", "dev": true, "requires": { + "@babel/parser": "7.0.0-beta.53", "async": "^2.1.4", "babel-runtime": "^6.9.2", - "babylon": "7.0.0-beta.31", "commander": "^2.9.0", "doctrine": "^2.0.0", "node-dir": "^0.1.10", - "recast": "^0.12.6" + "recast": "^0.15.0" + }, + "dependencies": { + "@babel/parser": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.53.tgz", + "integrity": "sha1-H0XrYXv5Rj1IKywE00nZ5O2/SJI=", + "dev": true + } } }, "react-docgen-typescript": { @@ -19299,13 +19456,13 @@ } }, "react-emotion": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/react-emotion/-/react-emotion-9.2.5.tgz", - "integrity": "sha1-DkDt8GLX7qJg1DJ6n+JyiOyDg14=", + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/react-emotion/-/react-emotion-9.2.6.tgz", + "integrity": "sha512-3GwLQ0Vib/X0G7KWY7N1F/7NmfoBLkkg3g9Jd8t6By5QUH0PyCSUVdDmfFBbgVKsAcpkjA0vcsMbiWeZusaRrA==", "dev": true, "requires": { - "babel-plugin-emotion": "^9.2.5", - "create-emotion-styled": "^9.2.5" + "babel-plugin-emotion": "^9.2.6", + "create-emotion-styled": "^9.2.6" } }, "react-error-overlay": { @@ -19358,6 +19515,31 @@ "integrity": "sha512-OQvgdiPfWbAjudT8Q+V2aWsySCJW0aQ3wNjfOw1ooamydEQkDxRfWqfnEhCYLneCz67d+r3kBYoZlpAx5S+VWA==", "dev": true }, + "react-popper": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.0.0.tgz", + "integrity": "sha1-uZRSFE6P5KzHf6PZWajHngemUIQ=", + "dev": true, + "requires": { + "babel-runtime": "6.x.x", + "create-react-context": "^0.2.1", + "popper.js": "^1.14.1", + "prop-types": "^15.6.1", + "typed-styles": "^0.0.5", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, "react-powerplug": { "version": "1.0.0-rc.1", "resolved": "https://registry.npmjs.org/react-powerplug/-/react-powerplug-1.0.0-rc.1.tgz", @@ -19498,6 +19680,14 @@ "ramda": "^0.25.0", "source-map-loader": "^0.2.3", "typescript": "^2.7.2" + }, + "dependencies": { + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "dev": true + } } }, "read-cache": { @@ -19574,13 +19764,12 @@ } }, "recast": { - "version": "0.12.9", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.12.9.tgz", - "integrity": "sha512-y7ANxCWmMW8xLOaiopiRDlyjQ9ajKRENBH+2wjntIbk3A6ZR1+BLQttkmSHMY7Arl+AAZFwJ10grg2T6f1WI8A==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.15.2.tgz", + "integrity": "sha512-L4f/GqxjlEJ5IZ+tdll/l+6dVi2ylysWbkgFJbMuldD6Jklgfv6zJnCpuAZDfjwHhfcd/De0dDKelsTEPQ29qA==", "dev": true, "requires": { - "ast-types": "0.10.1", - "core-js": "^2.4.1", + "ast-types": "0.11.5", "esprima": "~4.0.0", "private": "~0.1.5", "source-map": "~0.6.1" @@ -21923,6 +22112,12 @@ } } }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=", + "dev": true + }, "topo": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz", @@ -22489,6 +22684,12 @@ } } }, + "typed-styles": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.5.tgz", + "integrity": "sha512-ht+rEe5UsdEBAa3gr64+QjUOqjOLJfWLvl5HZR5Ev9uo/OnD3p43wPeFSB1hNFc13GXQF/JU1Bn0YHLUqBRIlw==", + "dev": true + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -22508,9 +22709,9 @@ "dev": true }, "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.1.tgz", + "integrity": "sha512-zQIMOmC+372pC/CCVLqnQ0zSBiY7HHodU7mpQdjiZddek4GMj31I3dUJ7gAs9o65X7mnRma6OokOkc6f9jjfBg==", "dev": true }, "ua-parser-js": { @@ -23904,9 +24105,9 @@ } }, "ws": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.0.tgz", - "integrity": "sha512-c18dMeW+PEQdDFzkhDsnBAlS4Z8KGStBQQUcQ5mf7Nf689jyGk0594L+i9RaQuf4gog6SvWLJorz2NfSaqxZ7w==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", "requires": { "async-limiter": "~1.0.0" } diff --git a/package.json b/package.json index 2529918e2..d02d7d0ef 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "@types/passport-oauth2": "^1.4.5", "@types/passport-strategy": "^0.2.33", "@types/query-string": "^6.1.0", + "@types/react-copy-to-clipboard": "^4.2.5", "@types/react-dom": "^16.0.6", "@types/react-relay": "github:coralproject/patched#types/react-relay", "@types/react-responsive": "^3.0.1", @@ -168,9 +169,11 @@ "query-string": "^6.1.0", "raw-loader": "^0.5.1", "react": "^16.4.0", + "react-copy-to-clipboard": "^5.0.1", "react-dev-utils": "6.0.0-next.3e165448", "react-dom": "^16.4.0", "react-final-form": "^3.6.4", + "react-popper": "^1.0.0-beta.6", "react-relay": "github:coralproject/patched#react-relay", "react-responsive": "^4.1.0", "react-test-renderer": "^16.4.1", @@ -199,7 +202,7 @@ "typed-css-modules": "^0.3.4", "typeface-manuale": "0.0.54", "typeface-source-sans-pro": "0.0.54", - "typescript": "^2.9.2", + "typescript": "^3.0.0", "uglifyjs-webpack-plugin": "^1.2.5", "webpack": "4.12.0", "webpack-cli": "^3.0.2", diff --git a/src/core/client/embed/Stream.ts b/src/core/client/embed/Stream.ts index 61db1863d..ae47e450e 100644 --- a/src/core/client/embed/Stream.ts +++ b/src/core/client/embed/Stream.ts @@ -5,9 +5,9 @@ import { Decorator, withAutoHeight, withClickEvent, - withCommentID, withEventEmitter, withIOSSafariWidthWorkaround, + withSetCommentID, } from "./decorators"; import PymControl from "./PymControl"; import { ensureEndSlash } from "./utils"; @@ -15,6 +15,7 @@ import { ensureEndSlash } from "./utils"; interface CreatePymControlConfig { assetID?: string; assetURL?: string; + commentID?: string; title?: string; eventEmitter: EventEmitter2; id: string; @@ -26,13 +27,14 @@ export function createPymControl(config: CreatePymControlConfig) { withIOSSafariWidthWorkaround, withAutoHeight, withClickEvent, - withCommentID, + withSetCommentID, withEventEmitter(config.eventEmitter), ]; const query = qs.stringify({ assetID: config.assetID, assetURL: config.assetURL, + commentID: config.commentID, }); const url = `${ensureEndSlash(config.rootURL)}stream.html?${query}`; return new PymControl({ @@ -73,6 +75,7 @@ export type StreamInterface = ReturnType; export interface CreateConfig { assetID?: string; assetURL?: string; + commentID?: string; title?: string; eventEmitter: EventEmitter2; id: string; diff --git a/src/core/client/embed/decorators/index.ts b/src/core/client/embed/decorators/index.ts index 59dbac12c..6e2d45195 100644 --- a/src/core/client/embed/decorators/index.ts +++ b/src/core/client/embed/decorators/index.ts @@ -4,7 +4,7 @@ export type CleanupCallback = () => void; export type Decorator = (pym: pym.Parent) => CleanupCallback | void; export { default as withAutoHeight } from "./withAutoHeight"; export { default as withClickEvent } from "./withClickEvent"; -export { default as withCommentID } from "./withCommentID"; +export { default as withSetCommentID } from "./withSetCommentID"; export { default as withEventEmitter } from "./withEventEmitter"; export { default as withIOSSafariWidthWorkaround, diff --git a/src/core/client/embed/decorators/withCommentID.ts b/src/core/client/embed/decorators/withCommentID.ts deleted file mode 100644 index 3fa968374..000000000 --- a/src/core/client/embed/decorators/withCommentID.ts +++ /dev/null @@ -1,36 +0,0 @@ -import qs from "query-string"; - -import { buildURL } from "../utils"; -import { Decorator } from "./"; - -const withCommentID: Decorator = pym => { - // Remove the comment id from the query. - pym.onMessage("view-all-comments", () => { - const search = qs.stringify({ - ...qs.parse(location.search), - commentId: undefined, - }); - - // Remove the commentId url param. - const url = buildURL({ search }); - - // Change the url. - window.history.replaceState({}, document.title, url); - }); - - // Add the permalink comment id to the query. - pym.onMessage("view-comment", (id: string) => { - const search = qs.stringify({ - ...qs.parse(location.search), - commentId: id, - }); - - // Remove the commentId url param. - const url = buildURL({ search }); - - // Change the url. - window.history.replaceState({}, document.title, url); - }); -}; - -export default withCommentID; diff --git a/src/core/client/embed/decorators/withCommentID.spec.ts b/src/core/client/embed/decorators/withSetCommentID.spec.ts similarity index 73% rename from src/core/client/embed/decorators/withCommentID.spec.ts rename to src/core/client/embed/decorators/withSetCommentID.spec.ts index 2c5802aa0..ed8f17e8b 100644 --- a/src/core/client/embed/decorators/withCommentID.spec.ts +++ b/src/core/client/embed/decorators/withSetCommentID.spec.ts @@ -1,17 +1,17 @@ -import withCommentID from "./withCommentID"; +import withSetCommentID from "./withSetCommentID"; it("should add commentID", () => { const previousLocation = location.toString(); const previousState = window.history.state; const fakePym = { onMessage: (type: string, callback: (id: string) => void) => { - if (type === "view-comment") { + if (type === "setCommentID") { callback("comment-id"); } }, }; - withCommentID(fakePym as any); - expect(location.toString()).toBe("http://localhost/?commentId=comment-id"); + withSetCommentID(fakePym as any); + expect(location.toString()).toBe("http://localhost/?commentID=comment-id"); window.history.replaceState(previousState, document.title, previousLocation); }); @@ -21,16 +21,16 @@ it("should remove commentID", () => { window.history.replaceState( previousState, document.title, - "http://localhost/?commentId=comment-id" + "http://localhost/?commentID=comment-id" ); const fakePym = { onMessage: (type: string, callback: () => void) => { - if (type === "view-all-comments") { + if (type === "setCommentID") { callback(); } }, }; - withCommentID(fakePym as any); + withSetCommentID(fakePym as any); expect(location.toString()).toBe("http://localhost/"); window.history.replaceState(previousState, document.title, previousLocation); }); diff --git a/src/core/client/embed/decorators/withSetCommentID.ts b/src/core/client/embed/decorators/withSetCommentID.ts new file mode 100644 index 000000000..b2b6812a3 --- /dev/null +++ b/src/core/client/embed/decorators/withSetCommentID.ts @@ -0,0 +1,22 @@ +import qs from "query-string"; + +import { buildURL } from "../utils"; +import { Decorator } from "./"; + +const withSetCommentID: Decorator = pym => { + // Add the permalink comment id to the query. + pym.onMessage("setCommentID", (id: string) => { + const search = qs.stringify({ + ...qs.parse(location.search), + commentID: id || undefined, + }); + + // Remove the commentId url param. + const url = buildURL({ search }); + + // Change the url. + window.history.replaceState({}, document.title, url); + }); +}; + +export default withSetCommentID; diff --git a/src/core/client/embed/index.html b/src/core/client/embed/index.html index 2eedfea25..297768c75 100644 --- a/src/core/client/embed/index.html +++ b/src/core/client/embed/index.html @@ -10,7 +10,7 @@

Talk 5.0 – Embed Stream

-
+
diff --git a/src/core/client/embed/index.ts b/src/core/client/embed/index.ts index 6c6ebc36d..c440f72a8 100644 --- a/src/core/client/embed/index.ts +++ b/src/core/client/embed/index.ts @@ -6,6 +6,7 @@ import createStreamInterface from "./Stream"; export interface Config { assetID?: string; assetURL?: string; + commentID?: string; rootURL?: string; id?: string; events?: (eventEmitter: EventEmitter2) => void; @@ -23,6 +24,7 @@ export function render(config: Config = {}) { return createStreamInterface({ assetID: config.assetID || query.assetID, assetURL: config.assetURL || query.assetURL, + commentID: config.commentID || query.commentID, id: config.id || "talk-embed-stream", rootURL: config.rootURL || location.origin, eventEmitter, diff --git a/src/core/client/framework/lib/relay/createMutationContainer.tsx b/src/core/client/framework/lib/relay/createMutationContainer.tsx index 0446e7d17..b8c74be18 100644 --- a/src/core/client/framework/lib/relay/createMutationContainer.tsx +++ b/src/core/client/framework/lib/relay/createMutationContainer.tsx @@ -7,7 +7,7 @@ import { } from "recompose"; import { Environment } from "relay-runtime"; -import { withContext } from "../bootstrap"; +import { TalkContext, withContext } from "../bootstrap"; /** * createMutationContainer creates a HOC that @@ -18,10 +18,14 @@ import { withContext } from "../bootstrap"; */ function createMutationContainer( propName: T, - commit: (environment: Environment, input: I) => Promise + commit: ( + environment: Environment, + input: I, + context: TalkContext + ) => Promise ): InferableComponentEnhancer<{ [P in T]: (input: I) => Promise }> { return compose( - withContext(({ relayEnvironment }) => ({ relayEnvironment })), + withContext(context => ({ context })), hoistStatics((BaseComponent: React.ComponentType) => { class CreateMutationContainer extends React.Component { public static displayName = wrapDisplayName( @@ -30,7 +34,11 @@ function createMutationContainer( ); private commit = (input: I) => { - return commit(this.props.relayEnvironment, input); + return commit( + this.props.context.relayEnvironment, + input, + this.props.context + ); }; public render() { diff --git a/src/core/client/framework/lib/relay/withLocalStateContainer.tsx b/src/core/client/framework/lib/relay/withLocalStateContainer.tsx index 9eaeb0ce8..421eb7d71 100644 --- a/src/core/client/framework/lib/relay/withLocalStateContainer.tsx +++ b/src/core/client/framework/lib/relay/withLocalStateContainer.tsx @@ -1,8 +1,14 @@ import * as React from "react"; -import { compose, hoistStatics, InferableComponentEnhancer } from "recompose"; +import { + compose, + hoistStatics, + InferableComponentEnhancer, + wrapDisplayName, +} from "recompose"; import { CSelector, CSnapshot, + Disposable, Environment, GraphQLTaggedNode, } from "relay-runtime"; @@ -36,6 +42,12 @@ function withLocalStateContainer( withContext(({ relayEnvironment }) => ({ relayEnvironment })), hoistStatics((BaseComponent: React.ComponentType) => { class LocalStateContainer extends React.Component { + public static displayName = wrapDisplayName( + BaseComponent, + "withLocalStateContainer" + ); + private subscription: Disposable; + constructor(props: Props) { super(props); const fragment = (fragmentSpec as any).data().default; @@ -53,7 +65,10 @@ function withLocalStateContainer( variables: {}, }; const snapshot = props.relayEnvironment.lookup(selector); - props.relayEnvironment.subscribe(snapshot, this.updateSnapshot); + this.subscription = props.relayEnvironment.subscribe( + snapshot, + this.updateSnapshot + ); this.state = { data: snapshot.data, }; @@ -63,6 +78,10 @@ function withLocalStateContainer( this.setState({ data: snapshot.data }); }; + public componentWillUnmount() { + this.subscription.dispose(); + } + public render() { const { relayEnvironment: _, ...rest } = this.props; return ; diff --git a/src/core/client/framework/testHelpers/createRelayEnvironment.ts b/src/core/client/framework/testHelpers/createRelayEnvironment.ts new file mode 100644 index 000000000..717c037ad --- /dev/null +++ b/src/core/client/framework/testHelpers/createRelayEnvironment.ts @@ -0,0 +1,78 @@ +import { IResolvers } from "graphql-tools"; +import { createFetch } from "relay-local-schema"; +import { + commitLocalUpdate, + Environment, + Network, + RecordProxy, + RecordSource, + RecordSourceProxy, + Store, +} from "relay-runtime"; + +import { + createAndRetain, + LOCAL_ID, + LOCAL_TYPE, + wrapFetchWithLogger, +} from "talk-framework/lib/relay"; + +import { loadSchema } from "talk-common/graphql"; + +export interface CreateRelayEnvironmentNetworkParams { + /** project name of graphql-config */ + projectName: string; + /** graphql resolvers */ + resolvers: IResolvers; + /** If enabled, graphql responses will be logged to the console */ + logNetwork?: boolean; +} + +export interface CreateRelayEnvironmentParams { + /** If set, creates a network to a local graphql server with a local schema */ + network?: CreateRelayEnvironmentNetworkParams; + /** Allows to set initial state for Local state */ + initLocalState?: ( + local: RecordProxy, + source: RecordSourceProxy, + environment: Environment + ) => void; + /** Use this source for creating the environment */ + source?: RecordSource; +} + +/** + * create Relay environment for tests environments. + */ +export default function createRelayEnvironment( + params: CreateRelayEnvironmentParams = {} +) { + let network: Network = null as any; + if (params.network) { + const schema = loadSchema( + params.network.projectName, + params.network.resolvers + ); + network = Network.create( + wrapFetchWithLogger(createFetch({ schema }), params.network.logNetwork) + ); + } + const environment = new Environment({ + network, + store: new Store(params.source || new RecordSource()), + }); + commitLocalUpdate(environment, sourceProxy => { + const root = sourceProxy.getRoot(); + const localRecord = createAndRetain( + environment, + sourceProxy, + LOCAL_ID, + LOCAL_TYPE + ); + root.setLinkedRecord(localRecord, "local"); + if (params.initLocalState) { + params.initLocalState!(localRecord, sourceProxy, environment); + } + }); + return environment; +} diff --git a/src/core/client/framework/testHelpers/index.ts b/src/core/client/framework/testHelpers/index.ts new file mode 100644 index 000000000..9cbc97018 --- /dev/null +++ b/src/core/client/framework/testHelpers/index.ts @@ -0,0 +1,4 @@ +export { + default as createRelayEnvironment, + CreateRelayEnvironmentParams, +} from "./createRelayEnvironment"; diff --git a/src/core/client/stream/components/App.css b/src/core/client/stream/components/App.css index 68c4e29c4..3f61a7cae 100644 --- a/src/core/client/stream/components/App.css +++ b/src/core/client/stream/components/App.css @@ -11,4 +11,5 @@ } .root { + width: 100%; } diff --git a/src/core/client/stream/components/App.spec.tsx b/src/core/client/stream/components/App.spec.tsx index 9b91a8974..d7dfdc3a7 100644 --- a/src/core/client/stream/components/App.spec.tsx +++ b/src/core/client/stream/components/App.spec.tsx @@ -5,17 +5,17 @@ import { PropTypesOf } from "talk-framework/types"; import App from "./App"; -it("renders correctly", () => { +it("renders stream", () => { const props: PropTypesOf = { - asset: {}, + showPermalinkView: false, }; const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); -it("renders correctly when asset is null", () => { +it("renders permalink view", () => { const props: PropTypesOf = { - asset: null, + showPermalinkView: true, }; const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); diff --git a/src/core/client/stream/components/App.tsx b/src/core/client/stream/components/App.tsx index 14659585a..39272cb17 100644 --- a/src/core/client/stream/components/App.tsx +++ b/src/core/client/stream/components/App.tsx @@ -3,23 +3,26 @@ import { StatelessComponent } from "react"; import { Flex } from "talk-ui/components"; -import StreamContainer from "../containers/StreamContainer"; +import PermalinkViewQuery from "../queries/PermalinkViewQuery"; +import StreamQuery from "../queries/StreamQuery"; import * as styles from "./App.css"; export interface AppProps { - asset: {} | null; + showPermalinkView: boolean; } const App: StatelessComponent = props => { - if (props.asset) { - return ( - - - - ); - } - return
Asset not found
; + const view = props.showPermalinkView ? ( + + ) : ( + + ); + return ( + + {view} + + ); }; export default App; diff --git a/src/core/client/stream/components/Comment/Comment.css b/src/core/client/stream/components/Comment/Comment.css new file mode 100644 index 000000000..c4bf8c532 --- /dev/null +++ b/src/core/client/stream/components/Comment/Comment.css @@ -0,0 +1,3 @@ +.footer { + margin-top: var(--spacing-unit); +} diff --git a/src/core/client/stream/components/Comment/Comment.spec.tsx b/src/core/client/stream/components/Comment/Comment.spec.tsx index cae068012..8b409adeb 100644 --- a/src/core/client/stream/components/Comment/Comment.spec.tsx +++ b/src/core/client/stream/components/Comment/Comment.spec.tsx @@ -7,6 +7,7 @@ import Comment from "./Comment"; it("renders username and body", () => { const props: PropTypesOf = { + id: "comment-id", author: { username: "Marvin", }, diff --git a/src/core/client/stream/components/Comment/Comment.tsx b/src/core/client/stream/components/Comment/Comment.tsx index 03a947a5a..932cd7155 100644 --- a/src/core/client/stream/components/Comment/Comment.tsx +++ b/src/core/client/stream/components/Comment/Comment.tsx @@ -1,13 +1,16 @@ import React from "react"; import { StatelessComponent } from "react"; - import { Typography } from "talk-ui/components"; +import * as styles from "./Comment.css"; +import PermalinkButtonContainer from "../../containers/PermalinkButtonContainer"; import Timestamp from "./Timestamp"; import TopBar from "./TopBar"; import Username from "./Username"; export interface CommentProps { + id: string; + className?: string; author: { username: string | null; } | null; @@ -24,6 +27,9 @@ const Comment: StatelessComponent = props => { {props.createdAt} {props.body} +
+ +
); }; diff --git a/src/core/client/stream/components/Comment/__snapshots__/Comment.spec.tsx.snap b/src/core/client/stream/components/Comment/__snapshots__/Comment.spec.tsx.snap index 2d047c200..c86fc9284 100644 --- a/src/core/client/stream/components/Comment/__snapshots__/Comment.spec.tsx.snap +++ b/src/core/client/stream/components/Comment/__snapshots__/Comment.spec.tsx.snap @@ -15,5 +15,12 @@ exports[`renders username and body 1`] = ` Woof +
+ +
`; diff --git a/src/core/client/stream/components/Logo.tsx b/src/core/client/stream/components/Logo.tsx deleted file mode 100644 index 2fe4dcac0..000000000 --- a/src/core/client/stream/components/Logo.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Localized } from "fluent-react/compat"; -import * as React from "react"; -import { StatelessComponent } from "react"; - -import { Typography } from "talk-ui/components"; - -export interface LogoProps { - className?: string; - gutterBottom?: boolean; -} - -const Logo: StatelessComponent = props => { - return ( - - ); -}; - -export default Logo; diff --git a/src/core/client/stream/components/PermalinkButton/PermalinkButton.css b/src/core/client/stream/components/PermalinkButton/PermalinkButton.css new file mode 100644 index 000000000..b303f61b6 --- /dev/null +++ b/src/core/client/stream/components/PermalinkButton/PermalinkButton.css @@ -0,0 +1,4 @@ +.popover { + width: 350px; + max-width: 80%; +} diff --git a/src/core/client/stream/components/PermalinkButton/PermalinkButton.tsx b/src/core/client/stream/components/PermalinkButton/PermalinkButton.tsx new file mode 100644 index 000000000..493625e2b --- /dev/null +++ b/src/core/client/stream/components/PermalinkButton/PermalinkButton.tsx @@ -0,0 +1,73 @@ +import { Localized } from "fluent-react/compat"; +import React from "react"; +import { oncePerFrame } from "talk-common/utils"; +import { + Button, + ButtonIcon, + ClickOutside, + MatchMedia, + Popover, +} from "talk-ui/components"; + +import * as styles from "./PermalinkButton.css"; +import PermalinkPopover from "./PermalinkPopover"; + +interface PermalinkProps { + commentID: string; + assetURL: string | null; +} + +class Permalink extends React.Component { + // Helper that prevents calling toggleVisibility more then once per frame. + // In essence this means we'll process an event only once. + // This might happen, when clicking on the button which will + // cause its onClick to happen as well as onClickOutside. + private toggleVisibilityOncePerFrame = oncePerFrame( + (toggleVisibility: () => void) => toggleVisibility() + ); + + public render() { + const { commentID, assetURL } = this.props; + const popoverID = "permalink-popover"; + return ( + ( + + this.toggleVisibilityOncePerFrame(toggleVisibility) + } + > + + + )} + > + {({ toggleVisibility, forwardRef, visible }) => ( + + )} + + ); + } +} + +export default Permalink; diff --git a/src/core/client/stream/components/PermalinkButton/PermalinkPopover.css b/src/core/client/stream/components/PermalinkButton/PermalinkPopover.css new file mode 100644 index 000000000..c2afa31b2 --- /dev/null +++ b/src/core/client/stream/components/PermalinkButton/PermalinkPopover.css @@ -0,0 +1,7 @@ +.root { + width: 100%; +} + +.textField { + flex-grow: 1; +} diff --git a/src/core/client/stream/components/PermalinkButton/PermalinkPopover.tsx b/src/core/client/stream/components/PermalinkButton/PermalinkPopover.tsx new file mode 100644 index 000000000..218bd78f6 --- /dev/null +++ b/src/core/client/stream/components/PermalinkButton/PermalinkPopover.tsx @@ -0,0 +1,60 @@ +import { Localized } from "fluent-react/compat"; +import React from "react"; +import CopyToClipboard from "react-copy-to-clipboard"; + +import { Button, Flex, TextField } from "talk-ui/components"; + +import * as styles from "./PermalinkPopover.css"; + +interface InnerProps { + permalinkURL: string; + toggleVisibility: () => void; +} + +interface State { + copied: boolean; +} + +class PermalinkPopover extends React.Component { + public state: State = { + copied: false, + }; + + private onCopy = async () => { + await this.toggleCopied(); + setTimeout(() => { + this.toggleCopied(); + }, 800); + }; + + private toggleCopied = () => { + this.setState((state: State) => ({ + copied: !state.copied, + })); + }; + + public render() { + const { permalinkURL } = this.props; + const { copied } = this.state; + return ( + + + + + + + ); + } +} + +export default PermalinkPopover; diff --git a/src/core/client/stream/components/PermalinkButton/index.ts b/src/core/client/stream/components/PermalinkButton/index.ts new file mode 100644 index 000000000..de6d61c1c --- /dev/null +++ b/src/core/client/stream/components/PermalinkButton/index.ts @@ -0,0 +1 @@ +export { default } from "./PermalinkButton"; diff --git a/src/core/client/stream/components/PermalinkView.css b/src/core/client/stream/components/PermalinkView.css new file mode 100644 index 000000000..a71a33af8 --- /dev/null +++ b/src/core/client/stream/components/PermalinkView.css @@ -0,0 +1,7 @@ +.root { + width: 100%; +} + +.button { + margin-bottom: calc(2 * var(--spacing-unit)); +} diff --git a/src/core/client/stream/components/PermalinkView.tsx b/src/core/client/stream/components/PermalinkView.tsx new file mode 100644 index 000000000..b7ea70370 --- /dev/null +++ b/src/core/client/stream/components/PermalinkView.tsx @@ -0,0 +1,43 @@ +import React, { StatelessComponent } from "react"; + +import { Button, Flex, Typography } from "talk-ui/components"; + +import CommentContainer from "../containers/CommentContainer"; +import * as styles from "./PermalinkView.css"; + +export interface PermalinkViewProps { + comment: {} | null; + assetURL: string | null; + onShowAllComments: () => void; +} + +const PermalinkView: StatelessComponent = ({ + assetURL, + comment, + onShowAllComments, +}) => { + return ( +
+ {assetURL && ( + + )} + {!comment && Comment not found} + {comment && ( + + + + )} +
+ ); +}; + +export default PermalinkView; diff --git a/src/core/client/stream/components/Stream.css b/src/core/client/stream/components/Stream.css index 13c90d031..88de80d08 100644 --- a/src/core/client/stream/components/Stream.css +++ b/src/core/client/stream/components/Stream.css @@ -1,4 +1,3 @@ .root { width: 100%; - max-width: 400px; } diff --git a/src/core/client/stream/components/Stream.tsx b/src/core/client/stream/components/Stream.tsx index 9dbb21c5b..ef1ed5bda 100644 --- a/src/core/client/stream/components/Stream.tsx +++ b/src/core/client/stream/components/Stream.tsx @@ -7,22 +7,20 @@ import { Button, Flex } from "talk-ui/components"; import CommentContainer from "../containers/CommentContainer"; import PostCommentFormContainer from "../containers/PostCommentFormContainer"; import ReplyListContainer from "../containers/ReplyListContainer"; -import Logo from "./Logo"; import * as styles from "./Stream.css"; export interface StreamProps { assetID: string; - isClosed: boolean; + isClosed?: boolean; comments: ReadonlyArray<{ id: string }>; - onLoadMore: () => void; - hasMore: boolean; - disableLoadMore: boolean; + onLoadMore?: () => void; + hasMore?: boolean; + disableLoadMore?: boolean; } const Stream: StatelessComponent = props => { return (
- - + `; -exports[`renders correctly when asset is null 1`] = ` -
- Asset not found -
+exports[`renders stream 1`] = ` + + + `; diff --git a/src/core/client/stream/components/__snapshots__/Stream.spec.tsx.snap b/src/core/client/stream/components/__snapshots__/Stream.spec.tsx.snap index 95b463311..5da869b02 100644 --- a/src/core/client/stream/components/__snapshots__/Stream.spec.tsx.snap +++ b/src/core/client/stream/components/__snapshots__/Stream.spec.tsx.snap @@ -4,9 +4,6 @@ exports[`renders correctly 1`] = `
- @@ -65,9 +62,6 @@ exports[`when there is more disables load more button 1`] = `
- @@ -140,9 +134,6 @@ exports[`when there is more renders a load more button 1`] = `
- diff --git a/src/core/client/stream/containers/AppContainer.spec.tsx b/src/core/client/stream/containers/AppContainer.spec.tsx deleted file mode 100644 index 17a0a676d..000000000 --- a/src/core/client/stream/containers/AppContainer.spec.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { shallow } from "enzyme"; -import React from "react"; - -import { PropTypesOf } from "talk-framework/types"; - -import { AppContainer } from "./AppContainer"; - -it("renders correctly", () => { - const props: PropTypesOf = { - data: { - asset: {}, - }, - }; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); -}); diff --git a/src/core/client/stream/containers/AppContainer.tsx b/src/core/client/stream/containers/AppContainer.tsx index 90eb1e1c6..7fbc7f6d8 100644 --- a/src/core/client/stream/containers/AppContainer.tsx +++ b/src/core/client/stream/containers/AppContainer.tsx @@ -1,30 +1,27 @@ -import React, { StatelessComponent } from "react"; -import { graphql } from "react-relay"; +import * as React from "react"; +import { StatelessComponent } from "react"; -import { withFragmentContainer } from "talk-framework/lib/relay"; -import { PropTypesOf } from "talk-framework/types"; -import { AppContainer as Data } from "talk-stream/__generated__/AppContainer.graphql"; +import { graphql, withLocalStateContainer } from "talk-framework/lib/relay"; +import { AppContainerLocal as Local } from "talk-stream/__generated__/AppContainerLocal.graphql"; import App from "../components/App"; interface InnerProps { - data: Data; + local: Local; } -export const AppContainer: StatelessComponent = props => { - return ; +const AppContainer: StatelessComponent = ({ + local: { commentID }, +}) => { + return ; }; -const enhanced = withFragmentContainer<{ data: Data }>({ - data: graphql` - fragment AppContainer on Query - @argumentDefinitions(assetID: { type: "ID!" }) { - asset(id: $assetID) { - ...StreamContainer_asset - } +const enhanced = withLocalStateContainer( + graphql` + fragment AppContainerLocal on Local { + commentID } - `, -})(AppContainer); + ` +)(AppContainer); -export type AppContainerProps = PropTypesOf; export default enhanced; diff --git a/src/core/client/stream/containers/CommentContainer.spec.tsx b/src/core/client/stream/containers/CommentContainer.spec.tsx index 4836991b8..44e4a0a19 100644 --- a/src/core/client/stream/containers/CommentContainer.spec.tsx +++ b/src/core/client/stream/containers/CommentContainer.spec.tsx @@ -8,6 +8,7 @@ import { CommentContainer } from "./CommentContainer"; it("renders username and body", () => { const props: PropTypesOf = { data: { + id: "comment-id", author: { username: "Marvin", }, @@ -23,6 +24,7 @@ it("renders username and body", () => { it("renders body only", () => { const props: PropTypesOf = { data: { + id: "comment-id", author: { username: null, }, diff --git a/src/core/client/stream/containers/CommentContainer.tsx b/src/core/client/stream/containers/CommentContainer.tsx index 0196f6b2e..0316112d1 100644 --- a/src/core/client/stream/containers/CommentContainer.tsx +++ b/src/core/client/stream/containers/CommentContainer.tsx @@ -2,16 +2,19 @@ import React, { StatelessComponent } from "react"; import { graphql } from "react-relay"; import withFragmentContainer from "talk-framework/lib/relay/withFragmentContainer"; -import { Omit, PropTypesOf } from "talk-framework/types"; +import { PropTypesOf } from "talk-framework/types"; import { CommentContainer as Data } from "talk-stream/__generated__/CommentContainer.graphql"; -import Comment, { CommentProps } from "../components/Comment"; +import Comment from "../components/Comment"; -type InnerProps = { data: Data } & Omit; +interface InnerProps { + data: Data; +} // tslint:disable-next-line:no-unused-expression graphql` fragment CommentContainer_comment on Comment { + id author { username } diff --git a/src/core/client/stream/containers/PermalinkButtonContainer.tsx b/src/core/client/stream/containers/PermalinkButtonContainer.tsx new file mode 100644 index 000000000..fd1509117 --- /dev/null +++ b/src/core/client/stream/containers/PermalinkButtonContainer.tsx @@ -0,0 +1,30 @@ +import React, { StatelessComponent } from "react"; +import { graphql } from "react-relay"; +import { withLocalStateContainer } from "talk-framework/lib/relay"; +import { PermalinkButtonContainerLocal as Local } from "talk-stream/__generated__/PermalinkButtonContainerLocal.graphql"; + +import PermalinkButton from "../components/PermalinkButton"; + +interface InnerProps { + local: Local; + commentID: string; +} + +export const PermalinkContainer: StatelessComponent = ({ + local, + commentID, +}) => { + return local.assetURL ? ( + + ) : null; +}; + +const enhanced = withLocalStateContainer( + graphql` + fragment PermalinkButtonContainerLocal on Local { + assetURL + } + ` +)(PermalinkContainer); + +export default enhanced; diff --git a/src/core/client/stream/containers/PermalinkViewContainer.tsx b/src/core/client/stream/containers/PermalinkViewContainer.tsx new file mode 100644 index 000000000..65fede9d8 --- /dev/null +++ b/src/core/client/stream/containers/PermalinkViewContainer.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import { graphql } from "react-relay"; +import { withFragmentContainer } from "talk-framework/lib/relay"; +import { PermalinkViewContainer_asset as AssetData } from "talk-stream/__generated__/PermalinkViewContainer_asset.graphql"; +import { PermalinkViewContainer_comment as CommentData } from "talk-stream/__generated__/PermalinkViewContainer_comment.graphql"; +import { + SetCommentIDMutation, + withSetCommentIDMutation, +} from "talk-stream/mutations"; +import PermalinkView from "../components/PermalinkView"; + +interface PermalinkViewContainerProps { + comment: CommentData | null; + asset: AssetData | null; + setCommentID: SetCommentIDMutation; +} + +class PermalinkViewContainer extends React.Component< + PermalinkViewContainerProps +> { + private showAllComments = () => { + this.props.setCommentID({ id: null }); + }; + public render() { + const { comment, asset } = this.props; + return ( + + ); + } +} + +const enhanced = withSetCommentIDMutation( + withFragmentContainer<{ + comment: CommentData | null; + asset: AssetData | null; + }>({ + comment: graphql` + fragment PermalinkViewContainer_comment on Comment { + ...CommentContainer + } + `, + asset: graphql` + fragment PermalinkViewContainer_asset on Asset { + url + } + `, + })(PermalinkViewContainer) +); + +export default enhanced; diff --git a/src/core/client/stream/containers/__snapshots__/AppContainer.spec.tsx.snap b/src/core/client/stream/containers/__snapshots__/AppContainer.spec.tsx.snap deleted file mode 100644 index f9a5c5316..000000000 --- a/src/core/client/stream/containers/__snapshots__/AppContainer.spec.tsx.snap +++ /dev/null @@ -1,7 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly 1`] = ` - -`; diff --git a/src/core/client/stream/containers/__snapshots__/CommentContainer.spec.tsx.snap b/src/core/client/stream/containers/__snapshots__/CommentContainer.spec.tsx.snap index f6ec9754b..fdf0b47f0 100644 --- a/src/core/client/stream/containers/__snapshots__/CommentContainer.spec.tsx.snap +++ b/src/core/client/stream/containers/__snapshots__/CommentContainer.spec.tsx.snap @@ -9,6 +9,7 @@ exports[`renders body only 1`] = ` } body="Woof" createdAt="1995-12-17T03:24:00.000Z" + id="comment-id" /> `; @@ -21,5 +22,6 @@ exports[`renders username and body 1`] = ` } body="Woof" createdAt="1995-12-17T03:24:00.000Z" + id="comment-id" /> `; diff --git a/src/core/client/stream/index.tsx b/src/core/client/stream/index.tsx index c6f9059e0..943d920dd 100644 --- a/src/core/client/stream/index.tsx +++ b/src/core/client/stream/index.tsx @@ -9,9 +9,9 @@ import { TalkContextProvider, } from "talk-framework/lib/bootstrap"; +import AppContainer from "./containers/AppContainer"; import { initLocalState } from "./local"; import localesData from "./locales"; -import AppQuery from "./queries/AppQuery"; // This is called when the context is first initialized. async function init({ relayEnvironment }: TalkContext) { @@ -29,7 +29,7 @@ async function main() { const Index: StatelessComponent = () => ( - + ); diff --git a/src/core/client/stream/local/initLocalState.ts b/src/core/client/stream/local/initLocalState.ts index d65ea6331..434c60e32 100644 --- a/src/core/client/stream/local/initLocalState.ts +++ b/src/core/client/stream/local/initLocalState.ts @@ -21,10 +21,23 @@ export default async function initLocalState(environment: Environment) { // Parse query params const query = qs.parse(location.search); + if (query.assetID) { localRecord.setValue(query.assetID, "assetID"); } + // Saving location host for permalink until we get the asset url - the url now points to the tenant + if (location && query.assetID) { + localRecord.setValue( + `${location.origin}/?assetID=${query.assetID}`, + "assetURL" + ); + } + + if (query.commentID) { + localRecord.setValue(query.commentID, "commentID"); + } + // Create network Record const networkRecord = createAndRetain( environment, diff --git a/src/core/client/stream/local/local.graphql b/src/core/client/stream/local/local.graphql index 3359458d0..9a2991607 100644 --- a/src/core/client/stream/local/local.graphql +++ b/src/core/client/stream/local/local.graphql @@ -6,6 +6,8 @@ type Network { type Local { network: Network! assetID: String + commentID: String + assetURL: String } extend type Query { diff --git a/src/core/client/stream/mutations/SetCommentIDMutation.spec.ts b/src/core/client/stream/mutations/SetCommentIDMutation.spec.ts new file mode 100644 index 000000000..8f296c79d --- /dev/null +++ b/src/core/client/stream/mutations/SetCommentIDMutation.spec.ts @@ -0,0 +1,54 @@ +import { Environment, RecordSource } from "relay-runtime"; +import sinon from "sinon"; + +import { timeout } from "talk-common/utils"; +import { LOCAL_ID } from "talk-framework/lib/relay"; +import { createRelayEnvironment } from "talk-framework/testHelpers"; + +import { commit } from "./SetCommentIDMutation"; + +let environment: Environment; +const source: RecordSource = new RecordSource(); + +beforeAll(() => { + environment = createRelayEnvironment({ + source, + }); +}); + +it("Sets comment id", () => { + const id = "comment1-id"; + commit(environment, { id }, {} as any); + expect(source.get(LOCAL_ID)!.commentID).toEqual(id); +}); + +it("Should call setCommentID in pym", async () => { + const id = "comment2-id"; + const context = { + pym: { + sendMessage: sinon + .mock() + .once() + .withArgs("setCommentID", id), + }, + }; + commit(environment, { id }, context as any); + await timeout(); + expect(source.get(LOCAL_ID)!.commentID).toEqual(id); + context.pym.sendMessage.verify(); +}); + +it("Should call setCommentID in pym with empty id", async () => { + const context = { + pym: { + sendMessage: sinon + .mock() + .once() + .withArgs("setCommentID", ""), + }, + }; + commit(environment, { id: null }, context as any); + await timeout(); + expect(source.get(LOCAL_ID)!.commentID).toEqual(null); + context.pym.sendMessage.verify(); +}); diff --git a/src/core/client/stream/mutations/SetCommentIDMutation.ts b/src/core/client/stream/mutations/SetCommentIDMutation.ts new file mode 100644 index 000000000..4bacb1181 --- /dev/null +++ b/src/core/client/stream/mutations/SetCommentIDMutation.ts @@ -0,0 +1,31 @@ +import { commitLocalUpdate, Environment } from "relay-runtime"; + +import { TalkContext } from "talk-framework/lib/bootstrap"; +import { createMutationContainer } from "talk-framework/lib/relay"; +import { LOCAL_ID } from "talk-framework/lib/relay/withLocalStateContainer"; + +export interface SetCommentIDInput { + id: string | null; +} + +export type SetCommentIDMutation = (input: SetCommentIDInput) => Promise; + +export async function commit( + environment: Environment, + input: SetCommentIDInput, + { pym }: TalkContext +) { + return commitLocalUpdate(environment, store => { + const record = store.get(LOCAL_ID)!; + record.setValue(input.id, "commentID"); + if (pym) { + // This sets the comment id on the parent url. + pym.sendMessage("setCommentID", input.id || ""); + } + }); +} + +export const withSetCommentIDMutation = createMutationContainer( + "setCommentID", + commit +); diff --git a/src/core/client/stream/mutations/SetNetworkStatusMutation.spec.ts b/src/core/client/stream/mutations/SetNetworkStatusMutation.spec.ts new file mode 100644 index 000000000..1a6196c45 --- /dev/null +++ b/src/core/client/stream/mutations/SetNetworkStatusMutation.spec.ts @@ -0,0 +1,26 @@ +import { Environment, RecordSource } from "relay-runtime"; + +import { createRelayEnvironment } from "talk-framework/testHelpers"; + +import { NETWORK_ID, NETWORK_TYPE } from "../local"; + +import { commit } from "./SetNetworkStatusMutation"; + +let environment: Environment; +const source: RecordSource = new RecordSource(); + +beforeAll(() => { + environment = createRelayEnvironment({ + source, + initLocalState: (localRecord, sourceProxy) => { + const networkRecord = sourceProxy.create(NETWORK_ID, NETWORK_TYPE); + networkRecord.setValue(false, "isOffline"); + localRecord.setLinkedRecord(networkRecord, "network"); + }, + }); +}); + +it("Sets comment id", () => { + commit(environment, { isOffline: true }); + expect(source.get(NETWORK_ID)!.isOffline).toEqual(true); +}); diff --git a/src/core/client/stream/mutations/SetNetworkStatusMutation.ts b/src/core/client/stream/mutations/SetNetworkStatusMutation.ts index e5c5d0145..5f2fa6486 100644 --- a/src/core/client/stream/mutations/SetNetworkStatusMutation.ts +++ b/src/core/client/stream/mutations/SetNetworkStatusMutation.ts @@ -8,9 +8,14 @@ export interface SetNetworkStatusInput { isOffline: boolean; } -export type SetNetworkStatusMutation = (input: SetNetworkStatusInput) => void; +export type SetNetworkStatusMutation = ( + input: SetNetworkStatusInput +) => Promise; -async function commit(environment: Environment, input: SetNetworkStatusInput) { +export async function commit( + environment: Environment, + input: SetNetworkStatusInput +) { return commitLocalUpdate(environment, store => { const record = store.get(NETWORK_ID)!; record.setValue(input.isOffline, "isOffline"); diff --git a/src/core/client/stream/mutations/index.ts b/src/core/client/stream/mutations/index.ts index 83ea079c0..1b71335e7 100644 --- a/src/core/client/stream/mutations/index.ts +++ b/src/core/client/stream/mutations/index.ts @@ -1,2 +1,15 @@ -export * from "./CreateCommentMutation"; -export * from "./SetNetworkStatusMutation"; +export { + withCreateCommentMutation, + CreateCommentMutation, + CreateCommentInput, +} from "./CreateCommentMutation"; +export { + withSetNetworkStatusMutation, + SetNetworkStatusMutation, + SetNetworkStatusInput, +} from "./SetNetworkStatusMutation"; +export { + withSetCommentIDMutation, + SetCommentIDMutation, + SetCommentIDInput, +} from "./SetCommentIDMutation"; diff --git a/src/core/client/stream/queries/AppQuery.tsx b/src/core/client/stream/queries/AppQuery.tsx deleted file mode 100644 index 3fe95b6d9..000000000 --- a/src/core/client/stream/queries/AppQuery.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import * as React from "react"; -import { StatelessComponent } from "react"; -import { ReadyState } from "react-relay"; - -import { - graphql, - QueryRenderer, - withLocalStateContainer, -} from "talk-framework/lib/relay"; -import { - AppQueryResponse, - AppQueryVariables, -} from "talk-stream/__generated__/AppQuery.graphql"; -import { AppQueryLocal as Local } from "talk-stream/__generated__/AppQueryLocal.graphql"; - -import AppContainer from "../containers/AppContainer"; - -export const render = ({ error, props }: ReadyState) => { - if (error) { - return
{error.message}
; - } - if (props) { - return ; - } - return
Loading
; -}; - -interface InnerProps { - local: Local; -} - -const AppQuery: StatelessComponent = props => { - return ( - - query={graphql` - query AppQuery($assetID: ID!) { - ...AppContainer @arguments(assetID: $assetID) - } - `} - variables={{ - assetID: props.local.assetID, - }} - render={render} - /> - ); -}; - -const enhanced = withLocalStateContainer( - graphql` - fragment AppQueryLocal on Local { - assetID - } - ` -)(AppQuery); - -export default enhanced; diff --git a/src/core/client/stream/queries/PermalinkViewQuery.spec.tsx b/src/core/client/stream/queries/PermalinkViewQuery.spec.tsx new file mode 100644 index 000000000..01b91218a --- /dev/null +++ b/src/core/client/stream/queries/PermalinkViewQuery.spec.tsx @@ -0,0 +1,34 @@ +import { shallow } from "enzyme"; +import React from "react"; + +import { render } from "./PermalinkViewQuery"; + +it("renders permalink view container", () => { + const data = { + props: { + asset: {}, + comment: {}, + } as any, + error: null, + }; + const wrapper = shallow(React.createElement(() => render(data))); + expect(wrapper).toMatchSnapshot(); +}); + +it("renders loading", () => { + const data = { + props: null, + error: null, + }; + const wrapper = shallow(React.createElement(() => render(data))); + expect(wrapper).toMatchSnapshot(); +}); + +it("renders error", () => { + const data = { + props: null, + error: new Error("error"), + }; + const wrapper = shallow(React.createElement(() => render(data))); + expect(wrapper).toMatchSnapshot(); +}); diff --git a/src/core/client/stream/queries/PermalinkViewQuery.tsx b/src/core/client/stream/queries/PermalinkViewQuery.tsx new file mode 100644 index 000000000..b37cdaaf5 --- /dev/null +++ b/src/core/client/stream/queries/PermalinkViewQuery.tsx @@ -0,0 +1,68 @@ +import * as React from "react"; +import { StatelessComponent } from "react"; +import { ReadyState } from "react-relay"; + +import { + graphql, + QueryRenderer, + withLocalStateContainer, +} from "talk-framework/lib/relay"; +import { + PermalinkViewQueryResponse, + PermalinkViewQueryVariables, +} from "talk-stream/__generated__/PermalinkViewQuery.graphql"; +import { PermalinkViewQueryLocal as Local } from "talk-stream/__generated__/PermalinkViewQueryLocal.graphql"; + +import PermalinkViewContainer from "../containers/PermalinkViewContainer"; + +interface InnerProps { + local: Local; +} + +export const render = ({ + error, + props, +}: ReadyState) => { + if (error) { + return
{error.message}
; + } + if (props) { + return ( + + ); + } + return
Loading
; +}; + +const PermalinkViewQuery: StatelessComponent = ({ + local: { commentID, assetID }, +}) => ( + + query={graphql` + query PermalinkViewQuery($assetID: ID!, $commentID: ID!) { + asset(id: $assetID) { + ...PermalinkViewContainer_asset + } + comment(id: $commentID) { + ...PermalinkViewContainer_comment + } + } + `} + variables={{ + assetID, + commentID, + }} + render={render} + /> +); + +const enhanced = withLocalStateContainer( + graphql` + fragment PermalinkViewQueryLocal on Local { + assetID + commentID + } + ` +)(PermalinkViewQuery); + +export default enhanced; diff --git a/src/core/client/stream/queries/AppQuery.spec.tsx b/src/core/client/stream/queries/StreamQuery.spec.tsx similarity index 83% rename from src/core/client/stream/queries/AppQuery.spec.tsx rename to src/core/client/stream/queries/StreamQuery.spec.tsx index a301a76b7..91a1a768c 100644 --- a/src/core/client/stream/queries/AppQuery.spec.tsx +++ b/src/core/client/stream/queries/StreamQuery.spec.tsx @@ -1,11 +1,13 @@ import { shallow } from "enzyme"; import React from "react"; -import { render } from "./AppQuery"; +import { render } from "./StreamQuery"; -it("renders app", () => { +it("renders stream container", () => { const data = { - props: {} as any, + props: { + asset: {}, + } as any, error: null, }; const wrapper = shallow(React.createElement(() => render(data))); diff --git a/src/core/client/stream/queries/StreamQuery.tsx b/src/core/client/stream/queries/StreamQuery.tsx new file mode 100644 index 000000000..40d57387e --- /dev/null +++ b/src/core/client/stream/queries/StreamQuery.tsx @@ -0,0 +1,58 @@ +import * as React from "react"; +import { StatelessComponent } from "react"; +import { ReadyState } from "react-relay"; + +import { + graphql, + QueryRenderer, + withLocalStateContainer, +} from "talk-framework/lib/relay"; +import { + StreamQueryResponse, + StreamQueryVariables, +} from "talk-stream/__generated__/StreamQuery.graphql"; +import { StreamQueryLocal as Local } from "talk-stream/__generated__/StreamQueryLocal.graphql"; + +import StreamContainer from "../containers/StreamContainer"; + +interface InnerProps { + local: Local; +} + +export const render = ({ error, props }: ReadyState) => { + if (error) { + return
{error.message}
; + } + if (props) { + return ; + } + return
Loading
; +}; + +const StreamQuery: StatelessComponent = ({ + local: { assetID }, +}) => ( + + query={graphql` + query StreamQuery($assetID: ID!) { + asset(id: $assetID) { + ...StreamContainer_asset + } + } + `} + variables={{ + assetID, + }} + render={render} + /> +); + +const enhanced = withLocalStateContainer( + graphql` + fragment StreamQueryLocal on Local { + assetID + } + ` +)(StreamQuery); + +export default enhanced; diff --git a/src/core/client/stream/queries/__snapshots__/PermalinkViewQuery.spec.tsx.snap b/src/core/client/stream/queries/__snapshots__/PermalinkViewQuery.spec.tsx.snap new file mode 100644 index 000000000..85a702a9a --- /dev/null +++ b/src/core/client/stream/queries/__snapshots__/PermalinkViewQuery.spec.tsx.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders error 1`] = ` +
+ error +
+`; + +exports[`renders loading 1`] = ` +
+ Loading +
+`; + +exports[`renders permalink view container 1`] = ` + +`; diff --git a/src/core/client/stream/queries/__snapshots__/AppQuery.spec.tsx.snap b/src/core/client/stream/queries/__snapshots__/StreamQuery.spec.tsx.snap similarity index 65% rename from src/core/client/stream/queries/__snapshots__/AppQuery.spec.tsx.snap rename to src/core/client/stream/queries/__snapshots__/StreamQuery.spec.tsx.snap index dbd2ad45a..317eb3f4a 100644 --- a/src/core/client/stream/queries/__snapshots__/AppQuery.spec.tsx.snap +++ b/src/core/client/stream/queries/__snapshots__/StreamQuery.spec.tsx.snap @@ -1,11 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders app 1`] = ` - -`; - exports[`renders error 1`] = `
error @@ -17,3 +11,9 @@ exports[`renders loading 1`] = ` Loading
`; + +exports[`renders stream container 1`] = ` + +`; diff --git a/src/core/client/stream/test/__snapshots__/loadMore.spec.tsx.snap b/src/core/client/stream/test/__snapshots__/loadMore.spec.tsx.snap index 688e88d38..619f3269d 100644 --- a/src/core/client/stream/test/__snapshots__/loadMore.spec.tsx.snap +++ b/src/core/client/stream/test/__snapshots__/loadMore.spec.tsx.snap @@ -7,11 +7,6 @@ exports[`loads more comments 1`] = `
-

- Talk NEO -

Joining Too

+
What's up?

+
Hey!

+
@@ -146,11 +150,6 @@ exports[`renders comment stream 1`] = `
-

- Talk NEO -

Joining Too

+
What's up?

+
+
+
+
+ + Markus + + +
+

+ Joining Too +

+
+
+
+
+
+`; + +exports[`show all comments 1`] = ` +
+
+ +
+